vue知识点

1. VUE特点

  • 轻量
    使用gzip最小压缩后有20kb,react有35kb,angular有60kb

  • 渐进式框架
    你不需要完全了解他,就可以用,各取所需,只是个轻量视图而已

  • 响应式的更新机制
    数据改变后,视图会自动更新,而不像react那样用shouldComponentUpdate进行性能优化

  • 学习成本低
    模板语法是基于html,容易上手

2. 生命周期

Vue 实例生命周期(来源官网)

Vue的所有生命周期函数都是自动绑定到this的上下文上,所以不需要用到箭头函数。
页面加载大致过程:

  • beforeCreate:无法获取到propdata
  • created: 经过数据初始化,此时可以获取prop和data,但是组件尚未挂载;
  • beforeMount: 开始创建虚拟dom,如果组件中有子组件,先递归挂载子组件,再挂载根组件的
  • mounted: 将虚拟dom渲染为真实dom。
  • 数据更新
    更新前:beforeUpdates
    更新后: updated
  • 页面销毁
    销毁前:beforeDestory
    销毁后: destoryed

4. Vue 的父组件和子组件生命周期钩子函数执行顺序

(1)加载渲染过程
beforeCreated(父) ->>created(父)->> beforeMounted(父) ->>
子组件渲染:beforeCreated(子) ->> created(子)->> beforeMounted(子) ->> mounted(子)
--> mounted(父)
(2)子组件更新
beforeUpdate(父) --> beforeUpdate(子) -> updated( 子) -> updated(父)
(3)父组件更新
beforeUpdate(父) --> updated(父)
(4)销毁
beforeDestroy(父) -> beforeDestroy (子)-> destroyed(子) -> destroyed(父)

3. vue基本指令

  • v-show
    当条件为true时候display: block,否则是display: none;无论什么条件都会被渲染出来,渲染之后只是css属性的切换,渲染时候有一定的开销,适合频繁切换的场景。

  • v-if
    根据条件来渲染,如果是false,那一开始就不会渲染,但是切换条件时会触发销毁和挂载,所以在切换时候开销比较大,不适合频繁切换的场景

  • v-for

<li v-for="(item, index) in items" :key="item.message">
    {{index}} {{ item.message }}
 </li>
  • v-model

v-model其实是一个语法糖

v-model 会忽略所有表单元素的 valuecheckedselected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

<input v-model="name"></input>
//等价于
<input 
    v-bind:value="name"
    v-bind:input="name = arguments[0]"
></input>

就是一种双向绑定的过程,视图影响数据,数据影响视图,说明视图是可交互的,一般和表单有关系

  • v-on简写@ 绑定事件
  • v-bind:简写: 绑定属性
  • v-text显示文本
  • v-pre不编译双花括号,直接显示字符串
  • v-html可以显示html字符串

4. 组件的三大核心概念

(1)属性

​ ① 自定义属性props,表示组件props中声明的属性
​ ②原生属性,比如<Test title="测试"></Test>,默认在组件中的根元素上就会有title="测试"的属性;
​ 在Test组件中设置inheritAttrs: false就可以关闭自动挂载
​ ③特殊属性,style, class挂载到组件根元素上

(2)事件

①普通事件

- @click
- @change
- @input
- @xxx

子组件通过this.$emit("xxx", 参数1, 参数2, 参数...)

②修饰符事件

  • @input.trim (去掉两端空格)

  • @click.stop(阻止冒泡事件或者在事件中使用e.stopPropagation)

  • @submit.prevent(去除默认事件)

    一般用于原生html的行为

(3)插槽

​ ① 普通插槽

//老语法
<template slot="xxx"> ...<template>
//2.6版本语法
<template v-slot:xxx> ...<template>

② 作用域插槽
可以获取属性

<template slot="xxx" slot-scope="props">{{props}}</template>
<template v-slot:xxx="props">{{props}}</template>

组件:

<template>
    <div>
        <slot name="xxx" />
        <slot v-bind="{value: 'value'}" />
    </div>
</template>

5. MVVM和MVC模式的区别

  • MVC

MVC是最经典的模型,即Model(模型)、Controller(控制器)、View(视图)

当用户操作页面时,view层会发出指令到controller,controller去通知model更新数据。一旦model发生改变,就通知视图更新。

  • MVVM

MVVMModel-View-ViewModel,实现了view和model的自动同步,ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来。VM起到了双向绑定的作用,View(视图) 和 Model(数据) 无法直接通讯,在它们之间存在着 ViewModel 中间介充当观察者角色。当用户操作 View(视图),ViewModel 感知到变化,然后通知 Model 发生相应改变;反之当 Model(数据) 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。

