Vite+Typescript+Vue3學(xué)習(xí)筆記
1、項(xiàng)目搭建
1.1、創(chuàng)建項(xiàng)目(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: ? TypeScript
Scaffolding project in D:\WebstromProject\vite-project...
Done. Now run:
cd vite-project
yarn
yarn dev
Done in 14.81s.
1.2、項(xiàng)目配置
1、配置vue文件識(shí)別
vite默認(rèn)情況下不識(shí)別.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,這里關(guān)閉Vue2的Option Api
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
define: {
// 關(guān)閉Vue2 Options Api
__VUE_OPTIONS_API__: false
},
plugins: [
vue(),
],
})
1.3、常用依賴
1、@types/node
ts需安裝node的類型,否則使用node相關(guān)會(huì)提示找不到
# @types/node
yarn add -D @types/node
2、auto-import
用于簡化Vue3中ref、reactive**、**watch和UI組件的導(dǎo)入
# unplugin-vue-components(自動(dòng)導(dǎo)入components下的組件)、unplugin-auto-import(自動(dòng)導(dǎo)入ref等)
yarn add -D unplugin-vue-components unplugin-auto-import
3、sass
# sass
yarn add -D sass
4、pinia
# pinia
yarn add -D pinia
5、router
# router
yarn add -D vue-router
6、eslint
# eslint
yarn add -D eslint
# vite-plugin-eslint(vite運(yùn)行的時(shí)候自動(dòng)檢測eslint規(guī)范)
yarn add -D vite-plugin-eslint
7、prettier
# prettier、eslint-config-prettier(關(guān)掉所有和Prettier沖突的ESLint的配置)、eslint-plugin-prettier(將Prettier的rules以插件的形式加入到 ESLint里面)
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
1.4、devtools
1、下載
https://github.com/vuejs/devtools
2、構(gòu)建
yarn install
# 等一段時(shí)間 控制臺(tái)不再輸出內(nèi)容ctrl+c退出執(zhí)行yarn run dev:chrome
yarn run build:watch
# 執(zhí)行完畢在packages\shell-chrome下找build文件夾
yarn run dev:chrome
3、chrome添加擴(kuò)展
將packages\shell-chrome文件夾拖入chrome瀏覽器擴(kuò)展
4、使用
2、Composition API
2.1、響應(yīng)式
可以在chrome瀏覽器開啟自定義格式化,便于查看
1、ref
ref通常用于聲明基礎(chǔ)類型響應(yīng)式數(shù)據(jù),引用類型也可以,會(huì)隱式調(diào)用reactive,同時(shí)取值賦值都需要.value
<script setup lang="ts">
import {Ref} from "vue";
// 非響應(yīng)式
// let number: number = Math.random()
// console.log(isRef(number))
// 響應(yīng)式
// let number = ref(Math.random())
// 顯式類型約束
let number: Ref<number> = ref<number>(Math.random())
console.log(isRef(number))
const h1click = () => {
// 非響應(yīng)式
// number = Math.random()
// 響應(yīng)式
number.value = Math.random()
console.log(number);
}
</script>
<template>
<div>
<h1 @click="h1click">{{ number }}</h1>
</div>
</template>
<style scoped>
</style>
2、shallowRef
只處理基本數(shù)據(jù)類型的響應(yīng)式, 不進(jìn)行對(duì)象的響應(yīng)式處理(不能與ref一起用,否則會(huì)被迫更新視圖)
<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用于聲明引用類型響應(yīng)式數(shù)據(jù),且對(duì)象解構(gòu)后會(huì)失去響應(yīng)式
<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
只處理對(duì)象最外層屬性的響應(yīng)式(淺響應(yīng)式),(不能與reactive一起用,否則會(huì)被迫更新視圖)
<script setup lang="ts">
let person = shallowReactive({
name: "xumeng",
hobby: [
"game",
"code"
]
})
console.log(isReactive(person))
const h1click = () => {
// 淺層次數(shù)據(jù)
// person.name = "xumeng03"
// 深層次數(shù)據(jù)(如果淺層次&深層次數(shù)據(jù)一起變化,則會(huì)一起更新)
person.hobby[0] = 'movie'
console.log(person);
}
</script>
<template>
<div>
<h1 @click="h1click">{{ person }}</h1>
</div>
</template>
<style scoped>
</style>
5、toRef
將對(duì)象某個(gè)屬性變?yōu)轫憫?yīng)式
<script setup lang="ts">
let person = reactive({
name: "xumeng"
})
// 如果person非響應(yīng)式對(duì)象,則不會(huì)引起視圖更新
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
將對(duì)象的一些解構(gòu)屬性變?yōu)轫憫?yīng)式
<script setup lang="ts">
let person = reactive({
name: "xumeng",
age: 22
})
// 解構(gòu)別名
let {name: pname, age: page} = toRefs(person)
const h1click = () => {
pname.value = "xumeng03"
page.value = 23
console.log(pname, page);
}
</script>
<template>
<div>
<h1 @click="h1click">{{ person }}</h1>
</div>
</template>
<style scoped>
</style>
7、toRaw
把響應(yīng)式對(duì)象變?yōu)槠胀▽?duì)象
<script setup lang="ts">
let person = reactive({
name: "xumeng",
age: 22
})
let rperson = toRaw(person)
const h1click = () => {
rperson.name = "xumeng03"
rperson.age = 23
console.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"
// 報(bào)錯(cuò)
// rperson.name = "xumeng03"
console.log(person);
}
</script>
<template>
<div>
<h1 @click="h1click">{{ person }}</h1>
</div>
</template>
<style scoped>
</style>
2.3、computed
1、選項(xiàng)式寫法
參數(shù)為對(duì)象,需傳入一個(gè)get和set函數(shù)自定義存取操作
<script setup lang="ts">
let person = reactive({
name: "xumeng03",
nickName: '眼眸流轉(zhuǎn)',
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、函數(shù)式寫法
<script setup lang="ts">
let person = reactive({
name: "xumeng03",
nickName: '眼眸流轉(zhuǎn)',
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、監(jiān)聽
1、watch
基本類型
<script setup lang="ts">
let age = ref(22)
const h1click = () => {
age.value++
}
watch(age, (value, oldValue) => {
console.log("監(jiān)聽到變化:", 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: '眼眸流轉(zhuǎn)',
age: 22
})
let person1 = reactive({
name: "xumeng03",
nickName: '眼眸流轉(zhuǎn)',
age: 22
})
const h1click = () => {
person.age++
}
let people = computed(() => {
return person.name + '-' + person.nickName + '-' + person.age
})
// 此時(shí)若監(jiān)聽person會(huì)發(fā)現(xiàn)新舊數(shù)據(jù)一致,可以使用computed處理一下返回(如返回新的信息或深拷貝原引用數(shù)據(jù))
// watch(person, (value, oldValue) => {
// console.log("監(jiān)聽到變化:", value, oldValue);
// }, {
// // 深度監(jiān)聽,reactive默認(rèn)就是深度監(jiān)聽
// deep: true,
// // 立即執(zhí)行一次
// immediate: true,
// // 刷新時(shí)機(jī)
// flush: 'pre'
// })
// 監(jiān)聽整個(gè)引用數(shù)據(jù)
watch(people, (value, oldValue) => {
console.log("監(jiān)聽到變化:", value, oldValue);
}, {
// 深度監(jiān)聽,reactive默認(rèn)就是深度監(jiān)聽
deep: true,
// 立即執(zhí)行一次
immediate: true,
// 刷新時(shí)機(jī)
flush: 'pre'
})
// 監(jiān)聽引用對(duì)象某個(gè)屬性
watch(() => person.age, (value, oldValue) => {
console.log("監(jiān)聽到變化:", value, oldValue);
}, {
// 深度監(jiān)聽,reactive默認(rèn)就是深度監(jiān)聽
deep: true,
// 立即執(zhí)行一次
immediate: true,
// 刷新時(shí)機(jī)
flush: 'pre'
})
// 監(jiān)聽多個(gè)數(shù)據(jù)源
watch([person, person1], (value, oldValue) => {
console.log("監(jiān)聽到變化:", value, oldValue);
}, {
// 深度監(jiān)聽,reactive默認(rèn)就是深度監(jiān)聽
deep: true,
// 立即執(zhí)行一次
immediate: true,
// 刷新時(shí)機(jī)
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返回一個(gè)停止監(jiān)聽函數(shù)
const stopwatch = watchEffect((oninvalidate) => {
oninvalidate(() => {
console.log("變化前");
})
console.log(age);
})
// 停止監(jiān)聽
// stopwatch()
</script>
<template>
<div>
<h1 @click="h1click">{{ age }}</h1>
</div>
</template>
<style scoped>
</style>
3、組件
3.1、全局組件
全局組件
<script setup lang="ts">
</script>
<template>
<div>
這是一個(gè)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: '默認(rèn)值'
// },
// PtoC2: {
// type: Object,
// default: {}
// }
// })
// typescript類型約束
const props = defineProps<{
PtoC1: number,
PtoC2: {
name: string,
age: number
}
}>()
// typescript默認(rèn)值
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、兄弟組件傳參
1、通過父組件傳參
父組件
<script setup lang="ts">
let c1_msg = ref<string>('')
const shareMsg = (message: string) => {
console.log(message);
c1_msg.value = message
}
</script>
<template>
<div id="parent">
<Child tag="111" :c1_msg="c1_msg" @onshareMsg="shareMsg"></Child>
<hr>
<Child tag="222" :c1_msg="c1_msg" @onshareMsg="shareMsg"></Child>
</div>
</template>
<style scoped lang="scss">
#parent {
color: v-bind(color);
background-color: #ffffff;
}
</style>
子組件
<script setup lang="ts">
const props = defineProps<{
tag: string,
c1_msg: string
}>()
let msg = ref('我是組件' + props.tag + '的消息')
const emit = defineEmits<{
(e: 'onshareMsg', message: string): void,
}>()
const shareMsg = () => {
console.log(msg.value)
emit('onshareMsg', msg.value)
}
</script>
<template>
<div @click="shareMsg" class="child">
組件{{ props.tag }}
<p>{{ props.c1_msg }}</p>
</div>
</template>
<style scoped lang="scss">
#child {
color: v-bind(color);
background-color: #ffffff;
}
</style>
2、通過Event Bus傳參
Vue3移除Event Bus,可使用替代方案mitt
待補(bǔ)。。。
3.6、動(dòng)態(tài)組件(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節(jié)約性能
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.7、插槽(slot)
1、匿名插槽
匿名插槽在一個(gè)組件中只能有一個(gè)
<script setup lang="ts">
</script>
<template>
<div>
<slot>
<!--插槽默認(rèn)值-->
<span>這個(gè)插槽好像是空的?</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">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件標(biāo)題好像是空的?</span>
</slot>
</div>
<div>
<slot name="content">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件內(nèi)容好像是空的?</span>
</slot>
</div>
<div>
<slot>
<!--插槽默認(rèn)值-->
<span>這個(gè)組件操作項(xiàng)好像是空的?</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>
組件標(biāo)題
</template>
<template v-slot:content>
組件標(biāo)題
</template>
<template v-slot>
組件操作項(xiàng)
</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">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件標(biāo)題好像是空的?</span>
</slot>
</div>
<div>
<slot name="content">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件內(nèi)容好像是空的?</span>
</slot>
</div>
<div>
<slot>
<!--插槽默認(rèn)值-->
<span>這個(gè)組件操作項(xiàng)好像是空的?</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">
組件標(biāo)題-{{ children.data }}
</template>
<template v-slot:content>
組件標(biāo)題
</template>
<template v-slot>
組件操作項(xiàng)
</template>
</Card>
</div>
</template>
<!--注意style必須加上lang="scss"-->
<style scoped lang="scss">
h1 {
color: $theColor;
}
</style>
4、動(dòng)態(tài)插槽
組件
<script setup lang="ts">
let titleData = ref("slot中的值")
</script>
<template>
<div>
<div>
<slot name="title" :data="titleData">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件標(biāo)題好像是空的?</span>
</slot>
</div>
<div>
<slot name="content" :data="titleData">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件內(nèi)容好像是空的?</span>
</slot>
</div>
<div>
<slot :data="titleData">
<!--插槽默認(rèn)值-->
<span>這個(gè)組件操作項(xiàng)好像是空的?</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.8、異步組件(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++
}
// 必須異步導(dǎo)入組件
const Async = defineAsyncComponent(() => import('@/components/Async.vue'))
</script>
<template>
<div>
<h1 @click="h1click">年齡:{{ age }}</h1>
<Suspense>
<template #default>
<Async></Async>
</template>
<template #fallback>
"還沒有準(zhǔn)備好哦"
</template>
</Suspense>
</div>
</template>
<style scoped lang="scss">
h1 {
color: $theColor;
}
</style>
3.9、傳送組件(Teleport)
組件
<script setup lang="ts">
</script>
<template>
<div id="teleport">
被傳送節(jié)點(diǎn)
</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.10、緩存組件(keep-alive)
keep-alive會(huì)新增onActivated、onDeactivated生命周期
// 掛載keep-alive內(nèi)組件
onActivated(() => {
console.log('keep-alive組件掛載');
})
// 卸載keep-alive內(nèi)組件
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節(jié)約性能
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.11、組件過渡(transition)
-
v-if
所觸發(fā)的切換 -
v-show
所觸發(fā)的切換 -
<component>
切換的動(dòng)態(tài)組件
1、name指定動(dòng)畫
<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">
我是一個(gè)div
</div>
<!--<div v-show="flag" id="div">-->
<!-- 我是一個(gè)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指定動(dòng)畫
<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">
我是一個(gè)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('元素進(jìn)入前');
}
const onEnter = () => {
console.log('元素進(jìn)入時(shí)');
}
const onAfterEnter = () => {
console.log('元素進(jìn)入后');
}
const onEnterCancelled = () => {
console.log('動(dòng)畫進(jìn)入被打斷');
}
const onBeforeLeave = () => {
console.log('元素離開前');
}
const onLeave = () => {
console.log('元素離開時(shí)');
}
const onAfterLeave = () => {
console.log('元素離開后');
}
const onLeaveCancelled = () => {
console.log('動(dòng)畫離開被打斷');
}
</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">
我是一個(gè)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、首次進(jìn)入顯示動(dòng)畫
<script setup lang="ts">
let age = ref(22)
const h1click = () => {
age.value++
}
let flag = ref(true)
const onBeforeEnter = () => {
console.log('元素進(jìn)入前');
}
const onEnter = () => {
console.log('元素進(jìn)入時(shí)');
}
const onAfterEnter = () => {
console.log('元素進(jìn)入后');
}
const onEnterCancelled = () => {
console.log('動(dòng)畫進(jìn)入被打斷');
}
const onBeforeLeave = () => {
console.log('元素離開前');
}
const onLeave = () => {
console.log('元素離開時(shí)');
}
const onAfterLeave = () => {
console.log('元素離開后');
}
const onLeaveCancelled = () => {
console.log('動(dòng)畫離開被打斷');
}
</script>
<template>
<div>
<h1 @click="h1click">年齡:{{ age }}</h1>
<button @click="()=>flag=!flag">切換</button>
<transition
appear
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">
我是一個(gè)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-group
appear
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-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>
3.12、provide&inject
child.vue
<script setup lang="ts">
import {Ref} from "vue";
let color = inject<Ref<string>>('color')
</script>
<template>
<div id="child">
子組件
</div>
</template>
<style scoped lang="scss">
#child {
color: v-bind(color);
background-color: #ffffff;
}
</style>
parent.vue
<script setup lang="ts">
import {Ref} from "vue";
let color = inject<Ref<string>>('color')
</script>
<template>
<div id="parent">
父組件
<hr>
<Child></Child>
</div>
</template>
<style scoped lang="scss">
#parent {
color: v-bind(color);
background-color: #ffffff;
}
</style>
App.vue
<script setup lang="ts">
let color = ref('#0f0')
let buttons = [
{
key: '紅色',
value: 'red'
},
{
key: '黃色',
value: 'yellow'
},
{
key: '綠色',
value: 'green'
},
]
provide('color', color)
const colorChange = (value) => {
color.value = value
}
</script>
<template>
<div id="xumeng03">
<a>
<img src="/vite.svg" class="logo" alt="Vite logo"/>
</a>
<a>
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo"/>
</a>
<div>
<button v-for="button in buttons" @click="colorChange(button.value)">{{ button.key }}</button>
</div>
<Parent></Parent>
</div>
</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>
5、生命周期
<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、v-model
1、內(nèi)置組件使用
<script setup lang="ts">
let text = ref('')
</script>
<template>
<div>
<input type="text" v-model="text">
<!--等價(jià)于-->
<!--<input type="text" :value="text" @input="text=$event.target.value">-->
<p>{{ text }}</p>
</div>
</template>
<style scoped lang="scss">
</style>
2、自定義組件使用
組件
<script setup lang="ts">
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits([
'update:modelValue'
])
const changeModelValue = () => {
emit('update:modelValue', Math.random().toString())
}
</script>
<template>
<div @click="changeModelValue">
{{ props.modelValue }}
</div>
</template>
<style scoped lang="scss">
</style>
使用
<script setup lang="ts">
let text = ref('')
</script>
<template>
<div>
<!--自定義組件(v-model綁定在自定義組件上時(shí),需要手動(dòng)去接收)-->
<Card v-model="text"></Card>
<!--等價(jià)于-->
<Card :modelValue="text" @update:modelValue="text=$event"></Card>
</div>
</template>
<style scoped lang="scss">
</style>
3、自定義v-model名稱
v-model 默認(rèn)綁定的屬性名為:modelValue
v-model 默認(rèn)綁定的事件名為:update:modelValue
組件
<script setup lang="ts">
const props = defineProps<{
xumeng03: string
}>()
const emit = defineEmits([
'update:xumeng03'
])
const changeModelValue = () => {
emit('update:xumeng03', Math.random().toString())
}
</script>
<template>
<div @click="changeModelValue">
{{ props.xumeng03 }}
</div>
</template>
<style scoped lang="scss">
</style>
使用
<script setup lang="ts">
let text = ref('')
</script>
<template>
<div>
<input type="text" v-model="text">
<Card1 v-model:xumeng03="text"></Card1>
</div>
</template>
<style scoped lang="scss">
</style>
4、多v-model
組件
<script setup lang="ts">
const props = defineProps<{
xumeng: string,
xumeng03: string
}>()
const emit = defineEmits([
'update:xumeng',
'update:xumeng03'
])
const changeModelValue = () => {
emit('update:xumeng', Math.random().toString())
}
const changeModelValue03 = () => {
emit('update:xumeng03', Math.random().toString())
}
</script>
<template>
<div @click="changeModelValue">
{{ props.xumeng }}
</div>
<div @click="changeModelValue03">
{{ props.xumeng03 }}
</div>
</template>
<style scoped lang="scss">
</style>
使用
<script setup lang="ts">
let text = ref('null')
</script>
<template>
<div>
<input type="text" v-model="text">
<Card1 v-model:xumeng="text" v-model:xumeng03="text"></Card1>
</div>
</template>
<style scoped lang="scss">
</style>
5、修飾符
- .number: v-model 綁定在表單組件中不會(huì)出現(xiàn)數(shù)值類型,numer 可以將綁定的字符串轉(zhuǎn)換為數(shù)值
- .trim: 去掉綁定數(shù)據(jù)左右兩側(cè)的空格
- .lazy: 表單組件綁定時(shí)生效,從原來綁定的 input 高頻率觸發(fā)事件改為 blur 低頻率觸發(fā)事件來提高性能
6、自定義v-model修飾符
組件
<script setup lang="ts">
const props = defineProps<{
xumeng03: number
xumeng03Modifiers?: {
yanmou: boolean
}
}>()
const emit = defineEmits([
'update:xumeng03'
])
const changeModelValue = () => {
emit('update:xumeng03', props.xumeng03Modifiers?.yanmou ? Math.ceil(Math.random() * 10) : -1)
}
</script>
<template>
<div @click="changeModelValue">
{{ props.xumeng03 }}
</div>
</template>
<style scoped lang="scss">
</style>
使用
<script setup lang="ts">
let text = ref(0)
</script>
<template>
<div>
<input type="text" v-model="text">
<Card1 v-model:xumeng03="text"></Card1>
<Card1 v-model:xumeng03.yanmou="text"></Card1>
</div>
</template>
<style scoped lang="scss">
</style>
5.2、自定義指令
1、基本使用
<script setup lang="ts">
import {Directive} from "vue";
let text = ref("xumeng03")
const vColor: Directive = {
// 元素初始化
created() {
},
// 掛載前
beforeMount() {
},
// 掛載后
mounted(el, binding, vNode) {
// el:元素
console.log(el);
// binding.arg:指令名稱即font
// binding.modifiers:指令修飾符
// binding.value:指令的值
console.log(binding);
// vNode:虛擬dom解構(gòu)
console.log(vNode);
},
// 更新前
beforeUpdate() {
},
// 更新后
updated(el, binding, vNode) {
console.log(binding);
},
// 卸載前
beforeUnmount() {
},
// 卸載后
unmounted() {
}
}
</script>
<template>
<div>
<input type="text" v-model="text">
<Card v-color:font.readonly="text" :message="text"></Card>
</div>
</template>
<style scoped lang="scss">
</style>
2、自定義指令-鑒權(quán)
<script setup lang="ts">
import {Directive} from "vue";
const operates=[
'operate1',
'operate2',
// 'operate3',
]
const vCan: Directive = (el, binding) => {
console.log(binding);
if (!operates.includes(binding.value)){
el.remove()
}
}
</script>
<template>
<div>
<button v-can="'operate1'">操作1</button>
<button v-can="'operate2'">操作2</button>
<button v-can="'operate3'">操作3</button>
</div>
</template>
<style scoped lang="scss">
</style>
3、自定義指令-圖片懶加載
<script setup lang="ts">
import {Directive} from "vue";
import img from './assets/i.jpg'
// 懶加載
let imgs = Object.values(import.meta.glob('@/assets/img*.jpg')).map(item => item.name)
// 靜態(tài)加載
// let imgs = import.meta.glob('@/assets/img*.jpg', {eager: true})
console.log(imgs);
const vImgLazy: Directive = async (el, binding) => {
// console.log(el);
let observer = new IntersectionObserver((entries) => {
console.log(el, entries[0].intersectionRatio);
if (entries[0].intersectionRatio > 0) {
setTimeout(() => {
el.src = binding.value
// 取消監(jiān)聽
observer.unobserve(el)
}, 2000)
}
})
// 監(jiān)聽元素
observer.observe(el)
}
</script>
<template>
<img v-for="(i,index) in imgs" v-img-lazy="i" :src="img" alt="">
</template>
<style scoped lang="scss">
img {
display: inline-block !important;
width: 390px;
height: 585px;
}
</style>
5.3、自定義Hook
待補(bǔ)。。。
6、全局設(shè)置
6.1、全局變量
Vue3移除了prototype,需通過app.config.globalProperties來設(shè)置
1、設(shè)置全局變量
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router";
let app = createApp(App)
// 設(shè)置全局變量
app.config.globalProperties.$author = 'xumeng03'
app.use(router).mount('#app')
2、解決飄紅
globalProperties.d.ts
// 全局變量報(bào)錯(cuò)
import { ComponentCustomProperties } from 'vue'
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$author: string
}
}
3、使用
<script setup lang="ts">
</script>
<template>
<h1>{{ $author }}</h1>
</template>
<style scoped lang="scss">
</style>
6.2、全局樣式/變量
1、全局樣式
@/style/variables.scss
$theColor: lightblue;
h1 {
font-size: 100px;
}
2、配置css預(yù)處理
vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// 自動(dòng)導(dǎo)入vue中ref、reactive、watch等
import AutoImport from "unplugin-auto-import/vite"
// 自動(dòng)導(dǎo)入ui組件
import Components from 'unplugin-vue-components/vite';
// https://vitejs.dev/config/
export default defineConfig({
define: {
// 關(guān)閉Vue2 Options Api
__VUE_OPTIONS_API__: false
},
plugins: [
vue(),
AutoImport({
// 在組件中不用再導(dǎo)入ref、reactive、watch等
imports: ['vue', 'vue-router'],
// 聲明文件存放的位置,會(huì)自動(dòng)生成,無需自己維護(hù)
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";`
}
}
},
})
3、使用
<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>
7、組件庫
7.1、常用組件庫
- Element
- Ant
- Iview
7.2、樣式穿透
待補(bǔ)。。。
8、nextTick
<script setup lang="ts">
const chats = reactive<Array<{
message: string
}>>([])
let Rchats = ref<HTMLDivElement>()
let ipt = ref('')
// let send = () => {
// chats.push({message: ipt})
// nextTick(() => {
// Rchats.value!.scrollTop = 999999
// })
// }
let send = async () => {
chats.push({message: ipt})
// await nextTick()下面的都是異步的
await nextTick()
Rchats.value!.scrollTop = 999999
}
</script>
<template>
<div>
<div ref="Rchats" style="width: 400px;height: 300px;border: 1px solid #333;overflow-y: auto">
<div class="chat" v-for="chat in chats">{{ chat.message }}</div>
</div>
<div>
<input type="text" v-model="ipt">
<button @click="send">發(fā)送</button>
</div>
</div>
</template>
<style scoped lang="scss">
.chat {
background-color: gray;
padding: .5rem;
margin: 5px;
}
</style>
9、Vue文件風(fēng)格
9.1、template
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>
9.2、tsx
待補(bǔ)。。。
9.3、h函數(shù)
<script setup lang="ts">
let data = reactive<{
id: number,
name: string
address: string,
operate: () => {}
}[]>([
{
id: 1,
name: 'zhangsan',
address: 'shanghai',
},
{
id: 1,
name: 'lisi',
address: 'shanghai',
},
{
id: 1,
name: 'wangwu',
address: 'shanghai',
},
{
id: 1,
name: 'zhaoliu',
address: 'shanghai',
}
])
const btn = (props, ctx) => {
// props:組件上屬性,ctx:組件樣式、插槽、emit
console.log(props, ctx);
return h('button', {
// 樣式
style: {
color: props.type === 'update' ? 'green' : 'red'
},
// 事件
onClick: () => {
console.log('click');
}
}, ctx.slots.default())
}
</script>
<template>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>address</th>
<th>operate</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.address }}</td>
<td>
<btn type="update">編輯</btn>
<btn type="delete">刪除</btn>
</td>
</tr>
</tbody>
</table>
</template>
<style scoped lang="scss">
</style>
10、環(huán)境變量
10.1、配置環(huán)境變量
環(huán)境變量的值必須以 VITE_ 開頭
1、.env.development
VITE_APP_BASE_URL='/development'
2、.env.production
VITE_APP_BASE_URL='/production'
10.2、添加TS聲明
interface ImportMetaEnv {
readonly VITE_APP_BASE_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
10.3、配置啟動(dòng)命令
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode development",
"prod": "vite --mode production",
"build": "vue-tsc && vite build --mode production",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.3.4",
"vue-router": "4"
},
"devDependencies": {
"@types/node": "^20.4.5",
"@vitejs/plugin-vue": "^4.2.3",
"sass": "^1.64.1",
"typescript": "^5.0.2",
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.1",
"vite": "^4.4.5",
"vue-tsc": "^1.8.5"
}
}
11、Proxy
11.1、后端接口
@RestController
public class HelloWorldController {
@RequestMapping("/")
public String hello() {
return "<h1>Hello world!</h1>";
}
}
11.2、vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// 自動(dòng)導(dǎo)入vue中ref、reactive、watch等
import AutoImport from "unplugin-auto-import/vite"
// 自動(dòng)導(dǎo)入ui組件
import Components from 'unplugin-vue-components/vite';
// https://vitejs.dev/config/
export default defineConfig({
define: {
// 關(guān)閉Vue2 Options Api
__VUE_OPTIONS_API__: false
},
plugins: [
vue(),
AutoImport({
// 在組件中不用再導(dǎo)入ref、reactive、watch等
imports: ['vue', 'vue-router'],
// 聲明文件存放的位置,會(huì)自動(dòng)生成,無需自己維護(hù)
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";`
}
}
},
server: {
// dev環(huán)境解決跨域
proxy: {
'/api': {
target: 'http://localhost:8080/',
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
11.3、使用
<script setup lang="ts">
fetch('/api')
</script>
<template>
<h1>Hello world!</h1>
</template>
<style scoped lang="scss">
</style>
12、Pinna
Pinna用于替代Vuex作為全局狀態(tài)管理
- 完整的ts支持
- 輕量
- 支持同步和異步
12.1、基礎(chǔ)使用
1、main.ts
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router";
import {createPinia} from "pinia";
// pinia
let store = createPinia()
let app = createApp(App)
app.config.globalProperties.$author = 'xumeng03'
// pinia
app.use(router).use(store).mount('#app')
2、store
import {defineStore} from "pinia";
const enum storeName {
xumeng03 = 'xumeng03'
}
export const useXumeng03Store = defineStore(storeName.xumeng03, {
state: () => {
return {
id: 1,
name: 'xumeng03'
}
},
})
3、使用
<script setup lang="ts">
import {useXumeng03Store} from "./store";
const xumeng03 = useXumeng03Store()
</script>
<template>
<h1>{{ xumeng03.id }}--{{ xumeng03.name }}</h1>
</template>
<style scoped lang="scss">
</style>
12.2、state
1、直接修改
<script setup lang="ts">
import {useXumeng03Store} from "./store";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
xumeng03.id++
}
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}</h1>
</template>
<style scoped lang="scss">
</style>
2、$patch
<script setup lang="ts">
import {useXumeng03Store} from "./store";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// 批量修改
// xumeng03.$patch({
// id: 10,
// name: 'Xumeng03'
// })
// 函數(shù)寫法
xumeng03.$patch((state) => {
state.id = 100
state.name = xumeng03.name.toUpperCase()
})
}
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}</h1>
</template>
<style scoped lang="scss">
</style>
3、action
定義actions
import {defineStore} from "pinia";
const enum storeName {
xumeng03 = 'xumeng03'
}
export const useXumeng03Store = defineStore(storeName.xumeng03, {
state: () => {
return {
id: 1,
name: 'xumeng03'
}
},
actions: {
setId(id: number) {
this.id = id
}
}
})
使用
<script setup lang="ts">
import {useXumeng03Store} from "./store";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// action
xumeng03.setId(100)
}
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}</h1>
</template>
<style scoped lang="scss">
</style>
12.3、storeToRefs
用于解決state解構(gòu)失去響應(yīng)式
<script setup lang="ts">
import {useXumeng03Store} from "./store";
import {storeToRefs} from "pinia";
const xumeng03 = useXumeng03Store()
// 直接解構(gòu)無響應(yīng)式
// let {id, name} = xumeng03
let {id, name} = storeToRefs(xumeng03)
const idAdd = () => {
xumeng03.id++
// id.value++
}
</script>
<template>
<h1 @click="idAdd">{{ id }}--{{ name }}</h1>
</template>
<style scoped lang="scss">
</style>
12.4、actions
定義actions
import {defineStore} from "pinia";
const enum storeName {
xumeng03 = 'xumeng03'
}
const getName = (): Promise<{
id: number,
name: string
}> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 200,
name: '眼眸'
})
}, 2000)
})
}
export const useXumeng03Store = defineStore(storeName.xumeng03, {
state: () => {
return {
id: 1,
name: 'xumeng03'
}
},
actions: {
// 同步方法
setId(id: number) {
this.id = id
// 方法間可相互調(diào)用
this.setName()
},
// 異步方法
async setName() {
const result = await getName()
this.name = result.name
// 方法間可相互調(diào)用
// this.setId(result.id)
}
}
})
使用
<script setup lang="ts">
import {useXumeng03Store} from "./store";
import {storeToRefs} from "pinia";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// xumeng03.setId(2)
xumeng03.setName()
}
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}</h1>
</template>
<style scoped lang="scss">
</style>
12.5、getter
定義getters
import {defineStore} from "pinia";
const enum storeName {
xumeng03 = 'xumeng03'
}
const getName = (): Promise<{
id: number,
name: string
}> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 200,
name: '眼眸'
})
}, 2000)
})
}
export const useXumeng03Store = defineStore(storeName.xumeng03, {
state: () => {
return {
id: 1,
name: 'xumeng03'
}
},
getters: {
decorateName(): string {
return `$${this.name}`
}
},
actions: {
// 同步方法
setId(id: number) {
this.id = id
// 方法間可相互調(diào)用
this.setName()
},
// 異步方法
async setName() {
const result = await getName()
this.name = result.name
// 方法間可相互調(diào)用
// this.setId(result.id)
}
}
})
使用
<script setup lang="ts">
import {useXumeng03Store} from "./store";
import {storeToRefs} from "pinia";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// xumeng03.setId(2)
xumeng03.setName()
}
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}--{{ xumeng03.decorateName }}</h1>
</template>
<style scoped lang="scss">
</style>
12.6、API
1、$reset
<script setup lang="ts">
import {useXumeng03Store} from "./store";
import {storeToRefs} from "pinia";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// xumeng03.setId(2)
xumeng03.setName()
}
const reset = () => {
// xumeng03.setId(2)
xumeng03.$reset()
}
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}--{{ xumeng03.decorateName }}</h1>
<div @click="reset">
reset
</div>
</template>
<style scoped lang="scss">
</style>
2、$subscribe
訂閱state的變化
<script setup lang="ts">
import {useXumeng03Store} from "./store";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// xumeng03.setId(2)
xumeng03.setName()
}
xumeng03.$subscribe((args, state) => {
// args:記錄了變化前后值
console.log(args);
// state:改變后的state值
console.log(state);
})
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}--{{ xumeng03.decorateName }}</h1>
</template>
<style scoped lang="scss">
</style>
3、$onAction
<script setup lang="ts">
import {useXumeng03Store} from "./store";
const xumeng03 = useXumeng03Store()
const idAdd = () => {
// xumeng03.setId(2)
xumeng03.setName()
}
xumeng03.$onAction((args) => {
// args: 調(diào)用action相關(guān)信息
console.log(args);
// action調(diào)用完畢的回調(diào)
args.after(() => {
console.log('action調(diào)用完畢');
})
})
</script>
<template>
<h1 @click="idAdd">{{ xumeng03.id }}--{{ xumeng03.name }}--{{ xumeng03.decorateName }}</h1>
</template>
<style scoped lang="scss">
</style>
12.7、持久化
在狀態(tài)改變時(shí)將其同步到瀏覽器的存儲(chǔ)中,如 cookie、localStorage、sessionStorage
1、persist.ts
import {PiniaPluginContext} from 'pinia'
export const piniaPersist = (context: PiniaPluginContext) => {
console.log(context);
const {store} = context
const localstore = getLocalStorage(`store-${store.$id}`)
store.$subscribe(() => {
// console.log('store change')
setLocalStorage(`store-${store.$id}`, toRaw(store.$state))
})
return {
...localstore
}
}
const setLocalStorage = (key: string, value: object) => {
// console.log(key, value)
localStorage.setItem(key, JSON.stringify(value))
}
const getLocalStorage = (key: string) => {
const store = localStorage.getItem(key)
return store ? JSON.parse(store) : {};
}
2、store/index.ts
import {createPinia, defineStore} from "pinia";
import {piniaPersist} from './persist'
const enum storeName {
xumeng03 = 'xumeng03',
xumeng04 = 'xumeng04'
}
const getName = (): Promise<{
id: number,
name: string
}> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 200,
name: '眼眸'
})
}, 2000)
})
}
export const useXumeng03Store = defineStore(storeName.xumeng03, {
state: () => {
return {
id: 3,
name: 'xumeng03'
}
},
getters: {
decorateName(): string {
return `$${this.name}`
}
},
actions: {
// 同步方法
setId(id: number) {
this.id = id
},
// 異步方法
async setName() {
const result = await getName()
this.name = result.name
}
}
})
export const useXumeng04Store = defineStore(storeName.xumeng04, {
state: () => {
return {
id: 4,
name: 'xumeng04'
}
},
getters: {
decorateName(): string {
return `$${this.name}`
}
},
actions: {
// 同步方法
setId(id: number) {
this.id = id
},
// 異步方法
async setName() {
const result = await getName()
this.name = result.name
}
}
})
// pinia
const pinia = createPinia()
// pinia使用piniaPluginPersistedstate
pinia.use(piniaPersist)
export default pinia;
3、main.ts
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router";
import pinia from './store/index.ts'
let app = createApp(App)
app.config.globalProperties.$author = 'xumeng03'
// 使用router、pinia
app.use(router).use(pinia).mount('#app')
13、Router
13.1、基礎(chǔ)使用
1、router/index.ts
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import('../view/About.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
export default router
2、main.ts
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router";
import pinia from './store/index.ts'
let app = createApp(App)
app.config.globalProperties.$author = 'xumeng03'
// 使用router、pinia
app.use(router).use(pinia).mount('#app')
3、使用
<script setup lang="ts">
</script>
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
13.2、路由跳轉(zhuǎn)方式
1、path
<script setup lang="ts">
</script>
<template>
<nav>
<!--path形式-->
<router-link to="/">Home</router-link>
|
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
2、name
<script setup lang="ts">
</script>
<template>
<nav>
<!--name形式-->
<router-link :to="{name:'home'}">Home</router-link>
|
<router-link :to="{name:'about'}">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
3、ts
<script setup lang="ts">
const router = useRouter()
const to = (route: string) => {
// path
// router.push(route)
router.push({
path: route
})
// name
// router.push({
// name: route
// })
}
</script>
<template>
<nav>
<!--path-->
<button @click="to('/')">home</button>
<button @click="to('/about')">about</button>
<!--name-->
<!--<button @click="to('home')">home</button>-->
<!--<button @click="to('about')">about</button>-->
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
13.3、路由傳參
1、query傳參
query傳參跳轉(zhuǎn)時(shí)可自由選擇name、path,參數(shù)會(huì)拼接在路徑上,刷新不會(huì)消失
App.vue
<script setup lang="ts">
const router = useRouter()
// ts方式取消記錄
const to = (route: string, message: string | null) => {
if (message) {
router.push({
name: route,
query: {
message
}
})
} else {
router.push({
name: route,
})
}
}
</script>
<template>
<nav>
<!--ts方式-->
<button @click="to('home', null)">home</button>
<button @click="to('about', '222')">about</button>
<!--router-link方式-->
<router-link replace :to="{name:'home'}">Home</router-link>
|
<router-link replace :to="{name:'about', query: { message: '222' }}">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
home.vue
<script setup lang="ts">
</script>
<template>
<h1>This is a home page</h1>
</template>
<style scoped lang="scss">
</style>
About.vue
<script setup lang="ts">
// 注意取值時(shí)是useRoute(),操作路由的時(shí)候才是useRouter()
const router = useRoute()
</script>
<template>
<h1>This is a about page</h1>
<p>{{ router.query.message}}</p>
</template>
<style scoped lang="scss">
</style>
2、params傳參
注意:params在新版僅支持在路由動(dòng)態(tài)參數(shù)時(shí)使用
router/index.ts
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about/:message',
name: 'about',
component: () => import('../view/About.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
export default router
App.vue
<script setup lang="ts">
const router = useRouter()
// ts方式取消記錄
const to = (route: string, message: string | null) => {
if (message) {
router.push({
name: route,
params: {
message
}
})
} else {
router.push({
name: route,
})
}
}
</script>
<template>
<nav>
<!--ts方式-->
<button @click="to('home', null)">home</button>
<button @click="to('about', '222')">about</button>
<!--router-link方式-->
<router-link replace :to="{name:'home'}">Home</router-link>
|
<router-link replace :to="{name:'about', params: { message: '222'}}">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
13.3、路由記錄
1、路由前進(jìn)后退
也可通過瀏覽器前進(jìn)后退按鈕來前進(jìn)后退
<script setup lang="ts">
const router = useRouter()
const to = (route: string) => {
router.push({
name: route
})
}
const prev = (route: string) => {
// router.go(-1)
router.back()
}
const next = (route: string) => {
router.go(1)
}
</script>
<template>
<nav>
<!--path-->
<button @click="prev">prev</button>
<button @click="next">next</button>
<button @click="to('home')">home</button>
<button @click="to('about')">about</button>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
2、取消路由記錄
<script setup lang="ts">
const router = useRouter()
// ts方式取消記錄
const to = (route: string) => {
// router.push({
// name: route
// })
router.replace({
name: route
})
}
const prev = (route: string) => {
// router.go(-1)
router.back()
}
const next = (route: string) => {
router.go(1)
}
</script>
<template>
<nav>
<!--ts取消記錄-->
<button @click="prev">prev</button>
<button @click="next">next</button>
<button @click="to('home')">home</button>
<button @click="to('about')">about</button>
<!--router-link取消記錄-->
<router-link replace :to="{name:'home'}">Home</router-link>
|
<router-link replace :to="{name:'about'}">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
13.4、路由嵌套
1、router/index.ts
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about/:message',
name: 'about',
component: () => import('../view/About.vue')
},
{
path: '/mine',
component: () => import('../view/Mine.vue'),
children: [
{
path: '',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
{
path: 'mine2',
name: 'mine2',
component: () => import('../components/Mine2.vue')
},
]
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
export default router
2、App.vue
<script setup lang="ts">
const router = useRouter()
// ts方式取消記錄
const to = (route: string, message: string | null) => {
if (message) {
router.push({
name: route,
params: {
message
}
})
} else {
router.push({
name: route,
})
}
}
</script>
<template>
<nav>
<!--ts方式-->
<button @click="to('home', null)">home</button>
<button @click="to('mine1', '222')">mine</button>
<button @click="to('about', '222')">about</button>
<!--router-link方式-->
<router-link replace :to="{name:'home'}">Home</router-link>
|
<router-link replace :to="{name:'mine1'}">Mine</router-link>
|
<router-link replace :to="{name:'about', params: { message: '222'}}">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
3、Mine.vue
<script setup lang="ts">
const router = useRouter()
const to = (route: string) => {
// name形式
// router.push({
// name: route,
// })
// path形式
router.push({
path: route,
})
}
</script>
<template>
<h1>個(gè)人中心</h1>
<div>
<!--name形式-->
<!--ts方式-->
<!--<button @click="to('mine1')">mine1</button>-->
<!--<button @click="to('mine2')">mine2</button>-->
<!--<!–router-link方式–>-->
<!--<router-link replace :to="{name:'mine1'}">mine1</router-link>-->
<!--|-->
<!--<router-link replace :to="{name:'mine2'}">mine2</router-link>-->
<!--path形式-->
<!--ts方式-->
<button @click="to('/mine')">mine1</button>
<button @click="to('/mine/mine2')">mine2</button>
<!--router-link方式-->
<router-link replace to="/mine">mine1</router-link>
|
<router-link replace to="/mine/mine2">mine2</router-link>
</div>
<router-view/>
</template>
<style scoped lang="scss">
</style>
13.5、路由重定向
1、router/index.ts
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about/:message',
name: 'about',
component: () => import('../view/About.vue')
},
{
path: '/mine',
name: 'mine',
// 重定向方式1
// redirect: {
// // path: '/mine1'
// name: 'mine1'
// }
// 重定向方式2
// redirect: (to) => {
// console.log(to);
// return "/mine1"
// }
// 重定向方式3
redirect: (to) => {
console.log(to);
return {
path: '/mine1',
query: to.query
}
}
},
{
path: '/mine1',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
{
path: '/mine2',
name: 'mine2',
component: () => import('../components/Mine2.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
export default router
2、使用
<script setup lang="ts">
const router = useRouter()
// ts方式取消記錄
const to = (route: string, message: string | null) => {
if (message) {
router.push({
name: route,
params: {
message
}
})
} else {
router.push({
name: route,
})
}
}
</script>
<template>
<nav>
<!--ts方式-->
<button @click="to('home', null)">home</button>
<button @click="to('mine', null)">mine</button>
<button @click="to('about', '222')">about</button>
<!--router-link方式-->
<router-link replace :to="{name:'home'}">Home</router-link>
|
<router-link replace :to="{name:'mine'}">Mine</router-link>
|
<router-link replace :to="{name:'about', params: { message: '222'}}">About</router-link>
</nav>
<router-view/>
</template>
<style scoped lang="scss">
</style>
13.6、路由別名
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
alias: ['/index', '/root'],
component: Home
},
{
path: '/about/:message',
name: 'about',
component: () => import('../view/About.vue')
},
{
path: '/mine',
name: 'mine',
// 重定向方式1
// redirect: {
// // path: '/mine1'
// name: 'mine1'
// }
// 重定向方式2
// redirect: (to) => {
// console.log(to);
// return "/mine1"
// }
// 重定向方式3
redirect: (to) => {
console.log(to);
return {
path: '/mine1',
query: to.query
}
}
},
{
path: '/mine1',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
export default router
13.7、導(dǎo)航守衛(wèi)
1、前置守衛(wèi)
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
alias: ['/index', '/root'],
component: Home
},
{
path: '/about/:message',
name: 'about',
component: () => import('../view/About.vue')
},
{
path: '/mine',
name: 'mine',
redirect: {
// path: '/mine1'
name: 'mine1'
}
},
{
path: '/mine1',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
// next: 放行,可傳入指定path
router.beforeEach((to, from, next) => {
console.log(to, from, next);
next()
})
export default router
2、后置守衛(wèi)
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
alias: ['/index', '/root'],
component: Home
},
{
path: '/about/:message',
name: 'about',
component: () => import('../view/About.vue')
},
{
path: '/mine',
name: 'mine',
redirect: {
// path: '/mine1'
name: 'mine1'
}
},
{
path: '/mine1',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
// next: 放行,可傳入指定path
router.beforeEach((to, from, next) => {
console.log(to, from, next);
next()
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
router.afterEach((to, from) => {
console.log(to, from);
})
export default router
13.8、路由元信息
1、router/index.ts
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
// declare module 'vue-router' {
// interface RouteMeta {
// title: string
// }
// }
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
alias: ['/index', '/root'],
meta: {
title: 'home'
},
component: Home
},
{
path: '/about/:message',
name: 'about',
meta: {
title: 'about'
},
component: () => import('../view/About.vue')
},
{
path: '/mine',
name: 'mine',
redirect: {
// path: '/mine1'
name: 'mine1'
}
},
{
path: '/mine1',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
// next: 放行,可傳入指定path
router.beforeEach((to, from, next) => {
console.log(to, from, next);
document.title = to.meta.title
next()
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
router.afterEach((to, from) => {
console.log(to, from);
})
export default router
2、聲明文件
declare module 'vue-router' {
interface RouteMeta {
title: string
}
}
13.9、路由過渡
<script setup lang="ts">
const router = useRouter()
// ts方式取消記錄
const to = (route: string) => {
router.push({
name: route
})
}
</script>
<template>
<nav>
<!--ts方式-->
<button @click="to('home')">home</button>
<button @click="to('mine')">mine</button>
<button @click="to('about')">about</button>
<!--router-link方式-->
<router-link replace :to="{name:'home'}">Home</router-link>
|
<router-link replace :to="{name:'mine'}">Mine</router-link>
|
<router-link replace :to="{name:'about'}">About</router-link>
</nav>
<router-view #default="{route, Component}">
<transition name="v">
<component :is="Component"></component>
</transition>
</router-view>
</template>
<style scoped lang="scss">
.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>
13.10、滾動(dòng)行為
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
// declare module 'vue-router' {
// interface RouteMeta {
// title: string
// }
// }
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
alias: ['/index', '/root'],
meta: {
title: 'home'
},
component: Home
},
{
path: '/about',
name: 'about',
meta: {
title: 'about'
},
component: () => import('../view/About.vue')
},
{
path: '/mine',
name: 'mine',
redirect: {
// path: '/mine1'
name: 'mine1'
}
},
{
path: '/mine1',
name: 'mine1',
component: () => import('../components/Mine1.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior: (to, from, savedPosition) => {
console.log(to, from);
if (savedPosition) {
return savedPosition
} else {
return {
top: 0
}
}
}
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
// next: 放行,可傳入指定path
router.beforeEach((to, from, next) => {
console.log(to, from);
document.title = to.meta.title
next()
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
router.afterEach((to, from) => {
console.log(to, from);
})
export default router
13.11、動(dòng)態(tài)路由
import {createRouter, createWebHistory, Router, RouteRecordRaw} from "vue-router";
import Home from '../view/Home.vue'
// declare module 'vue-router' {
// interface RouteMeta {
// title: string
// }
// }
let routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
meta: {
title: 'home'
},
component: Home
},
{
path: '/about',
name: 'about',
meta: {
title: 'about'
},
component: () => import('@/view/About.vue')
},
]
const router: Router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior: (to, from, savedPosition) => {
console.log(to, from);
if (savedPosition) {
return savedPosition
} else {
return {
top: 0
}
}
}
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
// next: 放行,可傳入指定path
router.beforeEach((to, from, next) => {
console.log(to, from);
document.title = to.meta.title
next()
})
// to: 去哪個(gè)路由
// from: 從哪個(gè)路由來
router.afterEach((to, from) => {
console.log(to, from);
})
// 動(dòng)態(tài)路由
const dynamicRoutes = [
{
path: '/mine1',
name: 'mine1',
component: 'Mine1'
},
{
path: '/mine2',
name: 'mine2',
component: 'Mine2'
},
]
dynamicRoutes.forEach((item) => {
router.addRoute({
path: item.path,
name: item.name,
component: () => import(`@/view/${item.component}.vue`)
})
console.log(router.getRoutes())
})
export default router
14、Eslint
14.1、初始化eslint
1、選擇模式(To check syntax and find problems)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
? How would you like to use ESLint? ...
To check syntax only
> To check syntax and find problems
To check syntax, find problems, and enforce code style
2、選擇語言(JavaScript modules (import/export))
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
? What type of modules does your project use? ...
> JavaScript modules (import/export)
CommonJS (require/exports)
None of these
3、選擇框架(Vue.js)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
? Which framework does your project use? ...
React
> Vue.js
None of these
4、是否使用ts(Yes)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
? Does your project use TypeScript? ? No / Yes
5、運(yùn)行環(huán)境(Browser)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
? Where does your code run? ... (Press <space> to select, <a> to toggle all, <i> to invert selection)
√ Browser
√ Node
6、配置文件格式(JavaScript)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser
? What format do you want your config file to be in? ...
> JavaScript
YAML
JSON
7、是否現(xiàn)在安裝依賴(Yes)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser
√ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:
@typescript-eslint/eslint-plugin@latest eslint-plugin-vue@latest @typescript-eslint/parser@latest
? Would you like to install them now? ? No / Yes
8、包管理器(yarn)
PS D:\WebstromProject\yanyang> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser
√ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:
@typescript-eslint/eslint-plugin@latest eslint-plugin-vue@latest @typescript-eslint/parser@latest
√ Would you like to install them now? · No / Yes
? Which package manager do you want to use? ...
npm
> yarn
pnpm
等待安裝完成即可
14.2、.eslintrc.cjs
module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-essential",
'plugin:prettier/recommended',
'eslint-config-prettier'
],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parserOptions": {
"ecmaVersion": "latest",
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"vue"
],
"rules": {
}
}
14.3、package.json
{
"name": "yanyang",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
// 格式化命令
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix"
},
"dependencies": {
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"vue": "^3.3.4"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vitejs/plugin-vue": "^4.2.3",
"eslint": "^8.46.0",
"eslint-plugin-vue": "^9.16.1",
"prettier": "^3.0.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vite-plugin-eslint": "^1.8.1",
"vue-tsc": "^1.8.5"
}
}
15.4、webstrom配置
會(huì)自動(dòng)匹配.eslintrc.*文件作為配置文件
15、Prettier
15.1、.prettierrc.cjs
module.exports = {
printWidth: 80, // 單行長度
tabWidth: 4, // 縮進(jìn)長度
useTabs: false, // 使用空格代替tab縮進(jìn)
semi: false, // 句末使用分號(hào)
singleQuote: true, // 使用單引號(hào)
}
15.2、.eslintrc.cjs
module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-essential",
// 添加prettier配置
'plugin:prettier/recommended',
'eslint-config-prettier'
],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parserOptions": {
"ecmaVersion": "latest",
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"vue"
],
"rules": {
}
}
15.3、package.json
{
"name": "yanyang",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.ts,.tsx,.js,.jsx . --fix",
// 格式化命令
"prettier": "prettier --write ./**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}"
},
"dependencies": {
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"vue": "^3.3.4"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vitejs/plugin-vue": "^4.2.3",
"eslint": "^8.46.0",
"eslint-plugin-vue": "^9.16.1",
"prettier": "^3.0.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vite-plugin-eslint": "^1.8.1",
"vue-tsc": "^1.8.5"
}
}
15.4、webstrom配置
會(huì)自動(dòng)匹配.prettierrc.*文件作為配置文件文章來源:http://www.zghlxwxcb.cn/news/detail-608531.html
16、TSX語法
16.1、組件創(chuàng)建方式
1、函數(shù)式
// 函數(shù)式組件
export default () => <div>Hello TSX!</div>
2、defineComponent
// defineComponent
export default defineComponent({
render() {
return <div>Hello TSX!</div>
},
})
3、defineComponent(setup)
// defineComponent(setup)
export default defineComponent({
setup(props, ctx) {
console.log(props, ctx)
return () => {
return <div>Hello TSX!</div>
}
},
})
16.2、修飾符
tsx取值需自行加value文章來源地址http://www.zghlxwxcb.cn/news/detail-608531.html
import { withModifiers } from 'vue'
export default defineComponent({
setup(props, ctx) {
console.log(props, ctx)
const count = ref(0)
const add = () => {
count.value++
}
return () => {
return (
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
)
}
},
})
16.3、v-model
import { withModifiers } from 'vue'
export default defineComponent({
setup(props, ctx) {
console.log(props, ctx)
const count = ref(0)
const add = () => {
count.value++
}
return () => {
return (
<div>
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
<input type="texe" v-model={count.value} />
</div>
)
}
},
})
16.4、v-if
import { withModifiers } from 'vue'
export default defineComponent({
setup(props, ctx) {
console.log(props, ctx)
const count = ref(0)
const add = () => {
count.value++
}
return () => {
return (
<div>
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
<input type="texe" v-model={count.value} />
<div>
{count.value % 2 === 0 ? <span>偶數(shù)</span> : <span>奇數(shù)</span>}
</div>
</div>
)
}
},
})
16.5、v-for
import { withModifiers } from 'vue'
export default defineComponent({
setup(props, ctx) {
console.log(props, ctx)
const count = ref(0)
const add = () => {
count.value++
}
const users = reactive([
{
name: 'xumeng03',
},
{
name: '眼眸',
},
])
return () => {
return (
<div>
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
<input type="texe" v-model={count.value} />
<div>
{count.value % 2 === 0 ? <span>偶數(shù)</span> : <span>奇數(shù)</span>}
</div>
<ul>
{users.map((item) => (
<li>{item.name}</li>
))}
</ul>
</div>
)
}
},
})
16.6、指令
import { withModifiers } from 'vue'
export default defineComponent({
directives: {
foucs: {
mounted(el) {
el.focus()
},
},
},
setup(props, ctx) {
console.log(props, ctx)
const count = ref(0)
const add = () => {
count.value++
}
const users = reactive([
{
name: 'xumeng03',
},
{
name: '眼眸',
},
])
return () => {
return (
<div>
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
<input v-foucs type="texe" v-model={count.value} />
<div>
{count.value % 2 === 0 ? <span>偶數(shù)</span> : <span>奇數(shù)</span>}
</div>
<ul>
{users.map((item) => (
<li>{item.name}</li>
))}
</ul>
</div>
)
}
},
})
16.7、slot
import { ref, defineComponent, withModifiers } from 'vue'
export default defineComponent({
setup(props, { slots }) {
console.log(props, slots)
const count = ref(0)
const add = () => {
count.value++
}
return () => {
return (
<div>
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
<div>{slots.default ? slots.default() : '插槽無內(nèi)容'}</div>
<div>{slots.remark ? slots.remark() : '插槽無內(nèi)容'}</div>
</div>
)
}
},
})
<script setup lang="ts">
import HelloTSX from './components/HelloTSX.tsx'
</script>
<template>
<HelloTSX>
<template #default> default插槽測試 </template>
<template #remark> remark插槽測試 </template>
</HelloTSX>
</template>
<style scoped>
</style>
16.8、emit
import { ref, defineComponent, withModifiers } from 'vue'
export default defineComponent({
emits: ['click'],
setup(props, { slots, emit }) {
console.log(props, slots, emit)
const count = ref(0)
const add = () => {
emit('click')
}
return () => {
return (
<div>
<div onClick={withModifiers(add, ['self'])}>Hello {count.value}!</div>
</div>
)
}
},
})
<script setup lang="ts">
import HelloTSX from './components/HelloTSX.tsx'
</script>
<template>
<HelloTSX @click="() => console.log('HelloTSX Click')"> </HelloTSX>
</template>
<style scoped>
</style>
到了這里,關(guān)于Vite+Typescript+Vue3學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!