Vue3---Composition-API-包括setup、ref等新特性详解-与-实战

93 阅读7分钟

#完整原文地址见简书 #更多完整Vue笔记目录敬请见《前端 Web 笔记 汇总目录(Updating)》



#本文内容提要

  • Composition API 的作用
  • setup函数
  • 例程,打印查看setup内容
  • 非响应引用的案例
  • ref()概念、原理 与 实战
  • reactive()概念、原理 与 实战
  • 使用readonly限制对象的访问权限
  • 使用toRefs()reactive对象进一步封装
  • 多个属性进行解构
  • 多个属性 配合toRefs() 进行解构
  • toRefs()无法处理 undefined的键值
  • 使用toRef()应对上述问题
  • 关于setup函数的三个参数【attrs、slots、emit】
  • 回顾 没有 CompositionAPI时,emit的用法
  • 使用setup的 context.emit 替代 this.$emit
  • 使用Composition API开发 todoList
  • 完善toDoList案例
  • 优化上例的逻辑结构!
  • setup的 computed 计算属性
  • 当然以上是computed 的默认用法,实际上它可以接收一个对象
  • 将上例的处理值换成 Object类型,再例
  • setup 中的 watch 监听
  • setup 中的 watch 监听:监听Object类型
  • setup 中的 watch 监听:监听Object类型的 多个属性
  • setup 中的 watchEffect监听 以及 与 watch 的异同比较
  • 两者都可以用以下的方式,在一个设定的时延之后,停止监听
  • 为 watch 配置 immediate属性,可使具备同watchEffect的 即时性
  • setup 中的 生命周期
  • setup中的provide、inject用法
  • 配合上ref实现 响应特性 以及 readonly实现 单向数据流
  • setup结合ref指令


####Composition API 的作用 **使得相同的、相关的功能代码 可以比较 完整地聚合起来, 提高可维护性、可读性,提高开发效率;
规避 同一个功能的代码, 却散落在 组件定义中的`data、methods、computed、directives、template、mixin`等各处 的问题;**
####setup函数 >**--- Composition API 所有代码编写之前, 都要 建立在setup函数 之上;
--- 在created 组件实例 被完全初始化之前 回调; (所以注意在`setup`函数中, 使用与`this`相关的调用是没有用的)
--- setup函数中的内容, 可以在 该组件的 模板`template` 中直接使用; (如下例程)** ``` Hello World! heheheheheheda
``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7553aa4310974d10a4dc729fb008b2ae~tplv-k3u1fbpfcp-zoom-1.image)
####例程,打印查看setup内容 ``` ``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e368bfde19e9484d86eb8acd47a78677~tplv-k3u1fbpfcp-zoom-1.image)
####由于调用时序的关系,setup中 无法调用this等相关 如变量、methods中 等 其他内容,但是其他内容 却可以调用 setup函数!!【setup生时众为生,众生时setup已生】 ``` ``` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c034eb23844048de80b86f5a3d995374~tplv-k3u1fbpfcp-zoom-1.image)
####非响应引用的案例 **如下,这样没有使用`ref`/`reactive`的写法,是不会有响应的:** ``` Hello World! heheheheheheda
``` 如下,运行之后,两秒延时之后,DOM文本展示并不会自动改成`zhao`,而是一直展示初始化的`guan`:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/419bac60ea2b4d368c1a7eb79c5244cf~tplv-k3u1fbpfcp-zoom-1.image)
####`ref()`概念、原理 与 实战 >**使用`ref`可以 用于处理 `基础类型的数据`,赋能`响应式`;
原理:通过 proxy 将 `数据` 封装成 类似 `proxy({value: '【变量值】'})`这样的一个`响应式引用`, 当`数据`变化时,就会 触发`template`等相关UI的更新
【赋予 非data中定义的变量 以`响应式`的能力 —— 原先,我们是借助Vue的`data函数`,完成`响应式变量`的定义的; 有了`ref`之后,我们可以不借助`data`中的定义, 而直接在`普通的函数`中对`js变量`做`proxy`封装, 就可以对 `普通的js引用` 赋能`响应式`了】;** ``` Hello World! heheheheheheda
``` **运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1699e82d61a04c88bd14c2cc91901c63~tplv-k3u1fbpfcp-zoom-1.image) 两秒后自动变化:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4dd098589c49443788bbb349cde6d75f~tplv-k3u1fbpfcp-zoom-1.image)**
####`reactive()`概念、原理 与 实战 >**使用`reactive()`用于处理 `非基础类型的数据(如Object、Array)`,赋能`响应式`; 原理类似`ref()`,只是处理的数据格式不同而已;**

如下,普通的Object类型是没有响应式的效果的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj.name}}</div>    
        `,
        setup(props, context) {
            // const { ref } = Vue;
            const nameObj = { name: 'guan'};
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);
            return { nameObj }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果,两秒后无反应:

