Skip to content

Vue3.0 六大亮点:

  • Performance:性能比 Vue2.x 快 2.2 倍。

  • Tree shaking support:按需编译,体积比 Vue2.x 更小。

  • Composition API:组合 API (类似 React Hooks)。

  • Better TypeScript support:更好的 TS 支持。

  • Custom Renderer API:暴露了自定义渲染 API

  • Fragment,Teleport(Protal),Suspense:更先进的组件。

Vue3.0 是如何变快的。

diff 方法优化

  • Vue2.x 中虚拟 dom 是进行全量的对比。

  • Vue3.0 新增了静态标记(PatchFlag)。

    在与上次虚拟节点进行对比时候,只对比带有 patch flag 的节点。

    并且可以通过 flag 的信息得知当前节点要对比的具体内容。

hoistStatic 静态提升

  • Vue2.x 中无论元素是否参与更新,每次都会重新创建。

  • Vue3.0 中对于不参与更新的元素,只会被创建一次,之后会在每次渲染的时候被不停的复用。

cacheHandlers 事件侦听器缓存

  • 默认情况下 onClick 会被视为动态绑定,所以每次都会去追踪它的变化。但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可。

SSR 渲染

  • 当有大量静态内容的时候,这些内容会被当做纯字符串推进一个 buffer 里面,即使存在动态的绑定,会通过模板插值嵌入进去。这样比虚拟 dom 来渲染的快上很多。

  • 当静态内容达到一定量级时候,会用 _createStaticVNode 方法在客户端生成一个 static node,这些静态 node,会被直接 innerHTML,就不需要创建对象,然后根据对象渲染。

组合 API

  • setup 函数是组合 API 入口函数。
import { ref } from 'vue'

    setup() {
        let count = ref(0)

        function fn() {
            console.log(count)
        }

        return { count, fn };
    }
import { ref } from 'vue'

    setup() {
        let count = ref(0)

        function fn() {
            console.log(count)
        }

        return { count, fn };
    }

组合 API 中定义变量或者函数必须 return 暴露给外界;定义方法不必定义到 methods中, 直接在组合 API 中定义即可。

Ref 只能监听简单类型的变化,不建议监听复杂类型(数组/对象等)的变化。

Reactive 可以监听复杂类型的变化。

setup() {
    let list = reactive({
        lists: [{id: 1, name: 'zs'}]
    })

    return {list}
}
setup() {
    let list = reactive({
        lists: [{id: 1, name: 'zs'}]
    })

    return {list}
}

组合 API 本质:

setup 中注册的变量或函数,会自动注册到 vue2.x 中的 datamethods 中。

注意点:

  • 执行setup 函数,是在 beforeCreate 钩子之前完成的。

  • setup 函数中,无法使用 datamethods

  • setup 函数中,this 指向 undefined

  • setup 函数只能是同步的不能是异步的。

Reactive

什么是 Reactive

  • ReactiveVue3.0 中提供的实现响应式数据的方法。

  • Vue2.0 中响应式数据是通过 defineProperty 实现,在 Vue3.0 中响应式数据通过 ES6Propxy 实现。

Reactive 注意点:

  • Reactive 函数是对象(jsonarr)。

  • 在创建响应式数据时,传递的不是对象,则无法更新视图。

  • 如果给 Reactive 传递其他对象

    • 默认情况下修改对象,界面不会自动更新。

    • 如果想更新,可以通过重新赋值的方式。

setup() {
    let state = reactive({
        time: new Date()
    })

    function fn() {
        const newTime = new Date(state.time.getTime())
        newTime.setDate(state.time.getDate() + 1)
        state.time = newTime
    }

    return { state, fn}
}
setup() {
    let state = reactive({
        time: new Date()
    })

    function fn() {
        const newTime = new Date(state.time.getTime())
        newTime.setDate(state.time.getDate() + 1)
        state.time = newTime
    }

    return { state, fn}
}

Ref

什么是 Ref

  • Ref 是用来实现响应式数据的方法。

  • 由于 Reactive 传入一个对象,导致在开发中如果只想让某个变量实现响应式时会比较麻烦,因此 Vue3.0 提供了 Ref 方法,实现对简单值的监听。

Ref 本质:

  • Ref 底层的本质还是 Reactive

