美文网首页
Vue 响应式

Vue 响应式

作者: 西域战神 | 来源:发表于2021-01-17 15:32 被阅读0次

    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:`<div>{{ n }}</div>`
    }).$mount("#app");
    
    setTimeout(()=>{
      myData.n += 100
      console.log(myData)
      console.log(vm)
    },2000)
    

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


    image.pngimage.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:`<div>{{ obj.b }}</div>`
    }).$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<this.length; i++){
                Vue.set(this, i, this[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 响应式

          本文链接:https://www.haomeiwen.com/subject/chnaaktx.html