6. 单向数据流和双向绑定

双向绑定:
model更新会触发view更新;
view的更新会触发model更新;
单向数据流:
model更新会触发view更新;
view的更新和model没有关系

vue是单向数据流,不是双向绑定,vue的双向绑定不过是语法糖,而Object.defineProperty是用来做响应式更新的,对属性设置set函数,当属性改变时候,就会触发这个函数
实现过程:

vue自定义v-model

一个组件的v-model默认会有prop: value,event: input,但是我们可以自定义

//定义子组件
Vue.component('Child', {
    model: {
        prop: 'checked',
        event: 'change'
    },
    props: {
        checked: Boolean
    },
    template: <input type="checkbox" :checked="checked" @change="$emit('change',            $event.target.checked)">
})
//在父组件中使用
<Child v-model="data" />

此时data的值会被传递到子组件中,为checked属性

7. vue中sync修饰符

.sync是为了方便父子之间通信,当子组件想要改变父组件传递进来的属性时,比如一个控制modal是否显示的属性,子组件就要使用$emit('xxx', 'xxx')

vue提供update: propertyName模式触发事件

//子组件更改属性
this.$emit("update:visible", false)
//父组件
<Child :visible="show" @update:visible="val => show = val" />

那么为了简化写法,出现了sync修饰符

//子组件更改属性
this.$emit("update:visible", false)
//父组件
<Child :visible.sync="show" />

父组件不需要定义,默认存在@update:visible="val => show = val"

官方文档

8. 组件更新

组件更新的条件

①属性需要在data里面声明,这样才能做响应式
②如果data中声明一个对象

data() {
    return {
        info: {}
    }
}
//这种不会触发更新
this.info.name = "a"

修改为:

data() {
    return {
        info: { name: undefined }
    }
}

③如果在模板中没有用到某个属性,那么修改这个属性也不会触发组件更新
④如果data中有一个数组属性
直接push也会触发组件更新

那么vue是如何进行组件更新的呢?

当vue进行实例化时候,会使用Object.defineProperty对data中的属性设置set和get方法,相当于代理层;
将模板中需要用到的属性放到watch,如果修改了属性,在set函数中会去通知watch,然后watch去触发组件的更新。


9. compute和watch属性的区别

计算属性compute

作用:
① 减少模板中计算逻辑
② 缓存数据
③ 依赖固定的数据类型,不会重新再计算,只有在数据变化时候才会计算
对比:

<p>{{reverseStr}}</p>
<p>{{reverseStr2}}</p>
computed: {
    reverseStr: function() {
        this.str.split("").reverse.join("")
    }
}
data() {
    reverseStr2: function() {
        this.str.split("").reverse.join("")
    }
}

$forceUpdate刷新页面,str没变化时候,reverseStr不会执行计算,reverseStr2会被调用

监听属性watch

监听数据的变化

data() {
    a:1,
    b: {
        c: 2
    },
    d: {
        e: {
            f: 3
        }
    }
}
//各个属性监听方式,
watch: {
    a(val, old) {}, 
    "b.c": function(val, old),
    // d及以下的所有属性都会被监听,deep表示深度监听;immediate表示是否在第一次渲染就执行这个监听
    d: {
        handler: function(val, old) {},
        deep: true,
        immediate: true
    }
}

如果一个数据需要经过复杂计算就用 computed;
如果一个数据需要被监听并且对数据做一些操作就用 watch

10. vue组件通信方式

①props和$emit父子组件通信

这个是最基础的组件通信,父组件通过v-bind:xxx="xx"传递属性,v-on:xxx="xx"绑定事件;
子组件通过props获取,$emit触发父组件传递的事件。

//父组件
<template>
  <div>
    <Child :msg="message" @changeStates="change"></child>
  </div>
</template>

//子组件
<template>
  {{msg}}
  <button @click="clickHandle">点击</button>
</template>
<script>
  import Child from "./Child";
  export default {
    name: "Child",
    props: ["msg"],
    components:{
        Child
    },
    methods: {
      clickHandle() {
        this.$emit("changeStates", "变了");
      }
    }
  };
</script>

②ref

ref如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

//父组件
<template>
  <div>
    <child ref="son"></child>
  </div>
</template>
//子组件
<template>
  <div> 
    <input ref="inputRef" type="text" value="hi"></child>
  </div>
