国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Vite+Typescript+Vue3學(xué)習(xí)筆記

這篇具有很好參考價(jià)值的文章主要介紹了Vite+Typescript+Vue3學(xué)習(xí)筆記。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

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.

Vite+Typescript+Vue3學(xué)習(xí)筆記,typescript,學(xué)習(xí),筆記

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、使用

Vite+Typescript+Vue3學(xué)習(xí)筆記,typescript,學(xué)習(xí),筆記

2、Composition API

2.1、響應(yīng)式

可以在chrome瀏覽器開啟自定義格式化,便于查看
Vite+Typescript+Vue3學(xué)習(xí)筆記,typescript,學(xué)習(xí),筆記
Vite+Typescript+Vue3學(xué)習(xí)筆記,typescript,學(xué)習(xí),筆記

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ǔ)中,如 cookielocalStorage、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>-->
        
        <!--&lt;!&ndash;router-link方式&ndash;&gt;-->
        <!--<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.*文件作為配置文件
Vite+Typescript+Vue3學(xué)習(xí)筆記,typescript,學(xué)習(xí),筆記

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.*文件作為配置文件
Vite+Typescript+Vue3學(xué)習(xí)筆記,typescript,學(xué)習(xí),筆記

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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Vite4+Typescript+Vue3+Pinia 從零搭建(4) - 代碼規(guī)范

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 要求代碼規(guī)范,主要是為了提高多人協(xié)同和代碼維護(hù)效率,結(jié)合到此項(xiàng)目,具體工作就是為項(xiàng)目配置 eslint 和 prettier 。 安裝 EditorConfig for VS Code 插件,根目錄下新建 .editorconfig 文件,增加以下配置 如果是非windows系統(tǒng), end_of_line 設(shè)置為 cr 安

    2024年02月05日
    瀏覽(92)
  • Vue3 + Vite2 + TypeScript4搭建企業(yè)級(jí)項(xiàng)目框架

    1. 創(chuàng)建項(xiàng)目 使用命令行工具進(jìn)入到你想要?jiǎng)?chuàng)建項(xiàng)目的目錄,然后執(zhí)行以下命令: 這將會(huì)創(chuàng)建一個(gè)新的項(xiàng)目文件夾和一個(gè) package.json 文件。 2. 安裝依賴 接下來你需要在項(xiàng)目中安裝 Vue、Vite、TypeScript 和其他需要的依賴。執(zhí)行以下命令: 以上命令會(huì)安裝最新的 Vue、Vite 和 TypeSc

    2024年02月08日
    瀏覽(93)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(7) - request封裝

    Vite4+Typescript+Vue3+Pinia 從零搭建(7) - request封裝

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 基于 axios 封裝請(qǐng)求,支持多域名請(qǐng)求地址 utils 目錄下新建 request 文件夾,并新建 index.ts 、 request.ts 和 status.ts 文件。 此時(shí),eslint會(huì)報(bào) switch 前面的空格錯(cuò)誤,需要修改 .eslintrc.cjs 里的 indent ,修改后,錯(cuò)誤消失。 src 目錄下新建 api 文件夾,

    2024年02月04日
    瀏覽(98)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(2) - ts配置

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 關(guān)于tsconfig的配置字段可查看其他文檔,如 typeScript tsconfig配置詳解 文件修改如下: 修改文件如下: 新建文件夾 types ,用來存放類型定義。比如新建 index.d.ts : 后續(xù)也可以新增其他文件,比如 global.d.ts 存放全局定義, router.d.ts 存放路由定

    2024年02月05日
    瀏覽(102)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(5) - 路由router

    Vite4+Typescript+Vue3+Pinia 從零搭建(5) - 路由router

    項(xiàng)目代碼同步至碼云 weiz-vue3-template Vue Router 是 Vue.js 的官方路由。它與 Vue.js 核心深度集成,讓用 Vue.js 構(gòu)建單頁應(yīng)用變得輕而易舉。 在 src/view 下新建 home.vue 和 login.vue ,內(nèi)容如下: login.vue 里修改下對(duì)應(yīng)name即可 index.ts 作為路由入口, static.ts 作為靜態(tài)路由, modules 內(nèi)還可以

    2024年02月05日
    瀏覽(100)
  • vue3 + typescript + vite + naive ui + tailwindcss + jsx 仿蘋果桌面系統(tǒng)

    vue3 + typescript + vite + naive ui + tailwindcss + jsx 仿蘋果桌面系統(tǒng)

    基于 vue3.x + typescript + vite + naive ui + tailwindcss + jsx + vue-router + pinia,項(xiàng)目使用 tsx 作為模版輸出,全程沒有使用vue提供的SFC, 仿macos桌面前端項(xiàng)目,開源免費(fèi)模版,希望減少工作量和學(xué)習(xí)新技術(shù),希望能夠幫助大家; 本人主要是后端的開發(fā),對(duì)于前端我也是剛?cè)腴T的小白,有很

    2024年02月07日
    瀏覽(22)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(6) - 狀態(tài)管理pina

    Vite4+Typescript+Vue3+Pinia 從零搭建(6) - 狀態(tài)管理pina

    項(xiàng)目代碼同步至碼云 weiz-vue3-template pina 是 vue3 官方推薦的狀態(tài)管理庫,由 Vue 核心團(tuán)隊(duì)維護(hù),旨在替代 vuex。pina 的更多介紹,可從 pina官網(wǎng) 查看 更簡潔直接的 API,提供組合式風(fēng)格的 API 支持模塊熱更新和服務(wù)端渲染 對(duì)TS支持更為友好 src目錄下新建store文件夾,并新建index.t

    2024年02月05日
    瀏覽(23)
  • Vue3集成ThreeJS實(shí)現(xiàn)3D效果,threejs+Vite+Vue3+TypeScript 實(shí)戰(zhàn)課程【一篇文章精通系列】

    Vue3集成ThreeJS實(shí)現(xiàn)3D效果,threejs+Vite+Vue3+TypeScript 實(shí)戰(zhàn)課程【一篇文章精通系列】

    這是一個(gè)使用Vue3,TypeScript,Vite和Three.js的項(xiàng)目。Vue3是一個(gè)流行的JavaScript框架,用于構(gòu)建用戶界面。TypeScript是一種靜態(tài)類型的編程語言,它是JavaScript的超集,可以編譯成純JavaScript。Vite是一個(gè)由Evan You開發(fā)的新的前端構(gòu)建工具,能夠提供快速的冷啟動(dòng)和即時(shí)熱更新。 Three.j

    2024年02月03日
    瀏覽(24)
  • 快速搭建Electron+Vite3+Vue3+TypeScript5腳手架 (無需梯子,快速安裝Electron)

    快速搭建Electron+Vite3+Vue3+TypeScript5腳手架 (無需梯子,快速安裝Electron)

    Electron是一個(gè)使用 JavaScript、HTML 和 CSS 構(gòu)建桌面應(yīng)用程序的框架。 嵌入?Chromium?和?Node.js?到 二進(jìn)制的 Electron 允許您保持一個(gè) JavaScript 代碼代碼庫并創(chuàng)建 在Windows上運(yùn)行的跨平臺(tái)應(yīng)用 macOS和Linux——不需要本地開發(fā)經(jīng)驗(yàn)(這段話是來自官網(wǎng))。 根據(jù)vite官網(wǎng)文檔 項(xiàng)目創(chuàng)建完成后進(jìn)

    2024年02月02日
    瀏覽(109)
  • 前端2023最全面試題(javaScript、typeScript、vue2、vue3、html、css、uniapp、webpack、vite、react)

    答案:JavaScript中的閉包是一種函數(shù),它有權(quán)訪問其詞法環(huán)境的變量和其它函數(shù)。這意味著,即使其包含它的函數(shù)已經(jīng)執(zhí)行完畢,其詞法環(huán)境仍然存在,因此可以訪問其作用域內(nèi)的變量。 答案:回調(diào)函數(shù)是在某個(gè)特定事件之后執(zhí)行的函數(shù)。在JavaScript中,通常使用回調(diào)函數(shù)來處

    2024年02月06日
    瀏覽(34)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包