系统会自动根据给 Ref 传入的值将它转化为 ref(xx) --> reactive({value: xx})

ref 注意点:

  • Vue 中使用 Ref 的值,不用通过 value 获取。

  • JS 中使用 Ref 的值必须通过 value 获取。

refreactive 区别

  • 如果在 template 中使用 Ref 类型的数据,Vue 会自动帮我们添加 .value

  • 如果在 template 中使用 Reactive 类型的数据,Vue 不会自动帮我们添加 .value

如何决定是否需要自动添加 .vlue

  • Vue 解析数据之前,会自动判断这个数据是否是 Ref 类型的;如果是,就自动添加 .value,如果不是就不自动添加 .value

如何判断当前的数据是否是 Ref 类型

  • 通过当前数据的 __v_ref 来判断的。如果有这个私有属性,并且取值为 true,就代表是一个 Ref 类型的数据。

如何自行判断是 Ref,还是 Reactive

  • 通过 isRefisReactive
import {ref, isRef, isReactive} from 'vue'

        setup() {
            let count = ref(18)

            console.log(isRef(count))
            console.log(isReactive(count))
            return {count}
        }
import {ref, isRef, isReactive} from 'vue'

        setup() {
            let count = ref(18)

            console.log(isRef(count))
            console.log(isReactive(count))
            return {count}
        }

递归监听

默认情况下,无论是通过 Ref 还是 Reactive 都是递归监听。

递归监听存在的问题:

  • 如果数据量较大时,非常消耗性能。

创建递归监听,如:

<template>
        <p>{{state.a}}</p>
    </template>

    import {reactive} from 'vue'

    export default {
        setup() {
            let state = reactive({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

            state.a = 1;
            state.gf.b = 2

            console.log(state.a, state, state.gf.b, state.gf)
            return {state}
        }
    }
<template>
        <p>{{state.a}}</p>
    </template>

    import {reactive} from 'vue'

    export default {
        setup() {
            let state = reactive({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

            state.a = 1;
            state.gf.b = 2

            console.log(state.a, state, state.gf.b, state.gf)
            return {state}
        }
    }

监听到每一层数据变化, 界面 UI 更新数据。

非递归监听

非递归监听:只能监听第一层, 不能监听其他层;即只有第一层包装成 proxy

创建非递归监听,使用 shallowReactiveshallowRef

注意点:

  • 如果是通过 shallowReactive 创建数据,只要第一层数据变化,就会更新界面 UI 第二层,第三层等等数据;如果第一层数据不更新,界面 UI 不会更新。

  • 如果是通过 shallowRef 创建数据,Vue 监听的是 .value 的变化,并不是第一层数据的变化。

  • 如果是通过 shallowRef 创建数据,想监听第 n 层数据,并主动更新 UI 界面,这时使用 triggerRef方法。

  • Vue3.0 只提供了 triggerRef 方法,没有提供 triggerReactive 方法;因此如果使用 triggerReactive 类型的数据,无法主动触发更新界面。

<template>
        <p>{{ state.a }}</p>
        <p>{{ state.gf.b }}</p>
    </template>

    import {reactive} from 'vue'

    export default {
        setup() {
            let state = shallowReactive({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

            /*
            let state = shallowRef({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

           // state.a.value = 1;
           // state.gf.b.value = 2

            state.gf.f.s.d.value = 4;
            triggerRef(state)

            */

            state.a = 1;
            state.gf.b = 2

            console.log(state.a, state, state.gf.b, state.gf)
            return {state}
        }
    }
<template>
        <p>{{ state.a }}</p>
        <p>{{ state.gf.b }}</p>
    </template>

    import {reactive} from 'vue'

    export default {
        setup() {
            let state = shallowReactive({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

            /*
            let state = shallowRef({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

           // state.a.value = 1;
           // state.gf.b.value = 2

            state.gf.f.s.d.value = 4;
            triggerRef(state)

            */

            state.a = 1;
            state.gf.b = 2

            console.log(state.a, state, state.gf.b, state.gf)
            return {state}
        }
    }

shallowRef 的本质

shallowRef 底层调用 shallowReactive,即: shallowRef(18) --> shallowReactive({value: 18})

如果是通过 shallowRef 创建的数据,它监听的是 .value 的变化。因为底层本质上 .value 才是第一层。

import {reactive} from 'vue'

    export default {
        setup() {
            let state = shallowReactive({
                value: {
                    a: 'a',
                    gf: {
                        b: 'b',
                        f: {
                            c: 'c',
                            s: {
                                d: 'd'
                            }
                        }
                    }
                }
            })

            /*
            let state = shallowRef({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

            state.a.value = 1;
            state.gf.b.value = 2

            */

            state.a = 1;
            state.gf.b = 2

            console.log(state.a, state, state.gf.b, state.gf)
            return {state}
        }
    }
import {reactive} from 'vue'

    export default {
        setup() {
            let state = shallowReactive({
                value: {
                    a: 'a',
                    gf: {
                        b: 'b',
                        f: {
                            c: 'c',
                            s: {
                                d: 'd'
                            }
                        }
                    }
                }
            })

            /*
            let state = shallowRef({
                a: 'a',
                gf: {
                    b: 'b',
                    f: {
                        c: 'c',
                        s: {
                            d: 'd'
                        }
                    }
                }
            })

            state.a.value = 1;
            state.gf.b.value = 2

            */

            state.a = 1;
            state.gf.b = 2

            console.log(state.a, state, state.gf.b, state.gf)
            return {state}
        }
    }

toRaw

例子:

<template>
        <p>{{obj.name}}</p>
    </template>

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            obj.name = 'ming'

            return {obj}
        }
    }
<template>
        <p>{{obj.name}}</p>
    </template>

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            obj.name = 'ming'

            return {obj}
        }
    }

修改名字数据发生改变,UI 没有更新,即不是响应式数据。如果先变成响应式使用 Ref 或者 Reactive。

<template>
        <p>{{state.name}}</p>
    </template>

    import {reactive} from 'vue'

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            //obj.name = 'ming'

            let state = reactive(obj);
            state.name = 'ming'

            console.log(obj == state) // false

            return {state}
        }
    }
<template>
        <p>{{state.name}}</p>
    </template>

    import {reactive} from 'vue'

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            //obj.name = 'ming'

            let state = reactive(obj);
            state.name = 'ming'

            console.log(obj == state) // false

            return {state}
        }
    }

