Vue2 中的数据劫持简写示例

目录
  • package.json 相关依赖
  • Webpack.config.js 配置文件
  • public/index.html 文件内容
  • 全部文件目录结构
  • 实例一个模拟的 Vue 应用
    • vue/index.js 文件主要是负责初始化内容
    • initState方法
    • 核心文件vue/observer.js
    • vue/observer.js文件对数组进行处理

package.json 相关依赖

我们今天要编写的项目通过需要使用 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
};

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

// 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(`                        
(0)

相关推荐

  • 深入浅出 Vue 系列 -- 数据劫持实现原理

    一.前言 数据双向绑定作为 Vue 核心功能之一,其实现原理主要分为两部分: 数据劫持 发布订阅模式 本篇文章主要介绍 Vue 实现数据劫持的思路,下一篇则会介绍发布订阅模式的设计. 二.针对 Object 类型的劫持 对于 Object 类型,主要劫持其属性的读取与设置操作.在 JavaScript 中对象的属性主要由一个字符串类型的"名称"以及一个"属性描述符"组成,属性描述符包括以下选项: value: 该属性的值: writable: 仅当值为 true 时

  • 3分钟了解vue数据劫持的原理实现

    目的: 了解Object.defineProperty如何实现数据劫持 大致原理是这样的: 定义一个监听函数,对对象的每一个属性进行监听 通过Object.defineProperty对监听的每一个属性设置get 和 set 方法. 对对象实行监听 对对象内嵌对象进行处理 对数组对象进行处理 1. 先定义一个对象 let obj = { name: 'jw' } 2. 定义一个监听函数 /** * 判断监听的是否是对象 * 如果是对象,就遍历,并且对每个属性进行定义get 和 set */ fu

  • Vue数据劫持详情介绍

    目录 前言 创建Vue实例 数据观测存在的问题 结语 前言 Vue会对我们在data中传入的数据进行拦截: 对象:递归的为对象的每个属性都设置get/set方法 数组:修改数组的原型方法,对于会修改原数组的方法进行了重写 在用户为data中的对象设置值.修改值以及调用修改原数组的方法时,都可以添加一些逻辑来进行处理,实现数据更新页面也同时更新. Vue中的响应式(reactive): 对对象属性或数组方法进行了拦截,在属性或数组更新时可以同时自动地更新视图.在代码中被观测过的数据具有响应性 创建

  • 手写Vue源码之数据劫持示例详解

    源代码: 传送门 Vue会对我们在data中传入的数据进行拦截: 对象:递归的为对象的每个属性都设置get/set方法 数组:修改数组的原型方法,对于会修改原数组的方法进行了重写 在用户为data中的对象设置值.修改值以及调用修改原数组的方法时,都可以添加一些逻辑来进行处理,实现数据更新页面也同时更新. Vue中的响应式(reactive): 对对象属性或数组方法进行了拦截,在属性或数组更新时可以同时自动地更新视图.在代码中被观测过的数据具有响应性 创建Vue实例 我们先让代码实现下面的功能:

  • 手写Vue2.0 数据劫持的示例

    一:搭建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文件中: cons

  • vue MVVM双向绑定实例详解(数据劫持+发布者-订阅者模式)

    目录 实现过程 1.实现一个Observer 2.实现Watcher 3.实现Compile 总结 参考文献:https://www.jb51.net/article/160654.htm https://www.jb51.net/article/239554.htm MVVM拆开来即为Model-View-ViewModel,有View,ViewModel,Model三部分组成.View层代表的是视图.模版,负责将数据模型转化为UI展现出来.Model层代表的是模型.数据,可以在Model层中

  • 详解vue的数据劫持以及操作数组的坑

    TL;DR 给data添加新属性的时候vm.$set(vm.info,'newKey','newValue') data上面属性值是数组的时候,需要用数组的方法操作数组,而不能通过index或者length属性去操作数组,因为监听不到属性操作的动作. 安装和初使用vue vue是构建用户界面的渐进式框架.所谓的渐进式:vue + components + vue-router + vuex + vue-cli可以根据需要选择相应的功能. 来串命令mkdir vue-apply;cd vue-ap

  • Vue2 中的数据劫持简写示例

    目录 package.json 相关依赖 Webpack.config.js 配置文件 public/index.html 文件内容 全部文件目录结构 实例一个模拟的 Vue 应用 vue/index.js 文件主要是负责初始化内容 initState方法 核心文件vue/observer.js vue/observer.js文件对数组进行处理 package.json 相关依赖 我们今天要编写的项目通过需要使用 Webpack 进行编译,package.json 相关依赖如下: { "scri

  • C++中静态数据成员使用示例

    #include<iostream>//尝试静态数据成员 using namespace std; class easy { private: int num1; int num2; static int sum,count; static float ave; public: easy(int, int); easy(const easy&);//拷贝构造函数,这里要加const,否则使用时会显示没有合适的拷贝构造函数 void output(); ~easy(); }; easy:

  • Python PyQt5中窗口数据传递的示例详解

    目录 单一窗口数据传递 多窗口数据传递:调用属性 多窗口数据传递:信号与槽 开发应用程序时,若只有一个窗口则只需关心这个窗口里面的各控件之间如何传递数据.如果程序有多个窗口,就要关心不同的窗口之间是如何传递数据. 单一窗口数据传递 对于单一窗口的程序来说,一个控件的变化会影响另一个控件的变化通过信号与槽的机制就可简单解决. import sys from PyQt5.QtWidgets import QWidget, QLCDNumber, QSlider, QVBoxLayout, QAppl

  • Java替换int数组中重复数据的方法示例

    本文实例讲述了Java替换int数组中重复数据的方法.分享给大家供大家参考,具体如下: package test; import java.util.HashSet; public class TestList { /** * 根据传递过来的参数过滤掉重复数据 * @param number:需要过滤掉的数据 * @return:筛选好的新数组 */ public static int[] Filter(int[] number){ HashSet<Integer> hs=new HashSe

  • JavaScript按日期查询MongoDB中的数据的要点示例

    group by date 聚合查询日期 统计每天数据(信息量) 1 { "_id" : ObjectId("557ac1e2153c43c320393d9d"), "msgType" : "text", "sendTime" : ISODate("2015-06-12T11:26:26.000Z") } 2 { "_id" : ObjectId("557a

  • Android获取assets文件夹中的数据并写入SD卡示例

    本文示例主要实现了Android获取assets文件夹中的数据并将其写入到SD卡中,该程序实现的步骤主要为:首先读取assets文件夹中的数据库,再将其写入到SD存储卡中. 完整示例代码如下: import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import android.content.Context; /*将assets文件

  • vue2.0 + element UI 中 el-table 数据导出Excel的方法

    1.安装相关依赖 主要是两个依赖 npm install --save xlsx file-saver 如果想详细看着两个插件使用,请移步github. https://github.com/SheetJS/js-xlsx https://github.com/eligrey/FileSaver.js 2.组件里头引入 import FileSaver from 'file-saver' import XLSX from 'xlsx' 3.组件methods里写一个方法 exportExcel

  • vue2.0 获取从http接口中获取数据,组件开发,路由配置方式

    vue 2.0 从接口中获取数据 <template> <div id="admins"> <h1>I am a title.</h1> <a> written by {{ author }} </a> <div v-for="admin in users"> {{admin.name}}<br>{{admin.password}} </div> </d

随机推荐