ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] >[success] # toRef 以及 context 参数 这章讲解一下 **2** 个新的知识点,一个是 **composition API** 中的 **toRef** API,另一个是我们之前讲过的 **setup 函数** 的 **第2个参数 context** 。 >[success] ## toRef 1. **解构赋值响应式数据正确写法** 我们之前通过 **toRefs** 来解决 **解构赋值数据响应式问题** ,代码如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>toRef</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // toRef const app = Vue.createApp({ template: ` <div>{{name}}</div> `, /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { const { reactive, toRefs } = Vue const data = reactive({ name: 'dell' }) // 解构赋值 const { name } = toRefs(data) // 2s 后修改name变量 setTimeout(() => { name.value = 'lee' }, 2000) return { name } } }) const vm = app.mount('#root') </script> </html> ~~~ 2. **解构赋值响应式数据错误写法** 如果在 **解构赋值时** ,我们把 **name** 改成 **age** ,这时候我们就会发现,在一开始 **data** 中没有 **age** , 这时候 **解构赋值时有 age ,就会发生错误** ,这是 **因为 toRefs 这种语法,它从 data 这样一个响应式对象里去找数据时,如果找不到,它不会给 age 一个默认的响应式的引用,而是给 age 赋值为 undefined,这样的话, age 最开始不存在 data 中,它后面就不具备响应式,再改它,也无效果** ,代码如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>toRef</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // toRef const app = Vue.createApp({ template: ` <div>{{age}}</div> `, /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { const { reactive, toRefs } = Vue const data = reactive({ name: 'dell' }) // 解构赋值 const { age } = toRefs(data) // 2s 后修改name变量 setTimeout(() => { age.value = 'lee' }, 2000) return { age } } }) const vm = app.mount('#root') </script> </html> ~~~ 3. **toRef** 如果想解决上面的问题,我们可以用 **toRef** ,代码如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>toRef</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // toRef const app = Vue.createApp({ template: ` <div>{{age}}</div> `, /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { const { reactive, toRef } = Vue const data = reactive({ name: 'dell' }) // 意思是从 data 中取 age ,如果能取到就用取到的值, // 取不到就声明一个空的响应式数据 const age = toRef(data, 'age') // 2s 后给 age 赋值 setTimeout(() => { age.value = 'lee' }, 2000) // 导出 age ,这里必须是对象的形式,就像在 vue2.x 时 return {} 一样 return { age } } }) const vm = app.mount('#root') </script> </html> ~~~ **建议一般情况不要用 toRef** ,一般来说,如果后面代码中要用到 **age** ,不妨直接在 **data** 中提前定义一个空的 **age** ,代码如下: ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>toRef</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // toRef const app = Vue.createApp({ template: ` <div>{{age}}</div> `, /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { const { reactive, toRefs } = Vue const data = reactive({ name: 'dell', age: '' }) // 2s 后给 age 赋值 setTimeout(() => { data.age = 'lee' }, 2000) // 解构赋值 const { age } = toRefs(data) // 导出 age ,这里必须是对象的形式,就像在 vue2.x 时 return {} 一样 return { age } } }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## Setup 的第二个参数 context 接下来我们讲一下 **context** , **context** 是个 **对象**, 它有 **3** 个 **属性值** 1. **attrs** **context** 的第 **1** 个参数是 **attrs(None-Props属性)** ,下面用它写一个小例子: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Setup 的第二个参数 context </title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 父组件 const app = Vue.createApp({ template: `<child style="color:red" />` }) // 子组件 app.component('child', { /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { // 引入h函数 const { h } = Vue const { attrs, slots, emit } = contex // 使用render函数,并且使用 attrs return () => { return h('div', { style: attrs.style }, 'child') } } }) const vm = app.mount('#root') </script> </html> ~~~ 2. **slots** **context** 的第 **2** 个参数是 **slots** , **slots** 是 **父组件传递过来的插槽** ,在 **vue2.x** 版本中,我们使用 **slots** 都需要 **this.$slots** ,在新版 **composition API** 中可以使用 **slots** ,例子如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Setup 的第二个参数 context </title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 父组件 const app = Vue.createApp({ template: `<child>parent</child>` }) // 子组件 app.component('child', { /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { // 引入h函数 const { h } = Vue const { attrs, slots, emit } = contex // 使用render函数,并且使用 slots return () => { return h('div', {}, slots.default()) } } }) const vm = app.mount('#root') </script> </html> ~~~ 3. **emit** 在旧版 **vue2.x** 版本中, **子组件** 给 **父组件传值** 通过 **this.$emit** ,代码如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Setup 的第二个参数 context </title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 父组件 const app = Vue.createApp({ template: `<child @change="handleChange">parent</child>`, methods: { handleChange(){ alert('handleChange') } } }) // 子组件 app.component('child', { template: '<div @click="handleClick">123123</div>', methods: { handleClick(){ this.$emit('change') } } }) const vm = app.mount('#root') </script> </html> ~~~ 在新版 **composition API** 中这样写即可,代码如下: ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Setup 的第二个参数 context </title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 父组件 const app = Vue.createApp({ methods: { handleChange(){ alert('handleChange') } }, template: `<child @change="handleChange">parent</child>` }) // 子组件 app.component('child', { template: '<div @click="handleClick">123123</div>', /** * created 实例被完全初始化之前 * @param {object} props - 当前组件的props * @param {number} contex - 上下文 */ setup(props, contex) { const { attrs, slots, emit } = contex // 定义方法 function handleClick(){ emit('change') } // 导出方法 return { handleClick } } }) const vm = app.mount('#root') </script> </html> ~~~