一、初识vue3
1.vue3简介
- 2020年9月18日,vue3发布3.0版本,代号大海贼时代来临,One Piece
- 特点:
- 无需构建步骤,渐进式增强静态的 HTML
- 在任何页面中作为 Web Components 嵌入
- 单页应用 (SPA)
- 全栈 / 服务端渲染 (SSR)
- Jamstack / 静态站点生成 (SSG)
- 开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
2.Vue3带来了什么
- 打包大小减少40%
- 初次渲染快55%,更新渲染快133%
- 内存减少54%
3.分析目录结构
- main.js中的引入
- 在模板中vue3中是可以没有根标签了,这也是比较重要的改变
- 应用实例并不只限于一个。createApp API 允许你在同一个页面中创建多个共存的 Vue 应用,而且每个应用都拥有自己的用于配置和全局资源的作用域。
//main.js//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数import {createApp} from 'vueimport App from './App.vue//创建应用实例对象-app(类似于之前vue2中的vm实例,但是app比vm更轻)createApp(APP).mount('#app')//卸载就是unmount,卸载就没了//createApp(APP).unmount('#app')//之前我们是这么写的,在vue3里面这一块就不支持了,会报错的,引入不到 import vue from 'vue'; new Vue({render:(h) => h(App)}).$mount('#app')//多个应用实例const app1 = createApp({ /* ... */})app1.mount('#container-1')const app2 = createApp({ /* ... */})app2.mount('#container-2')
安装vue3的开发者工具
- 方式一: 打开chrome应用商店,搜索vue: 里面有个Vue.js devtools,且下面有个角标beta那个就是vue3的开发者工具
- 方式二: 离线模式下,可以直接将包丢到扩展程序
二、 常用Composition API(组合式API)
1. setup函数
理解:Vue3.0中一个新的额配置项,值为一个函数
2.setup是所有Composition API(组合api) “表演的舞台”
组件中所用到的:数据、方法等等,均要配置在setup中
setup函数的两种返回值:
- 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注)
- 若返回一个渲染函数:则可以自定义渲染内容。
注意点:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data ,methos, computed…)中访问到setup中的属性,方法
- 但在setup中不能访问到Vue2.x配置(data.methos,compued…)
- 如果有重名,setup优先
- setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性
- 尽量不要与Vue2.x配置混用
import {h} from 'vue'//向下兼容,可以写入vue2中的data配置项module default {name: 'App',setup(){//数据let name = '张三',let age = 18,//方法function sayHello(){console.log(name)},//f返回一个对象(常用)return {name,age,sayHello}//返回一个函数(渲染函数)//return () => {return h('h1','学习')} return () => h('h1','学习')}}
1.1关于单文件组件
- 每个 *.vue 文件最多可以包含一个
。(不包括一般的 )
- 这个脚本块将被预处理为组件的 setup() 函数,这意味着它将为每一个组件实例都执行。
中的顶层绑定都将自动暴露给模板。
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的
语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。这个我下面是有说明的
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
(1)基本语法:
/* 里面的代码会被编译成组件 setup() 函数的内容。 这意味着与普通的 `` 只在组件被首次引入的时候执行一次不同, `` 中的代码会在每次组件实例被创建的时候执行。*/<script setup>console.log('hello script setup')</script>
顶层的绑定会被暴露给模板
当使用 的时候,任何在
声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用:
<script setup>// 变量const msg = '王二麻子'// 函数function log() { console.log(msg)}</script><template> <button @click="log">{{ msg }}</button></template>
import 导入的内容也会以同样的方式暴露。这意味着我们可以在模板表达式中直接使用导入的 action 函数,而不需要通过 methods 选项来暴露它:
<script setup>import { say } from './action'</script><template> <div>{{ say ('hello') }}</div></template>
(2)响应式:
响应式状态需要明确使用响应式 API 来创建。和 setup() 函数的返回值一样,ref 在模板中使用的时候会自动解包:
<script setup>import { ref } from 'vue'const count = ref(0)</script><template> <button @click="count++">{{ count }}</button></template>
(3)使用组件:
范围里的值也能被直接作为自定义组件的标签名使用:
/***这里 MyComponent 应当被理解为像是在引用一个变量。*如果你使用过 JSX,此处的心智模型是类似的。*其 kebab-case 格式的 同样能在模板中使用——不过,*强烈建议使用 PascalCase 格式以保持一致性。同时这也有助于区分原生的自定义元素。*/<script setup>import MyComponent from './MyComponent.vue'</script><template> <MyComponent /></template>
动态组件
/***由于组件是通过变量引用而不是基于字符串组件名注册的,*在 中要使用动态组件的时候,应该使用*动态的 :is 来绑定:*/<script setup>import Foo from './Foo.vue'import Bar from './Bar.vue'</script><template> <component :is="Foo" /> <component :is="someCondition " /> //usePoint.jsimport {reactive,onMounted,onBeforeUnmount } from 'vue'function savePoint(){ //实现鼠标打点的数据 let point = reactive({ x: null, y: null }) //实现鼠标点的方法 const savePoint = (e)=>{ point.x = e.pageX point.y = e.pageY } //实现鼠标打点的生命周期钩子 onMounted(()=>{ window.addEventListener('click',savePoint) }) onBeforeUnmount(()=>{ window.removeEventListener('click',savePoint) }) return point}export default savePoint
//组件test.vue<template> <p>当前求和为: {{sum}} </p> <button @click="sum++">加一</button> <hr> <h2>当前点击时候的坐标: x: {{point.x}} y:{{point.y}}</h2></template><script>import { ref } from 'vue'import usePoint from '../hooks/usePoint'export default { name: 'test8', setup(props,context){ let sum = ref(0) let point = usePoint() return { sum, point } }}</script>
9.toRef
- 作用: 创建一个ref对象,其value值指向另一个对象中的某个属性值
- 语法: const name = toRef(person, ‘name’)
- 应用:要将响应式对象中的某个属性单独提供给外部使用
- 扩展: toRefs与toRef功能一致,但是可以批量创建多个ref对象,语法: toRefs(person)
<template> <h2>姓名: {{name2}}</h2> <h2>年龄: {{person.age}}</h2> <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增长年龄</button></template><script> //使用setup的注意事项 import { reactive, toRef, toRefs } from 'vue' export default { name: 'test9', setup(){ let person = reactive({ name: '张三', age: 18, job:{ salary: '15k' }, }) //toRef const name2 = toRef(person,'name') //第一个参数是对象,第二个参数是键名 console.log('toRef转变的是',name2); //ref定义的对象 //toRefs,批量处理对象的所有属性 //const x = toRefs(person) //console.log('toRefs转变的是',x); //是一个对象 return { person, name2, ...toRefs(person) } }, }</script>
三、TypeScript 与组合式 API
1.为组件的 props 标注类型
//场景一: 使用<script setup lang="ts">const props = defineProps({ foo: { type: String, required: true }, bar: Number})props.foo // stringprops.bar // number | undefined</script>//也可以将 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 定义默认值的能力。通过目前实验性的响应性语法糖来解决:<script setup lang="ts">interface Props { foo: string bar?: number}// 对 defineProps() 的响应性解构// 默认值会被编译为等价的运行时选项const { foo, bar = 100 } = defineProps<Props>()</script>
2.为组件的 emits 标注类型
//场景一: 使用<script setup lang="ts">const emit = defineEmits(['change', 'update'])// 基于类型const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void}>()</script>//场景二: 不使用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 会根据初始化时的值推导其类型:// 推导出的类型:Refconst year = ref(2020)// => TS Error: Type 'string' is not assignable to type 'number'.year.value = '2020'//2.指定一个更复杂的类型,可以通过使用 Ref 这个类型:const year: Ref<string | number> = ref('2020')year.value = 2020 // 成功!//3.在调用 ref() 时传入一个泛型参数,来覆盖默认的推导行为:// 得到的类型:Refconst year = ref<string | number>('2020')year.value = 2020 // 成功!//4.如果你指定了一个泛型参数但没有给出初始值,那么最后得到的就将是一个包含 undefined 的联合类型:// 推导得到的类型:Refconst n = ref<number>()
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)// 推导得到的类型:ComputedRefconst 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>(() => { // 若返回值不是 number 类型则会报错})
6.为事件处理函数标注类型
//在处理原生 DOM 事件时,应该为我们传递给事件处理函数的参数正确地标注类型<script setup lang="ts">function handleChange(event) { // 没有类型标注时 `event` 隐式地标注为 `any` 类型, // 这也会在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 时报出一个 TS 错误。 console.log(event.target.value)}</script><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<string>provide(key, 'foo') // 若提供的是非字符串值会导致错误const foo = inject(key) // foo 的类型:string | undefined//建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入。//当使用字符串注入 key 时,注入值的类型是 unknown,需要通过泛型参数显式声明:const foo = inject<string>('foo') // 类型:string | undefined//注意注入的值仍然可以是 undefined,因为无法保证提供者一定会在运行时 provide 这个值。//当提供了一个默认值后,这个 undefined 类型就可以被移除:const foo = inject<string>('foo', 'bar') // 类型:string//如果你确定该值将始终被提供,则还可以强制转换该值:const foo = inject('foo') as string
8.为模板引用标注类型
//模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:<script setup lang="ts">import { ref, onMounted } from 'vue'const el = ref<HTMLInputElement | null>(null)onMounted(() => { el.value?.focus()})</script>/**注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null。*/<template> <input ref="el" /></template>
9.为组件模板引用标注类型
//有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法<!-- MyModal.vue --><script setup lang="ts">import { ref } from 'vue'const isContentShown = ref(false)const open = () => (isContentShown.value = true)defineExpose({ open})</script>//为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:<!-- App.vue --><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 模式。
四、Vuex与组合式API
- 组合式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') } }}
五、 其他的Composition API
1.shallowReactive与shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)只考虑第一层数据的响应式。
- shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理,传递基本数据类型的话跟ref没有任何区别,ref是可以进行对象的响应式处理的
我们正常的ref创建的数据,里面的.value是一个proxy,而shallowRef创建的数据 .value里面是一个object数据类型,所以不会响应式数据
- 什么时候使用?:
- 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactive
- 如果有一个对象数据,后续功能不会修改对象中的属性,而是生新的对象来替换 ===> shallowRef
2.readonly与shallowReadonly
- readonly:让一个响应式的数据变成只读的(深只读)
- shallowReadonly: 让一个响应式数据变成只读的(浅只读)
- 应用场景:不希望数据被修改的时候
<script> 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 } }, }</script>
3.toRaw与markRaw
- 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) //一旦这么做时候,他就永远不能当成响应式数据去做了}}
4.customRef
- 创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制
- 实现防抖效果:
<template> <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>
5.provide与inject
- 作用:实现祖孙组件间的通信
- 套路:父组件有一个provide选项提供数据,子组件有一个inject选项来开始使用这些数据
- 具体写法:
//父组件<script setup>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)</script><template> <div class="app"> <h3>我是父组件, {{name}}--{{price}}</h3> <ChildVue></ChildVue> </div></template><style>.app{ background-color: gray; padding: 10px; box-sizing: border-box;}</style>
//子组件<script setup>import { ref } from '@vue/reactivity';import SonVue from './Son.vue';</script><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>
//孙组件<script setup>import { ref,inject } from 'vue';let car = inject('car') //拿到父组件的数据const {name, price} = car</script><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>
6.响应式数据的判断
- isRef:检查一个值是否为ref对象
- isReactivce:检查一个对象是否是由reactive创建的响应式代理
- isReadonly:检查一个对象是否由readonly创建的只读代理
- isProxy:检查一个对象是否由reactive或者readonly方法创建的代理
六、Composition API的优势
1.传统options API存在的问题
- 使用传统的Options API中,新增或者修改一个需求,就需要分别在data,methods,computed里面修改
2.Composition API的优势
- 我们可以更加优雅的组织我们的代码,函数,让相关功能的代码更加有序的组织在一起
七、新的组件
1.Transition
- 会在一个元素或组件进入和离开 DOM 时应用动画
- 它是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:
- 由 v-if 所触发的切换
- 由 v-show 所触发的切换
- 由特殊元素 切换的动态组件
<button @click="show = !show">切换</button><Transition> <p v-if="show">HelloWord</p></Transition>//当一个 组件中的元素被插入或移除时,会发生下面这些事情/**1.Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡 class 会在适当的时机被添加和移除2.如果有作为监听器的 JavaScript 钩子,这些钩子函数会在适当时机被调用3.如果没有探测到 CSS 过渡或动画、也没有提供 JavaScript 钩子,那么 DOM 的插入、删除操作将在浏览器的下一个动画帧后执行*///针对CSS 的过渡效果/**1.v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。2.v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型3.v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。4.v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除5.v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。6.v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。*/
.v-enter-active,.v-leave-active { transition: opacity 0.5s ease;}.v-enter-from,.v-leave-to { opacity: 0;}
2.Fragment
- 在vue2中:组件必须有一个根标签
- 在vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处:减少标签层级,减少内存占用
3.Teleport
- 什么是Teleport? —— Teleport是一种能够将我们组件html结构移动到指定位置的技术(开发的时候非常有用)
//弹窗实现<script setup>import { ref,inject } from 'vue';let isShow = ref(false)</script><template> <div> <button @click="isShow = true">点我弹窗</button> <teleport to="body"> //定位到body <div class="mask" v-if="isShow"> <div class="dialog"> <h4>我是一个弹窗</h4> <h5>内容</h5> <h5>内容</h5> <h5>内容</h5> <button @click="isShow = false">关闭</button> </div> </div> </teleport> </div></template><style>.dialog{ width: 300px; height: 300px; text-align: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); background-color: blueviolet; margin: 0 auto;}.mask{ position: absolute; top: 0; left: 0; bottom: 0; right: 0; background-color: rgba(0, 0, 0, 0.5);}</style>
4.Suspense
<script setup>import { defineAsyncComponent } from 'vue'; //引入异步组件const ChildVue = defineAsyncComponent(()=> import('./components/Child.vue')) //这叫做动态引入//这种引入叫做异步引入,如果app不出来的话,那么Child组件也不会引入进来,有一个先后顺序// import ChildVue from './components/Child.vue'; //静态引入// 得等,等所有的组件加载完成之后app才会一起出现/** * Suspense这个标签,底层就内置了插槽,就可以解决异步引入有时候刷新先后出来慢的问题 * v-slot:default 表示默认的输出组件 * v-slot:fallback 表示如果页面加载的慢了,会优先展示这个内容,有点像刷新页面的时候数据回来的慢了,就加载一会儿*/</script><template> <div class="app"> <h3>我是父组件</h3> <Suspense> <template v-slot:default> <ChildVue></ChildVue> </template> <template v-slot:fallback> <h3>稍等,加载中....</h3> </template> </Suspense> </div></template><style>.app{ background-color: gray; padding: 10px; box-sizing: border-box;}</style>/**还有一种方法就是在子组件中,setup返回一个promise对象,这里之所以可以使用setup返回promise的原因是: 我们引入的是异步组件且使用了*/
- 等待异步组件时渲染一些后备内容,获得更好的用户体验
八: 新的生命周期钩子
1.常见的生命周期钩子
onMounted()onUpdated()onUnmounted()onBeforeMount()onBeforeUpdate()onBeforeUnmount()onActivated()onDeactivated()onServerPrefetch()
2.新的生命周期钩子
//1.onErrorCaptured():注册一个钩子,在捕获了后代组件传递的错误时调用。function onErrorCaptured(callback: ErrorCapturedHook): voidtype ErrorCapturedHook = ( err: unknown, instance: ComponentPublicInstance | null, info: string) => boolean | void//2.onRenderTracked():注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。 function onRenderTracked(callback: DebuggerHook): voidtype DebuggerHook = (e: DebuggerEvent) => voidtype DebuggerEvent = { effect: ReactiveEffect target: object type: TrackOpTypes /* 'get' | 'has' | 'iterate' */ key: any}//3.onRenderTriggered():注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。function onRenderTriggered(callback: DebuggerHook): voidtype DebuggerHook = (e: DebuggerEvent) => voidtype DebuggerEvent = { effect: ReactiveEffect target: object type: TriggerOpTypes /* 'set' | 'add' | 'delete' | 'clear' */ key: any newValue?: any oldValue?: any oldTarget?: Map<any, any> | Set<any>}//4.onServerPrefetch():注册一个异步函数,在组件实例在服务器上被渲染之前调用。function onServerPrefetch(callback: () => Promise<any>): void/**补充:1.如果这个钩子返回了一个 Promise,服务端渲染会在渲染该组件前等待该 Promise 完成。 2.这个钩子仅会在服务端渲染中执行,可以用于执行一些仅存在于服务端的数据抓取过程*///试例:<script setup>import { ref, onServerPrefetch, onMounted } from 'vue'const data = ref(null)onServerPrefetch(async () => { // 组件作为初始请求的一部分被渲染 // 在服务器上预抓取数据,因为它比在客户端上更快。 data.value = await fetchOnServer(/* ... */)})onMounted(async () => { if (!data.value) { // 如果数据在挂载时为空值,这意味着该组件 // 是在客户端动态渲染的。将转而执行 // 另一个客户端侧的抓取请求 data.value = await fetchOnClient(/* ... */) }})</script>
九: 解决没有this + 各种api的方法
- 在Vue2项目中可以使用this.$router.push等方法进行路由的跳转,但是在Vue3的setup函数里,并没有this这个概念,因此如何使用路由方法
// 在新的vue-router里面尤大加入了一些方法,比如这里代替this的useRouter,具体使用如下://引入路由函数import { useRouter } from "vue-router";//使用setup() { //初始化路由 const router = useRouter(); router.push({ path: "/" }); return {};}
- 在vue2中可以通过this来访问到$refs,vue3中由于没有this所以获取不到了,但是官网中提供了方法来获取:
<template> <h2 ref="root">姓名</h2></template><script> //使用setup的注意事项 import { onMounted, ref } from 'vue' export default { name: 'test9', setup(){ const root = ref(null) onMounted(()=>{ console.log(root.value); }) return { root } }, }</script>//第二种方法,也可以通过getCurrentInstance来获取<template> <h2 ref="root">姓名</h2></template><script> //使用setup的注意事项 import { onMounted, ref, getCurrentInstance } from 'vue' export default { name: 'test9', setup(){) const {proxy} = getCurrentInstance() onMounted(()=>{ console.log(proxy.$refs.root); }) return { } }, }</script>
- 关于element在vue3的使用方法,没有this.$message等方法解决方案
//关于element在vue3的使用方法,没有this.$message等方法解决方案<template> <!-- 测试组件 --> <button @click="doLogin">登录</button></template><script>import { getCurrentInstance } from 'vue'export default { name: 'Test', setup () { const instance = getCurrentInstance() // vue3提供的方法,创建类似于this的实例 const doLogin = () => { instance.proxy.$message({ type: 'error', text: '登录失败' }) // 类似于this.$message() } return { doLogin } }, // 如果想试用this.$message,须在mounted钩子函数中,setup中没有this实例, //但vue3.0中还是建议在setup函数中进行逻辑操作 mounted () { this.$message({ type: 'error', text: '登录失败' }) }}</script>