7.过渡效果
vue提供了两个内置组件,可以帮助你制作基于状态变化的过渡和动画
Transition
会在一个元素或组件进入和离开DOM时应用动画TransitionGroup
会在一个v-for
列表中的元素或组件被插入,移动,或移除时应用动画
7-1过渡效果
过渡模式
<Transition mode="out-in">
...
</Transition>
组件间过渡
<Transition name="yiLing">
<slot></slot>
</Transition>
7-2列表过渡
TransitionGroup
是一个内置组件,用于对v-for
列表中的元素或组件的插入,移除和顺序改变添加动画效果
区别
-
默认情况下,他不会渲染一个容器元素,但是可以通过
tag
属性来指定一个元素作为容器元素来渲染 -
过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换。
-
列表中的每个元素必须有一个独一无二的
key
属性 -
css过渡的动画是会被应用在列表的元素上,而不是容器元素上
<!-- <ul> -->
<!-- 添加动画
tag="ul" 就相当于把TransitionGroup解析为ul标签了
-->
<TransitionGroup name="yiLing" tag="ul" mode="out-in">
<li v-for="(data,index) in datalist" :key="data">
{{ data }}
<button @click="del(index)">删除</button>
</li>
</TransitionGroup>
<!-- </ul> -->
移动动画(列表)
当某一项被插入或移除时,它周围的元素会立即发生“跳跃”而不是平稳地移动。我们可以通过添加一些额外的 CSS 规则来解决这个问题:
.list-move /* 对移动中的元素应用的过渡 */
{
transition: all 0.5s ease;
}
/* 确保将离开的元素从布局流中删除
以便能够正确地计算移动的动画。 */
.list-leave-active {
position: absolute;
}
7-3 可复用过渡
得益于 Vue 的组件系统,过渡效果是可以被封装复用的。要创建一个可被复用的过渡,我们需要为 Transition
组件创建一个包装组件,并向内传入插槽内容:
app.vue
<template>
<div>
<button @click="isShow=!isShow">显示/隐藏</button>
<kerwinTransition myname="rtol">
<div v-if="isShow">11111111111111</div>
</kerwinTransition>
</div>
</template>
<script>
import kerwinTransition from './kerwinTransition.vue'
export default {
components: {
kerwinTransition
},
data() {
return {
isShow: true,
}
},
}
</script>
kerwinTransition.vue
<template>
<div>
<Transition :name="myname">
<slot></slot>
</Transition>
</div>
</template>
<script>
export default {
props:["myname"]
}
</script>
<style>
/* 下面我们会解释这些 class 是做什么的 */
.ltor-enter-active{
animation: animate 0.5s;
}
.ltor-leave-active {
animation: animate 0.5s reverse;
}
@keyframes animate {
0%{
transform: translateX(100px);
opacity: 0;
}
100%{
transform: translateX(0px);
opacity: 1;
}
}
.rtol-enter-active{
animation: animate 0.5s;
}
.rtol-leave-active {
animation: animate 0.5s reverse;
}
@keyframes animate {
0%{
transform: translateX(-100px);
opacity: 0;
}
100%{
transform: translateX(0px);
opacity: 1;
}
}
html,body{
overflow-x: hidden;
}
</style>
animate.css库的使用
官网地址:https://animate.style/
案例
<template>
<div>
<button @click="isShow=!isShow">切换</button>
<!-- 使用过渡组件,里面只能写一个元素的插入和删除 -->
<!-- 这里的name要和css的name一致 -->
<!-- enter-active-class:使用的是animate.css库的动画 -->
<!-- <transition name="yiLing" enter-active-class="animate__animated animate__bounceInRight" leave-active-class="animate__animated animate__bounceOutRight">
<div v-show="isShow">123</div>
</transition> -->
<!-- 使用动画 -->
<transition name="yiLing">
<div v-show="isShow">123</div>
</transition>
</div>
</template>
<script>
//----------------引入animate库-----------------
import "animate.css";
export default {
data() {
return {
isShow:true
}
},
}
</script>
<style>
/* 过渡 */
/* .yiLing-enter-active,
.yiLing-leave-active {
transition: opacity 0.5s ease;
}
.yiLing-enter-from,
.yiLing-leave-to {
opacity: 0;
} */
/* 动画 */
.yiLing-enter-active {
animation: bounce-in 0.5s;
}
.yiLing-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: translateX(100px);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
</style>
四.vue3组合式API
起初定义的是Vue-Function-API,后经过社区意见收集,更名为Vue-Composition-API.
1.reactive
作用:包装响应式对象,非包装对象,可以认为是模版中的状态
- template可以放兄弟节点
- reactive类似于useState,如果参数是字符串,数字,布尔,null,undefined等基本数据类型,会报警告,value cannot be made reactive, 所以应该设置对象,这样可以数据驱动页面
<template>
<div>
{{ obj.myName }}
<button @click="handleClick">click</button>
</div>
</template>
<script>
import { reactive } from "vue"
export default {
// 页面一加载,setup就会自动执行
setup() {
console.log("setup:复合api,状态,函数,计算属性,生命周期....");
// reactive:支持复杂数据类型
const obj = reactive({
myName: "张三",
})
const handleClick = () => {
// 修改状态
obj.myName="123"
console.log(obj.myName);
}
return {
obj,
handleClick
}
}
}
</script>
2.ref
作用:创建一个包装式对象,含有一个响应式属性value,它和reactive的差别,就是前者没有包装属性value,还有就是前者不能定义基本的数据类型
<template>
<div>
{{ myName }}
<button @click="handleClick">click</button>
</div>
</template>
<script>
import { ref } from "vue"
export default {
setup(props) {
console.log("setup:复合api,状态,函数,计算属性,生命周期");
const myName = ref("小小易")//字符串,数字,bool,对象,数组,比reactive更为强大
const handleClick = () => {
// ref,使用value包裹值,所以使用的使用需要.value
console.log(myName);
myName.value="123"
}
return {
myName,
handleClick
}
}
}
</script>
2-1ref嵌套在reactive中
<template>
<div>
{{ val }}
<input type="text" v-model="obj.val" @keyup.enter="add">
<ul>
<li v-for="(data,index) in obj.dataList " :key="data">
{{ data }}
<button @click="del(index)">del</button>
</li>
</ul>
</div>
</template>
<script>
import { reactive, ref } from "vue"
export default {
setup(props) {
// 定义ref的val;
const val = ref("")
const obj = reactive({
// 把ref的val赋值给obj的val,放在reactive里面
val,
dataList:[]
})
const add = () => {
console.log(obj);
// 使用ref获取值的方式
if( val.value.trim() != "") {
// 记得有一层value包裹
// 使用reactive添加值的方式
obj.dataList.push(val.value)
console.log(val);
val.value=""
}
}
const del = (index) => {
obj.dataList.splice(index,1)
}
return {
obj,
add,
del,
val
}
}
}
</script>
2-2ref绑定节点
<template>
<div>
<div ref="myDom">123</div>
<button @click="dom">获取节点</button>
</div>
</template>
<script>
import { ref } from "vue"
export default {
setup(props) {
console.log(props);
// 获取节点,给初始值为null
const myDom = ref(null);
const dom = () => {
// 获取节点
console.log(myDom.value);
}
return {
dom,
myDom
}
}
}
</script>
2-3toRefs
默认直接展开State,那么此时reactive数据变成普通数据,通过toRefs,可以把reactive里的每个属性,转换为ref对象,这样展开后,就会变成多个ref对象,依然具有响应式特性
目的:目的是为了在传递属性时保留响应性
<template>
<div>
<input type="text" v-model="val" @keyup.enter="add">
<ul>
<li v-for="(data,index) in dataList" :key="data">
{{ data }}
<button @click="del(index)">del</button>
</li>
</ul>
</div>
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
setup(props) {
const obj = reactive({
val: "",
dataList: []
})
// 对象解构以后,就可以把reactive定义的使用ref的方式进行使用
const {val,dataList}=toRefs(obj)
const add = () => {
console.log(val.value);
if (val.value.trim!="") {
obj.dataList.push(obj.val)
obj.val = ""
}
}
const del = (index) => {
obj.dataList.splice(index,1)
}
return {
...toRefs(obj),//reactive==>n个ref单独管理
del,add
}
}
}
</script>
3.计算属性
computed是一个回调函数
模糊搜索案例
<template>
<div>
<input type="text" v-model="search">
<ul>
<li v-for="data in computedList" :key="data">
{{ data }}
</li>
</ul>
</div>
</template>
<script>
// 引入计算属性和ref
import { computed, ref } from "vue"
export default {
setup(props) {
const search = ref("");
const datalist = ref(["aaa", "abb", "aab", "bcc", "abc", "bcd", "add", "acd"])
// computed是一个回调函数
const computedList = computed(() => {
return datalist.value.filter(item=>item.includes(search.value))
})
return {
search,
datalist,
computedList
}
}
}
</script>ba
对上述代码进行优化
创建一个hooks文件夹
在此下面创建一个useFilter.js文件
import { computed, ref } from "vue"
function useFilter(datalist) {
const search = ref("");
const computedList = computed(() => {
return datalist.value.filter(item => item.includes(search.value))
})
return {
search,
computedList
}
}
export default (useFilter);
App.vue
<template>
<div>
<input type="text" v-model="search">
<ul>
<li v-for="data in computedList" :key="data">
{{ data }}
</li>
</ul>
</div>
</template>
<script>
import useFilter from "./hooks/useFilter";
import { ref } from "vue"
export default {
setup(props) {
const datalist = ref(["aaa", "abb", "aab", "bcc", "abc", "bcd", "add", "acd"])
return {
...useFilter(datalist),
datalist
}
}
}
</script>
4.watch
计算属性允许我们声明性计算衍生值,然而在有些情况下,我们需要状态在状态变化时执行一些"副作用":列如更改DOM,或是根据异步操作的结果去修改另一处的状态。
在组合式 API 中,我们可以使用 watch
函数在每次响应式状态发生变化时触发回调函数:
第一个参数是监听的值,第二个参数可以执行监听时候的回调
<template>
<div>
<input type="text" v-model="myText">
<input type="text" v-model="myage">
</div>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
setup(props) {
const myText = ref('')
const myage = ref('')
const obj = reactive({
myText
})
//----------------第一种ref-----------------
// watch(myText, (newValue, oldValue) => {
// // 异步
// console.log("异步",newValue,oldValue,myText);
// }, { immediate: true, deep: true })
//----------------第二种ref-----------------
// watch(() => {return myText.value }, (newValue, oldValue) => {
// console.log("异步",newValue,oldValue,myText);
// })
//----------------第三种多个ref-----------------
// watch([myText,myage], (newValue, oldValue) => {
// console.log("异步",newValue,oldValue,myText,myage);
// })
//----------------第一种reactive-----------------
// watch(() => obj.myText, (newValue, oldValue) => {
// console.log("异步",newValue,oldValue,myText);
// })
//----------------第二种reactive-----------------
watch(obj, (newValue, oldValue) => {
console.log("异步",newValue,oldValue,obj.myText);
})
return {
myText,
myage,
obj
}
}
}
</script>
5. VCA中的watchEffect函数
(1)watch:
-
具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行(可以添加immediate:true),则页面一加载就可以箭头数据的变化
watch(myText, (newValue, oldValue) => { // 异步 console.log("异步",newValue,oldValue,myText); }, { immediate: true, deep: true })
-
参数可以拿到当前值和原始值
-
可以侦听多个数据的变化,用一个侦听器承载
(2)watchEffect:
- 立即执行,没有惰性,页面的首次加载就会执行。
- 自动检测内部代码,代码中有依赖 便会执行
- 不需要传递要侦听的内容 会自动感知代码依赖,不需要传递很多参数,只要传递一个回调函数
- 不能获取之前数据的值 只能获取当前值
- 一些异步的操作放在这里会更加合适
<template>
<div>
<input type="text" v-model="myText">
<input type="text" v-model="myAge">
<input type="text" v-model="obj.myText">
</div>
</template>
<script>
import { watchEffect, ref,reactive } from "vue"
export default {
setup(props) {
const myText = ref("")
const myAge = ref("")
const obj = reactive({
myText:""
})
//----------------ref-----------------
watchEffect(()=>{
console.log(`基于mytext和myage条件改变,发ajax`,myText.value,myAge.value)
})
//----------------reactive-----------------
watchEffect(()=>{
console.log(`基于mytext和myage条件改变,发ajax`,obj.myText)
})
return {
myAge,
myText,obj
}
}
}
</script>
6. prop & emit
App.vue
<template>
<div>
<!-- @event="handleEvent":处理子组件传过来的数据 -->
<!-- title="首页":给子组件传值 -->
<Child title="首页" @event="handleEvent" :isLeftShow="false"></Child>
</div>
</template>
<script>
import Child from "./components/Child.vue";
export default {
components: {
Child
},
setup(props) {
const handleEvent = (el) => {
// el:获取子组件传过来的数据
console.log("父组件定义",el);
}
return {
handleEvent
}
}
}
</script>
Child.vue
<template>
<div>
{{ title }}
<button @click="handleClick">子传父</button>
</div>
</template>
<script>
export default {
props: {
title: {
// 限定类型
type: String,
},
isLeftShow: {
type:Boolean
}
},
// {title },{emit}接收父传过来的数据
setup({title },{emit}) {
const handleClick = (el) => {
emit("event","1111")
}
return {
handleClick
}
}
}
</script>
7. VCA中provide&inject
provide、inject 是 vue-composition-api 的一个功能:依赖注入功能
App.vue
<template>
<div>
<Tabber></Tabber>
<Navbar></Navbar>
</div>
</template>
<script>
import Navbar from './Navbar.vue'
import Tabber from './Tabber.vue'
import { provide,ref } from 'vue';
export default {
components: {
Navbar,
Tabber
},
setup(props) {
const title=ref('首页')
provide("myTitle",title)
}
}
</script>
Navbar
<template>
<div style="text-align: center;">
{{ title }}
</div>
</template>
<script>
import { inject } from 'vue'
export default {
setup(props) {
//进行注入
const title = inject("myTitle")
return {
title
}
}
}
</script>
Tabber
<template>
<div>
<ul>
<li v-for="data in dataList" :key="data" @click="handleClick(data.name)">//(data.name)传值
{{ data.name }}
</li>
</ul>
</div>
</template>
<script>
import { ref, inject } from 'vue'
export default {
setup(props) {
//进行注入
const title=inject("myTitle")
const dataList = ref([{
name: "首页"
},
{
name: "列表"
},
{
name: "我的"
}
])
const handleClick = (data) => {
console.log(title.value);
//改变值
title.value=data
}
return {
dataList,
handleClick
}
}
}
</script>
<style>
ul{
list-style: none;
display: flex;
position: fixed;
bottom: 0;
width: 100%;
}
li{
flex: 1;
text-align: center;
height: 60px;
line-height: 60px;
}
</style>