好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

手写Vue2.0 数据劫持的示例

一:搭建webpack

简单的搭建一下webpack的配置。新建一个文件夹,然后init一下。之后新建一个webpack.config.js文件,这是webpack的配置文件。安装一下简单的依赖。

?

npm install webpack webpack-cli webpack-dev-server -D

在同级目录下新建一个public/index.html和src/index.js,作为出口文件和入口文件。

j简单配置一下webpack, 在webpack.config.js文件中:

?

const path = require( 'path' );

const HtmlWebpackPlugin = require( 'html-webpack-plugin' );

module.exports = {

  entry: './src/index.js' ,

  output: {

   filename: 'bundle.js' ,

   path: path.resolve(__dirname, 'dist' )

  },

  resolve: {

   modules: [

   path.resolve(__dirname, '' ), path.resolve(__dirname, 'node_modules' ) 

   ]

  },

  plugins: [ 

   new HtmlWebpackPlugin({  

    template: path.resolve(__dirname, 'public/index.html' ) 

   })

  ]

}

ok,基本配置好webpack就可以开始正题了。

二:数据劫持

在v2中,通过new Vue(el, options)的方式,完成vue的实例化。我们需要新建一下vue文件,把数据劫持的方法统一到vue中。

新建一个vue/index.js,作为数据劫持的入口文件。

?

import {initState} from './init.js' ;

 

function Vue (options) {

  this ._init(options);  // 数据初始化

}

Vue.prototype._init = function (options) {

  var vm = options; // 保存一下实例

  vm.$options = options; // 实例挂载

  initState(vm);   // 实例初始化

}

新建一个init.js文件初始化实例:

初始化的时候注意几个问题: 

1.  需要分别对computed,watch, data进行处理。

2. 不要在用户定义的data上直接修改。

3. 官方指定data为函数,是为了保证组件内部有自己的作用域不会有污染,直接访问data函数是不行的,需要自动执行。data也可以是对象(需要考虑到这个情况)

4. 这种方式获取data是通过vm._data.xxx 但是在vue中不需要data来获取,所以这里需要拦截重写。

5. 内部的引用类型需要递归

?

function initState (vm) {

  var options = vm.$options; // 获取options

  if (options.data) {

   initData(vm); // 因为computed,watch都需要在这里初始化,所以针对data初始化

};

 

function initData (vm) {

  var data = vm.$options.data; // 对data重新赋值,不要改变用户定义的data

  data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};

  for ( var key in data) {

   proxyData(vm, '_data' , key);   // 对data的取值重新赋值

  };

  observe(vm._data); // 对data内部进行观察

}

新建一个proxy.js作为代理层:

?

function proxyData(vm, target, key) {

  Object.defineProperty(vm, key, { 

    get () {  

    // 这里做了拦截: vm.xxx => vm._data.xxx  

    return vm[target][key]; 

   }, 

   set(newValue) {  

    // vm.xxx = yyy ===> vm._data.title = yyy  

    vm[target][key] = newValue; 

   }

  })

}

export default proxyData;

处理好了访问问题,现在需要递归一下data内部元素。obseve(vm._data);

新建一个observe.js:

?

function observe (data) {

  if ( typeof data !== 'object' || data = null ) return ;

  return new Observer(data); // 如果是应用类型,直接添加一个观察者

}

新建一个观察者:observer.js

?

function Observer(data) {

  if (Array.isArray(data)) {

   // 处理数组

    data._proto_ = arrMethods;

  }

  else {

   // 处理对象

   this .walks(data);

  }

}

Observer.prototype.walks = function (data) {

  let keys = Object.keys(data); // 拿到data下面所有的key,并且还是一个数组

  for ( var i = 0 ; i < keys.length ; i++) { 

   var key = keys[i]; 

   var value = data[key]; 

   defineReactiveData(data, key, value); // 每个重新生成响应式数据

  }}

新建一个reactive.js 处理对象等响应式

?

function defineReactiveData (data, key, value) {

  observe(value);  // 对子元素接着递归。

  Object.defineProperty(data, key, { 

   get() {  

    return value; 

   }, 

   set (newValue) {  

    if (newValue === value) return ;  

    value = newValue;  // 触发更改 

   }

  }

  )

};

ok,这里处理好了对象的数据劫持,剩余的需要处理数组了

在V2中采用重写原型上的7种方法,做到数据劫持。

劫持数组:

新建一个Array.js文件:

?

import {ARR_METHODS} from './config.js' ; 

  // 7个数组方法的合集

import observeArr from './observeArr.js' ;

var originArrMethods = Array.prototype,

arrMethods = Object.create(originArrMethods);

ARR_METHODS.map( function (m) {

  arrMethods[m] = function () { 

   var args = Array.prototype.slice.call(arguments); // 类数组转为数组 

   var rt = originArrMethods[m].apply( this , args); 

   var newArr; 

   switch (m) {  

    case 'push' :  

    case 'ushift' :    

     newArr = args;  

    case 'splice' :   

     newArr = args.slice(2);   

     break ;  

    default :    

     break ;  }; 

   newArr && observeArr(newArr); 

   return rt;

   }

});

  export { arrMethods }

observeArr(newArr): 数组也可能有嵌套,所以需要对数据进行观察。

?

import observe from "./observe" ;

function observeArr (arr) {

  for ( var i = 0 ; i < arr.length ; i++) { 

   observe(arr[i]); // 重新走到了observe上。

  }

}

export default observeArr;

三:总结

 基本流程就是这样的,不仅仅是object.defineProperty对数据进行get和set这么简单。总结一下主要流程:

(1): 在初始化的时候:保存一下实例,挂载实例。通过initState方法来初始化数据,这里主要是data数据,也有computed和watch需要处理。

(2): 调用initData(); 重新赋值data,然后执行data,修改用户获取data属性的写法统一为this.xxx同时observe(data)

(3):在observe(data)的时候需要对data进行判断,如果是引用类型需要加上一个观察者observer,同时在观察者终判断data是为数组还是对象,对象直接重新触发object.defineProperty,同时对内部重新observe。如果是数组直接重新7种数组方法,然后对数组内部接着observe。

以上就是手写Vue2.0 数据劫持的示例的详细内容,更多关于手写vue 数据劫持的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/6935354560718307365

查看更多关于手写Vue2.0 数据劫持的示例的详细内容...

  阅读:34次