从上述得知,state 与 obj 是引用关系,state 引用了 obj。

如果直接修改 obj,是无法触发界面更新。

只有通过包装之后的对象修改,才会触发界面更新。

从 Reactive 中获取原始数据

<template>
        <p>{{state.name}}</p>
    </template>

    import {reactive, toRaw} from 'vue'

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            let state = reactive(obj);

            state.name = 'ming'

            let obj2 = toRaw(state)

            console.log(obj2, obj == obj2)

            return {state}
        }
    }
<template>
        <p>{{state.name}}</p>
    </template>

    import {reactive, toRaw} from 'vue'

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            let state = reactive(obj);

            state.name = 'ming'

            let obj2 = toRaw(state)

            console.log(obj2, obj == obj2)

            return {state}
        }
    }

Ref 或 Reactive 数据类型,每次修改都会被追踪,UI 界面都会被更新,这样非常消耗性能。如果有一些不需要追踪,不需要更新 UI 界面,这时就可以通过 toRaw 方法拿到原始数据,对原始数据进行修改,就不会被追踪,不会更新 UI 界面,性能提高。

从 Ref 中获取原始数据

<template>
        <p>{{state.name}}</p>
    </template>

    import {ref, toRaw} from 'vue'

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            let state = ref(obj);

            state.name = 'ming'

            let obj2 = toRaw(state.value)

            console.log(obj2, obj == obj2)

            return {state}
        }
    }
<template>
        <p>{{state.name}}</p>
    </template>

    import {ref, toRaw} from 'vue'

    export default {
        setup() {
            let obj = { name: 'zs', age: 18 }
            let state = ref(obj);

            state.name = 'ming'

            let obj2 = toRaw(state.value)

            console.log(obj2, obj == obj2)

            return {state}
        }
    }

从 Ref 中获取原始数据必须添加 .value,因为 Ref 底层是使用 Reactive。

markRaw

如果原始值永远不想被追踪变化,使用 markRaw。

例子:

