目录
5、由于少了 export,没法传参,也不方便暴露接口,所以就增加了三个工具方法
6.1.shallowReactive与shallowRef
组合式Api要求我们要将响应式的变量声明在setup函数中,setup函数是一个专门用于组合式Api的钩子函数。而且我们需要借助reactive函数来创建响应式的对象。
setup函数的两种返回值:
若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注)
若返回一个渲染函数:则可以自定义渲染内容。
setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性
setup的两个注意点
setup执行的时机:在beforeCreate之前执行一次,this是undefined
setup的参数
- props:值为对象,包含: 组件外部传递过来,且组件内部声明接收了属性
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于 this.$attrs
- slots:收到插槽的内容,相当于$slots
- emit: 分发自定义事件的函数,相当于this.$emit
-
- //父组件
- // This starter template is using Vue 3
-
- <template>
- <img alt="Vue logo" src="./assets/logo.png" />
- <HelloWorld msg="传递吧" @hello="hello">
- <template v-slot:cacao>
- <span>是插槽吗span>
- template>
- <template v-slot:qwe>
- <span>meiyouspan>
- template>
-
- HelloWorld>
- template>
-
-
- //子组件
- export default {
- name: 'test3',
- props: ['msg'],
- emits:['hello'],
- //这里setup接收两个参数,一个是props,一个是上下文context
- setup(props,context){
- /**
- * props就是父组件传来的值,但是他是Porxy类型的对象
- * >Proxy:{msg:'传递吧'}
- * 可以当作我们自定义的reactive定义的数据
- */
-
- /**
- * context是一个对象 包含以下内容:
- * 1.emit触发自定义事件的
- * 2.attrs 相当于vue2里面的 $attrs 包含:组件外部传递过来,但没有在props配置中声明的属性
- * 3.slots 相当于vue2里面的 $slots
- * 3.expose 是一个回调函数
- */
- console.log(context.slots);
- let person = reactive({
- name: '张三',
- age: 17,
- })
-
-
-
- function changeInfo(){
- context.emit('hello', 666)
- }
-
- //返回对象
- return {
- person,
- changeInfo
- }
-
- //返回渲染函数(了解) 这个h是个函数
- //return () => h('name','age')
- }
- }
- <button @click="add1">点我啊button>
- {{ datas.num }}
-
- <script>
- import {reactive} from 'vue'
- export default {
-
- setup() {
- const datas = reactive({ num: 0 })
- function add1(){
- datas.num++
- }
- return {
- datas,
- add1
- }
- }
- }
- script>
-
- <style>
- style>
如上,即为组合式Api的典型写法。
在组合式Api中一定记得将需要暴露到模板的对象return出去(单独使用setup的时候)。
更多声明响应式对象的方法
前面我们已经了解了data方法(选项式Api中使用)、reactive(组合式Api中使用)两种不同的声明响应式对象的方式,那还有其他方式吗?
ref
因为reactive方法仅仅对,对象,数组、Map、Set类型生效,对于string、number、boolean类型是无效的。
鉴于这种情况,vue3引入了ref方法来帮助开发者创建任意类型的响应式对象。
- import {reactive,ref} from 'vue'
- export default {
-
- setup() {
- //const num = reactive(0) reactive不支持声明number类型的响应式对象
- const num = ref(0)
- function add1(){
- this.num++
- }
- return {
- num,
- add1
- }
- }
- }
toRef
当我们想要将reactive创建的响应式对象的某个属性单独传递出来时,我们可以使用toRef实现
-
- <button @click="add1">点我啊button>
- {{ tms }}
-
- 姓名:{{ datas.name }}
- 年龄:{{ datas.age }}
-
-
-
- <script setup>
- import {reactive,toRef} from 'vue'
-
- const datas = reactive({name: 'phyger',age: 18, num: 0})
- // 通过toRef将datas中的num转换为了ref对象
- const tms = toRef(datas,'num')
- const add1=()=>{
- // 在此对tms的值进行修改
- tms.value++
- }
-
- script>
toRefs
类似toRef,toRefs可以将reactive声明的响应式变量的所有元素都转换为ref对象。
- <button @click="add1">点我啊button>
- {{ tms.num }}
-
- 姓名:{{ tms.name }}
- 年龄:{{ tms.age }}
-
-
- <script setup>
- import {reactive,toRefs} from 'vue'
-
- const datas = reactive({name: 'phyger',age: 18, num: 0})
- // 通过toRef将datas中的num转换为了ref对象
- const tms = toRefs(datas)
- const add1=()=>{
- // 在此对tms的值进行修改
- tms.num.value++
- }
- script>
toRefs的返回值是一个字典,格式为:
{name: ObjectRefImpl, age: ObjectRefImpl, num: ObjectRefImpl}
如上,我们已经将reactive定义的对象元素全部转换为了ref对象,而且属性值的变化会同步更新到reactive的对象中。
isRef
vue3为我们提供了判断时否为ref对象的方法,即isRef。
-
- import {reactive,toRefs,isRef} from 'vue'
-
- const datas = reactive({name: 'phyger',age: 18, num: 0})
- // 通过toRef将datas中的num转换为了ref对象
- const tms = toRefs(datas)
- const add1=()=>{
- // 在此对tms的值进行修改
- tms.num.value++
- // 判断datas,datas.name,tms,tms.age是否为ref对象
- console.log(tms) // toRefs的返回值是一个字典{name: ObjectRefImpl, age: ObjectRefImpl, num: ObjectRefImpl}
- console.log(isRef(datas),isRef(datas.name),isRef(tms),isRef(tms.age))
- // 输出是 false false false true
- }
结果: 简化在setup中的return 前面我们有说,在组合式Api中,我们必须将模板需要的属性暴露出去。通常大多数的属性都是需要暴露出去的,为了方便,vue3支持在单文件组件(SFC)的场景下,使用
请注意:
onActivated 在组件挂载时也会调用,并且 onDeactivated 在组件卸载时也会调用。
这两个钩子不仅适用于 缓存的根组件,也适用于缓存树中的后代组件。


