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


Posted in Vue.js onMarch 04, 2021

一:搭建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 数据劫持的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue自定义插件封装,实现简易的elementUi的Message和MessageBox的示例
Nov 20 Vue.js
vue打开其他项目页面并传入数据详解
Nov 25 Vue.js
vue中defineProperty和Proxy的区别详解
Nov 30 Vue.js
Vue router安装及使用方法解析
Dec 02 Vue.js
Vue 简单实现前端权限控制的示例
Dec 25 Vue.js
Vue实现多页签组件
Jan 14 Vue.js
vue监听键盘事件的相关总结
Jan 29 Vue.js
vue实现可移动的悬浮按钮
Mar 04 Vue.js
vue 中 get / delete 传递数组参数方法
Mar 23 Vue.js
vue3中的组件间通信
Mar 31 Vue.js
vue判断按钮是否可以点击
Apr 09 Vue.js
vue3种table表格选项个数的控制方法
Apr 14 Vue.js
vue3.0封装轮播图组件的步骤
Mar 04 #Vue.js
vue3.0 项目搭建和使用流程
Mar 04 #Vue.js
vue 数据双向绑定的实现方法
Mar 04 #Vue.js
vue3.0中使用element的完整步骤
Mar 04 #Vue.js
VUE实现吸底按钮
Mar 04 #Vue.js
vue实现可移动的悬浮按钮
Mar 04 #Vue.js
vue中axios封装使用的完整教程
Mar 03 #Vue.js
You might like
浅谈PHP解析URL函数parse_url和parse_str
2014/11/11 PHP
织梦sitemap地图实时推送给百度的教程
2015/08/03 PHP
PHP生成word文档的三种实现方式
2016/11/14 PHP
php实现的网页版剪刀石头布游戏示例
2016/11/25 PHP
firebug的一个有趣现象介绍
2011/11/30 Javascript
js 获取class的元素的方法 以及创建方法getElementsByClassName
2013/03/11 Javascript
js 事件截取enter按键页面提交事件示例代码
2014/03/04 Javascript
JavaScript中的getTimezoneOffset()方法使用详解
2015/06/10 Javascript
JavaScript获取当前运行脚本文件所在目录的方法
2016/02/03 Javascript
AngularJs html compiler详解及示例代码
2016/09/01 Javascript
jQuery的中 is(':visible') 解析及用法(必看)
2017/02/12 Javascript
js 简易版滚动条实例(适用于移动端H5开发)
2017/06/26 Javascript
将 vue 生成的 js 上传到七牛的实例
2017/07/28 Javascript
JavaScript调用模式与this关键字绑定的关系
2018/04/21 Javascript
解决VUE-Router 同一页面第二次进入不刷新的问题
2020/07/22 Javascript
JavaScript ES 模块的使用
2020/11/12 Javascript
python静态方法实例
2015/01/14 Python
Python 3.x 判断 dict 是否包含某键值的实例讲解
2018/07/06 Python
Python3 jupyter notebook 服务器搭建过程
2018/11/30 Python
pandas求两个表格不相交的集合方法
2018/12/08 Python
Python中面向对象你应该知道的一下知识
2019/07/10 Python
python用match()函数爬数据方法详解
2019/07/23 Python
python实现桌面托盘气泡提示
2019/07/29 Python
pytorch AvgPool2d函数使用详解
2020/01/03 Python
Python 多线程共享变量的实现示例
2020/04/17 Python
input元素的url类型和email类型简介
2012/07/11 HTML / CSS
Omio波兰:全欧洲低价大巴、火车和航班搜索和比价
2018/02/16 全球购物
北京大学自荐信范文
2014/01/28 职场文书
甜美蛋糕店创业计划书
2014/01/30 职场文书
大学生开西餐厅创业计划书
2014/02/01 职场文书
政治思想表现评语
2014/05/04 职场文书
创先争优个人承诺书
2014/08/30 职场文书
大学生就业协议书范本(适用于公司企业)
2014/10/07 职场文书
2015年实习班主任工作总结
2015/04/23 职场文书
解决Windows Server2012 R2 无法安装 .NET Framework 3.5
2022/04/29 Servers
Linux安装Docker详细教程
2022/07/07 Servers