第一章、介绍VUE
vue是一种构建界面的渐进式框架,与其它框架不同的是,它被设计为可以从底层向上逐层应用的框架。
vue的核心只关心视图层,不仅容易上手,还便于与第三方库或者既有项目进行整合。另一方面,它与现代化工具链以及各种支持类库结合使用时,vue也能够为复杂的单页面应用提供支持。
MVVM
1、[View]:视图层(ui界面界面)
2、[ViewModel]:业务逻辑(一切js可视为业务逻辑)
3、[Model]:数据库,(存储数据以及对数据的处理:增删改查)
第二章:回顾VUE2对比VUE3
发现传统的vue2逻辑比较分散,可读性差,可维护性差。对比vue3逻辑分明,可维护性强
vue2:Options API
vue3:Composition API (组合式API)
vue3新特性
- 重写双向绑定
在vue2中基于object.defineProperty()实现
object.defineProperty(obj, key, {
get: function fun1 () {}
set: function fun2 () {}
})
// 处理数组是重写了原型方法
const methodsToPatch = [
'push',
'pop',
'shift',
'sort',
...
]
vue3基于proxy劫持
const proxy = new Proxy(
target,
taegetType === targetType.COLLECTION ? collectionHandlers : baseHandlers
)
// object.difineProprety()对数组是不太友好的,我们直接修改数组的length,它是监听不到的,所以更换为Proxy,对数组更加友好。
- VDOM性能瓶颈
增加patch flag 优化静态树
- 支持Fragments
vue3支持多个根节点
<template>
<div></div>
<div></div>
<div></div>
<template>
// 底层原理是vue3帮我门在<template>标签里边新增了一个虚拟节点,不用我门在自己手动添加,当然这个虚拟节点是不会展示的
// 同时支持 render JSX
render() {
return (
<>
{this.visable ? (
<div>{this.obj.name}</div>
)}
</>
)
}
// 同时新增了两个内置组件Suspense teleport 和多v-model的用法
- 支持Tree-Shaking
简单来讲就是在保持代码运行结果的前提下,去除无用代码。
例如在vue3中我们想用一个API:import {watch, computed} form 'vue'
这样只有在我们使用的时候才会帮我们打进包里去
- Composition API
Setup语法糖模式
例如ref reactive watch computed toRefs toRows
第三章:(vite目录 & Vue单文件组件 & npm run dev)
public: 用于存放一些静态文件,但是该文件下的静态文件不会被vite所编译
src/assets:该文件夹下也是用于存放一些静态文件,像一些比较小的图片,可以通过配置将其转为Base64格式节省资源,以及一些其它配置。
components:存放组件的文件加
app.vue:app的一个全局入口页面文件
main.ts:全局的TS文件
vite-env.d.ts:声明文件的扩充
index.html:全局文件的入口,通过<script type="module" src="/src/main.ts"></script>来引入main.ts主文件
package.json:运行命令和依赖配置文件
tsconfig.json: TS的配置文件
vite.config.ts:vite配置文件
vite是基于esBuild来做一个编译,打包时基于rollup.js
单文件组件:
由<script>标签,可以有多个,但是带有setup的只能有一个,<template>标签,只能有一个,<style>标签,可以有多个
扩展插件
安装:volar
禁用VUE2使用的vetur
因为两者存在冲突
npm run dev执行
当执行npm run 时 程序回去找package.json中的script标签中配置的命令
npm run dev 去执行的时vite命令
控制台直接执行不好使:
原因时vite依赖在它的package.json中做了一个软连接的配置"bin": { "vite": "bin/vite.js" },
然后找到bin依赖发现其中有三个vite的配置
执行顺序是:先从我们本地的node_module里边找bin文件里边有没有可执行的vite,如果没有他会去npm install -g(全局包)里边去找,如果那个里边也没有, 他会去找环境变量。如果也没有,就会执行报错
nodejs源码
nodejs主要是由V8、libuv和第三方库组成
1、libuv:跨平台的异步IO库,但它提供的功能不仅仅包含IO,还包含进程,线程,信号,定时器,进程通讯,线程池等。
2、第三方库:异步DNS解析,http解析,HTTP2解析,解压压缩库(zlib),加密解密库(openssl)等等
3、V8实现JS解析,执行和自定义拓展,得益于V8的自定义拓展才有了Node.js
介虚拟你Dome
虚拟Dome是通过js生成的一个节点树
有key的:
指令: 以v-开头的vue指令
v-text:用来显示文本
v-html:用来显示富文本
v-if:用来显示和隐藏,(切换真假Demo)
v-else-if:表示v-if的“else if”块,可以链式调用
v-else:v-if的条件守卫指令 可缺省
v-show:用来显示和隐藏(display node block Css切换)
v-on:简写用@来给元素添加事件,另外有修饰符可以辅助事件。
v-bind:简写 : 用来给元素绑定属性Attr
v-model:双向绑定
v-for:用来遍历元素
ref全家桶
ref介绍:接受一个内布值,并返回一个响应式的ref对象,ref仅有一个,value property,志向该内部值。
improt {ref, Ref} from 'vue'
const str: Ref<string> = ref('你好')
const str = ref<string>('你好')
isRef
improt {ref, isRef} from 'vue'
const str = ref<string>('你好')
const str1: string = '你好'
console.log(isRef(str)) // true
console.log(isRef(str1)) // false
shallowRef
improt {shallowRef} from 'vue'
const str = shallowRef({
name: '张三'
})
// 此时str直接修改name不是响应式
str.value.name = '李四'
// 但是修改value可以
str.value = {
name: '李四'
}
triggerRef
improt {shallowRef, triggerRef} from 'vue'
const str = shallowRef({
name: '张三'
})
// 此时str直接修改name不是响应式
str.value.name = '李四'
// 使用triggerRef可以把str变成响应式
triggerRef(str)
customRef
improt {customRef} from 'vue'
function mRef<T>(hhh: T) {
return customRef((trank, trigger) => {
return {
get() {
trank()
return hhh
},
set(newValue) {
console.log('set')
hhh = newValue
trigger()
}
}
})
}
// 自定义响应式ref
const mStr = mRef<string>('你好呀')
reactive全家桶
reactive介绍:用来绑定复杂数据类型。例如数组,对象
ref和reactive的区别
reactive不接收简单数据类型,只接收引用类型 array,object,map,set等
reactive函数一样接收泛型
reactive取值和赋值都不需要.vulue
ref支持所有的类型,但是ref定义的复杂数据类型,源码中一样调用了toReactive将其转为了reactive
reactive取值和赋值都需要.vulue
reactive
improt {reactive} from 'vue'
type dts {
name: string
age: number
}
const info = reactive<dts>({
name: '张三',
age: 35
})
readonly / shallowReactive
import { reactive,readonly,shallowReactive } from "vue";
type dts = {
name: string,
info: {
eage: {
hh: string
}
}
}
let message = shallowReactive<dts>({
// 只有第一层是响应式
name: '张三',
info: {
eage: {
hh: '324234'
}
}
})
const rMsg = readonly(message)
const arr = reactive([1,2,3,4]) // 接受数组或者对象等复杂数据类型
function changeMsg1() {
message.name = '李四'
}
function changeMsg2() {
message.info.eage.hh = '1000000'
}
to系列全家桶
toRef
improt {toRef} from 'vue' const obj = reactive({ name: '张三', eage: 34 }) // 元数据类型是双向绑定,就是双向绑定,元数据不是执行后依旧不是,但是会改变数据,不会改变页面 const stage = toRef(obj, 'eage')
toRefs
improt {toRef} from 'vue' const obj = reactive({ name: '张三', eage: 34 }) // 元数据类型是双向绑定,就是双向绑定,元数据不是执行后依旧不是,但是会改变数据,不会改变页面 // 进行对象解构 const {name, eage}= toRefs(obj)
toWap
improt {toRef} from 'vue' const obj = reactive({ name: '张三', eage: 34 }) // 将响应式对象改变成原始对象 const wap= toWap(obj)
watch监听器
watch:主要用于数据发生改变之后执行一些脚本。需要监听一些特定的数据源,并在单独的回调函数中执行副作用。
improt {ref, watch} from 'vue' let str = ref<string>('张三') watch( str, (newDat, oldData) => { console.log('do somesting') }, {immediate: true, deep: true} )
第一个参数是监听数据源
可以是特定某个数据:str,也可以是某几个特定数据的集合:[str,str1,str2...],可以是某个特定数据的:() => obj.name
第二个参数是回调函数(newDat, oldData)=> {'do somesting'}
newDat, oldData:监听源是数组,那么这两个数据也是数组,且与监听源一 一对应
第三个参数是配置项option{immediate: true, //立即执行deep: true // 深度监听}
watchEffect高级监听器
更加方便的使用方法,接收两个参数,第一个参数是一个回调函数,需要监听的数据源只需要放在回调函数就就可以,第二个参数是options配置项,常用的属性有两个
属性 | 值 | 功能 |
---|---|---|
flush | pre | 组件更新前执行,默认 |
flush | aync | 强制效果,始终同步触发 |
flush | post | 组件更新后执行,一般使用这个 |
onTrigger | 一个函数 | 可以在里边添加debugger |
import {ref, watchEffect} from "vue";
let str = ref<string>('张三')
let str1 = ref<string>('李四')
const elll:HTMLInputElement = document.querySelector('#idt') as HTMLInputElement
// 将要监听的数据源放进watchEffect中,就会自动监听,且立即执行
const stop = watchEffect(
(oninvaldata) => {
const elll:HTMLInputElement = document.querySelector('#idt') as HTMLInputElement
console.log(`监听str${str.value}`)
console.log(`监听str${str1.value}`)
console.log(elll)
// 一个前置的执行函数,总是第一时间执行这里边的脚本,接收一个回调函数。但是before中的脚本不会在初始化的时候执行
oninvaldata(() => {
console.log(`before`)
})
},
{
flush: 'post',
onTrigger() {
debugger
}
}
)
vue组件
组件:每一个.vue文件都可以充当组件来用,每一个组件都可以复用
<template>
<div>
<HelloWorld :msg="message"></HelloWorld>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import HelloWorld from "./components/HelloWorld.vue"
const message = ref<string>('张三')
</script>
vue生命周期
<template>
<div id="idt">我是一个块级元素</div>
<div id="idt1">更新生命周期测试:{{count}}</div>
<button @click="updatedCount">++</button>
</template>
<script setup lang="ts">
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated} from "vue";
// 第一个执行setup
const str = ref<string>('张三')
let count = ref(0)
const updatedCount = ():void => {
count.value++
}
console.log(str, 'setup')
onBeforeMount(() => {
// 这里边回去不到DOM
const ellll:HTMLInputElement = document.querySelector('#idt') as HTMLInputElement
console.log('创建之前:onBeforeMount拿不到dom', ellll)
})
onMounted(() => {
const ellll:HTMLInputElement = document.querySelector('#idt') as HTMLInputElement
console.log('创建完成:onMounted拿到dom', ellll)
})
onBeforeUpdate(() => {
console.log('更新之前:onBeforeUpdate')
})
onUpdated(() => {
console.log('更新完毕:onUpdated' )
})
onBeforeUnmount(() => {
console.log('x卸载之前:onBeforeUnmount')
})
onUnmounted(() => {
console.log('卸载完成:onUnmounted')
})
onActivated(() => {
console.log('创建之前:onBeforeMount')
})
onDeactivated(() => {
console.log('创建之前:onBeforeMount')
})
</script>
css预处理器less以及style标签属性scoped
// 通过命令安装less到开发环境
npm install less less-loader -D
// 查看package.json安装成功,之后可以直接npm run dev
// 在style标签上添加 lang = less 进行使用。
<style lang = "less"></less>
scoped为避免不同组件文件中相同class名称样式污染
加上scoped,在打包运行的时候会在元素上添加一个 data-v-***属性,然后我门的样式都会变成属性选择器 例如:.layout-right[data-v-64b82e29]{....}
父子传参
1、父给子传参
父组件通过v-bing绑定一个数据,然后子组件通过defineProps接收传递过来的数据
如果传递的是字符串类型是不需要v-bing的
如下代码// 父组件 <template> <Menu title="我是父组件传过来的值" :data="list"></Menu> </template> <script lang="ts" setup> import { reactive, ref } from "vue"; const menus = ref(null) const list = reactive<number[]>([1,2,3,4,5]) </script > // 子组件 // 子组件通过defineProps接收来自父组件传递的数据,如果不确定父组件是否传递 //了该数据,则可以在定义时使用?来规避,如果这时候还需要给子组件添加默认值 //需要使用withDefaults函数来添加,该函数接收两个参数, //第一个参数是defineProps本身,第二个是接收数据的默认值配置项 <template> <div class="menu"> {{title}} <div>{{data}}</div> </div> </template> <script lang="ts" setup> import { reactive, ref } from "vue"; type props ={ title?: string, data?: number[], obj?: {name: string} } withDefaults(defineProps<props>(), { title: '我是默认值', data: () => [1,2,4], obj: () => {return {name: '张三'}} }) </script >
2、子组件向符组件传参
子组件需要使用defineEmits来创建一个传递函数,然后这个函数接收子组件与父组件约定好的函数函数名与所要传递的数据,并通过触发该函数向父组件传递数据,父组件需要绑定该函数名并创建该函数名的函数,然后通过形参来接收子组件传递过来的数据
代码如下:// 子组件 <template> <div> <button @click="distribute1">派发数据1</button> </div> </template> <script lang="ts" setup> import { reactive, ref } from "vue"; const list = reactive<number[]>([1,2,3,4,5]) const emit = defineEmits(['distribute',]) const distribute = () => { emit('distribute', list) } </script> // 父组件组件 <template> <div class="menu"> <Menu @distribute="distribute"></Menu> </div> </template> <script lang="ts" setup> import { reactive, ref } from "vue"; const distribute1 = (list1: number[]) => { console.log(list1, '我是子组件传过来的数据') } </script >
3、父组件直接访问并修改子组件的数据
在vue2中我们可以直接通过$refs来做到,但是vue3中增强了对子组件数据的安全性保障
所以想要父组件做到访问并修改子组件的数据,需要子组件通过defineExpose将要被访问的数据暴漏出去,否者访问无效
代码如下:
// 子组件
<script lang="ts" setup>
import { reactive } from 'vue';
const list = reactive<number[]>([1,2,3,4,5])
defineExpose({ list })
</script>
// 父组件
<template>
<div class="layout">
// 父组件需要先使用ref绑定子组件实例
<Menu ref="menus"></Menu>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
// 然后需要定义一个ref实例
const menus = ref(null)
const distribute = () => {
//
console.log(menus.value)
}
</script>
全局组件
组件使用频率非常高,例如(table,input,button,等), 这些组件几乎每个页面都在使用,就可以封装成全局组件
// 在main.ts中注册
import card from "./components/card/index.vue";
createApp(App).component('card', card).mount('#app')
局部组件
使用频率不高,只是在局部使用以下,通过import局部引入使用
也是我们最常用的组件
// 在要使用的vue文件中引入
import card from "./components/card/index.vue";
递归组件
原理和我们写JS递归是差不多的,自己调用自己,通过一个条件结束递归,否则导致内存泄漏。
代码示例如下:
// 父组件
<template>
<div class="menu">
<Tree @on-click="getItem" :TreeListData="TreeListData"></Tree>
</div>
</template>
<script lang="ts" setup>
import Tree from "../../Tree/index.vue";
import { reactive } from 'vue';
const getItem = (item: TreeListDts) => {
console.log(item)
}
type TreeListDts = {
name: string,
icon?: string,
children?: TreeListDts[] | []
}
const TreeListData = reactive<TreeListDts[]>([
{
name: 'no-1',
icon: '',
children: [
{
name: 'no-1-1',
icon: '',
children: [
{
name: 'no-1-1-1',
icon: '',
}
]
},
]
},
{
name: 'no-2',
icon: '',
children: [
{
name: 'no-2-1',
icon: '',
},
{
name: 'no-2-2',
icon: '',
},
{
name: 'no-2-3',
icon: '',
},
]
},
{
name: 'no-3'
},
])
</script>
<style lang="less" scoped>
.menu{
width: 200px;
border-right: 1px solid #ccc;
}
</style>
// 子组件
<template>
<div class="card" style="margin-left: 10px;">
<div @click.stop="clickItem(item)" :key="item.name" v-for="item in TreeListData">
{{item.name}}
<TreeList @on-click="clickItem" v-if="item?.children?.length" :TreeListData="item.children"></TreeList>
</div>
</div>
</template>
<script setup lang="ts">
import TreeList from "./index.vue";
type TreeListDts = {
name: string,
icon?: string,
children?: TreeListDts[] | []
}
type props = {
TreeListData: TreeListDts[]
}
defineProps<props>()
const emit = defineEmits(['on-click'])
const clickItem = (item: TreeListDts) => {
emit('on-click', item)
}
</script>
<style lang="less" scoped>
</style>
动态组件
什么是动态组件:就是让多个 组件使用同一个挂载点,并动态切换,这就是动态组件。
在挂载点使用component标签,然后使用v-bind:is="组件"
import A from './a.vue'
import B from './b.vue'
import C from './c.vue'
<component :is="a"></component>
然后通过JS切换组件,使用场景为tab切换居多
demo代码实例如下
// 父组件
<template>
<div class="content">
<button @click="switchCom(index)" v-for="(item, index) in componentsList" :key="index">
{{item.name}}
</button>
<component :is="current.comName"></component>
</div>
</template>
<script lang="ts" setup>
import { markRaw, reactive, ref } from "vue";
import A from "./a.vue";
import B from "./b.vue";
import C from "./c.vue";
type componentsDts = {
name: string,
comName: any
}
// 直接写A会有警告,因为proxy回去代理组件,但是组件不需要代理
// markRaw的作用是给数据添加一个属性,_v_skip: true, vue3使用proxy代理遇到这个属性为true时会跳过代理
const componentsList = reactive<componentsDts[]>([
{
name: '我是组件A',
comName: markRaw(A)
},
{
name: '我是组件B',
comName: markRaw(B)
},
{
name: '我是组件C',
comName: markRaw(C)
}
])
type comNameDts = Pick<componentsDts, 'comName'>
let current = reactive<comNameDts>({
comName: componentsList[0].comName
})
const switchCom = (index: number) => {
current.comName = componentsList[index]?.comName
}
</script>
<style lang="less" scoped>
.content{
flex: 1;
margin: 20px;
border: 1px solid #ccc;
overflow: auto;
&-items{
padding: 20px;
border: 1px solid #ccc;
}
}
</style>
// 子组件A示例
<template>
<div class="a">
AAAAAA
</div>
</template>
<script lang="ts" setup>
</script>
<style lang="less" scoped>
.a{
height: 300px;
background-color: red;
border: 1px solid, #ccc;
padding: 10px;
}
</style>
递归组件
插槽slot
查抄就是子组件提供给父组件的一个占位符,用<slot></slot>标签表示,父组件可以在这个占位符中填写任何模板代码,如HTML,组件等,填充的内容会替换子组件<slot></slot>标签
1、匿名插槽
子组件只是用<slot></slot>标签,并且不添加name属性,此时页面默认展示<slot></slot>标签内的内容,父组件调用时只需要使用v-slot 或者 #dedefault 就可以替换子组件里边的内容
2、具名插槽
子组件只是用<slot></slot>标签,并且添加name属性,此时页面默认展示<slot name="aaa"></slot>标签内的内容,父组件调用时只需要使用v-slot:aaa 或者 #aaa就可以替换子组件里边的内容
3、动态插槽
不论时匿名还是剧名插槽,父组件均可使用#[slotName] 的形式动态使用插槽
代码实例如下:
// 子组件
<template>
<div class="a">
<slot name="comA">啵啵啵啵啵啵AAA</slot>
</div>
<div class="b">
<div v-for="(item, index) in nameObj" :key="index">
<slot :index="index" :data="item"></slot>
</div>
</div>
<div class="c">
<slot name="comC">啵啵啵啵啵啵CCCC</slot>
</div>
<div class="d">
<div v-for="(item, index) in nameObj" :key="index">
<slot :name="`name${index}`" :index="index" :data="item"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
type nameDts = {
name: string,
age: number
}
const nameObj = reactive<nameDts[]>([
{
name: '张三',
age: 20
},
{
name: '李四',
age: 21
},
{
name: '王二',
age: 22
},
{
name: '麻子',
age: 23
},
])
</script>
<style lang="less" scoped>
.a, .b, .c, .d{
height: 100px;
background-color: pink;
border: 1px solid, #ccc;
padding: 10px;
margin-top: 20px;
}
.b{
background-color: yellow;
}
.c{
background-color: blue;
}
.d{
background-color: rgb(225, 87, 99);
}
</style>
// 父组件
<template>
<div class="content">
<Dialog>
<template #comA>我是组件A</template>
<template #default={data,index}>{{`我叫${data.name},我今年${data.age}岁了,我是第${index + 1}名`}}</template>
<template #[slotName]>我是组件C</template>
<template v-for="i in [0,1,2,3]" :key="`name${i}`" #[`name${i}`]={data,index}>{{`我叫${data.name},我今年${data.age}岁了,我是第${index + 1}名`}}</template>
</Dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import Dialog from "../../Dialog/index.vue";
type componentsDts = {
name: string,
comName: any
}
const slotName = ref<string>('comC')
</script>
<style lang="less" scoped>
.content{
flex: 1;
margin: 20px;
border: 1px solid #ccc;
overflow: auto;
&-items{
padding: 20px;
border: 1px solid #ccc;
}
}
</style>
异步组件 && 代码分包suspense
我们在执行完npm run build 之后 vite会为我们生成一个打包文件,打开这个打包文件的assets文件会发现我们这个项目的一些打包文件
其中index-css是我们的css文件
index-*****.js是我们项目的主逻辑文件
还会有一个vendor-.js,这是我们的第三方依赖所在的一个文件
那么在用户打开我们项目的时候。浏览器就会去加载这些文件,随着我们项目越来越大,那么js文件也会越来越大,加载时间越来越长,为了避免用户体验白屏时间,这个时候我们可以使用CDN的形式加载第三方依赖,以及使用异步组件的形式,将我们的主逻辑打包文件进行异步加载。
ps:XHLHttpRequest(XHR).readyState介绍
代码示例如下:
// 子组件
<template>
<div class="a">
<div v-for="(item,index) in nameList" :key="index">
{{item.name}}
</div>
</div>
</template>
<script lang="ts" setup>
import {axios} from "./server.ts";
const nameList = await axios('./data.json')
console.log(nameList)
</script>
<style lang="less" scoped>
.a{
height: 10px;
background-color: red;
border: 1px solid, #ccc;
padding: 10px;
}
</style>
// 父组件
<template>
<div class="header">
我是主入口
<!-- 配置defineAsyncComponent使用加载异步组件 -->
<!-- 它里边有两个插槽,一个默认插槽和一个具名fallback插槽 -->
<!-- 默认插槽里边放我门的组件,fallback插槽是我么在组件加载的时候可以做些什么,例如写一个loading -->
<Suspense>
<template #default>
<A></A>
</template>
<template #fallback>
<div>
loaddig....
</div>
</template>
</Suspense>
<A></A>
</div>
</template>
<script lang="ts" setup>
// import A from "../../A/index.vue";
import { defineAsyncComponent } from "vue";
const A = defineAsyncComponent(() => import('../../A/index.vue'))
</script>
<style lang="less" scoped>
.header{
height: 60px;
border-bottom: 1px solid #ccc;
}
</style>
// 本地数据调用server.ts
type nameDts = {
name: string
}
export const axios = (url: string): Promise<nameDts[]> => {
return new Promise((res) => {
let xhr:XMLHttpRequest = new XMLHttpRequest()
xhr.open('get', url)
xhr.onreadystatechange = () => {
setTimeout(() => {
// readyState一共四种状态
if (xhr.readyState === 4 && xhr.status === 200) {
res(JSON.parse(xhr.responseText))
}
}, 1000)
}
xhr.send()
})
}
Teleport(传送组件):
teleport是vue3的新特性,是一种能够将我们的组件渲染到指定DOM节点,不受父级style和v-show影响,但是data和porp数据依旧能够公用的技术,类似于react的portal
// to的值可以是定义的class名
<Teleport to="body">
<div>
哈哈哈哈哈
</div>
</Teleport>
keep-alive缓存组件
有时候我们不希望组件被从新渲染影响用户体验,或者处于性能考虑,避免多次渲染降低性能,而是希望组件缓存下来,维持当前状态,这时候我们需要keep-alice组件。
开启keep-alive组件生命周期的变化:
- 初次进入时:onMounted>onActivated
- 退出后触发:onDeactivated
- 再次进入只会触发onActivated
- 事件挂载的方法等,只执行一次的放在onMounted,组件每次进入都要执行的方法放在onActivated
代码示例如下:
// 子组件login
<template>
<div>
<table>
<tbody>
<tr>
<td>账号</td>
<td>
<input v-model="form.name" type="text">
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input v-model="form.psd" type="password">
</td>
</tr>
</tbody>
</table>
<button @click="login">登录</button>
</div>
</template>
<script lang="ts" setup>
import { onActivated, onDeactivated, onMounted, onUnmounted, reactive } from 'vue';
onMounted(() => {
console.log('我是onMounted')
})
onUnmounted(() => {
console.log('我是onUnmounted')
})
onActivated(() => {
console.log('我是onActivated')
})
onDeactivated(() => {
console.log('我是onDeactivated')
})
const form = reactive({
name: '',
psd: ''
})
const login = () => {
console.log('我是用户名和密码', form)
}
</script>
<script lang="ts">
// 暴漏子组件的组件名
export default {
name: 'login'
}
</script>
</style>
// 子组件register
<template>
<div>
<table>
<tbody>
<tr>
<td>账号</td>
<td>
<input v-model="form.name" type="text">
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input v-model="form.psd" type="password">
</td>
</tr>
<tr>
<td>验证码</td>
<td>
<input v-model="form.code" type="text">
</td>
</tr>
</tbody>
</table>
<button @click="login">登录</button>
</div>
</template>
<script lang="ts" setup>
import {onActivated, onDeactivated } from "vue";
import { reactive } from 'vue';
onActivated(() => {
console.log('我是onActivated')
form.code = ''
})
onDeactivated(() => {
console.log('我是onDeactivated')
form.code = ''
})
const form = reactive({
name: '',
psd: '',
code: ''
})
const login = () => {
console.log('我是用户名和密码', form)
}
</script>
<script lang="ts">
export default {
name: 'register'
}
</script>
// 父组件
<template>
<!--keep-alive中只能存放一个组件 -->
<div class="content">
<button @click="switchCom">切换</button>
// keep-alive有三个常用属性,
// include,规定keep-alive要缓存的组件名
// exclude, 规定keep-alive不缓存的组件名字
// ps: 要实现以上两个属性,就要在子组件中讲组件的名字暴漏出来,否则无效
<keep-alive :include="['login']" :exclude="['register']" :max="10">
<login v-if="flag"></login>
<register v-else></register>
</keep-alive>
// 使用v-if切换,或者component动态组件都是可以的
<!-- <keep-alive>
<component :is="comName"></component>
</keep-alive> -->
</div>
</template>
<script lang="ts" setup>
import { markRaw, reactive, ref } from "vue";
import login from "../../login/index.vue";
import register from "../../register/index.vue";
const arr = reactive<any[]>([
markRaw(login),
markRaw(register)
])
let comName = ref(markRaw(login))
let flag = ref(false)
const switchCom = () => {
flag.value = !flag.value
comName.value = flag.value ? arr[0] : arr[1]
}
</script>
<style lang="less" scoped>
.content{
flex: 1;
margin: 20px;
border: 1px solid #ccc;
overflow: auto;
}
</style>
transition动画组件
vue提供了transition的封装动画组件,在下列情况下,可以给任何组件添加进入和离开动画
- 条件渲染(使用v-if)
- 条件展示(使用v-show)
- 动态组件
组件根节点
自定义transition过度效果,你需要对transition组件的name属性自定义,并在css中写入对应样式
<template>
<div>
<button @click="switchBox">switch</button>
<Transition name="fade">
<div v-if="flag" class="box"></div>
</Transition>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const flag = ref<boolean>(false)
const switchBox = () => {
flag.value = !flag.value
}
</script>
<style lang="less" scoped>
.box{
width: 200px;
height: 200px;
background-color: red;
}
// 进入之前
.fade-enter-from{
width: 0px;
height: 0px;
}
// 激活状态
.fade-enter-active{
transition: all 1.5s ease;
}
// 进入之后
.fade-enter-to{
width: 200px;
height: 200px;
// transform: scale(2);
background-color: yellow;
}
// 消失之前
.fade-leave-form{
width: 200px;
height: 200px;
}
// 消失进行中
.fade-leave-active{
transition: all 1.5s ease;
}
// 消失之后
.fade-leave-to{
width: 0px;
height: 0px;
}
// 以上六种类型名称是transition自带的
也可以自定义,自定义名称的时候就是给transition组件添加六种属性,来替换默认的class名称
</style>