Vue 响应式

vue响应式

1.data变化

1.1 尝试在外部改变data的值

import Vue from "vue";

Vue.config.productionTip = false;
const myData = {
  n:0
}
console.log(myData)
const vm = new Vue({
  data:myData,
  template:`
{{ n }}
` }).$mount("#app"); setTimeout(()=>{ myData.n += 100 console.log(myData) console.log(vm) },2000)

2秒后myData.n也变为了100,页面上也渲染的是100,但是第7行打印的myData纯粹是一个只含有n的对象,
在15行的时候,myData含有了一堆原本没有的属性


image.png

这是因为使用Object.defineProperty进行了代理,让vm知道data改变,从而进行重新render。

1.2 defineProperty控制getter和setter

let myData1 = {n:0}
let data1 = proxy2({ data:myData1 }) // 括号里是匿名对象,无法访问

function proxy2({data}){
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })
  
  return obj // obj 就是代理
}

console.log(`${data1.n}`)
myData1.n = -1
console.log(`${data1.n},设置为 -1 失败了`)
myData1.n = 1
console.log(`${data1.n},设置为 1 成功了`)

首先使用defineProperty代理myData1,控制它的'n'的 getter和setter,然后创建一个新的对象obj,也用defineProperty控制它的'n',然后返回obj。此时即使用户直接修改原始的myData1的值也无法成功。

1.3 vm = new Vue({data:myData})

1. 会让vm成为myData的代理

2. 会对myData的所有属性进行监控,知道myData变化后进行render操作

Object.defineProperty存在问题

1.1 必须存在obj.n才能监听obj.n

假如之前不存在n,但是在页面上使用n,Vue会给出警告。

const vm = new Vue({
  data:{
    obj:{
      a:1
    }
  },
  template:`
{{ obj.b }}
` }).$mount("#app");

但是Vue只会监听第一层,如上图,假如用户只声明了obj.a,但是在页面上使用obj.b,此时Vue不会给出警告,
页面上也无法展示obj.b.** 如果是新增的属性要使用Vue.set才能生效**
**
解决方法:
1.提前声明好需要的key,后面不再添加属性
2.使用Vue.set或者this.$set

Vue.set和this.$set作用

1.新增key
2.创建代理和监听
3.触发UI更新

1.2 数组的变异

数组无法一开始就声明所有需要的下表,假如全部使用Vue.set会非常麻烦。
Vue会拦截数组,将push(),pop(),shift(),unshift(),splice(),sort()以及reverse()修改,自动执行Vue.set操作,然后更新render。

大致实现:

es6实现

class VueArray extends Array{
    push(...args){
        const oldLength = this.length // this
        super.push(...args)
        console.log(' push ')
        for(let i = oldLength; i

原型链的实现:

const vueArrayPrototype = {
    push: function(){
        return Array.prototype.push.apply(this, arguments)
    }
}

vueArrayPrototype.__proto__ = Array.prototype
const array = Object.create(vueArrayPrototype)
array.push(1)

你可能感兴趣的:(Vue 响应式)