升级脚手架
1 2 3
| npm i @vue/cli -g
vue create 项目名称
|
composition api (组合API)概念
- 组合API
- 框架层面的
- 把 api 拆分成一个个 hook(钩子) → 最后组合起来形成 → Vue3.0 Composition API 框架设计模式
1 2
| import { watch, ref, toRef, onMounted, computed } from 'vue';
|
setup
存在意义
setup 的存在,就是为了能够在其中使用 Composition API
🌈 调用时机
props 初始化完毕之后,beforeCreate 之前被调用
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div>{{ count }}</div> </template>
<script> export default { name: 'App', setup() { return { count: 0 } }, } </script>
|
返回值
- return 一个对象,对象中的属性会被合并到 render 函数的上下文中供 template 使用
- return 一个render函数,可以渲染模板
render function
- 可以把 template 删掉,在 setup 中直接 return 一个函数渲染模板
1 2 3 4 5 6 7 8 9 10 11 12
| <script>
import { ref, h } from 'vue';
export default { name: 'App', setup() { const count = ref(0); return () => h('h1', [count.value]); } } </script>
|
render JSX
1 2 3 4 5 6 7 8
| <script> export default { name: 'App', setup() { return () => <div>123</div>; } } </script>
|
参数
- props 接收的属性
- 不要在
**setup**
中解构 props ,会导致 props 被解构的值丧失响应式
- ❌
setup({ title })
- ❌
const { title } = props
- context 上下文选项列表
- 为什么不把 props 和 context 合并在一起当一个参数
- props 使用比 attrs、emit 更频繁
- 可以更好的为 props 做类型推断(TS)
props
父组件 App.vue
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
| <template> <div> <Test :title="title" /> </div> </template>
<script>
import { ref, h } from 'vue'; import Test from '@/components/Test';
export default { name: 'App', setup() { const title = ref('Lance'); setTimeout(() => { title.value = 'Lance233'; }, 1000); return { title }; }, components: { Test } } </script>
|
子组件 Test.vue
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
| <template> <h1>{{ title }}</h1> </template>
<script> import { watch, watchEffect } from 'vue'; export default { name: 'Test', props: { title: String }, setup(props) { console.log(props); // Proxy { title: 'Lance' } watchEffect(() => { console.log('title:', props.title); // 初始化Lance和变化后Lance233都能监听到 }); watch(() => { return props.title; // 要监听的值 }, (newVal) => { console.log('new Title:', newVal); // 只会监听变化后的值 }); } } </script>
|
context
- 区别于 props ,可以被解构
setup(props, { attrs, slots })
const { attrs, slots } = context
- 包含选项列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <h1>{{ title }}</h1> </template>
<script> export default { name: 'Test', props: { title: String }, setup(props, context) { console.log(context); } } </script>
|
attrs
ctx.attrs
- return 的时候最好别展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <h1>{{ attrs.title }}</h1> </template>
<script> export default { name: 'Test', // props: { // title: String // }, setup(props, context) { console.log(context.attrs); // 有 title 了 // 注意得把 props 中的 title 注释掉,才能出现在 attrs 中 return { attrs: context.attrs } } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <h1>{{ title }}</h1> </template>
<script> export default { name: 'Test', setup(props, context) { return { ...context.attrs // 展开后就没有响应式了 } } } </script>
|
emit
ctx.emit('事件名', 值)
- 注册:
emits: ['事件名']
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div> <Test :count="count" @plus="plus" /> </div> </template>
<script>
import { ref } from 'vue'; import Test from '@/components/Test';
export default { name: 'App', setup() { const count = ref(0); const plus = num => count.value += num; return { count, plus }; }, components: { Test } } </script>
|
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div>{{ count }}</div> <button @click="plus">增加</button> </template>
<script> export default { name: 'Test', props: { count: Number }, emits: ['plus'], setup(props, ctx) { const plus = () => ctx.emit('plus', 100); return { plus } } } </script>
|
setup 中的ctx与getCurrentInstance中的ctx的区别
1 2 3 4 5 6
| export default { setup(props, ctx) { console.log(ctx); console.log(getCurrentInstance().ctx); } }
|
🌈 setup 中 this 为 undefined
- setup 在组件实例化完成之前,是拿不到当前组件实例的(props之后,beforeCreate之前)