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包裹值,所以使用的使用需要.valueconsole.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);// 获取节点,给初始值为nullconst 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>