- .modal-mask {
- position: fixed;
- z-index: 9998;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
- display: table;
- transition: opacity 0.3s ease;
- }
-
- .modal-wrapper {
- display: table-cell;
- vertical-align: middle;
- }
-
- .modal-container {
- width: 300px;
- margin: 0px auto;
- padding: 20px 30px;
- background-color: #fff;
- border-radius: 2px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
- transition: all 0.3s ease;
- }
-
- .modal-header h3 {
- margin-top: 0;
- color: #42b983;
- }
-
- .modal-body {
- margin: 20px 0;
- }
-
- .modal-default-button {
- float: right;
- }
-
- /*
- * 对于 transition="modal" 的元素来说
- * 当通过 Vue.js 切换它们的可见性时
- * 以下样式会被自动应用。
- *
- * 你可以简单地通过编辑这些样式
- * 来体验该模态框的过渡效果。
- */
-
- .modal-enter-from {
- opacity: 0;
- }
-
- .modal-leave-to {
- opacity: 0;
- }
-
- .modal-enter-from .modal-container,
- .modal-leave-to .modal-container {
- -webkit-transform: scale(1.1);
- transform: scale(1.1);
- }

-
-
- <div>
- <div>{{ data.count }}div>
- <button @click="clickBtnNoChange">非响应button>
- <button @click="clickBtnChange">响应button>
- div>
-
-
- <div>
- <p>1s之后更新p>
- <p>{{ text }}p>
- <input type="text" v-model="text" />
- div>
-
-
- <div>
- <p>shallowReactivep>
- <div>{{ stateReactive.foo }}div>
- <button @click="clickBtnNoChange1">非响应button>
- <button @click="clickBtnChange1">响应button>
- div>
-
-
- <div>
- <p>effectScopep>
- <p>{{ counter}}p>
-
- div>
-
-
-
-
- <script lang="ts" setup>
- import { useDebouncedRef } from "./debounceRef";
- import {
- shallowRef,
- reactive,
- ref,
- watch,
- shallowReactive,
- getCurrentInstance,
- triggerRef,
- watchEffect,
- isReactive,
- effectScope,
- computed,
- } from "vue-demi";
-
- //shallowRef
-
- const text = useDebouncedRef("hello", 1000);
- const data = shallowRef({ count: 1 });
- //不会触发响应式-修改数据
- const clickBtnNoChange = () => {
- data.value.count++;
- console.log(data.value.count++, " data.value.count++===========");
- };
- //会修改数据源
- const clickBtnChange = () => {
- const random = Math.floor(Math.random() * 10 - 1);
- data.value = { count: random };
- };
- const state = shallowRef({ count: 1 });
- state.value = { count: 2 };
- const shallow = shallowRef({
- greet: "Hello world",
- });
- // 触发该副作用第一次应该会打印 "Hello, world"
- watchEffect(() => {
- console.log(shallow.value.greet);
- });
- // 这次变更不应触发副作用,因为这个 ref 是浅层的
- shallow.value.greet = "hello universe";
- // 打印 "hello, universe"
- triggerRef(shallow);
-
- // shallowReactive
-
- const stateReactive = shallowReactive({
- foo: 1,
- nested: {
- bar: 2,
- },
- });
-
- //不会触发响应式-修改数据
- const clickBtnNoChange1 = () => {
- //但下层嵌套对象不会被转化为响应式
- isReactive(stateReactive.nested); //false
- //不是响应式的
- stateReactive.nested.bar++;
- };
- //会修改数据源
- const clickBtnChange1 = () => {
- stateReactive.foo++;
- console.log(stateReactive.foo++, "stateReactive.foo++===========");
- };
- //更改状态自身的属性是响应式的
- stateReactive.foo++;
- //但下层嵌套对象不会被转化为响应式
- isReactive(stateReactive.nested); //false
- //不是响应式的
- stateReactive.nested.bar++;
-
- // effectScope()
-
- const disposables=[]
- const counter=ref(1)
- const scope=effectScope()
- scope.run(()=>{
- const doubled=computed(()=>counter.value*2)
- console.log(doubled,'doubled===============')
- watch(doubled,()=> {
- console.log(doubled.value)
- }
- )
- watchEffect(()=>console.log("Count:",doubled.value))
- })
- //处理掉当前作用域内的所有 effect
- scope.stop()
- console.log(scope.stop(),'scope.stop()=============')
- disposables.push(()=>scope.stop())
- console.log(disposables,'disposables=============')
-
- script>
- import {customRef} from 'vue'
-
- export function useDebouncedRef(value:any,delay=200){
- let timeout:any
- return customRef((track,trigger)=>{
- return {
- get(){
- track()
- return value
- },
- set(newValue){
- clearTimeout(timeout)
- timeout=setTimeout(()=>{
- value=newValue
- trigger()
- },delay)
- }
- }
- })
- }
export,没法传参,也不方便暴露接口,所以就增加了三个工具方法注意,这三个工具方法只是帮助 Vue 编译器构建组件,它们不会出现在最终代码里,我们也不能预期它们会像普通函数那样工作。比如下面这段代码,就得不到常见的结果:
- const props = defineProps({
- userMenu: {
- type: Array,
- default() {
- return []
- }
- }
- })
- console.log(props) // 该对象中的 userName 总是有值
- console.log(props.userMenu) // 该对象始终是一个空数据
- 因为 Vue 是 MVVM 框架,它的视图会在数据变化后自动渲染,于是通常情况下,
- props 里的值什么时候被填充并不重要,Vue 开发团队也不想追求 defineProps 工作的一般化。
- 所以使用目前版本,上面这段代码,访问到的 props 是 reactive 对象,数据被填充后就能看到
- userName 里有值;
- 而 props.userMenu 在访问时还没有被填充,所以得到的是 default() 返回的默认值,一直是空的。
-
- 同时大家还要知道,console.log() 输出的是对象的指针,而非快照。
- 所以里面的值只跟你展开时有关,跟运行时关系不大
在 script setup 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 script setup 中是直接可用的
子组件
- // 定义Props
- const props = defineProps<{
- result: number,
- name: string
- }>()
父组件
<Detail name="结果" :result="1">Detail>
适用于带默认值的Props,经测试不能与defineProps在同一组件同时定义。
子组件
- interface IProps {
- labels?: string[]
- result: number,
- name:string
- }
-
- // 定义带默认值的Props
- const defaultProps = withDefaults(defineProps<IProps>(), {
- name: 'hello',
- result:0,
- labels: () => ['one', 'two']
- })
父组件
- <Detail name="结果">Detail>
适用于父组件向子组件传递方法
子组件
-
- <hr />
- <button @click="btnReset">重置button>
- <hr />
-
- // 定义Emits
- const emits = defineEmits<{
- (e: 'add', id: number): void
- (e: 'reset', value: number): void
- }>()
-
- const btnAdd = () => {
- emits('add',2)
- }
- const btnReset = () => {
- emits("reset",0)
- }
夫组件
- <Detail @add="add" @reset="reset">Detail>
-
- const result = ref
(0); -
- const add = (num:number)=>{
- result.value+=num
- }
-
- const reset = (num:number)=>{
- result.value = num
- }
适用于子组件向父组件暴露方法和属性,父组件通过子组件示例进行调用。
子组件
- // 定义
- Expose const exposeStr = ref
("") defineExpose({ exposeStr })
父组件
-
- <Detail ref="detail">Detail>
-
- // 必须跟组件ref保持一致
- const detail = ref()
-
- setTimeout (() => {
- detail.value.exposeStr = "exposeStr"
- },1000)
完整代码
- <template>
- <div>
-
- <hr />
- {{defaultProps.name}} - {{defaultProps.result}} - {{defaultProps.labels}}
- <hr />
- <button @click="btnAdd">添加button>
- <hr />
- <button @click="btnReset">重置button>
- <hr />
- {{exposeStr}}
- div>
- template>
-
- <script setup lang="ts">
- import { ref,defineProps ,defineEmits,defineExpose} from "vue"
-
- // 定义Props
- // const props = defineProps<{
- // result: number,
- // name: string
- // }>()
-
- interface IProps {
- labels?: string[]
- result: number,
- name:string
- }
-
- // 定义带默认值的Props
- const defaultProps = withDefaults(defineProps<IProps>(), {
- name: 'hello',
- result:0,
- labels: () => ['one', 'two']
- })
-
- // 定义Emits
- const emits = defineEmits<{
- (e: 'add', id: number): void
- (e: 'reset', value: number): void
- }>()
-
- const btnAdd = () => {
- emits('add',2)
- }
- const btnReset = () => {
- emits("reset",0)
- }
-
- // 定义Expose
- const exposeStr = ref
("") - defineExpose({
- exposeStr
- })
-
- script>
- <style lang="scss" scoped>style>
- <template>
- <div>
-
- <Detail ref="detail" name="结果" :result="result" @add="add" @reset="reset">Detail>
- div>
- template>
-
- <script setup lang="ts">
- import Detail from './detail.vue'
-
- import {ref} from 'vue'
-
- const result = ref
(0); -
- const add = (num:number)=>{
- result.value+=num
- }
-
- const reset = (num:number)=>{
- result.value = num
- }
-
- // 必须跟子组件ref名保持一致
- const detail = ref()
- setTimeout (() => {
- // 调用子组件属性并修改值
- detail.value.exposeStr = "exposeStr"
- },1000)
- script>
shallowReactive:只处理对象最外层属性的响应式(浅响应式)只考虑第一层数据的响应式。
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理,传递基本数据类型的话跟ref没有任何区别,ref是可以进行对象的响应式处理的
我们正常的ref创建的数据,里面的.value是一个proxy,而shallowRef创建的数据 .value里面是一个object数据类型,所以不会响应式数据
什么时候使用?:
如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactive
如果有一个对象数据,后续功能不会修改对象中的属性,而是生新的对象来替换 ===> shallowRef
readonly:让一个响应式的数据变成只读的(深只读)
shallowReadonly: 让一个响应式数据变成只读的(浅只读)
应用场景:不希望数据被修改的时候
- import { reactive,readonly,shallowReadonly } from 'vue'
- export default {
- name: 'test9',
- setup(){
- let person = reactive({
- name: '张三',
- job:{
- salary: '20k',
- }
- })
- person = readonly(person) //这个时候修改人的信息就不会改变了,所有的都不能改
- /**
- * 页面不进行响应式的改变,一般存在两种情况:
- * 1.setup里面定义的数据改变了,但是vue没有检测到,这个时候是不会改变的
- * 2.setup里面定义的数据压根儿就不让你改,这个时候也没法响应式
- */
- person = shallowReadonly(person) //只有最外层不能修改是只读的,但是job还是可以改的
- return {
- person
- }
- },
- }
toRaw
作用:将一个由reactive生成的响应式对象转换为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象
使用场景:
- 1.有些值不应被设置成响应式的,例如复杂的第三方类库等
- 2.当渲染具有不可变数据的大列表时候,跳过响应式转换可以提高性能
- import {reactive,toRaw,markRaw} from 'vue'
- setup(){
- let person = reactive({
- name: '张三',
- })
- function showRawPerson(){
- const p = toRaw(person)
- p.age++
- console.log(p)
- }
- function addCar(){
- let car = {name: '奔驰'}
- person.car = markRaw(car) //一旦这么做时候,他就永远不能当成响应式数据去做了
- }
- }
创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制
实现防抖效果:
- <input type="text" v-model="keyword">
- <h3>{{keyword}}h3>
- template>
-
- <script>
- import { customRef, ref } from 'vue'
-
- export default {
- name: 'test10',
- setup(){
- let timer;
- //自定义一个ref——名为: myRef
- function myRef(value){
- return customRef((track,trigger)=>{
- return {
- get(){
- console.log(`有人读取我的值了,要把${value}给他`); //两次输出: v-model读取 h3里面的插值语法调了一次
- track() //追踪一下改变的数据(提前跟get商量一下,让他认为是有用的)
- return value
- },
- set(newValue){
- console.log(`有人把myRef这个容器中数据改了:${newValue}`);
- clearTimeout(timer)
- timer = setTimeout(()=>{
- value = newValue
- trigger() //通知vue去重新解析模板,重新再一次调用get()
- },500)
- }
- }
- })
- }
-
- // let keyword = ref('hello') //使用内置提供的ref
- let keyword = myRef('hello') //使用自定义的ref
- return {
- keyword,
- }
- },
-
- }
- script>
作用:实现祖孙组件间的通信
套路:父组件有一个provide选项提供数据,子组件有一个inject选项来开始使用这些数据
具体写法:
- //父组件
-
- import { ref,reactive,toRefs,provide } from 'vue';
- import ChildVue from './components/Child.vue';
-
- let car = reactive({
- name: '奔驰',
- price: '40w'
- })
- provide('car',car) //给自己的后代组件传递数据
- const {name, price} = toRefs(car)
- <template>
- <div class="app">
- <h3>我是父组件, {{name}}--{{price}}h3>
- <ChildVue>ChildVue>
- div>
- template>
- <style>
- .app{
- background-color: gray;
- padding: 10px;
- box-sizing: border-box;
- }
- style>
-
- //子组件
- import { ref } from '@vue/reactivity';
- import SonVue from './Son.vue';
-
- <template>
- <div class="app2">
- <h3>我是子组件h3>
- <SonVue>SonVue>
- div>
- template>
-
- <style>
- .app2{
- background-color: rgb(82, 150, 214);
- padding: 10px;
- box-sizing: border-box;
- }
- style>
- //孙组件
-
- import { ref,inject } from 'vue';
- let car = inject('car') //拿到父组件的数据
- const {name, price} = car
-
- <template>
- <div class="app3">
- <h3>我是孙组件h3>
- <p>{{name}}-{{price}}p>
- div>
- template>
- <style>
- .app3{
- background-color: rgb(231, 184, 56);
- padding: 10px;
- box-sizing: border-box;
- }
- style>
1.为组件的 props 标注类型
- // 场景一: 使用
-
-
- //也可以将 props 的类型移入一个单独的接口中
-
- <script setup lang="ts">
-
- interface Props {
-
- foo: string
-
- bar?: number
-
- }
-
- const props = defineProps<Props>()
-
- script>
- //场景二: 不使用
-
- import { defineComponent } from 'vue'
-
- export default defineComponent({
-
- props: {
-
- message: String
-
- },
-
- setup(props) {
-
- props.message // <-- 类型:string
-
- }
-
- })
-
-
- 注意点:为了生成正确的运行时代码,传给 defineProps() 的泛型参数必须是以下之一:
-
- //1.一个类型字面量:
-
- defineProps<{ /*... */ }>()
-
- //2.对同一个文件中的一个接口或对象类型字面量的引用
-
- interface Props {/* ... */}
-
- defineProps<Props>()
-
- //3.接口或对象字面类型可以包含从其他文件导入的类型引用,但是,传递给 defineProps 的泛型参数本身不能是一个导入的类型:
-
- import { Props } from './other-file'
-
- // 不支持!
-
- defineProps<Props>()
-
- Props 解构默认值
-
- //当使用基于类型的声明时,失去了对 props 定义默认值的能力。通过目前实验性的响应性语法糖来解决:
-
2.为组件的 emits 标注类型
- //场景一: 使用
-
-
-
-
-
- //场景二: 不使用
-
- import { defineComponent } from 'vue'
-
-
-
- export default defineComponent({
-
- emits: ['change'],
-
- setup(props, { emit }) {
-
- emit('change') // <-- 类型检查 / 自动补全
-
- }
-
- })
3.为 ref() 标注类型
- import { ref } from 'vue'
-
- import type { Ref } from 'vue'
-
- //1.ref 会根据初始化时的值推导其类型:
-
- // 推导出的类型:Ref
-
- const year = ref(2020)
-
- // => TS Error: Type 'string' is not assignable to type 'number'.
-
- year.value = '2020'
-
-
-
- //2.指定一个更复杂的类型,可以通过使用 Ref 这个类型:
-
- const year: Ref
= ref('2020') -
- year.value = 2020 // 成功!
-
-
-
- //3.在调用 ref() 时传入一个泛型参数,来覆盖默认的推导行为:
-
- // 得到的类型:Ref
-
- const year = ref
('2020') -
- year.value = 2020 // 成功!
-
-
-
- //4.如果你指定了一个泛型参数但没有给出初始值,那么最后得到的就将是一个包含 undefined 的联合类型:
-
- // 推导得到的类型:Ref
-
- const n = ref
()
4.为reactive() 标注类型
- import { reactive } from 'vue'
-
- //1.reactive() 也会隐式地从它的参数中推导类型:
-
- // 推导得到的类型:{ title: string }
-
- const book = reactive({ title: 'Vue 3 指引' })
-
-
-
- //2.要显式地标注一个 reactive 变量的类型,我们可以使用接口:
-
- interface Book {
-
- title: string
-
- year?: number
-
- }
-
- const book: Book = reactive({ title: 'Vue 3 指引' })
5.为 computed() 标注类型
- import { ref, computed } from 'vue'
-
- //1.computed() 会自动从其计算函数的返回值上推导出类型:
-
- const count = ref(0)
-
-
-
- // 推导得到的类型:ComputedRef
-
- const double = computed(() => count.value * 2)
-
-
-
- // => TS Error: Property 'split' does not exist on type 'number'
-
- const result = double.value.split('')
-
-
-
- //2.通过泛型参数显式指定类型:
-
- const double = computed
(() => { -
- // 若返回值不是 number 类型则会报错
-
- })
-
-
6.为事件处理函数标注类型
- //在处理原生 DOM 事件时,应该为我们传递给事件处理函数的参数正确地标注类型
-
-
-
-
- <template>
-
- <input type="text" @change="handleChange" />
-
- template>
-
-
-
- //因此,建议显式地为事件处理函数的参数标注类型,需要显式地强制转换 event 上的属性:
-
- function handleChange(event: Event) {
-
- console.log((event.target as HTMLInputElement).value)
-
- }
7.为 provide / inject 标注类型
- /*
- provide 和 inject 通常会在不同的组件中运行。要正确地为注入的值标记类型,
- Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,
- 可以用来在提供者和消费者之间同步注入值的类型:
- */
-
- import { provide, inject } from 'vue'
-
- import type { InjectionKey } from 'vue'
-
-
-
- const key = Symbol() as InjectionKey
-
-
-
- provide(key, 'foo') // 若提供的是非字符串值会导致错误
-
-
-
- const foo = inject(key) // foo 的类型:string | undefined
-
-
-
- //建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入。
-
- //当使用字符串注入 key 时,注入值的类型是 unknown,需要通过泛型参数显式声明:
-
- const foo = inject
('foo') // 类型:string | undefined -
-
-
- //注意注入的值仍然可以是 undefined,因为无法保证提供者一定会在运行时 provide 这个值。
-
- //当提供了一个默认值后,这个 undefined 类型就可以被移除:
-
- const foo = inject
('foo', 'bar') // 类型:string -
-
-
- //如果你确定该值将始终被提供,则还可以强制转换该值:
-
- const foo = inject('foo') as string
8.为模板引用标注类型
- //模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:
-
-
- /**
- 注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,
- 这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null。
- */
-
- <template>
-
- <input ref="el" />
-
- template>
9.为组件模板引用标注类型
- //有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法
-
-
- <script setup lang="ts">
-
- import { ref } from 'vue'
-
- const isContentShown = ref(false)
-
- const open = () => (isContentShown.value = true)
-
- defineExpose({
-
- open
-
- })
-
- script>
-
- //为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:
-
-
- <script setup lang="ts">
-
- import MyModal from './MyModal.vue'
-
- const modal = ref<InstanceType<typeof MyModal> | null>(null)
-
- const openModal = () => {
-
- modal.value?.open()
-
- }
-
- script>
-
注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用这种技巧,需要开启 Volar 的Takeover 模式。
组合式API 可以通过调用 useStore 函数,来在 setup 钩子函数中访问 store。这与在组件中使用选项式 API 访问 this.$store 是等效的。
- import { useStore } from 'vuex'
- export default {
- setup () {
- const store = useStore()
- }
- }
1.访问 state 和 getter
为了访问 state 和 getter,需要创建 computed 引用以保留响应性,这与在选项式 API 中创建计算属性等效。
- import { computed } from 'vue'
-
- import { useStore } from 'vuex'
-
- export default {
-
- setup () {
-
- const store = useStore()
-
- return {
-
- // 在 computed 函数中访问 state
-
- count: computed(() => store.state.count),
-
- // 在 computed 函数中访问 getter
-
- double: computed(() => store.getters.double)
-
- }
-
- }
-
- }
2.访问 Mutation 和 Action
要使用 mutation 和 action 时,只需要在 setup 钩子函数中调用 commit 和 dispatch 函数。
- import { useStore } from 'vuex'
-
- export default {
-
- setup () {
-
- const store = useStore()
-
- return {
-
- // 使用 mutation
-
- increment: () => store.commit('increment'),
-
- // 使用 action
-
- asyncIncrement: () => store.dispatch('asyncIncrement')
-
- }
-
- }
-
- }
后续更新中。。。