• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

vue中的响应式数据

开发技术 开发技术 1周前 (05-04) 3次浏览

很早之前就了解了数据劫持现,重温一下源码又有了新的理解主要还是了解里面的设计思想观察者模式

import Vue from 'vue'

let vm = new Vue({
  el: '#app',
  data () {
    return {
      title: '学生列表',
      classNum: 1,
      info:{
        a:{
          b: 1
        }
      },
      teachers: ['张三','李四'],
      students: [
        {
          id: 1,
          name:'小红'
        },
        {
          id: 2,
          name: '小明'
        }
      ]
    }
  }
})

写项目写多了感觉没提升就得看看源码,提高下自己的素养。经常这样写vue2.0,new出一个vue对象里面是一个对象里面有很多的options例如data,computed等 vue2.0实际上都是一些options api,这样的api对于开发来说能够做的选择却不是特别多所以在3.0中将大部分都改成了composition api让开发人员有了更多的选择很大一部和react类似在3.0中的数据劫持用了es6中的Proxy中的新方法很大程度上优化了2.0中的响应式数据。在2.0当中vue底层用到的defineProperty对对象进行了处理这个方法不能对数组进行一个劫持,所以重写数组的几种方法,对于数组在原型上重新定义了所有的方法,会造成代码臃肿,现在大部分的浏览器都支持es6所以在vue3中直接使用了es6的Proxy方法对于项目性能来说是很大的提升。
来了解下vue2中最基本的响应式数据理念 对于data中的数据进行了下面方式的处理

import proxyData from './proxy'
import observe from './observe'
function initState (vm) {
  var options = vm.$options
  if(options.data) {
    initData(vm)
  }
}

function initData (vm) {
  var data = vm.$options.data
  data = vm._data = typeof data === 'function' ? data.call(vm) : data || {}
  for (var key in data){
    proxyData(vm, '_data', key)
  }
  observe(vm._data)
}

export {
  initState
}

function proxyData (vm, target, key) {
  Object.defineProperty(vm, key, {
    get () {
      // console.log('proxy data: ' + vm[target][key])
      return vm[target][key]
    },
    set (newValue) {
      vm[target][key] = newValue
    }
  })
}

export default proxyData

这就是为什么我们写的数据是一个函数为什么能用vm.title访问到我们的标题。
在初始化函数之中直接对这个函数进行条用并且把这个值赋给了vm._data,在对整个数据进行一层代理用到了defineProperty这个es5中的方法只要我们去访问就会走到get这个函数。但是这样坐下来我们只是对于我们的data中的第一层数据进行了响应式处理。 我们来刨析这个问题如何让所有数据都能够获得响应式,数据的情况可能有一下几种,data({})中的这个对象里面可能有数组我们需要对这个数组的方法重写,数组里面还有对象,对象里面有对象嵌套多种情况,只需要递归对于整个数据进行分类我们就能够对于整体数据进行响应式绑定,利用到观察者模式

import Observer from './observer'
function observe (data) {
  if(typeof data !== 'object' || data === null) return
  return new Observer(data)
}

export default observe


import defineReactiveData from './reactive'
import { arrMethdos } from './array'
import observeArr from './observeArr'

function Observer (data) {
  if(Array.isArray(data)){
    data.__proto = arrMethdos
    observeArr(data)
  }
  else {
    this.walk(data)
  }
}

Observer.prototype.walk = function (data) {
  var keys = Object.keys(data)
  // console.log(keys)
  for (var i = 0; i < keys.length; i++){
    var key = keys[i],
        value = data[key]
    defineReactiveData(data, key, value);
  }
}

export default Observer

暂时不去分析是函数的情况 我们首先对于非数组进行讨论,源码中用到walk这个方法就是一个简单的循环遍历 只是给封装了起来我们能够取到key值value那么我们就能够直接进行一个defineReactiveData操作(一个definePropertyde的扩展性封装)

function defineReactiveData (data, key, value){
  observe(value);
  Object.defineProperty(data, key, {
    get () {
      console.log('响应式获取', value)
      return value;
    },
    set (newValue) {
      // console.log('响应式设置', newValue)
      if(newValue === value) return
      observe(newValue)
      value = newValue
    }
  })
}

这样递归我们就能够是整个过程都能够变成响应式数据。接下来就剩下数组的处理重写数组的7个方法


var ARR_METHODS = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]


import { ARR_METHODS } from './config'
import observeArr from './observeArr'

var originArrMethods = Array.prototype,
    arrMethdos = Object.create(originArrMethods)
ARR_METHODS.map(function (m) {
  arrMethdos[m] = function () {
    var args = Array.prototype.slice.call(arguments),
        rt = originArrMethods[m].apply(this, args)
    var newArr
    switch (m) {
      case 'push':
      case 'unshift':
        newArr = args;
        break
      case 'splice':
        newArr = args.slice(2)
        break
      default:
        break;
    }
    newArr && observeArr(newArr)
    return rt
  }
})

function observeArr (arr) {
  for (var i = 0; i < arr.length; i++){
    observe(arr[i])
  }
}

我们重写这些数组方法后得到的新数据我们还要进行遍历对于里面的所有数据还要加上一层响应式

这就是整个vue2中数据劫持的整理过程难点主要还是在对于数组的处理上 es6中的Proxy的引入可以使得整个的代码可读性更强。
个人在对于VUE学习的小笔记,弱鸡一个


程序员灯塔
转载请注明原文链接:vue中的响应式数据
喜欢 (0)