手写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+vant实现购物车全选和反选功能
Nov 17 Vue.js
解决vue页面刷新,数据丢失的问题
Nov 24 Vue.js
element-plus一个vue3.xUI框架(element-ui的3.x 版初体验)
Dec 02 Vue.js
Vue解决移动端弹窗滚动穿透问题
Dec 15 Vue.js
Vue实现小购物车功能
Dec 21 Vue.js
vue使用require.context实现动态注册路由
Dec 25 Vue.js
vue+vant 上传图片需要注意的地方
Jan 03 Vue.js
Vue使用Ref跨层级获取组件的步骤
Jan 25 Vue.js
VUE中的v-if与v-show区别介绍
Mar 13 Vue.js
使用vue判断当前环境是安卓还是IOS
Apr 12 Vue.js
关于vue-router-link选择样式设置
Apr 30 Vue.js
vue使用element-ui按需引入
May 20 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
图解上海144收音机
2021/03/02 无线电
window+nginx+php环境配置 附配置搭配说明
2010/12/29 PHP
openPNE常用方法分享
2011/11/29 PHP
php数组删除元素示例
2014/03/21 PHP
ThinkPHP结合AjaxFileUploader实现无刷新文件上传的方法
2014/10/29 PHP
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件
2015/08/23 PHP
PHP实现基于mysqli的Model基类完整实例
2016/04/08 PHP
在laravel中实现将查询的对象转换为多维数组的函数
2019/10/21 PHP
Alliance vs AM BO3 第一场2.13
2021/03/10 DOTA
根据鼠标的位置动态的控制层的位置
2009/11/24 Javascript
jQuery $.data()方法使用注意细节
2012/12/31 Javascript
5秒后跳转效果(setInterval/SetTimeOut)
2013/05/03 Javascript
IE和Firefox之间在JavaScript语法上的差异
2016/04/22 Javascript
JS使用单链表统计英语单词出现次数
2016/06/16 Javascript
Web打印解决方案之普通报表打印功能
2016/08/29 Javascript
使用Webpack提高Vue.js应用的方式汇总(四种)
2017/07/10 Javascript
微信小程序之几种常见的弹框提示信息实现详解
2019/07/11 Javascript
vue递归组件实战之简单树形控件实例代码
2019/08/27 Javascript
小程序如何定位所在城市及发起周边搜索
2020/02/11 Javascript
通过js随机函数Math.random实现乱序
2020/05/19 Javascript
[42:11]TNC vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
[01:16:37]【全国守擂赛】第三周决赛 Dark Knight vs. 一个弱队
2020/05/04 DOTA
Python查询IP地址归属完整代码
2017/06/21 Python
Python实现上下班抢个顺风单脚本
2018/02/07 Python
浅谈Series和DataFrame中的sort_index方法
2018/06/07 Python
Django重置migrations文件的方法步骤
2019/05/01 Python
Django之使用内置函数和celery发邮件的方法示例
2019/09/16 Python
Python HTTP下载文件并显示下载进度条功能的实现
2020/04/02 Python
Pycharm及python安装详细步骤及PyCharm配置整理(推荐)
2020/07/31 Python
Python3通过chmod修改目录或文件权限的方法示例
2020/06/08 Python
Keras搭建自编码器操作
2020/07/03 Python
西班牙语在线票务市场:SuperBoletería
2019/06/10 全球购物
牵手50新加坡:专为黄金岁月的单身人士而设的交友网站
2020/08/16 全球购物
大学学年自我鉴定
2013/10/28 职场文书
员工安全责任书范本
2014/07/24 职场文书
每日六道java新手入门面试题,通往自由的道路
2021/06/30 Java/Android