翼度科技»论坛 编程开发 JavaScript 查看内容

Vue2 中的数据劫持简写示例

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
package.json 相关依赖

我们今天要编写的项目通过需要使用 Webpack 进行编译,package.json 相关依赖如下:
  1. {
  2.   "scripts": {
  3.     "dev": "webpack-dev-server",
  4.     "build:": "webpack"
  5.   },
  6.   "devDependencies": {
  7.     "html-webpack-plugin": "^4.5.2",
  8.     "webpack": "^4.46.0",
  9.     "webpack-cli": "^3.3.12",
  10.     "webpack-dev-server": "^3.11.3"
  11.   }
  12. }
复制代码
Webpack.config.js 配置文件
  1. const path = require("path");
  2. const HtmlWebpackPlugin = require("html-webpack-plugin");
  3. module.exports = {
  4.   entry: "./src/index.js",
  5.   output: {
  6.     filename: "bundle.js",
  7.     path: path.resolve(__dirname, "dist")
  8.   },
  9.   devtool: "source-map",
  10.   resolve: {
  11.     // 表示解析模块引入的时候先从当前文件夹寻找模块,再去 node_modules 找模块
  12.     modules: [
  13.       path.resolve(__dirname, ""),
  14.       path.resolve(__dirname, "node_modules")
  15.     ]
  16.   },
  17.   plugins: [
  18.     new HtmlWebpackPlugin({
  19.       template: path.resolve(__dirname, "public/index.html")
  20.     })
  21.   ]
  22. };
复制代码
public/index.html 文件内容
  1. <!DOCTYPE html>
  2. <html lang="en">
  3.   <head>
  4.     <meta charset="UTF-8" />
  5.     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6.     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7.     <title></title>
  8.   </head>
  9.   <body>
  10.     <div id="app"></div>
  11.   </body>
  12. </html>
复制代码
全部文件目录结构


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

实例一个模拟的 Vue 应用

首先,我们需要编写我们的入口文件 index.js,该文件很普通主要就是实例一个模拟的 Vue 应用:
  1. // index.js
  2. // 我们在 webpack.config.js 中进行了配置,所以这里优先在当前目录下寻找 vue 文件,也就是我们的 vue/index.js 文件
  3. import Vue from "vue";
  4. let vm = new Vue({
  5.   el: "#app",
  6.   data() {
  7.     return {
  8.       title: "学生列表",
  9.       classNum: 1,
  10.       teacher: ["张三", "李四"],
  11.       info: {
  12.         a: {
  13.           b: 1
  14.         }
  15.       },
  16.       students: [
  17.         {
  18.           id: 1,
  19.           name: "小红"
  20.         },
  21.         {
  22.           id: 2,
  23.           name: "小明"
  24.         }
  25.       ]
  26.     };
  27.   }
  28. });
  29. console.log(vm);
复制代码
vue/index.js 文件主要是负责初始化内容
  1. // src/sindex.js
  2. import { initState } from "./init";
  3. function Vue(options) {
  4.   this._init(options);
  5. }
  6. Vue.prototype._init = function (options) {
  7.   // this 指向当前实例对象
  8.   var vm = this;
  9.   // 我们把 new Vue() 时候传递的数据统称为 options
  10.   // 并且挂载到 Vue 的实例对象上
  11.   vm.$options = options;
  12.   // 调用 initState 初始化 data 数据
  13.   initState(vm);
  14. };
  15. export default Vue;
复制代码
initState方法

vue/init.js 文件暴露出一个
  1. initState
复制代码
方法,该方法主要是处理初始化的数据:
  1. // vue/init.js
  2. import proxyData from "./proxy";
  3. import observer from "./observe"
  4. function initState(vm) {
  5.   var options = vm.$options;
  6.   // 如果 options 中存在 data 属性,我们才会继续处理
  7.   if (options.data) {
  8.     initData(vm);
  9.   }
  10. }
  11. function initData(vm) {
  12.   var data = vm.$options.data;
  13.   // 把 data 数据单独保存到 Vue 的实例化对象上,方便我们获取
  14.   // 如果 data 是一个函数,我们需要执行返回得到返回的对象
  15.   data = vm._data = typeof data === "function" ? data.call(vm) : data || {};
  16.   // 遍历 data 对象,通过 proxyData 对数据进行拦截
  17.   for (const key in data) {
  18.     // 传入的参数分别是:当前实例、key值(也就是 vm._data)、data 中的 key 值(例如 vm._data.title)
  19.     proxyData(vm, "_data", key);
  20.   }
  21.   // 调用观察者模式
  22.   observer(vm._data)
  23. }
  24. export {
  25.   initState
  26. };
复制代码
以上代码,我们通过
  1. proxyData
复制代码
  1. data
复制代码
中的数据进行拦截,详情如下:
  1. // vue/proxy.js
  2. function proxyData(vm, target, key) {
  3.   // 当访问 vm.title 的时候转换为 vm._data.title
  4.   //(请记住这句话!!!)
  5.   Object.defineProperty(vm, key, {
  6.     get: function () {
  7.       return vm[target][key];
  8.     },
  9.     set: function (newVal) {
  10.       vm[target][key] = newVal;
  11.     }
  12.   });
  13. }
  14. export default proxyData;
复制代码
我们还调用了
  1. observer
复制代码
方法进行事件订阅,详细如下:
  1. // vue/observe.js
  2. import Observer from "./observer"
  3. function observe(data) {
  4.   // 判断只处理对象,如果不是对象直接返回
  5.   if (typeof data !== "object" || data === null) {
  6.     return false;
  7.   }
  8.   // 观察数据
  9.   return new Observer(data)
  10. }
  11. export default observe;
复制代码
核心文件vue/observer.js

接下来就是我们的核心文件
  1. vue/observer.js
复制代码
,该文件主要负责对数据类型进行判断,如果是数组就需要单独处理数组,这个我们后面再说:
  1. // vue/observer.js
  2. import defineReactiveData from "./reactive";
  3. import { arrMethods } from "./array";
  4. import observeArr from "./observeArr";
  5. // 这个方法会在多个地方调用,请记住这个方法以它的作用
  6. function Observer(data) {
  7.   // 如果 data 是一个数组,那面需要单独处理
  8.   if (Array.isArray(data)) {
  9.     // 给数组新增一层原型
  10.     data._proto__ = arrMethods;
  11.     // 循环数组的每一项,然后让每一项都调用 Observer 方法进行订阅
  12.     observeArr(data)
  13.   } else {
  14.     // 处理对象
  15.     this.walk(data);
  16.   }
  17. }
  18. Observer.prototype.walk = function (data) {
  19.   // 获取到 data 全部的 key
  20.   // 也就是我们定义的 ['title', 'classNum', 'teacher', 'info', 'students']
  21.   let keys = Object.keys(data);
  22.   for (var i = 0; i < keys.length; i++) {
  23.     let key = keys[i];
  24.     let value = data[key];
  25.     // 拦截 data 数据
  26.     // 分别传入参数为:vm._data、data 中的 key、data 中 key 对应的 value
  27.     defineReactiveData(data, key, value);
  28.   }
  29. };
  30. export default Observer;
复制代码
以上代码,我们分别对数组和对象执行不同的操作,我们先来看对象的操作:
  1. Observer
复制代码
构造函数中我们新增了一个
  1. walk
复制代码
方法,该方法获取到了所有的
  1. key
复制代码
值,然后调用了
  1. defineReactiveData
复制代码
进行处理。
[code]// vue/reactive.jsimport 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(`
来源:https://www.jb51.net/article/276273.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具