手写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如何循环提取对象数组中的值
Nov 18 Vue.js
vue-cli4.0多环境配置变量与模式详解
Dec 30 Vue.js
vue+element table表格实现动态列筛选的示例代码
Jan 14 Vue.js
Vue实现多页签组件
Jan 14 Vue.js
vue-video-player 断点续播的实现
Feb 01 Vue.js
vue实现拖拽进度条
Mar 01 Vue.js
vue脚手架项目创建步骤详解
Mar 02 Vue.js
Vue实现tab导航栏并支持左右滑动功能
Jun 28 Vue.js
Vue h函数的使用详解
Feb 18 Vue.js
vue项目配置sass及引入外部scss文件
Apr 14 Vue.js
vue动态绑定style样式
Apr 20 Vue.js
vue el-table实现递归嵌套的示例代码
Aug 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实现扎金花游戏之大小比赛的方法
2015/03/10 PHP
Yii2中使用asset压缩js,css文件的方法
2016/11/24 PHP
Zend Framework处理Json数据方法详解
2016/12/09 PHP
jQuery中将函数赋值给变量的调用方法
2012/03/23 Javascript
jquery如何改变html标签的样式(两种实现方法)
2013/01/16 Javascript
JavaScript头像上传插件源码分享
2016/03/29 Javascript
JavaScript编写页面半透明遮罩效果的简单示例
2016/05/09 Javascript
jquery 遍历数组 each 方法详解
2016/05/25 Javascript
深入解析JavaScript中的arguments对象
2016/06/12 Javascript
KnockoutJS 3.X API 第四章之数据控制流with绑定
2016/10/10 Javascript
基于iscroll.js实现下拉刷新和上拉加载效果
2016/11/28 Javascript
百度地图API之百度地图退拽标记点获取经纬度的实现代码
2017/01/12 Javascript
原生js实现手风琴功能(支持横纵向调用)
2017/01/13 Javascript
js省市区级联查询(插件版&amp;无插件版)
2017/03/21 Javascript
Bootstrap警告(Alerts)的实现方法
2017/03/22 Javascript
javascript与PHP动态往类中添加方法对比
2018/03/21 Javascript
vue addRoutes实现动态权限路由菜单的示例
2018/05/15 Javascript
微信小程序实现自定义加载图标功能
2018/07/19 Javascript
深入学习js函数的隐式参数 arguments 和 this
2019/06/24 Javascript
微信小程序按钮点击动画效果的实现
2019/09/04 Javascript
python使用fork实现守护进程的方法
2017/11/16 Python
Python面向对象程序设计类变量与成员变量、类方法与成员方法用法分析
2019/04/12 Python
Python 解码Base64 得到码流格式文本实例
2020/01/09 Python
python如何使用Redis构建分布式锁
2020/01/16 Python
Python post请求实现代码实例
2020/02/28 Python
详解scrapy内置中间件的顺序
2020/09/28 Python
python里glob模块知识点总结
2021/01/05 Python
美国男士西装打折店:Jos. A. Bank
2017/11/13 全球购物
TripAdvisor瑞典:全球领先的旅游网站
2017/12/11 全球购物
Sasa莎莎海外旗舰店:香港莎莎美妆平台
2018/03/21 全球购物
怎样写好自我评价呢?
2014/02/16 职场文书
2014入党积极分子批评与自我批评思想汇报
2014/09/20 职场文书
学生犯错保证书
2015/05/09 职场文书
Java并发编程之原子性-Atomic的使用
2022/03/16 Java/Android
TV动画「神渣☆爱豆」公开第一弹主视觉图
2022/03/21 日漫
生命的关键成分来自太空?陨石说是的
2022/04/29 数码科技