<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, markRaw} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			obj = markRaw(obj)
			let state = reactive(obj)

			state.name = 'cherry'

			return {state}
		}

	}
<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, markRaw} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			obj = markRaw(obj)
			let state = reactive(obj)

			state.name = 'cherry'

			return {state}
		}

	}

toRef

和 Ref 一样,都是创建响应式数据。

使用 Ref 打印输出值:

<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, ref} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = ref(obj.name)

			state.value = 'cherry'
			console.log(obj, state)

			return {state}
		}

	}
<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, ref} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = ref(obj.name)

			state.value = 'cherry'
			console.log(obj, state)

			return {state}
		}

	}

结论:

  • 如果使用 Ref 将某一个对象中的属性变成响应式的数据,修改响应式数据是不会影响到原始数据。

  • 如果响应式数据通过 Ref 创建,修改了数据并会触发 UI 界面更新。

  • 相当于 Ref 是复制一份原始值。

使用 roRef 打印输出值:

<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, toRef} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = toRef(obj, 'name')

			console.log(state)
			state.value = 'cherry'
			console.log(obj, state)

			return {state}
		}

	}
<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, toRef} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = toRef(obj, 'name')

			console.log(state)
			state.value = 'cherry'
			console.log(obj, state)

			return {state}
		}

	}

结论:

  • 如果使用 toRef 将某一个对象中的属性变成响应式的数据,修改响应式数据会影响到原始数据。

  • 如果响应式数据通过 toRef 创建,修改了数据并不会触发 UI 界面更新。

  • toRef 的本质是引用原始值。

应用场景:

  • 如果让响应式数据和以前的数据关联起来,并且更新响应式数据之后并不想更新 UI 界面,就可以使用 toRef。

roRefs

如果数据是多个字段,使用 toRef 就必须写多个字段重新赋值:

<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, toRef} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = toRef(obj, 'name')
			let age = toRef(obj, 'age')

			state.value = 'cherry'
			age.value = 20
			console.log(obj, state)

			return {state}
		}

	}
<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, toRef} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = toRef(obj, 'name')
			let age = toRef(obj, 'age')

			state.value = 'cherry'
			age.value = 20
			console.log(obj, state)

			return {state}
		}

	}

上述可以通过 toRefs 简化,底层逻辑还是通过 toRef 实现。

<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, toRefs} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = toRefs(obj)

			console.log(obj, state)
			state.name.value = 'cherry'
			state.age.value = 20

			return {state}
		}

	}
<template>
		<p>{{state}}</p>
	</teamplate>

	import {reactive, toRefs} from 'vue'

	export default{
		setup() {
			let obj = {name: 'zs', age: 18}
			let state = toRefs(obj)

			console.log(obj, state)
			state.name.value = 'cherry'
			state.age.value = 20

			return {state}
		}

	}

customRef

理解为自定义 Ref。

返回一个 Ref 对象,可以显式的控制以来追踪和触发相应。

<template>
		<p>{{state}}</p>
	</teamplate>

	import {ref, customRef} from 'vue'

	export default{
		setup() {
			let state = myRef(18)

			state.value = 20;

			return {state}
		}

	}


	function myRef(value) {
		return customRef((track, trigger) => {
			//track -> 追踪
			//trigger -> 触发
			return {
				get(){
					track() //告诉Vue 这个数据是需要追踪变化
					return value
				},
				set(newVal) {
					value = newVal
					trigger() // 告诉Vue触发界面更新
				}
			}
		})
	}
<template>
		<p>{{state}}</p>
	</teamplate>

	import {ref, customRef} from 'vue'

	export default{
		setup() {
			let state = myRef(18)

			state.value = 20;

			return {state}
		}

	}


	function myRef(value) {
		return customRef((track, trigger) => {
			//track -> 追踪
			//trigger -> 触发
			return {
				get(){
					track() //告诉Vue 这个数据是需要追踪变化
					return value
				},
				set(newVal) {
					value = newVal
					trigger() // 告诉Vue触发界面更新
				}
			}
		})
	}

具体查看 Vue3.0 官方文档 API

Ref 获取元素

在 Vue2.x 中,可以通过给元素添加 ref='xxx',然后在代码中通过 refs.xxx 方式获取元素。

