写作不易,Star是最大鼓励,感觉写的不错的可以给个Star⭐,请多多指教。本博客的Github地址

[TOC]

基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="container"></div>
    <script src="./vue.global.js"></script>
    <script>
        // 获取鼠标位置,这个功能在很多地方都需要被用到
        function usePositon() {
            const position = Vue.reactive({x: 0, y: 0});
            function update(e) {
                position.x = e.pageX;
                position.y = e.pageY;
            }
            Vue.onMounted(() => {
                window.addEventListener('mousemove', update);
            });
            Vue.onUnmounted(() => {
                window.removeEventListener('mousemove', update);
            });
            // toRefs将基本数据类型转为引用数据类型
            return Vue.toRefs(position);
        }
        // composition api 多个方法进行组合来使用
        const App = {
            setup() { // 相当于created,只会执行一次
                const state = Vue.reactive({name: 'kobe'});
                const {x, y} = usePositon();
                function change() {
                    state.name = 'james';
                }
                return { // 这个对象会作为渲染的上下文
                    state,
                    change,
                    x,
                    y
                };
            },
            template: `<div @click="change">
                    {{state.name}}
                    x: {{x}}
                    y: {{y}}
            </div>`
        };
        Vue.createApp().mount(App, container);
    </script>
</body>
</html>
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

基于Vue.reactive实现响应式数据

<script src="./vue.global.js"></script>
<script>
    const proxy = Vue.reactive({name: 'kobe'});
    Vue.effect(() => {
        console.log(proxy.name);
    });
    proxy.name = 'james';
</script>
1
2
3
4
5
6
7
8
// Vue3.0响应式原理
// Vue2.x缺陷:
// 1. 默认递归,性能不好;2. 数组改变是监听不到的;3. 对象新增的属性不能被拦截
function isObject(val) {
    return typeof val === 'object' && val !== null;
}
// 1. 响应式的核心方法
function reactive(target) {
    // 创建响应式对象
    return createReactiveObject(target);
}

function createReactiveObject(target) {
    // 如果当前不是对象,直接返回即可
    if (!isObject(target)) {
        return target;
    }
    // Reflect优势:不会报错,有返回值,将来会替代Object上的方法
    const baseHandler = {
        get(target, key, receiver) {
            console.log('取值');
            const res = Reflect.get(target, key, receiver);
            if (isObject(target[key])) {
                return reactive(res); // 是个递归,如果取值是一个对象的话,继续代理(主要针对多层代理的,比如对象的属性还是对象)
            }
            return res;
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver);
            console.log('设置值');
            return res;
        },
        deleteProperty(target, key) {
            const res = Reflect.set(target, key);
            console.log('删除值');
            return res;
        }
    };
    const observed = new Proxy(target, baseHandler);
    return observed;
}

// 代理对象
const proxy = reactive({name: 'kobe', age: {value: 12}}); // 多层代理
// console.log(proxy.name);
// proxy.name = 'james';
proxy.age.value = 13;
// console.log(proxy.age.value);
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 核心基于proxy
// 基于Vue.reactive实现响应式数据
// Proxy兼容性差,ie11都不支持
// 创建两个哈希表
// 弱引用映射表
const toProxy = new WeakMap(); // 存放的是代理后的对象 原对象:代理过的对象
const toRaw = new WeakMap(); // 存放的是代理前的对象 代理过的对象:原对象
function trigger() {
    console.log('视图更新了');
}
function isObject(target) {
    return typeof target === 'object' && target !== null;
}
function reactive(target) {
    // 如果目标不是对象,则不进行代理
    if (!isObject(target)) {
        return target;
    }
    // 如果代理表中已经存在了,就把这个结果返回
    // 如果已经代理过了,就将代理过的结果返回即可
    const proxy = toProxy.get(target);
    if (proxy) {
        return proxy;
    }
    // 如果这个对象已经被代理过了,就把对象原封不动返回
    // 防止代理过的对象再次被代理
    if (toRaw.has(target)) {
        return target;
    }
    const handlers = { // 触发的方法
        set(target, key, value, receiver) {
            // console.log(key);
            // 屏蔽掉非私有属性的更新,比如屏蔽length属性的更新
            if (target.hasOwnProperty(key)) {
                trigger();
            }
            // Reflect有返回值,是个布尔值,表示操作是否成功
            return Reflect.set(target, key, value, receiver);
        },
        /**
         * @param {*} target 源对象
         * @param {*} key 对象的属性
         * @param {*} receiver 代理对象
         */
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver);
            if (isObject(target[key])) {
                return reactive(res); // 是个递归,如果取值是一个对象的话,继续代理
            }
            return res;
        },
        deleteProperty(target, key) {
            return Reflect.deleteProperty(target, key);
        }
    };
    // 操作代理对象的时候会触发handlers对应的方法
    let observed = new Proxy(target, handlers);
    toProxy.set(target, observed); // 将代理对象存储在哈希表作记录,防止重复代理
    toRaw.set(observed, target);
    console.log('proxy');
    // 返回代理后的对象
    return observed;
}

let obj = {
    name: 'lisi',
    arr: [1, 2, 3]
};

let p = reactive(obj);
p = reactive(obj);
p = reactive(obj);
// p.name = 'wangwu';
p.arr.push(4);

// let arr = [1, 2, 3];
// let p = reactive(arr);
// p.push(4); // 会触发length属性更新

// 原对象也发生了变化
console.log(obj); // { name: 'wangwu', arr: [ 1, 2, 3 ] }
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

参考文档

  1. 简析vue 3 的数据响应系统
  2. 从零实现Vue3.0响应式源码