Vite+Typescript+Vue3学习笔记
1、项目搭建
1.1、创建项目(yarn)
D:\WebstromProject>yarn create vite
yarn create v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...success Installed "create-vite@4.4.1" with binaries:- create-vite- cva
√ Project name: ... vite-project
√ Select a framework: » Vue
√ Select a variant: » TypeScriptScaffolding project in D:\WebstromProject\vite-project...Done. Now run:cd vite-projectyarnyarn devDone in 14.81s.
1.2、项目配置
1、配置vue文件识别
vite默认情况下不识别.vue后缀的文件,需在vite-env.d.ts配置下
/// <reference types="vite/client" />
declare module "*.vue" {import { DefineComponent } from "vue"const component: DefineComponent<{}, {}, any>export default component
}
2、Api选择
/* Options API */
export default {props: {},data(){},computed: {},watch: {},methods: {},created(),components:{}// ...other options
}
/* Composition API */
export default {props: {},setup(),components:{}
}
Vue3推荐使用Composition API,这里关闭Vue2的Option Api
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/
export default defineConfig({define: {// 关闭Vue2 Options Api__VUE_OPTIONS_API__: false},plugins: [vue(),],
})
1.3、常用依赖
1、@types/node
ts需安装node的类型,否则使用node相关会提示找不到
# @types/node
yarn add -D @types/node
2、auto-import
用于简化Vue3中ref、reactive**、**watch和UI组件的导入
# unplugin-vue-components、unplugin-auto-import
yarn add -D unplugin-vue-components unplugin-auto-import
3、sass
# sass
yarn -D add sass
2、Composition API
2.1、响应式
可以在chrome浏览器开启自定义格式化,便于查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sEWfdHAM-1690717347682)(C:\Users\lianxin\AppData\Roaming\Typora\typora-user-images\image-20230728093837507.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3UMPZ6T4-1690717347682)(C:\Users\lianxin\AppData\Roaming\Typora\typora-user-images\image-20230728093754299.png)]
1、ref
ref通常用于声明基础类型响应式数据,引用类型也可以,会隐式调用reactive,同时取值赋值都需要.value
<script setup lang="ts">
import {Ref} from "vue";
// 非响应式
// let number: number = Math.random()
// console.log(isRef(number))// 响应式
// let number = ref(Math.random())
// 显式类型约束
let number: Ref<number> = ref<number>(Math.random())
console.log(isRef(number))
const h1click = () => {// 非响应式// number = Math.random()// 响应式number.value = Math.random()console.log(number);
}
</script><template><div><h1 @click="h1click">{{ number }}</h1></div>
</template><style scoped></style>
2、shallowRef
只处理基本数据类型的响应式, 不进行对象的响应式处理(不能与ref一起用,否则会被迫更新视图)
<script setup lang="ts">
import {isShallow, Ref} from "vue";let number = shallowRef<number>(Math.random())
console.log(isShallow(number))
const h1click = () => {number.value = Math.random()console.log(number);
}
</script><template><div><h1 @click="h1click">{{ number }}</h1></div>
</template><style scoped></style>
3、reactive
reactive用于声明引用类型响应式数据,且对象解构后会失去响应式
<script setup lang="ts">let person = reactive({name: "xumeng"
})
console.log(isReactive(person))
const h1click = () => {person.name = "xumeng03"console.log(person);
}
</script><template><div><h1 @click="h1click">{{ person }}</h1></div>
</template><style scoped></style>
4、shallowReactive
只处理对象最外层属性的响应式(浅响应式),(不能与reactive一起用,否则会被迫更新视图)
<script setup lang="ts">let person = shallowReactive({name: "xumeng",hobby: ["game","code"]
})
console.log(isReactive(person))
const h1click = () => {// 浅层次数据// person.name = "xumeng03"// 深层次数据(如果浅层次&深层次数据一起变化,则会一起更新)person.hobby[0] = 'movie'console.log(person);
}
</script><template><div><h1 @click="h1click">{{ person }}</h1></div>
</template><style scoped></style>
5、toRef
将对象某个属性变为响应式
<script setup lang="ts">let person = reactive({name: "xumeng"
})
// 如果person非响应式对象,则不会引起视图更新
let name = toRef(person, 'name')
const h1click = () => {name.value = "xumeng03"console.log(person);
}
</script><template><div><h1 @click="h1click">{{ person }}</h1></div>
</template><style scoped></style>
6、toRefs
将对象的一些解构属性变为响应式
<script setup lang="ts">let person = reactive({name: "xumeng",age: 22
})
// 解构别名
let {name: pname, age: page} = toRefs(person)
const h1click = () => {pname.value = "xumeng03"page.value = 23console.log(pname, page);
}
</script><template><div><h1 @click="h1click">{{ person }}</h1></div>
</template><style scoped></style>
7、toRaw
把响应式对象变为普通对象
<script setup lang="ts">let person = reactive({name: "xumeng",age: 22
})let rperson = toRaw(person)
const h1click = () => {rperson.name = "xumeng03"rperson.age = 23console.log(rperson);
}
</script><template><div><h1 @click="h1click">{{ person }}</h1></div>
</template><style scoped></style>
2.2、修饰符
1、readonly
<script setup lang="ts">let person = reactive({name: "xumeng"
})
let rperson = readonly(person)
const h1click = () => {// 正常赋值person.name = "xumeng03"// 报错// rperson.name = "xumeng03"console.log(person);
}
</script><template><div><h1 @click="h1click">{{ person }}</h1></div>
</template><style scoped></style>
2.3、computed
1、选项式写法
参数为对象,需传入一个get和set函数自定义存取操作
<script setup lang="ts">let person = reactive({name: "xumeng03",nickName: '眼眸流转',age: 22
})
let people = computed({get() {return person.name + '-' + person.nickName},set(newValue) {[person.name, person.nickName] = newValue.split('-')}
})
const h1click = () => {people.value = '徐梦-眼眸'
}
</script><template><div><h1 @click="h1click">{{ people }}</h1></div>
</template><style scoped></style>
2、函数式写法
<script setup lang="ts">let person = reactive({name: "xumeng03",nickName: '眼眸流转',age: 22
})
let people=computed(()=>{return person.name + '-' + person.nickName
})
const h1click = () => {// 不支持修改// people.value = '徐梦-眼眸'
}
</script><template><div><h1 @click="h1click">{{ people }}</h1></div>
</template><style scoped></style>
2.4、监听
1、watch
基本类型
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
watch(age, (value, oldValue) => {console.log("监听到变化:", value, oldValue);
})
</script><template><div><h1 @click="h1click">{{ age }}</h1></div>
</template><style scoped></style>
引用类型
<script setup lang="ts">let person = reactive({name: "xumeng03",nickName: '眼眸流转',age: 22
})
let person1 = reactive({name: "xumeng03",nickName: '眼眸流转',age: 22
})
const h1click = () => {person.age++
}
let people = computed(() => {return person.name + '-' + person.nickName + '-' + person.age
})// 此时若监听person会发现新旧数据一致,可以使用computed处理一下返回(如返回新的信息或深拷贝原引用数据)
// watch(person, (value, oldValue) => {
// console.log("监听到变化:", value, oldValue);
// }, {
// // 深度监听,reactive默认就是深度监听
// deep: true,
// // 立即执行一次
// immediate: true,
// // 刷新时机
// flush: 'pre'
// })// 监听整个引用数据
watch(people, (value, oldValue) => {console.log("监听到变化:", value, oldValue);
}, {// 深度监听,reactive默认就是深度监听deep: true,// 立即执行一次immediate: true,// 刷新时机flush: 'pre'
})// 监听引用对象某个属性
watch(() => person.age, (value, oldValue) => {console.log("监听到变化:", value, oldValue);
}, {// 深度监听,reactive默认就是深度监听deep: true,// 立即执行一次immediate: true,// 刷新时机flush: 'pre'
})// 监听多个数据源
watch([person, person1], (value, oldValue) => {console.log("监听到变化:", value, oldValue);
}, {// 深度监听,reactive默认就是深度监听deep: true,// 立即执行一次immediate: true,// 刷新时机flush: 'pre'
})
</script><template><div><h1 @click="h1click">{{ people }}</h1></div>
</template><style scoped></style>
2、watchEffect
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
// watchEffect返回一个停止监听函数
const stopwatch = watchEffect((oninvalidate) => {oninvalidate(() => {console.log("变化前");})console.log(age);
})
// 停止监听
// stopwatch()
</script><template><div><h1 @click="h1click">{{ age }}</h1></div>
</template><style scoped></style>
3、组件
3.1、全局组件
全局组件
<script setup lang="ts"></script><template><div>这是一个card</div>
</template><style scoped lang="scss"></style>
注册
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router";
import Card from "./components/Card.vue";
// 这里要注册下组件
createApp(App).component('Card',Card).use(router).mount('#app')
// 批量注册
// let app = createApp(App)
// import Card from "./components/Card.vue";
// const components = [
// Card
// ];
// for (const componentsKey in components) {
// app.component(componentsKey, components[componentsKey])
// }
使用
<script setup lang="ts">import HomeView from "./view/HomeView.vue";let HomeViewFlag = ref(true)
</script><template><div><a @click="()=>{HomeViewFlag=!HomeViewFlag}"><img src="/vite.svg" class="logo" alt="Vite logo"/></a><a @click="()=>HomeViewFlag=!HomeViewFlag"><img src="./assets/vue.svg" class="logo vue" alt="Vue logo"/></a></div><HomeView v-if="HomeViewFlag"/><Card/>
</template><style scoped lang="scss">
.logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms;&:hover {filter: drop-shadow(0 0 2em #646cffaa);}.vue {&:hover {filter: drop-shadow(0 0 2em #42b883aa);}}
}
</style>
3.2、局部组件
局部组件
<script setup lang="ts">
let age = ref(22)
const h1click = () => {age.value++
}
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1></div>
</template>
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
使用
<script setup lang="ts">import HomeView from "./view/HomeView.vue";let HomeViewFlag = ref(true)
</script><template><div><a @click="()=>{HomeViewFlag=!HomeViewFlag}"><img src="/vite.svg" class="logo" alt="Vite logo"/></a><a @click="()=>HomeViewFlag=!HomeViewFlag"><img src="./assets/vue.svg" class="logo vue" alt="Vue logo"/></a></div><HomeView v-if="HomeViewFlag"/>
</template><style scoped lang="scss">
.logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms;&:hover {filter: drop-shadow(0 0 2em #646cffaa);}.vue {&:hover {filter: drop-shadow(0 0 2em #42b883aa);}}
}
</style>
3.3、递归组件
组件
<script setup lang="ts">
interface Tree {name: string,checked: boolean,children?: Tree[]
}const props = defineProps<{data: Tree[]
}>()// 自定义名称
defineOptions({name: "myTree"
})
</script><template><div v-for="item in data"><input type="checkbox" :checked="item.checked"/> <span>{{ item.name }}</span><!--<Tree v-if="item?.children?.length" :data="item?.children"/>--><myTree v-if="item?.children?.length" :data="item?.children"/></div>
</template><style scoped lang="scss"></style>
使用
<script setup lang="ts">
let age = ref(22)
const h1click = () => {age.value++
}interface Tree {name: string,checked: boolean,children?: Tree[]
}const data = reactive<Tree[]>([{name: "1",checked: true,children: [{name: "1-1",checked: false,children: [{name: "1-1-1",checked: true},]},{name: "1-2",checked: true},]},{name: "2",checked: false,children: [{name: "2-1",checked: false},{name: "2-2",checked: true},{name: "2-3",checked: true},]},{name: "3",checked: false,children: [{name: "3-1",checked: false},]}
])
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1></div><div><Tree :data="data"></Tree></div>
</template>
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3.4、父子组件传参
1、父传子(defineProps)
父组件
<script setup lang="ts">
let PtoC1 = ref(1)
let PtoC2 = ref({name: 'xumeng03',age: 22
})
</script><template><div>我是父组件<hr><Child :PtoC1="PtoC1" :PtoC2="PtoC2"/></div>
</template><style scoped lang="scss"></style>
子组件
<script setup lang="ts">
// js形式
// const props = defineProps({
// PtoC1: {
// type: Number,
// default: '默认值'
// },
// PtoC2: {
// type: Object,
// default: {}
// }
// })// typescript类型约束
const props = defineProps<{PtoC1: number,PtoC2: {name: string,age: number}
}>()// typescript默认值
withDefaults(defineProps<{PtoC1: number,PtoC2: {name: string,age: number}
}>(), {PtoC1: 0, PtoC2: () => ({name: 'name', age: 0})
})
</script><template><div>我是子组件<br><!--来自父组件的消息1: {{ FtoC }}-->来自父组件的消息1: {{ props.PtoC1 }}<br>来自父组件的消息2: {{ props.PtoC2 }}</div>
</template><style scoped lang="scss"></style>
2、子传父(defineEmits)
父组件
<script setup lang="ts">
let Pmessage1 = ref<string>()
let Pmessage2 = reactive<{name: string,age: number
}>({name: 'name',age: 0
})
const getChildMessage1 = (message: string) => {Pmessage1.value = message
}
const getChildMessage2 = (message: {name: string,age: number
}) => {Pmessage2 = message
}
</script><template><div>我是父组件<br>来自父组件的消息1: {{ Pmessage1 }}<br>来自父组件的消息1: {{ Pmessage2 }}<hr><Child @getChildMessage1="getChildMessage1" @getChildMessage2="getChildMessage2"/></div>
</template><style scoped lang="scss"></style>
子组件
<script setup lang="ts">
let message1 = ref("xumeng03")
let message2 = reactive({name: 'xumeng03',age: 22
})
// const emit = defineEmits(['getChildMessage1', 'getChildMessage2'])
const emit = defineEmits<{(e: 'getChildMessage1', message: string): void,(e: 'getChildMessage2', message: {name: string,age: number}): void
}>()
const transValue = () => {emit('getChildMessage1', message1.value)emit('getChildMessage2', message2)
}
</script><template><div @click="transValue">我是子组件</div>
</template><style scoped lang="scss"></style>
3、父组件访问子组件(defineExpose)
父组件
<script setup lang="ts">
import Child from "@/components/Child.vue";const child1 = ref<InstanceType<typeof Child>>()
const childAttributes = () => {console.log(child1.value.username)child1.value.changeName('xumeng03' + Math.ceil(Math.random() * 10))
}
</script><template><div @click="childAttributes">我是父组件<br>子组件属性:{{ child1 }}<hr><Child ref="child1"/></div>
</template><style scoped lang="scss"></style>
子组件
<script setup lang="ts">
let username = ref("child")
const changeName = (newName: string) => {console.log("newName:" + newName)username.value = newName
}defineExpose({username,changeName
})
</script><template><div>我是子组件</div>
</template><style scoped lang="scss"></style>
3.5、动态组件(component)
<script setup lang="ts">
import Card from "@/components/Card.vue";
import Tree from "@/components/Tree.vue";
import {DefineComponent} from "vue";let age = ref(22)
const h1click = () => {age.value++
}interface Tree {name: string,checked: boolean,children?: Tree[]
}const data = reactive<Tree[]>([{name: "1",checked: true,children: [{name: "1-1",checked: false,children: [{name: "1-1-1",checked: true},]},{name: "1-2",checked: true},]},{name: "2",checked: false,children: [{name: "2-1",checked: false},{name: "2-2",checked: true},{name: "2-3",checked: true},]},{name: "3",checked: false,children: [{name: "3-1",checked: false},]}
])
const buttons = [{name: '组件1',component: Card},{name: '组件2',component: Tree},
]
const btnclick = (item: {name: string,component: DefineComponent<{}, {}, any>
}) => {componentDefault.value = item.component
}
// 使用shallowRef节约性能
let componentDefault = shallowRef(Card)
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><div><button @click="btnclick(item)" v-for="item in buttons">{{ item.name }}</button></div><component :is="componentDefault" :data="data"></component></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3.6、插槽(slot)
1、匿名插槽
匿名插槽在一个组件中只能有一个
<script setup lang="ts"></script><template><div><slot><!--插槽默认值--><span>这个插槽好像是空的?</span></slot></div>
</template><style scoped lang="scss"></style>
使用
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><Card>测试插槽</Card></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
2、具名插槽
<script setup lang="ts"></script><template><div><div><slot name="title"><!--插槽默认值--><span>这个组件标题好像是空的?</span></slot></div><div><slot name="content"><!--插槽默认值--><span>这个组件内容好像是空的?</span></slot></div><div><slot><!--插槽默认值--><span>这个组件操作项好像是空的?</span></slot></div></div>
</template><style scoped lang="scss"></style>
使用
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><Card><template #title>组件标题</template><template v-slot:content>组件标题</template><template v-slot>组件操作项</template></Card></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3、插槽传参
组件
<script setup lang="ts">
let titleData = ref("slot中的值")
</script><template><div><div><slot name="title" :data="titleData"><!--插槽默认值--><span>这个组件标题好像是空的?</span></slot></div><div><slot name="content"><!--插槽默认值--><span>这个组件内容好像是空的?</span></slot></div><div><slot><!--插槽默认值--><span>这个组件操作项好像是空的?</span></slot></div></div>
</template><style scoped lang="scss"></style>
使用
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><Card><template #title="children">组件标题-{{ children.data }}</template><template v-slot:content>组件标题</template><template v-slot>组件操作项</template></Card></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
4、动态插槽
组件
<script setup lang="ts">
let titleData = ref("slot中的值")
</script><template><div><div><slot name="title" :data="titleData"><!--插槽默认值--><span>这个组件标题好像是空的?</span></slot></div><div><slot name="content" :data="titleData"><!--插槽默认值--><span>这个组件内容好像是空的?</span></slot></div><div><slot :data="titleData"><!--插槽默认值--><span>这个组件操作项好像是空的?</span></slot></div></div>
</template><style scoped lang="scss"></style>
使用
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
let position = 'content'
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><Card><template #[position]="children">组件-{{ children.data }}</template></Card></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3.7、异步组件(Suspense)
组件
<script setup lang="ts">
const title = ref('')
const init = () => {return new Promise((resolve, reject) => {setTimeout(() => {title.value = "xumeng03"resolve()}, 2000);})
}
await init()
</script><template>{{ title }}
</template><style scoped lang="scss"></style>
使用
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
// 必须异步导入组件
const Async = defineAsyncComponent(() => import('@/components/Async.vue'))
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><Suspense><template #default><Async></Async></template><template #fallback>"还没有准备好哦"</template></Suspense></div>
</template><style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3.8、传送组件(Teleport)
组件
<script setup lang="ts"></script><template><div id="teleport">被传送节点</div>
</template><style scoped lang="scss">
#teleport {position: absolute;
}
</style>
使用
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><!--<Modal></Modal>--><!--disabled是否启用传送,to把组件放在哪里--><Teleport :disabled="false" to="body"><Modal></Modal></Teleport></div>
</template><style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3.9、缓存组件(keep-alive)
keep-alive会新增onActivated、onDeactivated生命周期
// 挂载keep-alive内组件
onActivated(() => {console.log('keep-alive组件挂载');
})
// 卸载keep-alive内组件
onDeactivated(() => {console.log('keep-alive组件卸载');
})
使用
<script setup lang="ts">
import Card from "@/components/Card.vue";
import Tree from "@/components/Tree.vue";
import {DefineComponent} from "vue";let age = ref(22)
const h1click = () => {age.value++
}interface Tree {name: string,checked: boolean,children?: Tree[]
}const data = reactive<Tree[]>([{name: "1",checked: true,children: [{name: "1-1",checked: false,children: [{name: "1-1-1",checked: true},]},{name: "1-2",checked: true},]},{name: "2",checked: false,children: [{name: "2-1",checked: false},{name: "2-2",checked: true},{name: "2-3",checked: true},]},{name: "3",checked: false,children: [{name: "3-1",checked: false},]}
])
const buttons = [{name: '组件1',component: Card},{name: '组件2',component: Tree},
]
const btnclick = (item: {name: string,component: DefineComponent<{}, {}, any>
}) => {componentDefault.value = item.component
}
// 使用shallowRef节约性能
let componentDefault = shallowRef(Card)
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><div><button @click="btnclick(item)" v-for="item in buttons">{{ item.name }}</button></div><keep-alive :exclude="['Card']"><component :is="componentDefault" :data="data">测试插槽</component></keep-alive></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>
3.10、组件过渡(transition)
v-if
所触发的切换v-show
所触发的切换<component>
切换的动态组件
1、name指定动画
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
let flag = ref(true)
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><button @click="()=>flag=!flag">切换</button><transition name="v"><div v-if="flag" id="div">我是一个div</div><!--<div v-show="flag" id="div">--><!-- 我是一个div--><!--</div>--></transition></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}#div {background-color: lightgreen;
}.v-enter-active,.v-leave-active{transition: all 0.5s;
}
.v-enter-from,.v-leave-to{transform: translateY(-20px);opacity: 0;
}
.v-enter-to,.v-leave-from{transition: all 0.5s;
}
</style>
2、class指定动画
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
let flag = ref(true)
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><button @click="()=>flag=!flag">切换</button><transition:duration="{enter: 50,leave:500}"enter-from-class="v-enter-from"enter-to-class="v-enter-to"enter-active-class="v-enter-active"leave-from-class="v-leave-from"leave-active-class="v-leave-active"leave-to-class="v-leave-to"><div v-if="flag" id="div">我是一个div</div></transition></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}#div {background-color: lightgreen;
}.v-enter-active, .v-leave-active {transition: all 0.5s;
}.v-enter-from, .v-leave-to {transform: translateY(-20px);opacity: 0;
}.v-enter-to, .v-leave-from {transition: all 0.5s;
}
</style>
3、生命周期
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
let flag = ref(true)
const onBeforeEnter = () => {console.log('元素进入前');
}
const onEnter = () => {console.log('元素进入时');
}
const onAfterEnter = () => {console.log('元素进入后');
}
const onEnterCancelled = () => {console.log('动画进入被打断');
}
const onBeforeLeave = () => {console.log('元素离开前');
}
const onLeave = () => {console.log('元素离开时');
}
const onAfterLeave = () => {console.log('元素离开后');
}
const onLeaveCancelled = () => {console.log('动画离开被打断');
}
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><button @click="()=>flag=!flag">切换</button><transition:duration="{enter: 50,leave:500}"enter-from-class="v-enter-from"enter-to-class="v-enter-to"enter-active-class="v-enter-active"leave-from-class="v-leave-from"leave-active-class="v-leave-active"leave-to-class="v-leave-to"@before-enter="onBeforeEnter"@enter="onEnter"@after-enter="onAfterEnter"@enter-cancelled="onEnterCancelled"@before-leave="onBeforeLeave"@leave="onLeave"@after-leave="onAfterLeave"@leave-cancelled="onLeaveCancelled"><div v-if="flag" id="div">我是一个div</div></transition></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}#div {background-color: lightgreen;
}.v-enter-active, .v-leave-active {transition: all 0.5s;
}.v-enter-from, .v-leave-to {transform: translateY(-20px);opacity: 0;
}.v-enter-to, .v-leave-from {transition: all 0.5s;
}
</style>
4、首次进入显示动画
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
let flag = ref(true)
const onBeforeEnter = () => {console.log('元素进入前');
}
const onEnter = () => {console.log('元素进入时');
}
const onAfterEnter = () => {console.log('元素进入后');
}
const onEnterCancelled = () => {console.log('动画进入被打断');
}
const onBeforeLeave = () => {console.log('元素离开前');
}
const onLeave = () => {console.log('元素离开时');
}
const onAfterLeave = () => {console.log('元素离开后');
}
const onLeaveCancelled = () => {console.log('动画离开被打断');
}
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><button @click="()=>flag=!flag">切换</button><transitionappearenter-from-class="v-enter-from"enter-to-class="v-enter-to"enter-active-class="v-enter-active"leave-from-class="v-leave-from"leave-active-class="v-leave-active"leave-to-class="v-leave-to"@before-enter="onBeforeEnter"@enter="onEnter"@after-enter="onAfterEnter"@enter-cancelled="onEnterCancelled"@before-leave="onBeforeLeave"@leave="onLeave"@after-leave="onAfterLeave"@leave-cancelled="onLeaveCancelled"><div v-if="flag" id="div">我是一个div</div></transition></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}#div {background-color: lightgreen;
}.v-enter-active, .v-leave-active {transition: all 0.5s;
}.v-enter-from, .v-leave-to {transform: translateY(-20px);opacity: 0;
}.v-enter-to, .v-leave-from {transition: all 0.5s;
}
</style>
5、transition-group
<script setup lang="ts">let age = ref(22)
const h1click = () => {age.value++
}
const arr = reactive([{number: 1},{number: 2}
])
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1><button @click="arr.push({number: Math.ceil(Math.random() * 10)})">添加</button><button @click="()=>arr.pop()">删除</button><transition-groupappearenter-from-class="v-enter-from"enter-to-class="v-enter-to"enter-active-class="v-enter-active"leave-from-class="v-leave-from"leave-active-class="v-leave-active"leave-to-class="v-leave-to"><div v-for="(item,index) in arr" :key="index" class="div">{{ item.number }}</div></transition-group></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}.div {background-color: lightgreen;
}.v-enter-active, .v-leave-active {transition: all 0.5s;
}.v-enter-from, .v-leave-to {transform: translateY(-20px);opacity: 0;
}.v-enter-to, .v-leave-from {transition: all 0.5s;
}
</style>
4、生命周期
<script setup lang="ts">
let age = ref(22)
let h1 = ref<HTMLElement>()
// 生命周期(setup模式下无beforeCreate和created)// 挂载前
onBeforeMount(() => {console.log('挂载前', h1.value);
})
// 挂载完成
onMounted(() => {console.log('挂载完成', h1.value);
})// 更新前
onBeforeUpdate(() => {console.log('更新前', h1.value?.innerText);
})
// 更新完成
onUpdated(() => {console.log('更新完成', h1.value?.innerText);
})// 销毁前
onBeforeUnmount(() => {console.log('销毁前');
})
// 销毁后
onBeforeUnmount(() => {console.log('销毁后');
})
const h1click = () => {age.value++
}
</script><template><div><h1 @click="h1click" ref="h1">{{ age }}</h1></div>
</template><style scoped></style>
5、样式
5.1、全局样式/变量
@/style/variables.scss
$theColor: lightblue;h1 {font-size: 100px;
}
vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// 自动导入vue中ref、reactive、watch等
import AutoImport from "unplugin-auto-import/vite"
// 自动导入ui组件
import Components from 'unplugin-vue-components/vite';// https://vitejs.dev/config/
export default defineConfig({define: {// 关闭Vue2 Options Api__VUE_OPTIONS_API__: false},plugins: [vue(),AutoImport({// 在组件中不用再导入ref、reactive、watch等imports: ['vue', 'vue-router'],// 声明文件存放的位置,会自动生成,无需自己维护dts: "src/auto-import.d.ts",}),Components({// 引入组件的存放的位置,包括自定义组件dts: "src/components.d.ts",}),],resolve: {// 配置路径别名alias: {"@": path.resolve(__dirname, "./src"),},},css: {preprocessorOptions: {scss: {additionalData: `@import "@/style/variables.scss";`}}},
})
使用
<script setup lang="ts">
let age = ref(22)
const h1click = () => {age.value++
}
</script><template><div><h1 @click="h1click">年龄:{{ age }}</h1></div>
</template>
<!--注意style必须加上lang="scss"-->
<style scoped lang="scss">
h1 {color: $theColor;
}
</style>