vue3中的 Composition API 、新特性
1.setup的执行
是在 beforeCreate 之前执行,所以在 setup 内部,this 不是当前组件实例的引用,也就是说 setup 中无法直接调用组件的其他数据
Composition API 让我们 setup 中使用生命周期钩子函数、响应式数据等组件特性
2. props 、context
setup不是生命周期钩子函数,并且具备props 和 context
1 2 3 4 5 6 7 8 9 | import { toRefs } from 'vue' ;setup(props, context) {<br> //因为 props 是响应式的,所以<strong>不能直接对 props 使用 ES6 解构,因为它会消除 prop 的响应性</strong><br> const {title} = toRefs(props); //通过toRefs包装后的props可以在解构后仍然具有响应式 const { attrs, slots, emit } = context; // Attribute (非响应式对象) console.log(attrs); // 组件插槽 (非响应式对象) console.log(slots);<br> // 触发事件 (方法) console.log(emit); } |
3.在 setup 中使用生命周期钩子(需要从 Vue 引入带有 on 前缀的生命周期)
1 2 3 4 5 6 7 8 | import { toRefs, onMounted } from 'vue' ; setup (props) { const { title } = toRefs(props); onMounted(() => { console.log( 'onMounted' , title); }); } |
因为 setup 本身是基于beforeCreate运行, 所以在setup 中没有 beforeCreate 和 created 这两个生命周期
4.setup的返回值
(1)ref
通过ref 包装的变量会具有响应性
setup 内部,ref 包装后的变量需要通过 value 来修改变量的值
(2)setup 还可以返回一个渲染函数,则组件中定义的 template 将会失效
1 2 3 4 5 6 7 8 9 10 | import { h, ref, reactive } from 'vue' export default { setup() { const readersNumber = ref(0) const book = reactive({ title: 'Vue 3 Guide' }) // 返回一个渲染函数以覆盖 template return () => h( 'div' , [readersNumber.value, book.title]) } } |
5. Computed、Watch
1 2 3 4 5 6 7 8 9 10 | import { ref, computed } from 'vue' setup() { const list = ref([]); const lable = computed(() => list.value.length > 0 ? list.join( '-' ) : '暂无数据' ); return { list, lable, } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { ref, watch } from 'vue' setup() { const type = ref( '' ); watch(type, (newValue, oldValue) => { switch (type) { case 0: console.log( 'good' ) break ; case 1: console.log( "just so so" ); break ; case 2: console.log( "wrong" ); } }); return { type, } } |
监听多个数据
watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => { console.log( "新值:" , curAge, "老值:" , preAge); console.log( "新值:" , newVal, "老值:" , oldVal); }); |
监听复杂的嵌套对象
const state = reactive({ room: { id: 100, attrs: { size: "140平方米", type: "三室两厅", }, }, }); watch( () => state.room, (newType, oldType) => { console.log("新值:", newType, "老值:", oldType); }, { deep: true } );
如果不使用第三个参数deep:true
, 是无法监听到数据变化的。 前面我们提到,默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?其实使用也很简单, 给第三个参数中设置immediate: true
即可
7.抽取公共逻辑
例如有一个列表展示与数据搜索过滤的功能
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 | // src/composables/useList.js import { ref, onMounted, watch } from 'vue' ; // 发起接口请求 function fetchList() { // ... } export default function useList(query) { // 创建数据列表 list (data) const list = ref([]); // 创建查询并更新 list 的方法 (methods) const getList = async () => { list.value = await fetchList(query); }; // 生命周期 mounted onMounted(getList); // 监听 query 的变化并更新 list watch(query, getList); return { list, getList, }; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <br><br><br> // src/composables/useFilter.js import { ref, computed } from 'vue' ; export default function useFilter(list) { // 创建搜索关键字 keyword (data) const keyword = ref( '' ); // 创建筛选结果 filterRes (computed) const filterRes = computed(() => { return list.filter((value) => { return value.includes(keyword.value); }); }); return { keyword, filterRes, }; } |
在组件中引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import { defineComponent, toRefs } from 'vue' ; import useList from '@/composables/useList' ; import useFilter from '@/composables/useFilter' ; export default defineComponent({ props: [ 'query' ], setup(props) { const { query } = toRefs(props); const { list, getList } = useList(query); const { keyword, filterRes } = useFilter(list); return { keyword, // 筛选关键字 getList, // 查询并更新数据列表 list: filterRes, // 不需要返回未经筛选的列表 }; }, }); |
8.自定义hooks
import { ref, Ref, computed } from "vue"; type CountResultProps = { count: Ref<number>; multiple: Ref<number>; increase: (delta?: number) => void; decrease: (delta?: number) => void; }; export default function useCount(initValue = 1): CountResultProps { const count = ref(initValue); const increase = (delta?: number): void => { if (typeof delta !== "undefined") { count.value += delta; } else { count.value += 1; } }; const multiple = computed(() => count.value * 2); const decrease = (delta?: number): void => { if (typeof delta !== "undefined") { count.value -= delta; } else { count.value -= 1; } }; return { count, multiple, increase, decrease, }; }
在组件中使用这个hooks
<template> <p>count: {{ count }}</p> <p>倍数: {{ multiple }}</p> <div> <button @click="increase()">加1</button> <button @click="decrease()">减一</button> </div> </template> <script lang="ts"> import useCount from "../hooks/useCount"; setup() { const { count, multiple, increase, decrease } = useCount(10); return { count, multiple, increase, decrease, }; }, </script>
9.Teleport
使用场景,例如在子组件Header
中使用到Dialog
组件,但希望Dialog渲染到外部挂载的 DOM,同时还可以使用到 Vue 组件内的状态
在index.html中定一个供挂载的元素
<body> <div id="app"></div> <div id="dialog"></div> </body>
定义一个Dialog
组件, to
属性的值与挂载的id相同
<template>
<teleport to="#dialog">
<div class="dialog">
<div class="dialog_wrapper">
<div class="dialog_header" v-if="title">
<slot name="header">
<span>{{ title }}</span>
</slot>
</div>
</div>
<div class="dialog_content">
<slot></slot>
</div>
<div class="dialog_footer">
<slot name="footer"></slot>
</div>
</div>
</teleport>
</template>
在Header组件中使用Dialog组件
<div class="header"><navbar />
<Dialog v-if="dialogVisible"></Dialog>
</div>
10.Suspense
使用场景:在前后端交互获取数据时, 是一个异步过程,一般我们都会提供一个加载中的动画,当数据返回时配合v-if
来控制数据显示
vue3中内置组件Suspense
, 提供两个template
slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容
<Suspense> <template #default> <async-component></async-component> </template> <template #fallback> <div> Loading... </div> </template> </Suspense>
asyncComponent.vue
<<template> <div> <h4>这个是一个异步加载数据</h4> <p>用户名:{{user.nickname}}</p> <p>年龄:{{user.age}}</p> </div> </template> <script> import { defineComponent } from "vue" import axios from "axios" export default defineComponent({ setup(){ const rawData = await axios.get("http://xxx.xinp.cn/user") return { user: rawData.data } } }) </script>
11.自定义指令
在 Vue 3 中对自定义指令的 API 进行了更加语义化的修改, 就如组件生命周期变更一样
自定义指令
const { createApp } from "vue" const app = createApp({}) app.directive('focus', { mounted(el) { el.focus() } })
在组件中使用
<input v-focus />
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~