</template>
// 获取子组件的属性和方法
//获取子组件input框的内容
this.$refs.son.$refs.inputRef.value

parent,children
直接使用,但是建议少用
父组件:

<template>
  <div>
    <child></child>
  </div>
</template>
<script>
    export default {
     name: 'parent',
     data: () => ({
       msg: "Hi, I am your father"
     }),
     mounted() {
        console.log(this.$children[0].msg)
     }
    }
</script>

子组件:

<template>
  <div>Hello Boy</div>
</template>

<script>
  export default {
    name: "child",
    data: () => ({
      msg: "hi"
    }),
    mounted() {
      console.log(this.$parent.msg); //
    }
  };
</script>

④EventBus中央数据总线,emit/on

通过创建一个空的vue实例作为中央事件总线,用于触发和监听事件,类型于addListener

attrs和listeners隔代组件通信

$attrs 里存放的是父组件中绑定的非 Props 属性;

$listeners里存放的是父组件中绑定的非原生事件;

// 父组件
<template>
  <div>
    <child-a
      :name="name"
      :age="age"
      :job="job"
      title="This is a title"
      @click="post"
    ></child-a>
  </div>
</template>

// 子组件child-a中
<template>
  <div>
    <child-b v-bind="$attrs" v-on="$listeners"></child-b>
  </div>
</template>
this.$attrs 
// {name: "name", age: "28", job: "worker", title: "This is a title"}
this.$listeners.click()
// hello

// 子组件child-b中
<template>
  <div>
    <p>B-listeners: {{ this.$listeners.click() }}</p>
  </div>
</template>
<script>
  export default {
    props: ["name"], // name 作为props属性绑定
    inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
    created() {
      console.log(this.$attrs);
      // {age: "28", job: "worker", title: "This is a title"}
      console.log(this.$listeners.click()); // hello
    }
  };
</script>

⑥provide和inject

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。

provide 和 inject 主要在开发高阶插件/组件库时使用

除此之外,还有vuex状态管理,如下所示

参考资料

11. Vuex状态管理

vuex类似于react的redux,当开发大型单页应用,多个视图组件依赖同一个状态,比如登录状态,用户信息等。当状态更新时候,这些使用过状态的组件都要更新。针对这种情况,可以使用vuex来解决。

特点:

(1)状态存储是响应式的,组件从store中读取状态,如果状态发生变化,则会自动更新到组件
(2)不能直接修改store中的状态

使用方式:

import vue from 'vue';
import vuex from 'vuex';

const store = new Vuex.Store({
    state: {},
    getters: {}
    mutations: {},
    actions: {}
})
// 通过根组件直接注入,这样所有的组件都可以使用store
new Vue({
    e1: "#app",
    store
})

组成部分:

  • state

作为模块唯一数据源,包含了所有的状态,访问state方式:

this.$store.state.xxx

mapState简化方式访问:

import { mapState } from 'vuex';
export default {
    computed: mapState({
        count: state => state.count,
        myCount: 'count',
        // 和当前的数据操作
        add(state) {
            return state.count + this.nowCount
        }
    })
}

store中的变量名和当前组件中要使用的变量名相同

import { mapState } from 'vuex';
export default {
    computed: mapState([
        'count'
    ])
}

最常用的就是对象展开符的方式:

import { mapState } from 'vuex';
export default {
    computed: {
        ...mapState([
            'count'
        ])
        other(){}
    }
}
  • getters

当我们不满足直接使用store中state数据,比如筛选计算等处理,如果多个组件都需要这种,那么最好的方式是在store中定义:

getters: {
    toDo : state => {
        return state.toDo.filter(i => i.toDoThings) 
    }
}

视图中访问方式:

this.$store.getters.toDo

mapGetters简化的方式:

import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters([
      'tODo',
    ])
  }
}
//访问方式: this.tODo
  • mutation

这个用于修改state属性,调用的必须是同步的事件

mutations: {
    increment(state, num) {
        state.count = state.count + num
    }
}

调用方式:

this.$store.commit('increment', 10)

mapMutations简化方式:

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}
  • action

类似于mutation,可以提交mutation修改state

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
  //或者通过参数解构
  actions: {
      increment ({ commit }) {
        commit('increment')
      }
  }
})

处理异步的业务

actions: {
      increment ({ commit, state }, payload) {
        // ...
      }
  }

触发:

this.$store.dispatch('increment')

mapAtions简化方式

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

11.前端路由控制的两种方式以及区别

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342