加入收藏 | 设为首页 | 会员中心 | 我要投稿 银川站长网 (https://www.0951zz.com/)- 云通信、基础存储、云上网络、机器学习、视觉智能!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

通过模拟Vue2应用讲解如何达成数据劫持

发布时间:2023-06-10 10:59:16 所属栏目:语言 来源:
导读:今天这篇我们来学习和了解“通过模拟Vue2应用讲解如何实现数据劫持”,下文的讲解详细,步骤过程清晰,对大家进一步学习和理解“通过模拟Vue2应用讲解如何实现数据劫持”有一定的帮助。有这方面

今天这篇我们来学习和了解“通过模拟Vue2应用讲解如何实现数据劫持”,下文的讲解详细,步骤过程清晰,对大家进一步学习和理解“通过模拟Vue2应用讲解如何实现数据劫持”有一定的帮助。有这方面学习需要的朋友就继续往下看吧!

我们今天要编写的项目通过需要使用 Webpack 进行编译,package.json 相关依赖如下:

{

"scripts": {

"dev": "webpack-dev-server",

"build:": "webpack"

},

"devDependencies": {

"html-webpack-plugin": "^4.5.2",

"webpack": "^4.46.0",

"webpack-cli": "^3.3.12",

"webpack-dev-server": "^3.11.3"

}

}

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")

},

devtool: "source-map",

resolve: {

// 表示解析模块引入的时候先从当前文件夹寻找模块,再去 node_modules 找模块

modules: [

path.resolve(__dirname, ""),

path.resolve(__dirname, "node_modules")

]

},

plugins: [

new HtmlWebpackPlugin({

template: path.resolve(__dirname, "public/index.html")

})

]

};

public/index.html 文件内容

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title></title>

</head>

<body>

<div id="app"></div>

</body>

</html>

好了,接下来我们就开始发车!

实例一个模拟的 Vue 应用

首先,我们需要编写我们的入口文件 index.js,该文件很普通主要就是实例一个模拟的 Vue 应用:

// index.js

// 我们在 webpack.config.js 中进行了配置,所以这里优先在当前目录下寻找 vue 文件,也就是我们的 vue/index.js 文件

import Vue from "vue";

let vm = new Vue({

el: "#app",

data() {

return {

title: "学生列表",

classNum: 1,

teacher: ["张三", "李四"],

info: {

a: {

b: 1

}

},

students: [

{

id: 1,

name: "小红"

},

{

id: 2,

name: "小明"

}

]

};

}

});

console.log(vm);

vue/index.js 文件主要是负责初始化内容

// src/sindex.js

import { initState } from "./init";

function Vue(options) {

this._init(options);

}

Vue.prototype._init = function (options) {

// this 指向当前实例对象

var vm = this;

// 我们把 new Vue() 时候传递的数据统称为 options

// 并且挂载到 Vue 的实例对象上

vm.$options = options;

// 调用 initState 初始化 data 数据

initState(vm);

};

export default Vue;

initState方法

vue/init.js 文件暴露出一个initState方法,该方法主要是处理初始化的数据:

// vue/init.js

import proxyData from "./proxy";

import observer from "./observe"

function initState(vm) {

var options = vm.$options;

// 如果 options 中存在 data 属性,我们才会继续处理

if (options.data) {

initData(vm);

}

}

function initData(vm) {

var data = vm.$options.data;

// 把 data 数据单独保存到 Vue 的实例化对象上,方便我们获取

// 如果 data 是一个函数,我们需要执行返回得到返回的对象

data = vm._data = typeof data === "function" ? data.call(vm) : data || {};

// 遍历 data 对象,通过 proxyData 对数据进行拦截

for (const key in data) {

// 传入的参数分别是:当前实例、key值(也就是 vm._data)、data 中的 key 值(例如 vm._data.title)

proxyData(vm, "_data", key);

}

// 调用观察者模式

observer(vm._data)

}

export {

initState

};

以上代码,我们通过proxyData对data中的数据进行拦截,详情如下:

// vue/proxy.js

function proxyData(vm, target, key) {

// 当访问 vm.title 的时候转换为 vm._data.title

//(请记住这句话!!!)

Object.defineProperty(vm, key, {

get: function () {

return vm[target][key];

},

set: function (newVal) {

vm[target][key] = newVal;

}

});

}

export default proxyData;

我们还调用了observer方法进行事件订阅,详细如下:

// vue/observe.js

import Observer from "./observer"

function observe(data) {

// 判断只处理对象,如果不是对象直接返回

if (typeof data !== "object" || data === null) {

return false;

}

// 观察数据

return new Observer(data)

}

export default observe;

核心文件vue/observer.js

接下来就是我们的核心文件vue/observer.js,该文件主要负责对数据类型进行判断,如果是数组就需要单独处理数组,这个我们后面再说:

// vue/observer.js

import defineReactiveData from "./reactive";

import { arrMethods } from "./array";

import observeArr from "./observeArr";

// 这个方法会在多个地方调用,请记住这个方法以它的作用

