手写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+iview分页组件的封装
Nov 17 Vue.js
Vue项目如何引入bootstrap、elementUI、echarts
Nov 26 Vue.js
vue从后台渲染文章列表以及根据id跳转文章详情详解
Dec 14 Vue.js
vue+elementUI动态增加表单项并添加验证的代码详解
Dec 17 Vue.js
Vue3 实现双盒子定位Overlay的示例
Dec 22 Vue.js
vue.js watch经常失效的场景与解决方案
Jan 07 Vue.js
Vue页面渲染中key的应用实例教程
Jan 12 Vue.js
浅谈Vue开发人员的7个最好的VSCode扩展
Jan 20 Vue.js
vue 计算属性和侦听器的使用小结
Jan 25 Vue.js
vue仿携程轮播图效果(滑动轮播,下方高度自适应)
Feb 11 Vue.js
Vue基本指令实例图文讲解
Feb 25 Vue.js
vue自定义右键菜单之全局实现
Apr 09 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中操作MySQL数据库的一些要注意的问题
2006/10/09 PHP
114啦源码(114la)不能生成地方房产和地方报刊问题4级页面0字节的解决方法
2012/01/12 PHP
PHP临时文件的安全性分析
2014/07/04 PHP
Yii使用DbTarget实现日志功能的示例代码
2020/07/21 PHP
jquery 如何动态添加、删除class样式方法介绍
2012/11/07 Javascript
jquery获取被勾选的checked(选中)的那一行的3列和4列的值
2013/07/04 Javascript
JQuery文字列表向上滚动的代码
2013/11/13 Javascript
javascript实现回到顶部特效
2015/05/06 Javascript
jquery实现漫天雪花飞舞的圣诞祝福雪花效果代码分享
2015/08/20 Javascript
JavaScript必知必会(九)function 说起 闭包问题
2016/06/08 Javascript
JavaScript之RegExp_动力节点Java学院整理
2017/06/29 Javascript
深入理解vue $refs的基本用法
2017/07/13 Javascript
详解如何使用PM2将Node.js的集群变得更加容易
2017/11/15 Javascript
原生JS实现简单的倒计时功能示例
2018/08/30 Javascript
js使用文件流下载csv文件的实现方法
2019/07/15 Javascript
jQuery操作选中select下拉框的值代码实例
2020/02/07 jQuery
swiper自定义分页器的样式
2020/09/14 Javascript
javascript中导出与导入实现模块化管理教程
2020/12/03 Javascript
Python中处理字符串之endswith()方法的使用简介
2015/05/18 Python
python删除列表内容
2015/08/04 Python
Python入门_浅谈逻辑判断与运算符
2017/05/16 Python
浅谈python import引入不同路径下的模块
2017/07/11 Python
python使用正则表达式替换匹配成功的组并输出替换的次数
2017/11/22 Python
Cython编译python为so 代码加密示例
2019/12/23 Python
Selenium启动Chrome时配置选项详解
2020/03/18 Python
Python 使用Opencv实现目标检测与识别的示例代码
2020/09/08 Python
详解pandas映射与数据转换
2021/01/22 Python
中邮全球便购:中国邮政速递物流
2017/03/04 全球购物
阿迪达斯希腊官方网上商店:adidas希腊
2019/04/06 全球购物
大学生实习期自我评价范文
2013/10/03 职场文书
外贸业务员求职信范文
2013/12/12 职场文书
大班开学家长寄语
2014/04/04 职场文书
《学会合作》教学反思
2014/04/12 职场文书
普通话演讲稿
2014/09/03 职场文书
小学教研工作总结2015
2015/05/13 职场文书
Python+Selenium自动化环境搭建与操作基础详解
2022/03/13 Python