Vue3 知识点总结
一、爷孙组件通信 provide>inject
这边以爷孙组件来命名,provide是无法在父子组件、兄弟组件通信,只有在爷孙组件才行
Provide/Inject用于非父子组件之间共享数据∶
比如有一些深度嵌套的组件,孙组件想要获取爷组件的部分内容;
在这种情况下,如果我们仍然将props沿着组件链逐级传递下去,就会非常的麻烦。
对于这种情况下,我们可以使用Provide和 Inject :
无论层级结构有多深,爷组件都可以作为其所有孙组件的依赖提供者;
爷组件有一个provide选项来提供数据;
孙组件有一个inject选项来开始使用这些数据;
实际上,你可以将依赖注入看作是“long range props”,除了∶爷组件不需要知道哪些孙组件使用它provide的 property子组件不需要知道inject的property来自哪里
举个栗子
爷组件代码
export default ({
provide{
name:"abc"
}
})
孙组件
在孙子组件内拿到定义的值,界面上就可以直接使用了
export default {
inject: ["name"],
}
这边会有个问题,如果爷爷组件想传入当前data数据长度,使用this获取是会报错
export default ({
provide(){
return{
name:"why",
length:this.list.length
}
},
})
需要使用函数返回,this才会指向正常
处理响应式数据
我们先来验证一个结果∶如果我们修改了this.names的内容,那么使用length的子组件会不会是响应式的?我们会发现对应的子组件中是没有反应的:
这是因为当我们修改了names之后,之前在provide中引入的this.names.length本身并不是响应式的;那么怎么样可以让我们的数据变成响应式的呢?
非常的简单,我们可以使用响应式的一些API来完成这些功能,比如说computed函数;当然,这个computed是vue3的新特性,在后面我会专门讲解,这里大家可以先直接使用一下注意:我们在使用length的时候需要获取其中的value
这是因为computed返回的是一个ref对象,需要取出其中的value来使用;
二、事件传递
vu3在事件总线中不再使用vuex,Vue3从实例中移除了**$on、$off和$once方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库∶Vue3官方有推荐一些库,例如mitt** 或tiny-emitter ;
这里我们主要讲解一下mitt库的使用;
mitt库的使用
安装yarn add mitt
封装为eventbus.js
import mitt from 'mitt'
const emitter = mitt()
//export const emitter1 = mitt() //可以引入多个事件
//export const emitter2 = mitt()
//export const emitter3 = mitt()
export default emitter
发送事件
import emmiter from './utils/eventbus';
export default {
methods:{
click(){
emmiter.emit("clickElement",{name:"kobe"}) //可以传递对象参数
}
}
}
监听事件
import emmiter from './utils/eventbus';
export default {
created(){
emmiter.on("clickElement",(info)=>{
console.log(info)
})
//监听方式二:监听所有事件对象
emmiter.on("*",(type,info)=>{
console.log("* listener:",type,info) //“* listener:clicklement {name:"kobe"}”
})
}
}
取消事件
在默写情况下我们可能希望取消掉之前注册的函数监听:
emitter.all.clear() //取消所有监听
//单独取消某个监听事件需要先创建一个函数
function onFoo(){}
emitter.on("foo",onFoo) //监听
emitter.off("foo",onFoo) //取消监听
插槽Slot
在开发中,我们会经常封装一个个可复用的组件:
前面我们会通过props传递给组件一些数据,让组件来进行展示;
但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素
比如某种情况下我们使用组件,希望组件显示的是一个按钮,某种情况下我们使用组件希望显示的是一张图片;我们应该让使用者可以决定某一块区域到底存放什么内容和原生;
默认插槽
当没有指定插槽名时,其实vue是有给插槽默认名称
<slot name="defalut"></slot>
动态插槽
目前我们使用的插槽名称都是固定的;比如v-slot:left
、v-slot:center
等等;
我们可以通过v-slot:[dynamicSlotName]
方式动态绑定一个名称;
具名插槽
v-slot
可以使用缩写#
表示
<template #center>
<button>点我</button>
</template>
作用域插槽
在Vue中有渲染作用域的概念
父级模板里的所有内容都是在父级作用域中编译的;
子模板里的所有内容都是在子作用域中编译的;
动态组件使用
<component
is="about"
name="page"
:age="18"
></component>
component Is 所匹配的是components导入的名称
<script lang="ts">
import my from './my.vue'
import about from './about.vue'
export default({
components:{
my,
about,
},
})
</script>
keep-alive
keep-alive有一些属性:
include - string | RegExp | Array。只有名称匹配的组件会被缓存;
exclude - string | RegExp \ Array。任何名称匹配的组件都不会被缓存;
max - number | string。最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁;
include和exclude prop 允许组件有条件地缓存∶
二者都可以用逗号分隔字符串、正则表达式或一个数组来表示;
匹配首先检查组件自身的name选项;
如果出现设置完include没反应的情况,大部分原因是因为组件没有设置name属性
针对keep-alive的生命周期问题
对于缓存组件来说,再次进入时,我们是不会执行created或者mounted等生命周期函数的,但是有时候我们确实希望监听到何时重新进入到了组件,何时离开了组件;这个时候我们可以使用activated和deactivated这两个生命周期钩子函数来监听;
activated() {
console.log ("about activated") ;
},
deactivated(),{
console.log ("about deactivated") ;
}
Webpack的代码分包
在默认情况下,在构建整个组件树的过程中,因为组件和组件之间是通过模块化直接依赖的,那么webpack在打包时就会将组件模块打包到一起(比如一个app.js文件中) ;这个时候随着项目的不断庞大, app.js文件的内容过大,会造成首屏的渲染速度变慢;
所以,对于-些不需要立即使用的组件 ,我们可以单独对它们进行拆分,拆分成一些小的代码块chunk.js,这些chunk.js会在需要时从服务器加载下来,并且运行代码,显示对应的内容;
webpack如何可以对代码进行分包呢?
使用import函数引用,打包时就会自动分包
import(“./utils/math”).then((res)=>{
console.log(res.sum(20,30))
})
Vue实现异步组件
如果我们的项目过大了,对于某些组件我们希望通过异步的方式来进行加载(目的是可以对其进行分包处理) , 那
么Vue中给我们提供了一个函数: defineAsyncComponent.
defineAsyncComponent接受两种类型的参数:
类型一:工厂函数,该工厂函数需要返回一个Promise对象;
类型二:接受一个对象类型,对异步函数进行配置;
两种方式
2.工厂模式
import Loading from './loading.vue'
const AsyncCategory = defineA
/**
异步组件和Suspense
Suspense是-个内置的全局组件,该组件有两个插槽:
0 default :如果default可以显示,那么显示default的内容;
0 fallback :如果default无法显示,那么会显示fallback插槽的内容;
Transition动画组件原理
当插入或删除包含在transition组件中的元素时, Vue将会做以下处理:
- 自动嗅探目标元素是否应用了CSS过渡或者动画,如果有,那么在恰当的时机添加/删除CSS类名;
- 如果transition组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用;
- 如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画, DOM插入、删除操作将会立即执行;
- v-enter-from :定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一-帧移除。
- v-enter-active :定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动
画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 - v-enter-to :定义进入过渡的结束状态。在元素被插入之后下一帧生效(与此同时v-enter-from被移除) ,在过渡/
动画完成之后移除。 - v-leave-from :定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
- v-leave-active :定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在
过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 - v-leave-to :离开过渡的结束状态。在离开过渡被触发之后下一-帧生效(与此同时v-leave-from被删除) ,在过渡/
动画完成之后移除。
class的name命名规则如下:
如果我们使用的是一个没有name的transition ,那么所有的class是以V-作为默认前缀;
如果我们添加了一个name属性,比如
我们也可以显示的来指定过渡的时间,通过duration属性。
duration可以设置两种类型的值:
number类型:同时设置进入和离开的过渡时间;
object类型:分别设置进入和离开的过渡时间;
过度的模式mode
in-out
<transition name="ani" mode="out-in">
<component :is="isShow ? 'home' : 'about'"></component>
</transition>
out-in
<transition name="ani" mode="in-out">
<component :is="isShow ? 'home' : 'about'"></component>
</transition>
appear初次渲染
默认情况下,首次渲染的时候是没有动画的,如果我们希望给他添加_上去动画,那么就可以增加另外一 个属性appear属性
<transition name="ani" mode="in-out" appear>
<h2 v-if=“isShow”>你好,李银河</h2>
<h2 v-else>hello Word</h2>
</transition>
第三方动画库animate.css的使用
如果我们手动一一个个来编写这些动画,那么效率是比较低的,所以在开发中我们可能会引用一-些第三方库的动画库,
比如animate.css.
Animate.css是一个已经准备好的、 跨平台的动画库为我们的web项目,对于强调、主页、滑动、注意力引导
非常有用;
安装animate.css :
npm install animate.css
在main.js中导入animate.css :
import”"animate.css" ;
接下来在使用的时候我们有两种
用法一 :直接使用animate库中定义的keyframes动画;
用法二:直接使用animate库提供给我们的类;
Gsap库
某些情况下我们希望通过JavaScript来实现一 些动画的效果,这个时候我们可以选择使用gsap库来完成。
什么是gsap呢?
GSAP是The GreenSock Animation Platform ( GreenSock动画平台)的缩写;
它可以通过JavaScript为CSS属性、SVG、 Canvas等设置动画,并且是浏览器兼容的;
这个库应该如何使用呢?
第一步:需要安装gsap库;
第二步:导入gsap库;
第三步:使用对应的api即可;
在使用动画之前,我们先来看-下transition组件给我们提供的JavaScript钩子 ,这些钩子可以帮助我们监听动画执行到
什么阶段了。
添加:css=”false” ,会让Vue会跳过CSS的检测,除了性能略高之外,这可以避免过渡过程中CSS规则的影响。
当我们使用JavaScript来执行过渡动画时,需要进行done回调,否则它们将会被同步调用,过渡会立即完成。
Options API的弊端
在Vue2中,我们编写组件的方式是Options API :
Options API的一大特点就是在对应的属性中编写对应的功能模块;
比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命周期钩子;
但是这种代码有一个很大的弊端∶
当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中
当我们组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散;尤其对于那些一开始没有编写这些组件的人来说,这个组件的代码是难以阅读和理解的(阅读组件的其他人);
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。