vue 手写篇

实现一个迷你版的reactivity

1let activeEffect = undefined;
2const targetMap = new WeakMap();
3
4const effect = function (fn) {
5    activeEffect = fn;
6    fn();
7}
8
9const track = function (target, key) {
10    const dep = targetMap.get(target) || new Map();
11    const funcs = dep.get(key) || new Set();
12    funcs.add(activeEffect);
13    dep.set(key, funcs);
14    targetMap.set(target, dep);
15}
16
17const trigger = function (target, key) {
18    const dep = targetMap.get(target);
19    if (dep && dep.get(key)) {
20        dep.get(key).forEach(fn => fn());
21    }
22}
23
24const reactive = function (target) {
25    return new Proxy(target, {
26        get(target, key, reciever) {
27            track(target, key);
28            return Reflect.get(target, key, reciever);
29        },
30        set(target, key, value, reciever) {
31            Reflect.set(target, key, value, reciever);
32            trigger(target, key);
33        }
34    });
35}
36
37const person = {
38    firstName: 'Lu',
39    lastName: 'xun',
40    get name() {
41        return this.firstName + this.lastName;
42    }
43}
44
45const proxy = reactive(person);
46
47effect(() => {
48    console.log('effect1', proxy.name)
49});
50effect(() => {
51    console.log('effect2', proxy.name)
52});
53
54proxy.firstName = 'Zhang';

实现一个observer方法——要监听的对象属性可配置

1// 实现一个observer方法——要监听的对象属性可配置
2// https://juejin.cn/post/7114588899661316126
3function observer(obj, path, cb) {
4    path.forEach((key) => {
5        let value = "";
6        if (key) {
7            const _key = key.split(".");
8            if (_key.length > 1) {
9                let tmp = obj;
10                // 循环,为了取到属性值,不断地赋值给 tmp
11                _key.forEach((k) => {
12                    // 递归,对每一个层级都进行劫持,要不然无法监听到深层级的属性值变化
13                    observer(tmp, [k], cb);
14                    tmp = tmp[k];
15                });
16                value = tmp;
17            } else {
18                value = obj[key];
19            }
20
21            Object.defineProperty(obj, key, {
22                get() {
23                    return value;
24                },
25                set(newV) {
26                    if (newV !== value) {
27                        cb && cb(newV, value);
28                        value = newV;
29                    }
30                },
31            });
32        }
33    });
34}
35
36var o = {
37    a: 1,
38    b: 2,
39    c: {
40        x: 1,
41        y: 2,
42    },
43};
44
45observer(o, ['a', "c.x"], (v, prev) => {
46    console.log("newV=", v, " oldV=", prev);
47});
48
49o.a = 2; // 2, 1
50o.b = 3; // 不打印
51o.c.x = 3; // 3, 1
52o.c.y = 3; // 没有没监听,不打印

手写vue的patch方法

1//简单实现vue的更新方法,递归实现
2function update(obj, key, value) {
3    if (typeof obj !== 'object' || obj === null) {
4        return;
5    }
6    // 递归遍历对象属性
7    for (let prop in obj) {
8        if (obj.hasOwnProperty(prop)) {
9            if (typeof obj[prop] === 'object') {
10                update(obj[prop], key, value); // 递归调用更新方法
11            } else if (prop === key) {
12                obj[prop] = value; // 更新属性值
13            }
14        }
15    }
16}
17
18// 示例用法
19const data = {
20    name: 'John',
21    age: 30,
22    address: {
23        city: 'New York',
24        state: 'NY',
25        country: 'USA'
26    }
27};
28
29console.log('Before update:', data);
30update(data, 'city', 'San Francisco');
31console.log('After update:', data);

模板字符串解析

1let template = "我是{{name}},年龄{{age}},性别{{sex}},子姓名:{{nest.subName}}";
2let data = {
3    name: "姓名1",
4    age: 18,
5    nest: {
6        subName: '姓名1-1'
7    }
8};
9
10function render(template, data) {
11    return template.replace(/\{\{(.+?)\}\}/g, function (match, key) {
12        const keys = key.split('.'); // Split the key by dot to handle nested properties
13        let value = data;
14
15        // Traverse the nested properties
16        for (const k of keys) {
17            value = value[k];
18            if (value === undefined) {
19                break; // Stop if any intermediate property is undefined
20            }
21        }
22
23        return value !== undefined ? value : '';
24    });
25}
26
27console.log(render(template, data));
28// Output: 我是姓名1,年龄18,性别,子姓名:姓名1-1

vue2发布订阅响应式实现

1function isObject(val) {
2  return val !== null && !Array.isArray(val) && typeof val === "object";
3}
4
5function observe(obj) {
6  if (!isObject(obj)) {
7    return;
8  }
9  Object.keys(obj).forEach((key) => {
10    var dep = new Dep();
11    // 重新定义属性
12    var internalValue = obj[key]; // 缓存该属性的值
13    observe(internalValue); // 递归监听该属性
14    Object.defineProperty(obj, key, {
15      get() {
16        dep.depend(); // 看一下,是哪个函数用到了我这个属性,将该函数记录下来
17        return internalValue;
18      },
19      set(val) {
20        observe(val);
21        internalValue = val;
22        dep.notify(); // 通知所有用到我这个属性的函数,全部重新运行
23      },
24    });
25  });
26}
27
28function Dep() {
29  this.subscribes = new Set(); // 用于记录依赖
30}
31
32Dep.prototype.depend = function () {
33  if (activeUpdate) {
34    this.subscribes.add(activeUpdate);
35  }
36};
37
38Dep.prototype.notify = function () {
39  this.subscribes.forEach((fn) => fn());
40};
41
42var activeUpdate = null; // 当前正在收集依赖的函数
43
44function autorun(fn) {
45  function updateWrapper() {// 防止后面的依赖收集不到,所以谈一层函数 
46    activeUpdate = updateWrapper;
47    fn(); // 该函数的运行期间,activeUpdate一定有值
48    activeUpdate = null;
49  }
50  updateWrapper();
51}
52
53var state = {
54  name: "monica",
55  age: 18,
56  addr: {
57    province: "黑龙江",
58    city: "哈尔滨",
59  },
60};
61
62observe(state);
63
64autorun(() => {
65  state.age;
66  state.name
67  console.log('autorun')
68});
69
70state.age = 19;
71state.name = "Kevin Woo";
72// state.addr.province = "四川";
73// state.addr.city = "成都";
74
75// 设置没有的属性,删除属性监控不到,所以要用$set,$delete