手写Vue2.0 数据劫持的示例
作者:梳碧湖的砍柴人 发布时间:2024-05-22 10:43:17
目录
一:搭建webpack
二:数据劫持
三:总结
一:搭建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。
来源:https://juejin.cn/post/6935354560718307365


猜你喜欢
- 本文实例讲述了Python中pygame的mouse鼠标事件用法。分享给大家供大家参考,具体如下:pygame.mouse提供了一些方法获取
- 本文实例为大家分享了Python实现感知器模型、两层神经网络,供大家参考,具体内容如下python 3.4 因为使用了 numpy这里我们首
- 逻辑斯蒂回归模型多分类任务上节中,我们使用逻辑斯蒂回归完成了二分类任务,针对多分类任务,我们可以采用以下措施,进行分类。我们以三分类任务为例
- XMLHttpRequest的兼容代码功能结构上大体没有什么变动主要处理了这么几条:1.不同浏览器的创建方式2.事件大小写问题(ie7的XM
- validator库参数校验若干实用技巧在web开发中一个不可避免的环节就是对请求参数进行校验,通常我们会在代码中定义与请求参数相对应的模型
- Python TutorPython Tutor 是由 Philip Guo 开发的一个免费教育工具,可帮助学生攻克编程学习中的基础障碍,理
- 最近在折腾验证码识别。最终的脚本的识别率在92%左右,9000张验证码大概能识别出八千三四百张左右。好吧,其实是验证码太简单。下面就是要识别
- 随着编程语言的发展,Go 还很年轻。它于 2009 年 11 月 10 日首次发布。其创建者Robert GriesemerRob Pike
- 首先,必须安装vuex的依赖npm install vuex --save-dev创建专属vuex的文件夹和store.js:store.j
- 这里提供三种方法:1,使用正则表达式Function regKillHTML(str) &nb
- 废话不多说,大家直接看代码吧!"""遗传算法实现求函数极大值—Zjh"""imp
- BigPipe 是 Facebook 开发的优化网页加载速度的技术。网上几乎没有用 node.js 实现的文章,实际上,不止于 node.j
- 本文实例讲述了Python使用pickle模块报错EOFError Ran out of input的解决方法。分享给大家供大家参考,具体如
- 1.OUPUT参数返回值CREATE PROCEDURE [dbo].[nb_order_insert](@o_buyerid int ,@
- 用XMlhttp生成html页面,相关函数如下:<% ’定义xmlhttp function Get
- pytorch geometric的GNN、GCN节点分类# -*- coding: utf-8 -*-import osimport to
- 是否看见大站的广告都是放在内容中间实现文字环绕的呢,一般普通小站广告只能放在内容开头或者结尾,也许大站的cms系统带这个功能吧,我们小站常用
- 效果: 思路:利用onmousemove事件,然后获取鼠标的坐标,之后把DIV挨个遍历,最后把鼠标的坐标赋给DIV。代码:<
- 背景:先说一下应用吧,一般我们进行网络训练时,都有一个batchsize设置,也就是一个batch一个batch的更新梯度,能有这个batc
- 平时比较常用的时间、字符串、时间戳之间的互相转换,虽然常用但是几乎每次使用时候都喜欢去搜索一下用法;本文将作为一个笔记,整理一下三者之间的