在 Vue3.0 中,也可以通过 ref 获取元素,使用写法不同:

<template>
		<div ref="box">box</div>
	</template>

	import {ref, onMounted} from 'vue'

	export default {
		setup() {
			let box = ref(null)

			onMounted(() => {
				console.log(box.value) // ②
			})

			console.log(box.value) // ①

			return {box}
		}
	}
<template>
		<div ref="box">box</div>
	</template>

	import {ref, onMounted} from 'vue'

	export default {
		setup() {
			let box = ref(null)

			onMounted(() => {
				console.log(box.value) // ②
			})

			console.log(box.value) // ①

			return {box}
		}
	}

readonly 家族

  • readonly:用于创建一个只读的数据,并且是递归只读。
<template>
		<div>{{ state }}</div>
	</template>

	import {readonly} from 'vue'

	export default {
		setup() {
			const value = {name: 'ls', age: 26}

			let state = readonly({
				name: 'zs',
				attr{
					age: 18,
					height: 1.88
				}
			})

			state.name = 'cherry'
			state.attr.age = 20
			state.attr.height = 1.77


			value.name = 'xing'
			value.age = 66

			console.log(value)

			return {state}
		}
	}
<template>
		<div>{{ state }}</div>
	</template>

	import {readonly} from 'vue'

	export default {
		setup() {
			const value = {name: 'ls', age: 26}

			let state = readonly({
				name: 'zs',
				attr{
					age: 18,
					height: 1.88
				}
			})

			state.name = 'cherry'
			state.attr.age = 20
			state.attr.height = 1.77


			value.name = 'xing'
			value.age = 66

			console.log(value)

			return {state}
		}
	}

结论:

  • 修改响应式数据,修改后的数据没变化,UI 界面数据没变化。

const 和 readonly 区别:

  • const:赋值保护,不能给变量重新赋值

  • readonly:属性保护,不能给属性重新赋值

shallowReadonly:用于创建一个第一层只读的数据。

<template>
		<div>{{ state }}</div>
	</template>

	import {shallowReadonly} from 'vue'

	export default {
		setup() {
			let state = shallowReadonly({
				name: 'zs',
				attr{
					age: 18,
					height: 1.88
				}
			})

			state.name = 'cherry'
			state.attr.age = 20
			state.attr.height = 1.77

			return {state}
		}
	}
<template>
		<div>{{ state }}</div>
	</template>

	import {shallowReadonly} from 'vue'

	export default {
		setup() {
			let state = shallowReadonly({
				name: 'zs',
				attr{
					age: 18,
					height: 1.88
				}
			})

			state.name = 'cherry'
			state.attr.age = 20
			state.attr.height = 1.77

			return {state}
		}
	}

结论:

修改响应式数据,修改后的数据第一层没发生变化,第二层数据发生变化,并且 UI 界面没更新。

isReadonly:用于判断是否是 Readonly。

<template>
		<div>{{ state }}</div>
	</template>

	import {isReadonly,shallowReadonly} from 'vue'

	export default {
		setup() {
			let state = shallowReadonly({
				name: 'zs',
				attr{
					age: 18,
					height: 1.88
				}
			})

			state.name = 'cherry'
			state.attr.age = 20
			state.attr.height = 1.77

			console.log(isReadonly(state))

			return {state}
		}
	}
<template>
		<div>{{ state }}</div>
	</template>

	import {isReadonly,shallowReadonly} from 'vue'

	export default {
		setup() {
			let state = shallowReadonly({
				name: 'zs',
				attr{
					age: 18,
					height: 1.88
				}
			})

			state.name = 'cherry'
			state.attr.age = 20
			state.attr.height = 1.77

			console.log(isReadonly(state))

			return {state}
		}
	}

Vue3 响应式数据本质

  • Vue2.x 是通过 defineProperty 实现响应式数据。

  • Vue3.0 是通过 Proxy 实现响应式数据。

模拟:

let obj = { name: 'zs', age: 18 }

	let state = new Proxy(obj, {
		get(obj, key) {
			return obj[key]
		},
		set(obj, key, newVal) {
			obj[key] = newVal
			return true
		}
	})

	state.name = 'cherry'

	console.log(state)
