VUE响应式原理

VUE响应式原理

一、vue2的数据响应式原理

1. 什么是defineProperty

其实是定义对象的属性,并不是核心的微一个对象做数据双向绑定,而是去给对象做属性标签,只不过属性里的get和set实现了响应式。

属性名默认值说明
valueUndefined
GetUndefined取值
SetUndefined设置值
WritableFalse可写
EnumerableFalse可遍历
ConfigurableFalse可枚举

defineProperty的使用

let ob = {a: 123, b: 456}
let _value = ob.a  // 需要额外变量去存储值
Object.defineProperty(ob, "a", {
  writable: false,
  enumerable: false,
  configurable: false,
  get: () => {
    console.log("you get a number")
    return _value
  },
  set: (val) => {
    console.log("you set a number")
    _value = val
  }
})
console.log(Object.getOwnPropertyDescriptor(ob, "a"))

ob.a = 111;
console.log(ob.a)  // 123

for (let item in ob) {
  console.log(item)  // 只能打印出 b 属性
}

2. 双向绑定实现

2.1vue中从改变一个数据到发生改变的过程

数据改变出发set --> set部分出发notify --> 更改对应的虚拟dom --> 更新render

初始化 --> get部分手机依赖

2.2 代码实现

function vue() {
  this.$data = {a: 1};  // 写死了,不是传参,简化
  this.el = document.getElementById('app');
  this.virtualdom = '';  // 虚拟dom
  this.observe(this.$set)  // 将数据进行get/set处理
  this.render(); // 初始化页面
}
vue.prototype.observe = function(obj) {
  let value, self = this;
  for (let item in obj) {
    value = obj[item]
    if (typeof value === 'object') {
      this.observe(value)
    } else {
      Object.defineProperty(ob, item, {
        get: function() {
          // 依赖收集:收集这个属性,在哪几个组件使用
          return value
        },
        set: function(newvalue) {
          // 触发更新
          value = newvalue
          selt.render()
        }
      })
    }
  }
}

vue.prototype.render = function() {
  this.virtualdom = "i am" + this.$data.a  
  this.el.innerHTML = this.virtualdmo
}

数组更新

let arrayPro = Array.protorype
let arrayob = Object.create(arrayPro)
let arr = ['push', 'pop', 'shift', 'unshift']  // 触发数组更新的方法
arr.forEach((method, index) => {
  arrayob[method] = function() {
    let ret = arrayPro[method].apply(this, arguments)
    dep.notify()  // 触发更新
    return ret;
  }
})
// 最后将数组的prototype修改一下

二、vue3的数据响应式原理

1. 什么是proxy

proxy对象用于定义基本操作的自定义行为,和defineProperty类型功能几乎一样,只不过用法上有些不同。

2. 代码实现

let ob = {a: 1}
let obproxy = new Proxy(ob, {
  get: function(target, key, receiver) {
    return target[key]  // 可通过此方式直接取值,而不是还要需要中间的一个变量
  },
  set: function(target, key, value, receiver) {
    // target[key] = value
    return Reflect.set(target, key, value)
  },
})

defineProperty:是去改变原对象的,只能监听某个属性,不能对全对象监听(还要递归判断属性是否是对象)

proxy:是去新建一个代理对象,并不会改变原对象;可以省去for in提升效率,可以监听数组,不用再去单独的对数组做特异性操作。

function vue() {
  this.$data = {a: 1};  // 写死了,不是传参,简化
  this.el = document.getElementById('app');
  this.virtualdom = '';  // 虚拟dom
  this.observe()  // 将数据进行get/set处理
  this.render(); // 初始化页面
}
vue.prototype.observe = function() {
  let self = this
  this.$data = new Proxy(this.$data, {
    get: function(target, key, receiver) {
      return target[key]  // 可通过此方式直接取值,而不是还要需要中间的一个变量
    },
    set: function(target, key, value, receiver) {
      target[key] = value
      selt.render()
    },
  })
}

vue.prototype.render = function() {
  this.virtualdom = "i am" + this.$data.a  
  this.el.innerHTML = this.virtualdmo
}

3. 扩展:proxy还能做什么

3.1 类型校验
// 策略模式:新建一个策略对象,存放校验规则
let validtor = {
  name: function(value) {
    let reg = /^[\u4e00-\u9fa5]+$/;
    if (typeof value === 'string' && reg.test(value)) {
     return false
    }
    return true
  }
}

function person(name, age) {
  this.age= age
  this.name = name
  return new Proxy(this, {
    get: function(target, key) {},
    set: function(target, key, value) {
      if (validtor[key](value)) {
        return Reflect.set(target, key, value)
      } else {
        throw new Error(key + 'is not right')
      }
    }
  })
}
3.2 真正的私有变量

例如:this.$route不能进行更改,比如设置defineProperty只定义get不定义set,就无法进行set从而实现;

三、diff算法和virtual dom

1. 虚拟DOM

用js去虚拟的描述dom元素,例如:

<template>
  <div class="demo">
    <p>skdjfskdf</p>
  </div>
</template>
let ob = {
    el: "div",
    props: {class: "demo"},
    text: "",
    children: [{
      el: 'p',
      props: {},
      text: 'skdjfskdf',
      children: []
    }]
}

2. diff算法

计算虚拟dom是否有更新

patchVnode(oldVnode, vnode) {
  const el = vnode.el = oldVnode.el;
  let i, oldCh = oldVnode.children, ch = vnode.children;
  // 如果没有更新,则直接return
  if (oldVnode === vnode) return;
  // 如果是文字内容更新,则调用更新内容的方法
  if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
    api.setTextContent(el, vnode.text)
  } else {
    // 如果是子节点更新,调用方法更新dom节点
    updateEle(el, vnode, oldVnode)
    if (oldCh && ch && oldCh !== ch) {
      updateChildren()  // 节点都存在,则表示子节点有变动
    } else if (ch) {
      createEle(vnode)  // 如果新子节点存在,旧不存在,则创建
    } else {
      api.removeChildren(el)  // 如果旧存在,新不存在,则删除
    }
  }
}

3. vue性能优化

比较尖端的操作:ssr,app混合reata-native

给项目构建工具链流程(初始化-测试-规范-构建,如vue-cli),给项目构建有一套基础设施(项目的工具库,组件库,插件库)

各种的优化实践

源码:axios,vue-router,redux

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值