Appearance
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
中的 data
或 methods
中。
注意点:
执行
setup
函数,是在beforeCreate
钩子之前完成的。在
setup
函数中,无法使用data
和methods
。在
setup
函数中,this
指向undefined
。setup
函数只能是同步的不能是异步的。
Reactive
什么是 Reactive
。
Reactive
是Vue3.0
中提供的实现响应式数据的方法。在
Vue2.0
中响应式数据是通过defineProperty
实现,在Vue3.0
中响应式数据通过ES6
的Propxy
实现。
Reactive
注意点:
Reactive
函数是对象(json
或arr
)。在创建响应式数据时,传递的不是对象,则无法更新视图。
如果给
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
获取。
ref
与 reactive
区别
如果在
template
中使用Ref
类型的数据,Vue
会自动帮我们添加.value
。如果在
template
中使用Reactive
类型的数据,Vue
不会自动帮我们添加.value
。
如何决定是否需要自动添加 .vlue
Vue
解析数据之前,会自动判断这个数据是否是Ref
类型的;如果是,就自动添加.value
,如果不是就不自动添加.value
。
如何判断当前的数据是否是 Ref
类型
- 通过当前数据的
__v_ref
来判断的。如果有这个私有属性,并且取值为true
,就代表是一个Ref
类型的数据。
如何自行判断是 Ref
,还是 Reactive
- 通过
isRef
和isReactive
。
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
。
创建非递归监听,使用 shallowReactive
和 shallowRef
。
注意点:
如果是通过
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})
}