let obj = { name: 'zs', age: 18 }

	let state = new Proxy(obj, {
		get(obj, key) {
			return obj[key]
		},
		set(obj, key, newVal) {
			obj[key] = newVal
			return true
		}
	})

	state.name = 'cherry'

	console.log(state)

实现 shallowReactive

function shallowReactive(obj) {
		return new Proxy(obj, {
			get() {
				return obj[key]
			},
			set(obj, key, newVal) {
				obj[key] = newVal
				return true
			}
		})
	}


	let obj = {
		a: 'a',
		gf: {
			b: 'b',
			f: {
				c: 'c'
			}
		}
	}

	let state = shallowReactive(obj)

	state.a = 1
	state.gf.b = 2
	state.gf.f.c = 3

	console.log(state)
function shallowReactive(obj) {
		return new Proxy(obj, {
			get() {
				return obj[key]
			},
			set(obj, key, newVal) {
				obj[key] = newVal
				return true
			}
		})
	}


	let obj = {
		a: 'a',
		gf: {
			b: 'b',
			f: {
				c: 'c'
			}
		}
	}

	let state = shallowReactive(obj)

	state.a = 1
	state.gf.b = 2
	state.gf.f.c = 3

	console.log(state)

实现 shallowRef

function shallowRef(val) {
		return shallowReactive(obj: {value: val})
	}

	let state = shallowRef(obj)

	state.value.a = 1
	state.value.gf.b = 2
	state.value.gf.f.c = 3

	console.log(state)
function shallowRef(val) {
		return shallowReactive(obj: {value: val})
	}

	let state = shallowRef(obj)

	state.value.a = 1
	state.value.gf.b = 2
	state.value.gf.f.c = 3

	console.log(state)

实现 Reactive

function Reactive(obj) {

		if(typeof obj === 'object') {

			if(obj instanceof Array) {
				// 如果是一个数组,那取出数组中的每一个元素
				// 判断每一个元素是否又是一个对象,如果又是一个对象,那么也需要包装Proxy
				obj.forEach((item, index) => {
					if(typeof item === 'object') obj[index] = Reactive(item)
				})

			}else{
				// 如果是一个对象,那么取出对象属性的取值
				// 判断对象属性的取值是否又是一个对象,如果是一个对象,那么也需要包装成 Proxy

				for(let key in obj) {
					let item = obj[key]
					if(typeof item === 'object') obj[key] = Reactive(item)
				}
			}

			return new Proxy(obj, {
				get() {
					return obj[key]
				},
				set(obj, key, newVal) {
					obj[key] = newVal
					return true
				}
			})
		}else{
			console.log(`${obj} is not object`)
		}
	}


	let obj = {
		a: 'a',
		gf: {
			b: 'b',
			f: {
				c: 'c'
			}
		}
	}

	let state = Reactive(obj)

	state.a = 1
	state.gf.b = 2
	state.gf.f.c = 3

	console.log(state)
function Reactive(obj) {

		if(typeof obj === 'object') {

			if(obj instanceof Array) {
				// 如果是一个数组,那取出数组中的每一个元素
				// 判断每一个元素是否又是一个对象,如果又是一个对象,那么也需要包装Proxy
				obj.forEach((item, index) => {
					if(typeof item === 'object') obj[index] = Reactive(item)
				})

			}else{
				// 如果是一个对象,那么取出对象属性的取值
				// 判断对象属性的取值是否又是一个对象,如果是一个对象,那么也需要包装成 Proxy

				for(let key in obj) {
					let item = obj[key]
					if(typeof item === 'object') obj[key] = Reactive(item)
				}
			}

			return new Proxy(obj, {
				get() {
					return obj[key]
				},
				set(obj, key, newVal) {
					obj[key] = newVal
					return true
				}
			})
		}else{
			console.log(`${obj} is not object`)
		}
	}


	let obj = {
		a: 'a',
		gf: {
			b: 'b',
			f: {
				c: 'c'
			}
		}
	}

	let state = Reactive(obj)

	state.a = 1
	state.gf.b = 2
	state.gf.f.c = 3

	console.log(state)

实现 Ref

function Ref(val) {
		return Reactive(obj: {value: val})
	}
function Ref(val) {
		return Reactive(obj: {value: val})
	}