手写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使用echarts图表自适应的几种解决方案
Dec 04 Vue.js
Vue解决移动端弹窗滚动穿透问题
Dec 15 Vue.js
基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能
Jan 05 Vue.js
vue watch监控对象的简单方法示例
Jan 07 Vue.js
Vue实现摇一摇功能(兼容ios13.3以上)
Jan 26 Vue.js
Vue实现圆环进度条的示例
Feb 06 Vue.js
vue中h5端打开app(判断是安卓还是苹果)
Feb 26 Vue.js
深入讲解Vue中父子组件通信与事件触发
Mar 22 Vue.js
vue+elementUI实现表格列的显示与隐藏
Apr 13 Vue.js
vue2的 router在使用过程中遇到的一些问题
Apr 13 Vue.js
vue修饰符.capture和.self的区别
Apr 22 Vue.js
vue postcss-px2rem 自适应布局
May 15 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程序时遇到的三个问题
2012/01/17 PHP
PHP 获取远程文件大小的3种解决方法
2013/07/11 PHP
PHP技术开发微信公众平台
2015/07/22 PHP
PHP如何使用array_unshift()在数组开头插入元素
2020/09/01 PHP
jquery 弹出层实现代码
2009/10/30 Javascript
JQuery 操作select标签实现代码
2010/05/14 Javascript
JavaScript中对象property的删除方法介绍
2014/12/30 Javascript
JavaScript中pop()方法的使用教程
2015/06/09 Javascript
js实现三张图(文)片一起切换的banner焦点图
2015/08/25 Javascript
jQuery插件easyUI实现通过JS显示Dialog的方法
2016/09/16 Javascript
JS表单数据验证的正则表达式(常用)
2017/02/18 Javascript
浅谈React + Webpack 构建打包优化
2018/01/23 Javascript
jQuery的ztree仿windows文件新建和拖拽功能的实现代码
2018/12/05 jQuery
前端Vue项目详解--初始化及导航栏
2019/06/24 Javascript
layui prompt 设置允许空白提交的方法
2019/09/24 Javascript
[01:20]DOTA2上海特级锦标赛现场采访:谁的ID最受青睐
2016/03/25 DOTA
python通过exifread模块获得图片exif信息的方法
2015/03/16 Python
python引用DLL文件的方法
2015/05/11 Python
Python编程实现双链表,栈,队列及二叉树的方法示例
2017/11/01 Python
Python之ReportLab绘制条形码和二维码的实例
2018/01/15 Python
Python+OpenCV图片局部区域像素值处理改进版详解
2019/01/23 Python
Django 数据库同步操作技巧详解
2019/07/19 Python
春节到了 教你使用python来抢票回家
2020/01/06 Python
Python爬虫requests库多种用法实例
2020/05/28 Python
Python基于内置函数type创建新类型
2020/10/22 Python
纯CSS改变webkit内核浏览器的滚动条样式
2014/04/17 HTML / CSS
html5 canvas 画图教程案例分析
2012/11/23 HTML / CSS
ECCO爱步美国官网:来自丹麦的鞋履品牌
2016/11/23 全球购物
大学自我鉴定范文
2013/12/26 职场文书
2014年班主任自我评价范文
2014/04/23 职场文书
数控机床专业自荐信
2014/05/19 职场文书
放飞梦想演讲稿800字
2014/08/26 职场文书
会计求职自荐信
2015/03/26 职场文书
教你快速开启Apache SkyWalking的自监控
2021/04/25 Servers
Golang 实现超大文件读取的两种方法
2021/04/27 Golang
分享MySQL常用 内核 Debug 几种常见方法
2022/03/17 MySQL