function Observer(data) {

// 如果 data 是一个数组,那面需要单独处理

if (Array.isArray(data)) {

// 给数组新增一层原型

data._proto__ = arrMethods;

// 循环数组的每一项,然后让每一项都调用 Observer 方法进行订阅

observeArr(data)

} else {

// 处理对象

this.walk(data);

}

}

Observer.prototype.walk = function (data) {

// 获取到 data 全部的 key

// 也就是我们定义的 ['title', 'classNum', 'teacher', 'info', 'students']

let keys = Object.keys(data);

for (var i = 0; i < keys.length; i++) {

let key = keys[i];

let value = data[key];

// 拦截 data 数据

// 分别传入参数为:vm._data、data 中的 key、data 中 key 对应的 value

defineReactiveData(data, key, value);

}

};

export default Observer;

以上代码,我们分别对数组和对象执行不同的操作,我们先来看对象的操作:

在Observer构造函数中我们新增了一个walk方法,该方法获取到了所有的key值,然后调用了defineReactiveData进行处理。

// vue/reactive.js

import observe from "./observe";

function defineReactiveData(data, key, value) {

// 例如 info.a 还是个对象,那么就递归观察

observe(value);

// 这里的 data 是 vm._data,所以这里拦截的也是 vm._data

Object.defineProperty(data, key, {

get() {

console.log(` 响应式获取:data.${key},`, value);

return value;

},

set(newVal) {

console.log(` 响应式设置:data.${key},`, newVal);

if (newVal === value) {

return false;

}

// 如果新值还是对象,那么接着进行观察

observe(newVal);

value = newVal;

}

});

}

export default defineReactiveData;

以上代码,我们是对vm._data进行拦截的,这是因为我们前面说的proxyData拦截的是vm对象,当访问vm.title的时候,proxyData的拦截就会生效,而proxyData内部是通过vm._data来获取的,这样又会触发defineReactiveData的拦截!

vue/observer.js文件对数组进行处理

回到vue/observer.js文件,我们还需要对数组进行处理:

import defineReactiveData from "./reactive";

import { arrMethods } from "./array";

import observeArr from "./observeArr";

// 这个方法会在多个地方调用,请记住这个方法以它的作用

function Observer(data) {

// 如果 data 是一个数组,那面需要单独处理

if (Array.isArray(data)) {

// 为数组更改原型

data._proto__ = arrMethods;

// 循环数组的每一项,然后让每一项都调用 Observer 方法进行订阅

observeArr(data)

} else {

// ...

}

}

Observer.prototype.walk = function (data) {

// ...

};

export default Observer;

以上代码我们对数组更改一个原型arrMethods,那看看它到底做了什么事情:

// vue/array.js

// ARR_METHODS 是一些可以更改数组本身的方法,里面包括以下内容,我们就不展开看了

// ["push", "pop", "shift", "unshift", "splic", "sort", "reverse"]

import { ARR_METHODS } from "./config";

import observeArr from "./observeArr";

// 把数组本身的元素进行拷贝

var originArrayMethods = Array.prototype;

// 创建一个空对象,该空对象的原型就是数组的原型

var arrMethods = Object.create(originArrayMethods);

// 遍历这些数组的方法名称

ARR_METHODS.forEach(function (m) {

// 在新对象上重写数组的方法

arrMethods[m] = function () {

// 把数组接到的参数转换为一个数组

var args = Array.prototype.slice.call(arguments);

// 执行数组原本的方法

var rt = originArrayMethods[m].apply(this, args);

var newArr;

switch (m) {

case "push":

case "unshift":

// 例如 arr.push({a: 1})

// args 就是 [{a: 1}]

newArr = args;

break;

case "splice":

// 例如 arr.splice(1, 0, {a: 1}, {b: 2})

// args 就是 [{a: 1}, {b: 2}]

newArr = args.slice(2);

break;

default:

break;

}

// 如果有值那面就调用 observeArr 方法

// observeArr 方法就是循环数组的每一项,然后让每一项都调用 Observer 方法进行订阅

newArr && observeArr(newArr);

return rt;

};

});

export { arrMethods };

以上代码我们重写了数组相关的方法,这是因为这些方法被并不能被Object.defineProperty拦截到。

而我们的observeArr只是遍历了数组的每一项,让每一项都进行了拦截:

// vue/observeArr.js

import observe from "./observe";

function observeArr(arr) {

for (let i = 0; i < arr.length; i++) {

// 又回到了起点,进行更新订阅

observe(arr[i]);

}

}

export default observeArr;

然后我们去index.js文件获取属性,看看结果:

import Vue from "vue";

let vm = new Vue({

el: "#app",

data() {

return {

title: "学生列表",

classNum: 1,

teacher: ["张三", "李四"],

info: {

a: {

b: 1

}

},

students: [

{

id: 1,

name: "小红"

},

{

id: 2,

name: "小明"

}

]

};

}

});

console.log(vm);

console.log(vm.title);

console.log(vm.teacher);

console.log(vm.info.a);

到此这篇关于“通过模拟Vue2应用讲解如何实现数据劫持”的文章就介绍到这了,更多相关通过模拟Vue2应用讲解如何实现数据劫持内容,小编将为大家输出更多高质量的实用文章!

(编辑:银川站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!