vue学习笔记——持续更新中

204 阅读5分钟

一、Vue2.0响应式原理

下面是vue官方文档的内容,根据我个人的阅读思维习惯,做了些许的排版调整,便于以后自己时常回头梳理消化。
另外,推荐反复看这个文章,反复看!剖析Vue实现原理 - 如何实现双向绑定mvvm

1、如何追踪变化

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 为这些 property 设置 getter/setter函数。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

2、检测变化的注意事项

  • 由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上初始存在,才能让 Vue 将它转换为响应式的。
  • 对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。

3、不能检测数组和对象的变化

(1)对于对象

Vue 不能检测对象新增的key变化,例如:vm.someObject.b = 2
可以使用 Vue.set(object, propertyName, value) 方法或者使用 vm.$set 实例方法,向对象添加响应式 property。

Vue.set(vm.someObject, 'b', 2)
this.$set(this.someObject,'b',2)
  • 有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })` 
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

(2)对于数组

Vue 不能检测以下数组的变动:

  • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    其实Object.defineProperty是可以检测到通过索引改变数组的操作的,那Vue2.0为什么没有实现呢?主要是考虑性能问题,避免数组中元素过多,进行大量遍历。具体分析文章参考:vue为什么不能检测数组的变化
  • 当你修改数组的长度时,例如:vm.items.length = newLength
    为了解决第1个问题,有以下两种方式:
    set方法
    Vue.set(vm.items, indexOfItem, newValue)
    vm.$set(vm.items, indexOfItem, newValue)
    splice方法
    vm.items.splice(indexOfItem, 1, newValue)
    为了解决第2类问题,你可以使用 splice:
    vm.items.splice(newLength)

4、异步更新队列——$nextTick()

Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,在更新 DOM 时是异步执行的.简单来说,Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)或者vm.$nextTick(callback) 实例方法,这样回调函数callback将在 DOM 更新完成后被调用。
另外,可以阅读这个文章,学习一下 Vue.nextTick 的原理和用途

// html
<div id="example">{{message}}</div>


// js  
// 1、使用Vue.nextTick方法
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

// 2、使用vm.$nextTick实例方法
// 在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})

二、唯一key值的作用

1、key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
为什么 Vue 中不要用 index 作为 key?(diff 算法详解)
为什么使用v-for时必须添加唯一的key?

当我们在使用v-for时,需要给单元加上key

如果不用key,Vue会采用“就地更新”策略:最小化element的移动,并且会尝试尽最大程度在同适当的地方对相同类型的element,做patch或者reuse。

如果使用了key,Vue会根据keys的顺序记录element,曾经拥有了key的element如果不再出现的话,会被直接remove或者destoryed

2、不建议用index作为v-for循环的key
对于删除、翻转、插入等会改变原数组元素顺序的场景,index作为key的话,会导致更新dom的开销很大
3、不能用random()随机数作为v-for循环的key
明明是相同的节点,却因为key是2次随机数被判定为不同的节点,节点会经历销毁、重新创建的过程,开销简直就是毁灭性的

三、vue中的attrs、listeners、inheritAttrs




1、inheritAttrs属性和attrs实例方法的参考文章# Vue 2.4.0新增inheritAttrs,attrs详解
2、attrslisteners实例方法的参考文章# attrs与listeners的详解

四、组件上使用.sync和v-model的实现原理

1、.sync修饰符

在子组件通过以update:myPropName 的模式触发事件,在父组件监听这个事件并更新一个本地的数据 property。举例:

//子组件   包含名为title的prop,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', myNewTitle)

//父组件
<father
  v-bind:title="myTitle"
  v-on:update:title="myTitle = $event"
></father>

为了方便起见,我们为这种模式提供一个缩写,即 .sync 修饰符:
<father v-bind:title.sync="myTitle"></father>

2、v-model(本质上只是语法糖)

(1)表单 <input><textarea><select> 元素上使用v-model
v-model在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。
`<input v-model = 'something'>`
相当于
<input v-bind:value="something" v-on:input="something=$event.target.value">

(2)自定义组件上使用v-model
在组件上v-model 默认会利用名为 value 的 prop 和名为 input 的事件,也即:
第1步:父组件通过v-bind将本地数据绑定给子组件的一个名为value的prop
第2步:子组件的value值发生变化,则通过emit抛出一个名为input的事件
第3步:父组件通过捕获input事件,将本地数据的值更新为最新的value的值

<custom-input v-model="searchText"></custom-input>

// 相当于

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

//custom-input组件的代码需这样编写

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

五、待更

1、