1、插槽的具体定义及基本使用
- 插槽就是子组件提供给父组件使用的一个占位符,用<slot></slot>表示
- 父组件可以在这个占位符中填充任何模板代码,如html,组件等
- 填充的内容会替换子组件<slot></slot>标签
案例
<body>
<div id="app">
<sub-Box>
<!-- 组件标签对内的内容是用来填充slot插槽的-->
<button>我需要一个按钮</button>
</sub-Box>
<sub-Box> </sub-Box>
<sub-Box>
<input type="text" placeholder="please input words"/>
</sub-Box>
</div>
<!--把模板标签化了-->
<template id="sub">
<div style="width: 200px; height: 160px ; border:1px solid gray">
<h4>这是一个子组件</h4>
<p>这里是一些文字内容</p>
<!--预留一个插槽,注意插槽是放在template里面哦-->
<slot>我可以有一个默认内容</slot>
</div>
</template>
<!--把模板标签化了-->
<script>
//1、创建一个子组件
const subBox={
data(){
return {
}
},
template:'#sub'
}
//这里是根组件
const app=Vue.createApp({
data(){
return {
msg:'这是根组件的默认信息'
}
},
components:{
'subBox':subBox
}
})
app.mount('#app')
</script>
</body>
2、具名插槽
- 解决有多个插槽时,父组件的内容如何对应子组件的哪一个slot标签
- 如果每个插槽都有名字,那么父组件的内容就可以对号入座了
- 插槽通过name赋予插槽名字
- 父组件通过template及v-slot:name来对号入座
<body>
<div id="app">
<sub-com>
<!-- v-slot:name 去和具名的插槽一一对应,对号入座 -->
<template v-slot:headd>我把自己塞进了头部slot里面</template>
<template v-slot:boddy>我把自己塞进了身体slot里面</template>
<template v-slot:foott>我把自己塞进了脚部slot里面</template>
</sub-com>
</div>
<!--把模板标签化了-->
<template id="sub">
<h3>这是我的子组件</h3>
<div id="headd">
<!-- 通过name给插槽具名 -->
<slot name="headd"></slot>
</div>
<div id="bodyy">
<slot name="boddy"></slot>
</div>
<div id="foott">
<slot name="foott"></slot>
</div>
</template>
<script>
//1、创建一个子组件
const sub={
data(){
return {
}
},
template:'#sub'
}
//这里是根组件
const app=Vue.createApp({
data(){
return {
msg:'这是根组件的默认信息'
}
},
components:{
'sub-com':sub
}
})
app.mount('#app')
</script>
</body>
3、渲染作用域
- 父级模板里的所有内容都是在父级作用域中编译的,也就是访问不到子组件的data数据
- 子模板的所有内容都是在子作用域中编译的,也就是访问不到子组件的data数据
<body>
<div id="app">
<!-- 父级作用域读取父级组件的data数据-->
<subcom v-show="isShow"></subcom>
</div>
<!--把模板标签化了-->
<template id="sub">
<!-- 子级作用域读取子级组件的data数据-->
<div style="width: 200px; height: 100px; background-color: red">
<h3>我是一个子组件</h3>
<button v-show="isShow" >我是一个按钮</button>
</div>
</template>
<script>
//1、创建一个子组件
const sub={
data(){
return {
// 只能控制子组件内部元素的显示与否
isShow: false
}
},
template:'#sub'
}
//这里是根组件
const app=Vue.createApp({
data(){
return {
// 只能控制父组件元素的显示与否
isShow:true,
msg:'这是根组件的默认信息'
}
},
components:{
'subcom':sub
}
})
app.mount('#app')
</script>
</body>
4、作用域插槽
- 父组件替换插槽中的标签
- 但使用的是子组件中的数据(一般来说父组件不使用子组件的数据,但作用域插槽是使用子组件数据替换子组件插槽的内容)
- 说白了就是用子组件的数据在父组件内插入子组件(有没有多此一举的感觉)
<body>
<div id="app">
<subcom></subcom>
<p>---------</p>
<subcom>
<template v-slot:default></template>
</subcom>
<p>---------</p>
<subcom>
<!--接收从子组件模板传来的数据 -->
<template v-slot:default="slotProps">//由于子组件插槽没有具名,所以使用default,再在default后面使用slotProps接受从子组件:data传来的nameArray
<span>{{slotProps.data.join('--')}}</span>
</template>
</subcom>
</div>
<!--把模板标签化了-->
<template id="sub">
<div style="width: 200px; height: 300px; background-color: red">
<h3>我是一个子组件</h3>
<!-- 这里绑定data数据,方便父组件调用-->
<slot :data="nameArray">
<ul>
<li v-for="item in nameArray">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script>
//1、创建一个子组件
const sub={
data(){
return {
nameArray:['zhangsan','lisi','wangwu','zhaoliu']
}
},
template:'#sub'
}
//这里是根组件
const app=Vue.createApp({
data(){
return {
// 只能控制父组件元素的显示与否
msg:'这是根组件的默认信息'
}
},
components:{
'subcom':sub
}
})
app.mount('#app')
</script>
</body>
5、动态组件
- 当组件之间切换的时候,有时回想保持这些组件的状态,以避免反复重渲染导致的性能问题
- 根据数据的变化,结合component标签,来动态切换组件的显示代码
实际就是动态调整组件名称
一个传统的(非动态组件)切换组件显示转态代码
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data(){
return {
showItem:'mybutton'
}
},
methods:{
changeHandler(){
if (this.showItem ==='mybutton'){
this.showItem= 'myinput'
}else{
this.showItem= 'mybutton'
}
}
},
template:`
<mybutton v-show="showItem==='mybutton'"></mybutton>
<br/>
<myinput v-show="showItem==='myinput'"></myinput>
<p>-----</p>
<button @click="changeHandler">切换</button>
`
})
//1、创建一个子组件
app.component('mybutton',{
data(){
return {
}
},
template:`<button>按钮全局组件</button>`
})
app.component('myinput',{
data(){
return {
}
},
template:`<input type="text" placeholder="请输入一些东西"/>`
})
app.mount('#app')
</script>
</body>
使用动态组件:is=‘动态变量’来实现组件的动态呈现
实际上就是让component的名称动态来改变
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data(){
return {
//这里的开关变量的值是组件的名称
showItem:'mybutton' //还可以是myinput
}
},
methods:{
changeHandler(){
if (this.showItem ==='mybutton'){
this.showItem= 'myinput'
}else{
this.showItem= 'mybutton'
}
}
},
template:`
这里使用:is="数据名"来完成动态显示的效果,对于失活组件用keep-alive来缓存之前输入的数据<br/>
<keep-alive>
<component :is="showItem"></component>
</keep-alive>
<p>-----</p>
<button @click="changeHandler">切换</button>
`
})
//1、创建一个子组件
app.component('mybutton',{
data(){
return {
}
},
template:`<button>按钮全局组件</button>`
})
app.component('myinput',{
data(){
return {
}
},
template:`<input type="text" placeholder="请输入一些东西"/>`
})
app.mount('#app')
</script>
</body>
6、异步组件
- 在大型应用中,我们可能需要将应用分割成一些小的代码块,并且只在需要的时候才从服务器加载模块
- Vue中通过defineAsynccomponent(定义异步组件)的方法实现异步组件
- defineAsynccomponent通过异步函数来返回一个Promise对象
- 需在Promise的resolve对象中返回响应的组件模板
<body>
<div id="app">
<async-item></async-item>
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data(){
return {
}
},
})
//使用vue点出异步组件
app.component('async-item',Vue.defineAsyncComponent(()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve({
template:`<div>服务器端请求来的异步组件</div>`
})
},3000)
})
}))
app.mount('#app')
</script>
</body>
7、组件生命周期
各类钩子函数
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data(){
return {
count:0
}
},
methods:{
modifydata(){
this.count++
}
},
//组件实例生成之前执行
beforeCreate(){
console.log('beforeCreate excuted')
},
//组件实例生成之后执行
created(){
console.log('Create excuted')
},
//组件实例调用之前执行
beforeMount () {
console.log('before component mounted')
},
//组件实例调用之后执行
mounted(){
console.log('mounted')
},
//数据发生变化时会执行,注意数据已经变了
beforeUpdate(){
console.log(this.count)
console.log('datas is before upgrade')
},
//数据发生变化时,页面dom也更新完毕后会执行
updated(){
console.log(this.count)
console.log('datas is upgraded')
},
//实例失效时
beforeUnmount () {
console.log('component is beforeunmounted')
},
//实例失效后,dom也被销毁
unmount () {
console.log('component is unmounted')
},
template:`<h3>here is root element {{count}}</h3><br/><button @click="modifydata">修改数据</button>`
})
app.mount('#app')
</script>
</body>
8、watch侦听器
- watch侦听器是计算属性computed的底层实现
- computed和watch都能实现的功能,建议使用computed,因为其更简洁
- watch不用再页面渲染,计算属性需要渲染
- 计算属性默认深度依赖,watch默认浅度观测
- 计算属性适合做筛选,不可异步,watch适合做执行异步或开销较大的操作
<body>
<div id="app">
<h3>normal price:{{price}}</h3>
<h3>vip price:{{memberprice}}</h3>
<button @click="chagnePrice">change price</button>
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data(){
return {
price:19
}
},
methods:{
chagnePrice(){
this.price++
}
},
computed:{
memberprice(){
return this.price*0.8
}
},
//监听data里面所有数据的改变
watch:{
//可以做异步处理
price(current,prev){
setTimeout(()=>{
console.log('price is changing')
console.log(current)
console.log(prev)
this.memberprice=this.price*0.8
},2000)
},
}
})
app.mount('#app')
</script>
</body>
9、组件的单向数据流
- 子组件使用父组件的时候应该是单向数据流
- 如果多个组件同时修改父组件的值,有可能是危险的。
10、父子组件props传值的动态绑定传参
<body>
<div id="app">
<subcom></subcom>
</div>
<script>
//创建一个子组件
const sub={
data(){
return {
mytotal:this.total
}
},
props: ['number1','number2','number3','number4'],
template:`<h3>我是一个子组件</h3>
<div>数据1:{{number1}}</div>
<div>数据2:{{number2}}</div>
<div>数据3:{{number3}}</div>
<div>数据4:{{number4}}</div>
`
}
//这里是根组件
const app=Vue.createApp({
data(){
return {
a:100,
b:200,
c:300,
d:400,
numberObj:{
number1:100 ,
number2:200 ,
number3:300 ,
number4:400 ,
}
}
},
methods:{
},
components:{
'subcom':sub
},
computed:{
},
//监听data里面所有数据的改变
watch:{
},
template: `<subcom :number1="a" :number2="b" :number3="c" :number4="d" ></subcom>
<p>-----</p>
<subcom v-bind="numberObj"></subcom>
`
})
app.mount('#app')
</script>
</body>
11、no-props父组件向子组件传递标签属性
- no-props就是不通过绑定的方式,而是直接对props的内容进行赋值,这样依然可以把父组件的内容传递给子组件
- 主要是把父组件的属性传递给组件,而不仅仅满足与数据的传递
- 父组件传递给子组件的属性通过$attrs来获取,并通过
$attrs.具体属性名来指定子组件获取父组件传递来的哪一个属性。当然也可以不点具体的属性名,直接通过 $attrs获得所有属性 - 如果不想接受父组件传递来的no-props信息,则在子组件上设置inheritAttrs:false,
- 如果有多个同级别标签,no-props是不生效的
<body>
<div id="app">
</div>
<script>
//创建一个子组件
const sub={
data(){
return {
}
},
mounted(){
console.log(this.$attrs)
},
props:['info','message'],
//可以在子组件见设置不接受no-props数据,设定inheritAttrs:false
// inheritAttrs:false,
//如果有多个同级别标签,no-props是不生效的
// 父组件传递给子组件的属性通过$attrs来获取,并通过 $attrs.具体属性名来指定子组件获取父组件传递来的哪一个属性。当然也可以不点具体的属性名,直接通过 $attrs获得所有属性
template: `
<h3>我是子组件--{{info}}</h3>
<div :info="$attrs.info">我是子组件--{{info}}</div>
<div :style="$attrs.style">我是子组件--{{info}}</div>
<div v-bind="$attrs">我是子组件--{{info}}</div>
`
}
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg: 'hello world'
}
},
components: {
'subcom': sub
},
//父组件传递给子组件
template:`
<subcom info="test attrs" style="width: 200px; height: 100px; background-color: red"></subcom>
`
})
app.mount('#app')
</script>
</body>
12、过渡–基本形式
- 通过trasition或animation给铁定的目标元素添加或移除特定的class
- 过渡的相关类名
xxx-enter/xxx-leave:指定隐藏时的样式
xxx-enter-active:指定显示时的transition
xxx-leave-active:指定显示时的transition
一个基本案例
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
@keyframes shake {
0%{
transform: translateX(0);
}
50%{
transform: translateX(50px);
}
100%{
transform: translateX(0);
}
}
.box{
width:100px;
height: 100px;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
}
.animate{
animation:shake 2s infinite alternate;
}
/* 用来动态绑定的样式类 */
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
isAnimate:false,
msg: 'hello world'
}
},
methods:{
startAnimation(){
this.isAnimate=!this.isAnimate
}
},
template:`
<div>
<div class="box" :class="{animate:isAnimate}">测试文字</div>
<button @click="startAnimation">开始/结束</button>
</div>
`
})
app.mount('#app')
</script>
</body>
13、过渡–步骤
- 使用transition标签包裹过渡部分的模板代码
- 设置动画基于的样式
- 设置相应的入场和离场动画
xxx-enter/xxx-leave:指定隐藏时的样式
xxx-enter-active:指定显示时的transition
xxx-leave-active:指定显示时的transition
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
.box{
width: 100px;
height: 100px;
background-color: red;
}
/*入场动画类*/
.v-enter-from{
opacity: 0;
}
.v-enter-active{
transition: all 1s ease-in;
}
.v-enter-to{
opacity: 1;
}
/* 离场动画类*/
.v-leave-from{
opacity: 1;
}
.v-leave-active{
transition: all 1s ease-in;
}
.v-leave-to{
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg:'测试文本',
flag:false
}
},
methods:{
},
//对vue模板内的动画可以使用transition标签包裹
template:`
<div>
<transition>
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<br/>
<button @click="flag=!flag">显示/隐藏</button>
</div>
`
})
app.mount('#app')
</script>
</body>
14、过渡–使用自定义动画实现
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
.box{
width: 100px;
height: 100px;
background-color: red;
}
/*通过自定义动画实现*/
@keyframes shake-from {
0% {
opacity: 0;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes shake-to {
0% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 0;
transform: translateX(0);
}
}
/*入场动画类*/
.v-enter-active{
animation: 2s shake-from;
}
/* 离场动画类*/
.v-leave-active{
animation: 2s shake-to;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg:'测试文本',
flag:false
}
},
methods:{
},
//对vue模板内的动画可以使用transition标签包裹
template:`
<div>
<transition>
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<br/>
<button @click="flag=!flag">显示/隐藏</button>
</div>
`
})
app.mount('#app')
</script>
</body>
- 可以指定那些模板内容使用动画,那些不用。
- 方法是通过在transition标签上添加name属性
- 并在样式中以该name作为样式开头即.mytest-enter-active之类
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
.box{
width: 100px;
height: 100px;
background-color: red;
}
/*通过自定义动画实现*/
@keyframes shake-from {
0% {
opacity: 0;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes shake-to {
0% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 0;
transform: translateX(0);
}
}
/*入场动画类*/
.mytest-enter-active{
animation: 2s shake-from;
}
/* 离场动画类*/
.mytest-leave-active{
animation: 2s shake-to;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg:'测试文本',
flag:false
}
},
methods:{
},
//对vue模板内的动画可以使用transition标签包裹
template:`
<div>
<transition name="mytest">
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<transition >
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<transition >
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<br/>
<button @click="flag=!flag">显示/隐藏</button>
</div>
`
})
app.mount('#app')
</script>
</body>
15、同时使用过渡和动画
- 尽量让transition的市场和animation的时长一致
- 如果时长实在不一致,可以设置模板中transition标签的type值,如果值为transition则以transition为主,值为animation则以animation为主
- 在一个v-enter-from/to里面同时写过渡语句和自定义动画语句,如
.v-enter-active {
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-from;
}
.v-leave-active{
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-to;
}
案例如:
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
.box{
width: 100px;
height: 100px;
background-color: red;
}
/*通过自定义动画实现*/
@keyframes shake-from {
0% {
opacity: 0;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes shake-to {
0% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 0;
transform: translateX(0);
}
}
/*入场动画类*/
.v-enter-from{
opacity: 0;
}
.v-enter-active {
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-from;
}
.v-leave-active{
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-to;
}
/* 离场动画类*/
.v-enter-to{
opacity: 1;
}
.v-leave-to{
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg:'测试文本',
flag:false
}
},
methods:{
},
//对vue模板内的动画可以使用transition标签包裹
template:`
<div>
<transition type="animation">
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<br/>
<button @click="flag=!flag">显示/隐藏</button>
</div>
`
})
app.mount('#app')
</script>
</body>
- 也可以强制使用时间。方法是在模板的transition标签上使用:duration=时间,主要要有冒号,时间单位为毫秒
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
.box{
width: 100px;
height: 100px;
background-color: red;
}
/*通过自定义动画实现*/
@keyframes shake-from {
0% {
opacity: 0;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes shake-to {
0% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 0;
transform: translateX(0);
}
}
/*入场动画类*/
.v-enter-from{
opacity: 0;
}
.v-enter-active {
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-from;
}
.v-leave-active{
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-to;
}
/* 离场动画类*/
.v-enter-to{
opacity: 1;
}
.v-leave-to{
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg:'测试文本',
flag:false
}
},
methods:{
},
//对vue模板内的动画可以使用transition标签包裹
template:`
<div>
<transition :duration="2000">
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<br/>
<button @click="flag=!flag">显示/隐藏</button>
</div>
`
})
app.mount('#app')
</script>
</body>
- 还可以设置进入和离开的时间,方式是:duration={enter:3000,leave:1000}
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue3.js"></script>
<style type="text/css">
.box{
width: 100px;
height: 100px;
background-color: red;
}
/*通过自定义动画实现*/
@keyframes shake-from {
0% {
opacity: 0;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes shake-to {
0% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0;
transform: translateX(50px);
}
100% {
opacity: 0;
transform: translateX(0);
}
}
/*入场动画类*/
.v-enter-from{
opacity: 0;
}
.v-enter-active {
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-from;
}
.v-leave-active{
/*过渡*/
transition: all 1s ease-in;
/* 动画*/
animation: 2s shake-to;
}
/* 离场动画类*/
.v-enter-to{
opacity: 1;
}
.v-leave-to{
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
data () {
return {
msg:'测试文本',
flag:false
}
},
methods:{
},
//对vue模板内的动画可以使用transition标签包裹
template:`
<div>
<transition :duration="{enter:3000,leave:100}">
<div class="box" v-show="flag">{{msg}}</div>
</transition>
<br/>
<button @click="flag=!flag">显示/隐藏</button>
</div>
`
})
app.mount('#app')
</script>
</body>
16、animation.css第三方动画库
animate.css网站