使用reactive()处理Object类型后,具备响应式的能力:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj.name}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive({ name: 'guan'});
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);
            return { nameObj }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果: 两秒后自动变化:

使用reactive()处理Array类型数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj[0]}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive([123, 99, 567]);
            setTimeout(() => {
                nameObj[0] = 666;
            }, 2000);
            return { nameObj }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果: 两秒后自动变化:


####使用`readonly`限制对象的访问权限 **使用`readonly()`封装对象,可以限制对象为`只读权限`;** ``` ``` **运行两秒之后会有相应的报错:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/34984a40737f4ab7b63f60407680a41f~tplv-k3u1fbpfcp-zoom-1.image)**
####错误的解构案例 **如下的解构是行不通的, `const { name } = nameObj;`只能拿到`nameObj`的值,拿不到`proxy`对象;** ``` ```
####注意解构技巧 **赋值时 加上`{}`,会 直取 其 赋值右侧 Object的值;** ``` ``` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6dc097f102ad49d698a58a874b50422a~tplv-k3u1fbpfcp-zoom-1.image)
**直接赋值,便是直接赋值一份引用;** ``` ``` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d3d513f9923540baa994f6a4050fe5b7~tplv-k3u1fbpfcp-zoom-1.image)
####使用`toRefs()`对`reactive `对象进一步封装 >**--- `toRefs() expects a reactive object`; 首先,`toRefs()`需要接受一个`reactive `对象; 即,传给`toRefs()`之前,需要先用`reactive()`进行封装;
>--- 使用`toRefs()`对`reactive `封装的对象 进一步封装, >便可以顺利解构;
>--- 原理:`toRefs()`将类似`proxy({name:'guan'})`的结构, 转化成类似`{ name: proxy( {value:'guan'}) }`的结构, 这里可以看到`name`键的值,其实就类似于`ref`的处理结果;
然后使用`const { name } `对`{ name: proxy( {value:'guan'}) }`进行解构赋值, 左侧`name `变量 拿到的就是`proxy( {value:'guan'}) `这一部分的值, 所以放入DOM节点展示时候, 直接使用`name`即可;
>--- !!!注意: 类似`reactive()`的处理结果, 即`proxy({name:'guan'})`的结构, 放入DOM节点展示时候,需要使用`nameObj.name`的格式; 而类似`ref()`的处理结果, 即`proxy( {value:'guan'})`的结构, 放入DOM节点展示时候,直接使用`nameObj`的格式即可;** ``` ``` **运行两秒后,自动更新UI:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7d7c70c787514a59ab82f6b6ffd4f3bf~tplv-k3u1fbpfcp-zoom-1.image)![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb39318857f843a3821ca526936e6bfb~tplv-k3u1fbpfcp-zoom-1.image)**
####多个属性进行解构 >**注意多个属性解构时的写法 以及 return时的写法;** ``` ``` **运行结果(当然不会自动更新):![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9cae9f8bfacb4ccab57160a528c01093~tplv-k3u1fbpfcp-zoom-1.image)**
####多个属性 配合toRefs() 进行解构 ``` ``` **运行结果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c9e723d45d545d4965f39d833d80dba~tplv-k3u1fbpfcp-zoom-1.image)两秒后自动刷新:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cdaa7e72968d4f30b92bc7e5eb1bab87~tplv-k3u1fbpfcp-zoom-1.image)![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/68902da71a8e44edba98f95d0b1c7294~tplv-k3u1fbpfcp-zoom-1.image)**
####toRefs()无法处理 undefined的键值 **如果意图解构的键, 不存在于`toRefs()`封装的对象中, 使用时会报错:** ``` ``` 运行结果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cbebff154bd24735a40ccf210b13849a~tplv-k3u1fbpfcp-zoom-1.image)
####使用toRef()应对上述问题 `toRef(data, key)`会尝试从`data`中读取`key`对应的键值, 如果读得到,就直接取值, 如果读不到,会赋值undefined,后续可以为之赋实值: ``` ``` **运行两秒后自动更新:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b0083ac2261b4c2e95ee834d12d96884~tplv-k3u1fbpfcp-zoom-1.image)**
####关于setup函数的三个参数 >**setup函数的context参数中的三个属性,即`attrs, slots, emit`; 获取方法(解构`context参数`):** >``` >setup(props, context) { > const { attrs, slots, emit } = context; > return { }; > } >``` #####attrs >**-- 是一个`Proxy`对象; -- 子组件的`none-props属性`都存进`attrs`中;**

