CD's blog CD's blog
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

CD_wOw

内卷的行情,到不了的梦
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Vue2基础及原理

    • Vue2的生命周期
    • Vue2实用方法及原理
    • vue2 的 keepAlive 实现原理
    • Vue2的extend与手动挂载$mount
    • Vue2如何做同构SSR
    • Vue2.x之$set是怎么实现的
    • vue2在TypeScript中如何使用vuex
    • Vue2.x中一些技巧及痛点问题解决方法
    • Vue2实现原理
      • 响应式原理
      • 1.初始化数据
      • 2.递归属性劫持
      • 3.数组方法的劫持
      • 4.数据代理
    • Vue2 题解QA
  • Vue3基础及原理

  • 周边工具

  • Vue笔记
  • Vue2基础及原理
CD
2021-03-18
目录

Vue2实现原理

# 响应式原理

导出vue构造函数

import { initMixin } from "./init";

function Vue(options) {
  this._init(options);
}
initMixin(Vue); // 给原型上新增_init方法
export default Vue;
1
2
3
4
5
6
7

init方法中初始化Vue状态

import { initState } from "./state";
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    vm.$options = options;
    // 初始化状态
    initState(vm);
  };
}
1
2
3
4
5
6
7
8
9

根据不同属性进行初始化操作

export function initState(vm) {
  const opts = vm.$options;
  if (opts.props) {
    initProps(vm);
  }
  if (opts.methods) {
    initMethod(vm);
  }
  if (opts.data) {
    // 初始化data
    initData(vm);
  }
  if (opts.computed) {
    initComputed(vm);
  }
  if (opts.watch) {
    initWatch(vm);
  }
}
function initProps() {}
function initMethod() {}
function initData() {}
function initComputed() {}
function initWatch() {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 1.初始化数据

import { observe } from "./observer/index.js";
function initData(vm) {
  let data = vm.$options.data;
  data = vm._data = typeof data === "function" ? data.call(vm) : data;
  observe(data);
}
1
2
3
4
5
6

# 2.递归属性劫持

class Observer {
  // 观测值
  constructor(value) {
    this.walk(value);
  }
  walk(data) {
    // 让对象上的所有属性依次进行观测
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value);
    }
  }
}
function defineReactive(data, key, value) {
  observe(value);
  Object.defineProperty(data, key, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue == value) return;
      observe(newValue);
      value = newValue;
    },
  });
}
export function observe(data) {
  if (typeof data !== "object" && data != null) {
    return;
  }
  return new Observer(data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 3.数组方法的劫持

import { arrayMethods } from "./array";
class Observer {
  // 观测值
  constructor(value) {
    if (Array.isArray(value)) {
      value.__proto__ = arrayMethods; // 重写数组原型方法
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  }
  observeArray(value) {
    for (let i = 0; i < value.length; i++) {
      observe(value[i]);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 重写数组原型方法

let oldArrayProtoMethods = Array.prototype;
export let arrayMethods = Object.create(oldArrayProtoMethods);
let methods = ["push", "pop", "shift", "unshift", "reverse", "sort", "splice"];
methods.forEach((method) => {
  arrayMethods[method] = function (...args) {
    const result = oldArrayProtoMethods[method].apply(this, args);
    const ob = this.__ob__;
    let inserted;
    switch (method) {
      case "push":
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2);
      default:
        break;
    }
    if (inserted) ob.observeArray(inserted); // 对新增的每一项进行观测
    return result;
  };
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 增加ob属性

class Observer {
  constructor(value) {
    Object.defineProperty(value, "__ob__", {
      enumerable: false,
      configurable: false,
      value: this,
    });
    // ...
  }
}
1
2
3
4
5
6
7
8
9
10

给所有响应式数据增加标识,并且可以在响应式上获取Observer实例上的方法

# 4.数据代理

function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key];
    },
    set(newValue) {
      vm[source][key] = newValue;
    },
  });
}
function initData(vm) {
  let data = vm.$options.data;
  data = vm._data = typeof data === "function" ? data.call(vm) : data;
  for (let key in data) {
    // 将_data上的属性全部代理给vm实例
    proxy(vm, "_data", key);
  }
  observe(data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
编辑 (opens new window)
#Vue2
上次更新: 2023/03/27, 21:41:56
Vue2.x中一些技巧及痛点问题解决方法
Vue2 题解QA

← Vue2.x中一些技巧及痛点问题解决方法 Vue2 题解QA→

最近更新
01
gsap动画库学习笔记 - 持续~
06-05
02
远程组件加载方案笔记
05-03
03
小程序使用笔记
03-29
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CD | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式