- 起初 Vue3.0 暴露变量必须
return
出来,template
中才能使用; - Vue3.2 中 只需要在
script
标签上加上setup
属性,组件在编译的过程中代码运行的上下文是在setup()
函数中,无需return
,template
可直接使用。 - 本文章以Vue2的角度学习Vue3的语法,让你快速理解Vue3的Composition Api
- 本文章第十四节为状态库
Pinia
的安装、使用讲解
一、文件结构
Vue2中, 标签中只能有一个根元素,在Vue3中没有此限制
// ...
二、data
vue实战视频讲解:进入学习
三、method
// 调用方法
四、computed
五、watch
六、props父传子
子组件
{{props.name}}
// 可省略【props.】
{{name}}
父组件
引入子组件,组件会自动注册
七、emit子传父
子组件
{{props.name}}
// 可省略【props.】
{{name}}
父组件
八、v-model
支持绑定多个v-model
,v-model
是 v-model:modelValue
的简写
绑定其他字段,如:v-model:name
子组件
我叫{{ modelValue }},今年{{ age }}岁
父组件
// v-model:modelValue简写为v-model
// 可绑定多个v-model
九、nextTick
十、ref子组件实例和defineExpose
- 在标准组件写法里,子组件的数据都是默认隐式暴露给父组件的,但在 script-setup 模式下,所有数据只是默认 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。
- 如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose 来完成。
子组件
{{state.name}}
父组件
1. 获取一个子组件实例
2. 获取多个子组件实例:在 v-for 中获取子组件实例
这种情况仅适用于 v-for 循环数是固定的情况
,因为如果 v-for 循环数
在初始化之后发生改变,那么就会导致 childRefs 再一次重复添加,childRefs 中会出现重复的子组件实例
3. 获取多个子组件实例:动态 v-for 获取子组件实例
通过下标来向 childRefs 添加/修改,初始化之后,动态修改 v-for 循环数,会自动根据下标重新修改该下标对应的数据
// 通过下标向 childRefs 动态添加子组件实例
十、插槽slot
子组件
// 匿名插槽
// 具名插槽
// 作用域插槽
父组件
// 匿名插槽 我是默认插槽
// 具名插槽
我是具名插槽
我是具名插槽
我是具名插槽
// 作用域插槽
十二、路由useRoute和useRouter
十三、路由导航守卫
十四、store
Vuex
*Vue3 中的Vuex不再提供辅助函数写法
Pinia
*全面拥抱 Pinia
吧!
2021年11月24日,尤大在 Twitter 上宣布:Pinia
正式成为 Vue 官方的状态库,意味着 Pinia
就是 Vuex 5
,Pinia
的优点:
- 同时支持 Composition Api 和 Options api 的语法;
- 去掉 mutations ,只有 state 、getters 和 actions ;
- 不支持嵌套的模块,通过组合 store 来代替;
- 更完善的 Typescript 支持;
- 清晰、显式的代码拆分;
安装
# 使用 npm
npm install pinia
# 使用 yarn
yarn add pinia
main.js 引入
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
import { defineStore } from 'pinia'
// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useStore = defineStore({
// id: 必须,在所有 Store 中唯一
id: 'globalState',
// state: 返回对象的函数
state: () => ({
count: 1,
data: {
name: 'Jerry',
sex: '男'
}
}),
// getter 第一个参数是 state,是当前的状态,也可以使用 this 获取状态
// getter 中也可以访问其他的 getter,或者是其他的 Store
getters: {
// 通过 state 获取状态
doubleCount: (state) => state.count * 2,
// 通过 this 获取状态(注意this指向)
tripleCount() {
return this.count * 3
}
},
actions: {
updateData (newData, count) {
// 使用 this 直接修改
this.data = { ...newData }
this.count = count
// 使用 $patch 修改多个值
this.$patch({
data: { ...newData },
count
})
}
}
})
使用 store
// 获取 store 的 state
姓名:{{store.data.name}}
性别:{{store.data.sex}}
// 调用 actions 方法 / 修改 store
// 获取 getter
获取getter:{{store.doubleCount}}
其他方法
替换整个 state $state
可以让你通过将 store
的属性设置为新对象来替换 store
的整个 state
const store = useStore()
store.$state = {
name: 'Bob',
sex: '男'
}
重置状态
调用 store
上的 $reset()
方法将状态重置为初始值
const store = useStore()
store.$reset()
十五、生命周期
通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。
下表包含如何在 Option API 和 setup() 内部调用生命周期钩子
Option API | setup中 |
---|---|
beforeCreate | 不需要 |
created | 不需要 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
十六、原型绑定与组件内使用
main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 获取原型
const prototype = app.config.globalProperties
// 绑定参数
prototype.name = 'Jerry'
组件内使用
十七、v-bind() CSS变量注入
Jerry
十八、provide和inject
父组件
子组件
十九、自定义指令
Vue3相较于Vue2的自定义声明方法有些不同
const app = createApp({})
// 使 v-demo 在所有组件中都可用
app.directive('demo', {
// 在绑定元素的 attribute 前或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
})
比如实现一个默认密文身份证号,点击才展示的指令
app.directive('ciphertext', {
created: (el: any) => {
console.log(el, 1111)
el.style.cursor = 'pointer'
const value = el.innerText
if (!value || value === 'null' || value === '--') {
el.innerText = '--'
} else {
el.setAttribute('title', '点击查看')
el.innerText = hideText(value)
el.addEventListener('click', () => {
if (el.innerText.indexOf('*') > -1) {
el.innerText = value
} else {
el.innerText = hideText(value)
}
})
}
}
})
{{idNumber}}
二十、对 await 的支持
不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup 。
二十一、定义组件的name
用单独的块来定义