如下,父组件调用子组件,传递myfield属性, 子组件没有用props接收,则myfield作为none-props属性被 子组件承接, 这时候会存进setup函数的attrs中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({
        template: `
            <child myfield='heheda' />    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(attrs);
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果:可以看到attrs也是一个Proxy对象 (attrs.myfield)打印取值:

<script>

    const app = Vue.createApp({
        template: `
            <child myfield='heheda' />    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(attrs.myfield);
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>


#####slots >**-- 是一个`Proxy`对象; -- 其中有一个 以为`default`为键的函数, 这个函数会以 `虚拟DOM`的形式, 返回 传给 子组件的`slot插槽` 的`组件`;**
**打印slots属性:** ``` ``` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6dc71d8346414d4aac533b8028dbbb57~tplv-k3u1fbpfcp-zoom-1.image)

打印slots属性中的 default函数, 可见其返回的是虚拟DOM:

<script>

    const app = Vue.createApp({
        template: `
            <child>hehehe</child>    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
            <div><slot/></div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(slots.default());
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>


#####使用setup中 context参数的 slots属性中的 default方法所返回的 虚拟DOM,通过render函数的形式,在setup函数返回,可以覆盖`template`的内容,渲染UI ``` ``` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8abaabc870ab4b9b9f05cd036f90a432~tplv-k3u1fbpfcp-zoom-1.image)
####补充:不使用 Vue3 的这个compositionAPI,子组件只能这样去获取`slots`内容 **通过在其他函数中,使用`this.$slots`的方式调用到`slots`内容** ``` ``` **运行结果如下,可以看到跟`setup`函数的`context.slots`是一样的:** ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7122449c65a849dca9bd614ecd7c239d~tplv-k3u1fbpfcp-zoom-1.image)
#####回顾 没有 CompositionAPI时,emit的用法 ``` ``` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/414fe0bb01e0486082495735d8dd30e4~tplv-k3u1fbpfcp-zoom-1.image)
#####使用setup的 context.emit 替代 this.$emit ``` ``` **运行,点击文本:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f4ec234a0516449a99bc1cb72ba5e06c~tplv-k3u1fbpfcp-zoom-1.image)**
####使用Composition API开发 todoList #####调测input框事件 **setup中, --- `const inputValue = ref('6666');`定义到一个`proxy`对象, 可以将此对象 用于setup外的 template中, 完成`双向绑定`中的一环——`数据字段映射到 UI`!!!;
--- `handleInputValueChange`定义一个方法; 运行时,将对应`触发组件`的 `内容`, 赋值给 `inputValue`, 完成`双向绑定`中的另一环——`UI 映射到数据`!!!; template中, `:value="inputValue"`使得对象的内容 初始化显示`inputValue`的内容;
--- ` @input="handleInputValueChange"`使得输入框被用户输入新的内容时, 会调用对应的方法,这里调用:** ``` ``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7cee6fb6706b497aab4a47d4515c57d2~tplv-k3u1fbpfcp-zoom-1.image)
####完善toDoList案例 **--- setup 中定义 数组list,以及 handleSubmit处理函数, 并都在return中返回;
--- template中, button添加click事件回调; li 标签,使用v-for,完成列表渲染:** ``` Hello World! heheheheheheda
``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/293d5d78126042ffb2608b7e096afe7b~tplv-k3u1fbpfcp-zoom-1.image)
####优化上例的逻辑结构! >**如下,将 setup()中, >--- 与 list 相关的定义和操作函数, >封装到 `listHandleAction`中,然后return;
>--- 与 inputValue 相关的定义和操作函数, >封装到 `inputHandleAction`中,然后return;
>--- 最后 `setup()` 调用以上两个`业务模块封装函数`, >解构`返回的内容`,进行`中转调用`;
>【分模块 聚合`业务逻辑`,只留一个`核心方法`进行`统筹调度`,有`MVP`那味了】 >【这样设计,业务分明,方便定位问题,可读性、可维护性高!! 】** ``` ``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/73c6bce40db94985ba36446456d74ac3~tplv-k3u1fbpfcp-zoom-1.image)
####setup的 computed 计算属性 ``` ``` 运行结果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fcf7aeee7cbd40d88e7ff7854f6fffd8~tplv-k3u1fbpfcp-zoom-1.image)
####当然以上是computed 的默认用法,实际上它可以接收一个对象 **这个对象包含两个函数属性, 第一个是`get`函数,其内容即取值时候返回的内容,同默认用法; 第二个是`set`函数,当试图修改`computed`变量的值时,就会回调这个方法, 接收的参数,即`试图修改的值`:**

如下,试图在3秒后修改computed变量countAddFive的值, 这时回调set方法:

<script>

    const app = Vue.createApp({
        setup() {
            const { ref, computed } = Vue;
            const count = ref(0);
            const handleClick = () => {
                count.value += 1;
            }
            const countAddFive = computed({
                get: () => {
                    return count.value + 5;
                },
                set: (param) => {
                    count.value = param -5;
                }
            })

            setTimeout(() => {
                countAddFive.value = 1000;
            }, 2000);

            return { count, countAddFive, handleClick }
        },
        template: `
            <div>
                <div>
                    <span @click="handleClick">{{count}}</span> --- {{countAddFive}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

运行3s后:


####将上例的处理值换成 Object类型,再例 ``` ``` 运行效果同上例;
####setup 中的 watch 监听 **如下, ---`watch`一个参数为要监听的引用, 第二个参数为函数类型,当监听的引用发生变化时会回调, 其有两个参数,一个是当前(变化后的)值,一个是变化前的值;
--- `input`组件中,`v-model`完成`双向绑定`!!!
--- `input`输入内容时,触发 `双向绑定`的特性, 内容映射到`name`引用上, 由`ref`的`响应特性`,`name`的内容又映射到`{{name}}`这`DOM节点`上:** ``` Hello World! heheheheheheda
``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d40e0f92610648f78bf1db0085e164f4~tplv-k3u1fbpfcp-zoom-1.image)
####setup 中的 watch 监听:监听Object类型 #####注意setup的watch的 可监听数据类型 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b2ad96c22ab64f62bd80cb5a8cac9ff0~tplv-k3u1fbpfcp-zoom-1.image) **所以,这里主要是 ---将watch的 第一个参数改成 函数; ---使用toRefs,简化传递过程; (不然template要接收和处理的就是 nameObject.name了,而不是这里的name)** ``` ``` 运行效果同上例;
####setup 中的 watch 监听:监听Object类型的 多个属性 **注意watch的参数写法, 一参写成,以`函数类型`为`元素`的`数组`; 二参,参数列表写成两个数组, 第一个为current值数组,第二个为prev值数组;** ``` Hello World! heheheheheheda
``` 运行,先在Name输入框输入,后再EnglishName框输入,效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d75d117bbd34e53ab354d63e530f8b7~tplv-k3u1fbpfcp-zoom-1.image)
#### setup 中的 watchEffect监听 以及 与 watch 的异同比较 >**函数中,使得纯函数 变成 非纯函数的 异步处理等部分逻辑块, 称之为`effect块`;**
  • --- watch惰性的,只有watch监听的字段发生变化时, watch的处理逻辑才会被回调; --- watchEffect即时性的,也就是除了watch回调特性watchEffect处理逻辑还会在页面渲染完成时立马先执行一次, 即watchEffect监听的字段未曾改变, watchEffect就已经执行了一次; (实例可以见下例运行效果)

  • watch需要写明监听字段watchEffect不需要,直接写处理逻辑即可, 底层封装!自动监听!所写处理逻辑中用到的!所有字段!
    如下例子中, watchEffect的处理逻辑——console.log(nameObj.name, nameObj.englishName);, 仅一行代码, 完成对nameObj.namenameObj.englishName两个字段的监听, 并完成了处理逻辑;

  • watch可以直接从参数列表中获取到之前(变化前)的值当前(变化后)的值watchEffect不行,处理逻辑中拿到的直接就是当前(变化后)的值


  • 两者都可以用以下的方式,在一个设定的时延之后,停止监听
  • watch 配置 immediate属性,可使具备同watchEffect的 即时性
<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, watch, watchEffect, toRefs } = Vue;
            const nameObj = reactive({name: 'heheda', englishName:"lueluelue"});

            watchEffect(() => {
                console.log(nameObj.name, nameObj.englishName);
            })

            const { name, englishName } = toRefs(nameObj);
            return { name, englishName }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
                <div>
                    EnglishName:<input v-model="englishName">
                </div>
                <div>
                    EnglishName is {{englishName}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

跟紧console.log(nameObj.name, nameObj.englishName);, 先在Name输入框输入123,后再EnglishName框输入456,运行效果:

注意第一行打印,第一行是页面渲染完成时立马执行, 用户未曾输入内容,watchEffect监听的字段未曾改变, watchEffect就已经执行了一次,体现watchEffect即时性!!!


#####两者都可以用以下的方式,在一个设定的时延之后,停止监听 >**将`watch / watchEffect`的`函数返回值` 赋给一个字段(如下`stopWatch / stopWatchEffect`); 接着在`watch / watchEffect`的`处理逻辑`中, 编写类似`setTimeout`的异步函数, 并在其中调用 与 刚刚定义的`字段` 同名的 函数(如下`stopWatch() / stopWatchEffect()`), 即可停止`对应的监听`;** ``` ``` **运行,可以看到, 前3s(我们设定的时延)两个输入框是可以响应监听的, 但是3s之后,无论怎么输入内容,console也不会打印log, 因为这时两个监听效果已经取消了:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3f686db100f749f2894fb2e8d714edee~tplv-k3u1fbpfcp-zoom-1.image)**
#####为 `watch` 配置 immediate属性,可使具备同`watchEffect`的 即时性 **如下,使用`{ immediate: true}`为 `watch` 配置 immediate属性, 可使具备同`watchEffect`的 即时性:** ``` ``` **运行效果如下,`undefined`是因第一次执行时, 监听的变量还没有变化, 所以就没有`先前值(prevValue)`的说法,只有`当前值(currentValue)`:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4b84dad731ff43b7bfc78ea187569087~tplv-k3u1fbpfcp-zoom-1.image)**
####setup 中的 生命周期 **--- Vue3.0提供了一些对应生命周期的,可以写在`setup`函数中的回调方法; (具体请看 下方例程)
--- `setup`函数的`执行时间点` 在于`beforeCreate`和`Created`之间, 所以`CompositionAPI`里边是没有类似`onBeforeCreate`和`onCreated`的方法的, 要写在这两个周期中的逻辑, 直接写在`setup`中即可;
下面是两个Vue3.0引入的新钩子: --- onRenderTracked 渲染跟踪, 跟踪 收集响应式依赖的时机, 每次准备开始渲染时(onBeforeMount后,onMounted前)回调;
--- onRenderTriggered 渲染触发, 每次`触发`页面重新渲染时回调, 回调后,下一轮的 onBeforeMount紧跟其后;** ``` ``` **运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa595c81e5b240878dabfed5b24e4464~tplv-k3u1fbpfcp-zoom-1.image)**
####setup中的provide、inject用法 **--- 父组件拿出`provide`,提供键值对; --- 子组件拿出`inject`,一参为接收键,二参为默认值;** ``` Hello World! heheheheheheda
``` 运行结果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/14d1549c4c794f029aebc073433a2d4d~tplv-k3u1fbpfcp-zoom-1.image)
####配合上`ref`实现 响应特性 以及 `readonly`实现 单向数据流 **--- setup中, 借`provide`传输的 数据字段 配合上`ref`, 使之具备`响应`的特性; --- 父组件提供 更改数据的接口, 在使用`provide`传递 数据字段时,加上 `readonly`包裹, 使得子组件 需要更改 父组件传递过来的数据字段 时, 无法直接 修改字段, 需调用 父组件的接口方法 更改, 按 单向数据流 规范编程;** ``` ``` 运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8b584c687ba446339062579d5cb48fc1~tplv-k3u1fbpfcp-zoom-1.image)点击:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/18e431d7352c4c65917bcd27b475d1a0~tplv-k3u1fbpfcp-zoom-1.image)
####setup结合ref指令 >**前面笔记[《Vue3 | Mixin、自定义指令、Teleport传送门、Render函数、插件 详解 及 案例分析》](https://www.jianshu.com/p/dc7652457d2a)有写到普通场景的ref指定;**

--- setup中的ref是前面说的生成响应式字段的意思; --- template中的ref是获取对应的dom节点; --- 而当 template中的ref指定的字段名, 跟setup中的ref生成的响应式字段名一样的时候,两者就会关联起来;

如下, template中和setup中的字段heheda关联起来, 在setup的onMounted中,使用这个DOM节点字段, 打印DOM代码:

<script>

    const app = Vue.createApp({
        setup() {
            const { ref, onMounted } = Vue;
            const heheda = ref(null);
            onMounted(() => {
                console.log(heheda);
                console.log(heheda.value);
            })
            return { heheda }
        },
        template: `
            <div>
                <div ref="heheda">lueluelue</div>
            </div>    
        `
    });


    const vm = app.mount('#heheApp');
</script>

运行结果:


####
####
####
####
####