vue3 compositionAPI 相关 (vue3.2 setup语法糖)
1、文件结构
vue2中限制template只能有一个根元素,vue3没有限制
2、data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script setup> import { reactive, ref, toRefs } from 'vue' // ref声明响应式数据,用于声明基本数据类型 const name = ref('YoLin') // 修改 name.value = 'Deng' // reactive声明响应式数据,用于声明引用数据类型 const state = reactive({ name: 'YoLin', sex: '男' }) // 修改 state.name = 'Deng'
// 使用toRefs解构 const { name, sex } = toRefs(state) // 在template中可直接使用{{ name }}、{{ sex }} </script>
|
3、method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> // 调用方法 <button @click='changeName'>按钮</button> </template>
<script setup> import { reactive } from 'vue'
const state = reactive({ name: 'YoLin' })
// 声明method方法 const changeName = () => { state.name = 'Deng' } </script>
|
4、computed
1 2 3 4 5 6 7 8
| <script setup> import { computed, ref } from 'vue' let count = ref(0) const doubuleCount = computed(() => { return count.value * 2 }) </script>
|
5、watch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script setup> import { watch, reactive } from 'vue' const state = reactive({ count: 1 }) const changeCount = () => { state.count = state.count * 2 } watch(() => state.count, (newVal, oldVal) => { console.log(newVal) }, { immediate: true, //立刻执行 deep: true // 深度监听 }) </script>
|
6、props 父传子
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 28 29 30 31
| 子组件 <template> <span>{{props.name}}</span> // 可省略【props.】 <span>{{name}}</span> </template>
<script setup> // import { defineProps } from 'vue' // defineProps在<script setup>中自动可用,无需导入 // 需在.eslintrc.js文件中【globals】下配置【defineProps: true】
// 声明props const props = defineProps({ name: { type: String, default: '' } }) </script>
父组件 引入子组件,组件会自动注册 <template> <child name='Jerry'/> </template>
<script setup> // 引入子组件 import child from './child.vue' </script>
|
7、emit 子传父
子组件.
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> <span>{{props.name}}</span> // 可省略【props.】 <span>{{name}}</span> <button @click='changeName'>更名</button> </template>
<script setup> // import { defineEmits, defineProps } from 'vue' // defineEmits和defineProps在<script setup>中自动可用,无需导入 // 需在.eslintrc.js文件中【globals】下配置【defineEmits: true】、【defineProps: true】 // 声明props const props = defineProps({ name: { type: String, default: '' } }) // 声明事件 const emit = defineEmits(['updateName']) const changeName = () => { // 执行 emit('updateName', 'Tom') } </script>
|
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <child :name='state.name' @updateName='updateName'/> </template>
<script setup> import { reactive } from 'vue' // 引入子组件 import child from './child.vue'
const state = reactive({ name: 'YoLin' }) // 接收子组件触发的方法 const updateName = (name) => { state.name = name } </script>
|
8、v-model
支持绑定多个v-model,v-model 是v-model:modelValue的简写
绑定其他字段,如v-model:name
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <span @click="changeInfo">我叫{{ modelValue }},今年{{ age }}岁</span> </template>
<script setup> // import { defineEmits, defineProps } from 'vue' // defineEmits和defineProps在<script setup>中自动可用,无需导入 // 需在.eslintrc.js文件中【globals】下配置【defineEmits: true】、【defineProps: true】
defineProps({ modelValue: String, age: Number })
const emit = defineEmits(['update:modelValue', 'update:age']) const changeInfo = () => { // 触发父组件值更新 emit('update:modelValue', 'Tom') emit('update:age', 30) } </script>
|
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> // v-model:modelValue简写为v-model // 可绑定多个v-model <child v-model="state.name" v-model:age="state.age" /> </template>
<script setup> import { reactive } from 'vue' // 引入子组件 import child from './child.vue'
const state = reactive({ name: 'Jerry', age: 20 }) </script>
|
9、nextTick
1 2 3 4 5 6 7
| <script setup> import { nextTick } from 'vue' nextTick(() => { // ... }) </script>
|
10、子组件ref变量和defineExpose
1、在标准的组件写法中,子组件的数据是默认隐式暴露给父组件的,但在script-setup模式下,所以的数据只是默认return给template使用,不会暴露到外部组件,所以父组件无法通过挂载ref变量来获取子组件的数据
2、如果想要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由defineExpose来完成
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <span>{{state.name}}</span> </template>
<script setup> import { reactive, toRefs } from 'vue' // defineExpose无需引入 // import { defineExpose, reactive, toRefs } from 'vue'
// 声明state const state = reactive({ name: 'YoLin' }) // 将方法、变量暴露给父组件使用,父组件才可通过ref API拿到子组件暴露的数据 defineExpose({ // 解构state ...toRefs(state), // 声明方法 changeName () { state.name = 'Deng' } }) </script>
|
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <child ref='childRef'/> </template>
<script setup> import { ref, nextTick } from 'vue' // 引入子组件 import child from './child.vue'
// 子组件ref(TypeScript语法) const childRef = ref<InstanceType<typeof child>>() // nextTick nextTick(() => { // 获取子组件name console.log(childRef.value.name) // 执行子组件方法 childRef.value.changeName() }) </script>
|
11、插槽
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> // 匿名插槽 <slot/> // 具名插槽 <slot name='title'/> // 作用域插槽 <slot name="footer" :scope="state" /> </template>
<script setup> import { useSlots, reactive } from 'vue' const state = reactive({ name: '张三', age: '25岁' }) const slots = useSlots() // 匿名插槽使用情况 const defaultSlot = reactive(slots.default && slots.default().length) console.log(defaultSlot) // 1 // 具名插槽使用情况 const titleSlot = reactive(slots.title && slots.title().length) console.log(titleSlot) // 3 </script>
|
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <child> // 匿名插槽 <span>我是默认插槽</span> // 具名插槽 <template #title> <h1>我是具名插槽</h1> <h1>我是具名插槽</h1> <h1>我是具名插槽</h1> </template> // 作用域插槽 <template #footer="{ scope }"> <footer>作用域插槽——姓名:{{ scope.name }},年龄{{ scope.age }}</footer> </template> </child> </template>
<script setup> // 引入子组件 import child from './child.vue' </script>
|
12、路由useRoute和useRouter
1 2 3 4 5 6 7 8 9 10 11
| <script setup> import { useRoute, useRouter } from 'vue-router' // 必须先声明 const route = useRoute() const router = useRouter() // 路由信息 console.log(route.query) // 路由跳转 router.push('/index') </script>
|
13、路由导航守卫
1 2 3 4 5 6 7 8 9 10 11
| <script setup> import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router' //添加一个导航守卫,当组件将要离开的时候触发 onBeforeRouteLeave((to, from, next) => { next() }) // 添加一个导航守卫,当组件将要更新时触发 onBeforeRouteUpdate((to, from, next) =>{ next() }) </script>
|
14、store
vuex
*Vue3 中的Vuex不再提供辅助函数写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script setup> import { useStore } from 'vuex' import { key } from '../store/index'
// 必须先声明调用 const store = useStore(key) // 获取Vuex的state store.state.xxx
// 触发mutations的方法 store.commit('fnName')
// 触发actions的方法 store.dispatch('fnName')
// 获取Getters store.getters.xxx </script>
|
15、pinia
Pinia
就是 Vuex 5
,Pinia
的优点:
1、同时支持 Composition Api 和 Options api 的语法;
2、去掉 mutations ,只有 state 、getters 和 actions ;
3、不支持嵌套的模块,通过组合 store 来代替;
*4、更完善的typescript支持
安装:
1 2 3 4 5
| npm install pinia
yarn add pinia
|
main.js引入
1 2 3 4 5 6 7
| import App from './App.vue' import { createApp } from 'vue' import { createPinia } from 'pinia'
const app = createApp(App) app.use(createPinia()) app.mount('#app')
|
配置store.js
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 28 29 30 31 32 33 34 35 36 37 38
| import { defineStore } from 'pinia'
export const useStore = defineStore({ id: 'globalState', state: () => ({ count: 1, data: { name: 'Jerry', sex: '男' } }), getters: { doubleCount: (state) => state.count * 2, tripleCount() { return this.count * 3 } }, actions: { updateData (newData, count) { this.data = { ...newData } this.count = count this.$patch({ data: { ...newData }, count }) } } })
|
使用store
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 28 29 30 31 32 33
| <template> // 获取 store 的 state <p>姓名:{{store.data.name}}</p> <p>性别:{{store.data.sex}}</p> // 调用 mutations 方法 / 修改 store <button @click='update'>修改用户信息</button> // 获取 getter <p>获取getter:{{store.doubleCount}}</p> </template>
<script setup> import { useStore } from '@store/store.js' const store = useStore() function update () { // 通过 actions 定义的方法修改 state store.updateData({ name: 'Tom', sex: '女' }) // 通过 store 直接修改 store.data = { name: 'Tom', sex: '女' } // 同时改变多个状态 store.$patch((state) => { state.data = { name: 'Tom', sex: '女' } state.count = 2 }) } </script>
<style lang="scss" scoped> </style>
|
其他方法
1、$state可以替换store的整个state
2、重制状态, $reset()
方法将状态重置为初始值
1 2 3
| const store = useStore() store.$state = {} store.$reset()
|
16、声明周期
通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。
Option API: beforeCreate、created、beforeMount、mounted、beforeUnmount(原vue2的beforeDestroy)、unmounted(原vue2的destroyed)、errorCaptured
activated、deactivated(在keep-alive标签下才有)
renderTracked(新增,状态跟踪, 每次渲染后重新收集响应式依赖)、renderTriggered(新增、状态触发, 每次触发页面重新渲染时自动执行), 新增的这两个用于调试使用
Composition API: setup中不需要beforeCreate 和 created, 其余只需要在Option API名称前面加上“on”
17、原型绑定与组件内使用
main.js
1 2 3 4 5 6 7 8 9
| import { createApp } from 'vue' import App from './App.vue' const app = createApp(App)
const prototype = app.config.globalProperties
prototype.name = 'Jerry'
|
组件内使用
1 2 3 4 5 6 7 8 9
| <script setup> import { getCurrentInstance } from 'vue'
// 获取原型 const { proxy } = getCurrentInstance() // 输出 console.log(proxy.name) </script>
|
18、V-bind() css 变量注入
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 28 29 30 31 32 33 34 35 36 37
| <template> <span>Jerry</span> </template>
<script setup> import { ref, reactive } from 'vue' // prop接收样式 const props = defineProps({ border: { type: String, default: '1px solid yellow' } }) // 常量声明样式 const background = 'red' // 响应式数据声明样式 const color = ref('blue') const style = reactive({ opacity: '0.8' }) </script>
<style lang="scss" scoped> span { // 使用常量声明的样式 background: v-bind(background); // 使用响应式数据声明的样式 color: v-bind(color); opacity: v-bind('style.opacity'); // 使用prop接收的样式 border: v-bind('props.border'); } </style>
|
19、Provide 和inject
父组件
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> <child/> </template>
<script setup> import { provide } from 'vue' import { ref, watch } from 'vue' // 引入子组件 import child from './child.vue'
let name = ref('Jerry') // 声明provide provide('provideState', { name, changeName: () => { name.value = 'Tom' } })
// 监听name改变 watch(name, () => { console.log(`name变成了${name}`) setTimeout(() => { console.log(name.value) // Tom }, 1000) }) </script>
|
子组件
1 2 3 4 5 6 7 8
| <script setup> import { inject } from 'vue' // 注入 const provideState = inject('provideState') // 子组件触发name改变 provideState.changeName() </script>
|
20、对await 的支持
不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup
1 2 3
| <script setup> const post = await fetch('/api').then(() => {}) </script>
|
21、定义组件的name
用单独的<script>
块来定义
1 2 3 4 5
| <script> export default { name: 'ComponentName', } </script>
|