中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

空間制作網(wǎng)站頭條收錄提交入口

空間制作網(wǎng)站,頭條收錄提交入口,wordpress收費主題推薦,做機械的網(wǎng)站目錄 1.Vue3簡介1.1.性能提升1.2.源碼升級1.3.擁抱TypeScript1.4.新特性 2.創(chuàng)建Vue3工程2.1.基于 vue-cli 創(chuàng)建2.2. 基于 vite 創(chuàng)建(推薦)2.3.代碼運行 3.Vue3核心語法3.1.OptionsAPI(選項式API) 與 CompositionAPI(組合式API)3.2.setup3.3.ref 創(chuàng)建&…

目錄

  • 1.Vue3簡介
    • 1.1.性能提升
    • 1.2.源碼升級
    • 1.3.擁抱TypeScript
    • 1.4.新特性
  • 2.創(chuàng)建Vue3工程
    • 2.1.基于 vue-cli 創(chuàng)建
    • 2.2. 基于 vite 創(chuàng)建(推薦)
    • 2.3.代碼運行
  • 3.Vue3核心語法
    • 3.1.OptionsAPI(選項式API) 與 CompositionAPI(組合式API)
    • 3.2.setup
    • 3.3.ref 創(chuàng)建:基本類型的響應(yīng)式數(shù)據(jù)
    • 3.4 reactive 創(chuàng)建:對象類型的響應(yīng)式數(shù)據(jù)
    • 3.5 ref 創(chuàng)建:對象類型的響應(yīng)式數(shù)據(jù)
    • 3.6 ref 對比 reactive
    • 3.7.toRefs 與 toRef
    • 3.8.computed
    • 3.9.watch
    • 3.10 watchEffect
    • 3.11.標(biāo)簽ref屬性
    • 3.12.TS接口、自定義類型、范型
    • 3.13.props
    • 3.14.生命周期
    • 3.15.自定義hook
  • 4.路由
    • 4.1. 對路由的理解
    • 4.2.基本切換效果
    • 4.3.兩個注意點
    • 4.4.路由器工作模式
    • 4.5.命名路由
    • 4.6.to的兩種寫法
    • 4.7.嵌套路由
    • 4.8.路由傳參
    • 4.9.路由的 props配置
    • 4.10.replace屬性
    • 4.11.編程式導(dǎo)航
    • 4.12.重定向
  • 5.pinia
    • 5.1.準(zhǔn)備案例代碼
    • 5.2.搭建 pinia 環(huán)境
    • 5.3.存儲+讀取數(shù)據(jù)
    • 5.4.修改數(shù)據(jù)(三種方式)
    • 5.5.storeToRefs
    • 5.6.getters
    • 5.7.$subscribe
    • 5.8.store組合式寫法
  • 6.組件通信
    • 6.1.props
    • 6.2.自定義事件
    • 6.3.mitt
    • 6.4.v-model
    • 6.5.$attrs
    • 6.6.$ refs、$parent
    • 6.7.provide、inject
    • 6.8.slot
  • 7.其它API
    • 7.1. shallowRef 與 shallowReactive
    • 7.2.readonly 與 shallowReadonly
    • 7.3.toRaw 與 markRaw
    • 7.4.customRef
  • 8.Vue3新組件
    • 8.1.teleport
    • 8.2.Suspense
    • 8.3.全局API轉(zhuǎn)移到應(yīng)用對象

1.Vue3簡介

  • 2020年9月18日,Vue.js發(fā)布版3.0版本,代號:One Piece(海賊王)
  • 經(jīng)歷:4800+次提交、40+個RFC、600+次PR、300+貢獻者
  • 官方發(fā)版地址:Release V3.0.0 One Piece vuejs/core

1.1.性能提升

  • 打包體積減少41%
  • 初次渲染快55%,更新渲染快133%
  • 內(nèi)存減省54%

1.2.源碼升級

  • 使用Proxy代替defineProperty實現(xiàn)響應(yīng)式
  • 重寫虛擬DOM的實現(xiàn)和Tree-Shaking

1.3.擁抱TypeScript

  • Vue3可以更好的支持TypeScript

1.4.新特性

  1. Composition API (組合API)
  2. 新內(nèi)置組件
  3. 其它

2.創(chuàng)建Vue3工程

2.1.基于 vue-cli 創(chuàng)建

目前 vue-cli 已處于維護模式,官方推薦基于 Vite 創(chuàng)建項目

## 查看@vue/cli版本,確保@vue/cli版本在4.5.0+
vue --version
## 安裝或者升級你的@vue/cli
npm install -g @vue/cli
## 執(zhí)行創(chuàng)建命令
vue create vue_test## 選擇Vue 3
## Vue CLI v5.0.8
## ? Please pick a preset: (Use arrow keys)
## > Default ([Vue 3] babel, eslint)
##   Default ([Vue 2] babel, eslint)
##   Manually select features## 啟動
cd vue_test
npm run serve

2.2. 基于 vite 創(chuàng)建(推薦)

vite 是新一代前端構(gòu)建工具,官網(wǎng)地址:https://vitejs.cn,vite的優(yōu)勢如下:

  • 輕量快速的熱得載(HMR),能實現(xiàn)極速的服務(wù)啟動
  • 對 TypeScript、JSX、CSS 等支持開箱即用
  • 真正的按需編譯,不再等待整個應(yīng)用編譯完成
  • webpack構(gòu)建 與 vite 構(gòu)建對比圖如下:

Bundle Based dev server

entry
route
route
...
module
module
module
module
...
Bundle
Server ready

Native ESM based dev server

HTTP request
Dynamic import code split point
Server ready
entry
route
route
...
module
module
module
module
module
  • 具體操作如下
## 1.創(chuàng)建命令
npm create vue@latest
## 2.具體配置
## Need to install the following packages:
##   create-vue@3.14.2
Ok to proceed? (y) y
## 請輸入項目名稱:
Project name:vue3-project
## 是否使用 TypeScript 語法?
Add TypeScript? Yes
## 是否啟用 JSX 支持
Add JSX Support? No
## 是否引入 Vue Router 進行單頁面應(yīng)用開發(fā)?
Add Vue Router for single Page Application development? No
## 是否引入 Pinia 用于狀態(tài)管理?
Add Pinia for state management? No
## 是否引入 Vitest 用于單元測試?
Add Vitest for Unit Testing? No
## 是否要引入一款端到端(End to End)測試工具?
Add an End-to-End Testing Solution? No
## 是否引入 ESLint 用于代碼質(zhì)量檢測?
Add ESLint for code quality? Yes
## 是否引入 Prettier 用于代碼格式化?
Add Prettier for code formatting? No
  • 文件作用
    • index.html
      • 入口文件,引入 /src/main.ts
    • package.json:
      • 項目的元數(shù)據(jù)文件,包括項目名稱、版本、描述、作者、依賴項等。
      • 定義了項目的腳本,如啟動、構(gòu)建、測試等命令。
    • public/:
      • 存放靜態(tài)資源,如HTML模板(index.html)、圖片、圖標(biāo)等。
      • 這些文件在構(gòu)建時會被復(fù)制到輸出目錄(通常是dist/),并且可能通相對咱徑在項目中引用。
    • src/:
      • 項目源代碼目錄。
      • main.js/main.ts:項目入口文件,用于創(chuàng)建Vue實例并掛載到DOM上。
      • App.vue:主組件文件,作為所有頁面組件的容器。
      • comments/:存放Vue組件的文件夾,這些組件可以在整個項目中復(fù)用。
      • assets/:存放項目中會使用的靜態(tài)資源,如圖片、字體、樣式文件等。這些資源在構(gòu)建時會被處理(如壓縮、轉(zhuǎn)換等)。
      • router/:如果項目使用Vue Router進行路由管理,則此文件夾包含路由的配置文件(如index.js 或 index.ts),定義了前端路由的映射關(guān)系。
      • store/:如果項目使用Vuex進行狀態(tài)管理,則此文件夾包含Vuex的配置文件(如 index.ts或index.ts),用于管理應(yīng)用的所有組件的狀態(tài)。
      • views/:在Vue CLI 3+ 的項目中,這個文件夾通常用來豐防御頁面級的組件,即路由對應(yīng)的組件。
    • vite.config.ts
      • 配置文件,用于修改webpack配置、添加新的loader選項、配置代理等。
    • .gitignore:
      • Git版本控制忽略文件,指定哪些文件或文件夾不需要納入GIt版本控制
    • tsconfig.json
      • TypeScript的配置文件,定譯了TypeScript編譯器的選項。

2.3.代碼運行

  1. 項目入口文件 index.html
<!-- index.html相關(guān)代碼 --><!-- 創(chuàng)建id=app 的容器 --><div id="app"></div><!-- 引入 /src/main.ts --><script type="module" src="/src/main.ts"></script>
  • 引入 /src/main.ts
// maint.ts 相關(guān)代碼
// 引入 createApp 創(chuàng)建應(yīng)用
import { createApp } from 'vue'
// 引入 App 根組件
import App from './App.vue'
// createApp(App):以App作為參數(shù)生成一個應(yīng)用實例對象
// mount('#app'):掛載到id=app節(jié)點上。
createApp(App).mount('#app')

3.Vue3核心語法

3.1.OptionsAPI(選項式API) 與 CompositionAPI(組合式API)

  • Vue2的API設(shè)計是Options(選項)風(fēng)格
  • Vue3的API設(shè)計是CompositionAPI(組合)風(fēng)格
    OptionsAPI 的弊端
    Options類型的API,數(shù)據(jù)、方法、計算屬性等,是分散在:data、methods、computed中的,若想新境或者改一個需求,就需要分別修改:data、methods、computed,不便于維護和復(fù)用。
    CompositionAPI 的優(yōu)勢
    可以用函數(shù)的方式,更加優(yōu)雅的組織代碼,讓相關(guān)功能的代碼更加有序的組織在一起。

3.2.setup

setup 概述
setup 是 Vue3 中一個新的配置項,值是一個函數(shù),組件中所用到的:數(shù)據(jù)、方法、計算屬性、監(jiān)視等,均配置在setup中。
特點如下:

  • setup函數(shù)返回的對象中的內(nèi)容,可直接在模板中使用
  • setup中訪問this是undefined
  • setup函數(shù)會在beforeCreate之前調(diào)用,它是“領(lǐng)先”所有鉤子執(zhí)行的。
<template><h2>姓名:{{name}}</h2><br/><h2>年齡:{{age}}</h2><br/><button @click="changeName">修改名字</button><br/><button @click="changeAge">修改年齡</button><br/><button @click="showTel">查看電話</button>
</template>
<script lang="ts">export default{name:'Person',setup(){let name = '張三'let age = 18let tel = '13888888888'function changeName(){name = "zhangsan"console.log(name)}function changeAge(){age += 1console.log(age)}function showTel(){alert(tel)}return {name,age,changeAge,changeName,showTel}// setup的返回值也可以是函數(shù)// return function(){return 'hello'}// return ()=>{return 'hello'} //簡寫// return ()=>'hello' //簡寫}}
</script>
<style>
</style>

setup 與 OptionsAPI的關(guān)系

  • Vue2 的選項(data、methods
    等)中可以訪問到setup中的屬性、方法。但在setup中不能訪問Vue2的選項(data、methods 等)。
  • 如果與Vue2沖突,則setup優(yōu)先

setup語法糖
setup 可以獨立出來

<template><h2>姓名:{{name}}</h2><br/><h2>年齡:{{age}}</h2><br/><button @click="changeName">修改名字</button><br/><button @click="changeAge">修改年齡</button><br/><button @click="showTel">查看電話</button>
</template><script lang="ts">export default{name:'Person'}
</script>
<script lang="ts" setup>let name = '張三'let age = 18let tel = '13888888888'function changeName(){name = "zhangsan"console.log(name)}function changeAge(){age += 1console.log(age)}function showTel(){alert(tel)}
</script>
<style>
</style>

擴展:上述代碼,還需要編寫一個不寫setup的script標(biāo)簽,去指定組件名字,比較麻煩,我們可以借助vite中的插件簡化

  • 第一步:npm i vite-plugin-vue-setup-extend -D
  • 第二步:vite.config.ts
import VueSetupExtend form 'vite-plugin-vue-setup-extend'//增加引入代碼
export default defineConfig({plugins: [VueSetupExtend(),//增加使用代碼]
})
  • 第三步:使用 name=“組件名”
//使用方法
<script setup lang="ts" name="Person">
</script>

3.3.ref 創(chuàng)建:基本類型的響應(yīng)式數(shù)據(jù)

  • 作用:定義響應(yīng)式變量
  • 語法:let xxx = ref(初始值)
  • 返回值:RefImpl的實例對象,簡稱ref對象或ref,ref對象的value屬性是響應(yīng)式的。
  • 注意點:
    • JS中操作數(shù)據(jù)需要:xxx.value,但模板中不需要.value,直接使用即可。
    • 對于let name = ref('張三’) 來說,name不是響應(yīng)式的,name.value是響應(yīng)式的
<template><h2>姓名:{{name}}</h2><br/><h2>年齡:{{age}}</h2><br/><button @click="changeName">修改名字</button><br/><button @click="changeAge">修改年齡</button><br/><button @click="showTel">查看電話</button>
</template>
<script lang="ts" setup name="Person1133">import {ref} from 'vue'//name和age是一個RefImpl的實例對象,簡稱ref對象,它們的value屬性是響應(yīng)式的。let name = ref('張三')let age = ref(18)let tel = '13888888888'function changeName(){name.value = "zhangsan"console.log(name)}function changeAge(){age.value += 1console.log(age)}function showTel(){alert(tel)}
</script>
<style>
</style>

3.4 reactive 創(chuàng)建:對象類型的響應(yīng)式數(shù)據(jù)

作用:定義 響應(yīng)式對象
語法:let 響應(yīng)式對象 = reactive(源對象)
返回值:Proxy的實例對象,簡稱:響應(yīng)式對象
注意點:reactive 定義的響應(yīng)式數(shù)據(jù)是“深層次”的,reactive 會自動解包ref數(shù)據(jù)

<template><h2>姓名:{{person.name}}</h2><br/><h2>年齡:{{person.age}}</h2><br/><button @click="changeName">修改名字</button><br/><button @click="changeAge">修改年齡</button><br/><button @click="showTel">查看電話</button>
</template>
<script lang="ts" setup name="Person1133">import {reactive} from 'vue'let person = reactive({name:'張三',age:18,tel:'13888888888'})console.log(person)function changeName(){person.name = "zhangsan"console.log(person.name)}function changeAge(){person.age += 1console.log(person.age)}function showTel(){alert(person.tel)}
</script>
<style>
</style>

3.5 ref 創(chuàng)建:對象類型的響應(yīng)式數(shù)據(jù)

  • ref 接收的數(shù)據(jù)可以是:基本類型、對象類型
  • 若ref接收的是對象類型,內(nèi)部其實也是調(diào)用了reactive函數(shù)
<template><h2>姓名:{{person.name}}</h2><br/><h2>年齡:{{person.age}}</h2><br/><button @click="changeName">修改名字</button><br/><button @click="changeAge">修改年齡</button><br/><button @click="showTel">查看電話</button>
</template>
<script lang="ts" setup name="Person1133">import {ref} from 'vue'let person = ref({name:'張三',age:18,tel:'13888888888'})console.log(person)function changeName(){person.value.name = "zhangsan"console.log(person.value.name)}function changeAge(){person.value.age += 1console.log(person.value.age)}function showTel(){alert(person.value.tel)}
</script>
<style>
</style>

3.6 ref 對比 reactive

宏觀角度看:

  • ref用來定義:基本數(shù)據(jù)類型、對象類型數(shù)據(jù);
  • reactive用來定義:對象類型數(shù)據(jù)。
    區(qū)別:
  • ref創(chuàng)建的變量必須使用.value(可以使用vscode 中的volar插件自動添加.value)。
  • reactive 重新分配一個新對象,會失去響應(yīng)式(可以使用Object.assign 去整體替換)。
    使用原則:
  • 若需要一個基本類型的響應(yīng)式數(shù)據(jù),必須使用ref。
  • 若需要一個響應(yīng)式對象,層級不深,ref、reactive 都可以。
  • 若需要一個響應(yīng)式對象,且層級較深,推薦使用 reactive。
<template><h2>姓名:{{personRef.name}}</h2><br/><h2>年齡:{{personRef.age}}</h2><br/><button @click="changePersonRef">ref修改</button><br/><h2>姓名:{{personReactive.name}}</h2><br/><h2>年齡:{{personReactive.age}}</h2><br/><button @click="changePersonReactive">reactive修改</button><br/>
</template>
<script lang="ts" setup name="Person1133">import {ref,reactive} from 'vue'let personRef = ref({name:'張三',age:18,tel:'13888888888'})let personReactive = reactive({name:'李四',age:19,tel:'13888888888'})function changePersonRef(){// personRef = ref({name:'張三ref',age:28,tel:'13888888888'}) //不行,不是響應(yīng)式personRef.value = {name:'張三ref',age:28,tel:'13888888888'}}function changePersonReactive(){// personReactive = reactive({name:'張三ref',age:28,tel:'13888888888'}) //不行,不是響應(yīng)式Object.assign(personReactive,{name:'李四reactive',age:29,tel:'13888888888'})}
</script>
<style>
</style>

3.7.toRefs 與 toRef

  • 作用:將一個響應(yīng)式對象中的每一個屬性,轉(zhuǎn)換為ref對象
  • 備注:toRefs 與 toRef 功能一致,但 toRefs 可以批量替換
<template><h2>姓名:{{name}}</h2><br/><h2>年齡:{{person.age}}</h2><br/><button @click="changeName">修改名字</button><br/><button @click="changeAge">修改年齡</button><br/><button @click="showTel">查看電話</button>
</template>
<script lang="ts" setup name="Person1133">import {reactive,toRefs,toRef} from 'vue'let person = reactive({name:'張三',age:18,tel:'13888888888'})let {name,age} = toRefs(person)let nl = toRef(person,'age')console.log(person)function changeName(){name.value = "zhangsan"console.log(name)}function changeAge(){age.value += 1console.log(age)}function showTel(){alert(person.tel)}
</script>
<style>
</style>

3.8.computed

<template><!-- :value(v-bind:value)是單向綁定(數(shù)據(jù)流向頁面),v-model(v-model:value)是雙向綁定 --><!-- 姓:<input type="text" :value="firstName"> -->姓:<input type="text" v-model="firstName"><br>名:<input type="text" v-model="lastName"><br>全名:<span>{{fullName}}</span><button @click="changeFullName">修改成li-si</button>
</template>
<script lang="ts" setup name="Person">import {ref,computed} from 'vue'let firstName = ref('zhang')let lastName = ref('san')//定義的fullName是一個計算屬性,且是只讀的//let fullName = computed(()=>{//	return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value//})//定義的fullName是一個計算屬性,可讀可寫let fullName = computed({get(){return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value},set(val){const [str1,str2] = val.split('-')firstName.value = str1lastName.value = str2}})function changeFullName(){fullName.value = 'li-si'}
</script>
<style>
</style>

3.9.watch

  • 作用:監(jiān)視數(shù)據(jù)變化
  • 特點:只能監(jiān)視以下四種數(shù)據(jù):
    1. ref 定義的數(shù)據(jù)。
    2. reactive 定義的數(shù)據(jù)。
    3. 函數(shù)返回一個值(getter 函數(shù))。
    4. 一個包含上數(shù)內(nèi)容的數(shù)組。

情況一
監(jiān)視 ref 定義的【基本類型】數(shù)據(jù):直接寫數(shù)據(jù)名即可,監(jiān)視的是其value值的改這。

<template><div><h1>sun值:{{ sum }}</h1><button @click="changeSum">sum++</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { ref, watch } from 'vue';
//數(shù)據(jù)
const sum = ref(1)
//方法
function changeSum(){sum.value++
}
//監(jiān)視
const stopWathc = watch(sum,(newSum,oldSum)=>{console.log(newSum,oldSum)if(newSum > 10){stopWathc()}
})
console.log(stopWathc)
</script>
<style>
</style>

情況二
監(jiān)視ref定義的【對象類型】數(shù)據(jù):直接寫數(shù)據(jù)名,監(jiān)視的是對象的【地址值】,若想監(jiān)視對象內(nèi)部的數(shù)據(jù),要手動開啟深度監(jiān)視。

注意:
- 若修改的是ref定義的對象的屬性:newValue 和 oldValue 都是新值,因為它們是同一個對象。
- 若修改整個ref定義的對象,newValue 是新值,oldValue 是舊值,因為不是同一個對象了。
<template><div><h1>姓名:{{ person.name }}</h1><h1>年齡:{{ person.age }}</h1><button @click="changeName">修改姓名</button><br><button @click="changeAge">修改年齡</button><br><button @click="changePerson">修改人</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { ref, watch } from 'vue';
const person = ref({name:'張三',age:18
})
function changeName(){person.value.name +="~"
}
function changeAge(){person.value.age ++
}
function changePerson(){person.value = {name:'李四',age:28}
}
/*
參數(shù)1:被監(jiān)視的數(shù)據(jù)
參數(shù)2:監(jiān)視的回調(diào)
參數(shù)3:配置對象(deep(深度監(jiān)視)、immediate(立即執(zhí)行 
) 等)
*/
watch(person,(newVal,oldVal)=>{console.log(newVal,oldVal)
},{deep:true})</script>
<style>
</style>

情況三
監(jiān)視 reactive 定義的【對象類型】數(shù)據(jù),默認開啟深度監(jiān)視。

<template><div><h1>姓名:{{ person.name }}</h1><h1>年齡:{{ person.age }}</h1><button @click="changeName">修改姓名</button><br><button @click="changeAge">修改年齡</button><br><button @click="changePerson">修改人</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { reactive, watch } from 'vue';
const person = reactive({name:'張三',age:18
})
function changeName(){person.name +="~"
}
function changeAge(){person.age ++
}
function changePerson(){Object.assign(person,{name:'李四',age:28})
}
//新值、舊值一樣
watch(person,(newVal,oldVal)=>{console.log(newVal,oldVal)
})
</script>
<style>
</style>

清況四
監(jiān)視 ref 或 reactive 定義的【對象類型】數(shù)據(jù)中的某個屬性,注意如下:

  1. 若該屬性值不是【對象類型】,需要寫成函數(shù)形式。
  2. 若該屬性值是【對象類型】,可以直接寫或?qū)懗珊瓟?shù)形式,建議寫成函數(shù)。

結(jié)論:監(jiān)視對象的屬性,建議寫成函數(shù)形式。若屬性是對象,則監(jiān)視的是地址值 ,如果要監(jiān)視對象內(nèi)部,則需要開啟深度監(jiān)視

<template><div><h1>姓名:{{ person.name }}</h1><h1>年齡:{{ person.age }}</h1><h1>汽車:{{ person.car.c1 }}、{{ person.car.c2 }}</h1><button @click="changeName">修改姓名</button><br><button @click="changeAge">修改年齡</button><br><button @click="changeC1">修改車一</button><br><button @click="changeC2">修改車二</button><br><button @click="changeCar">修改車</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { reactive, watch } from 'vue';
const person = reactive({name:'張三',age:18,car:{c1:"車一",c2:"車二"}
})
function changeName(){person.name +="~"
}
function changeAge(){person.age ++
}
function changeC1(){person.car.c1 = "車車一"
}
function changeC2(){person.car.c2 = "車車二"
}
function changeCar(){person.car = {c1:'一',c2:'二'}
}
//監(jiān)視一個屬性:基本類型
watch(()=>person.name,(newVal,oldVal)=>{console.log(newVal,oldVal)
})
//監(jiān)視一個屬性:對象類型
//結(jié)論:監(jiān)視對象的屬性,建議寫成函數(shù)形式。若屬性是對象,則監(jiān)視的是地址值 ,如果要監(jiān)視對象內(nèi)部,則需要開啟深度監(jiān)視
watch(()=>person.car,(newVal,oldVal)=>{console.log(newVal,oldVal)
},{deep:true})
</script>
<style>
</style>

情況五
監(jiān)視上述 多個數(shù)據(jù)

<template><div><h1>姓名:{{ person.name }}</h1><h1>年齡:{{ person.age }}</h1><h1>汽車:{{ person.car.c1 }}、{{ person.car.c2 }}</h1><button @click="changeName">修改姓名</button><br><button @click="changeAge">修改年齡</button><br><button @click="changeC1">修改車一</button><br><button @click="changeC2">修改車二</button><br><button @click="changeCar">修改車</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { reactive, watch } from 'vue';
const person = reactive({name:'張三',age:18,car:{c1:"車一",c2:"車二"}
})
function changeName(){person.name +="~"
}
function changeAge(){person.age ++
}
function changeC1(){person.car.c1 = "車車一"
}
function changeC2(){person.car.c2 = "車車二"
}
function changeCar(){person.car = {c1:'一',c2:'二'}
}
watch([()=>person.name,()=>person.car.c1],(newVal,oldVal)=>{console.log(newVal,oldVal)
},{deep:true})
</script>
<style>
</style>

3.10 watchEffect

  • 立即運行一個函數(shù),同時響應(yīng)式地追蹤其依賴,并在依賴更改時得新執(zhí)行該函數(shù)。
  • watch 對經(jīng) watchEffect
    • 都能監(jiān)聽響應(yīng)式數(shù)據(jù)的變化,但是監(jiān)聽數(shù)據(jù)變化的方式不同
    • watch:要明確指出監(jiān)視的數(shù)據(jù)
    • watchEffect:不用明確指出監(jiān)視的數(shù)據(jù)(函數(shù)中用到哪些屬性,那就監(jiān)視哪些屬性)
<template><div><h1>姓名:{{ person.name }}</h1><h1>年齡:{{ person.age }}</h1><button @click="changeName">修改姓名</button><br><button @click="changeAge">修改年齡</button><br><button @click="changePerson">修改人</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { reactive, watchEffect } from 'vue';
const person = reactive({name:'張三',age:18
})
function changeName(){person.name +="~"
}
function changeAge(){person.age ++
}
function changePerson(){Object.assign(person,{name:'李四',age:28})
}
//新值、舊值一樣
watchEffect(()=>{console.log(person.age)if(person.age > 35){console.log("35了")}
})
</script>
<style scoped>
</style>

3.11.標(biāo)簽ref屬性

作用:用于注冊模板引用。

  • 用在普通DOM標(biāo)簽上,獲取的是DOM節(jié)點
  • 用在組件標(biāo)簽上,獲取的是組件實例對象

用在普通DOM標(biāo)簽上

<template><div><h1>中國</h1><h2 ref="h2">北京</h2><button @click="getH2">獲取h2標(biāo)簽實例</button><br></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { ref,defineExpose } from 'vue';
const h2 = ref()
function getH2(){console.log(h2.value)//<h2 data-v-e17ea971="">北京</h2>//data-v-e17ea971 是因為 style scoped 局部樣式導(dǎo)致的
}
const a = ref(0)
const b = ref(1)
const c = ref(2)
defineExpose({a,b})
</script>
<style scoped>button{color: red;}
</style>

用在組件標(biāo)簽上

<template><HelloWorld ref="hw"/><button @click="getHw">獲取HelloWorld實例</button>
</template>
<script lang="ts" setup name="App">import HelloWorld from './components/HelloWorld.vue'import {ref} from 'vue'const hw = ref()function getHw(){console.log(hw.value)console.log(hw.value.a)}
</script>
<style scoped>
</style>

3.12.TS接口、自定義類型、范型

路徑:src/types/index.ts

//定義一個接口,用于限制pesson對象的具體屬性
export interface PersonInter {id:string,name:string,age:number,phone?:string //?表示可有可無
}
//自定義類型
//方法1
// export type PersonArr = Array<PersonInter>
//方法2
export type PersonArr = PersonInter[]
<template><div></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { type PersonInter,type PersonArr } from '@/types';
const person:PersonInter = {id:"doof01",name:"張三",age:19}
const personArr:PersonArr = [
{id:"doof01",name:"張三",age:19},
{id:"doof01",name:"張三",age:19,phone:"13888888888"}
]
console.log(person)
console.log(personArr)
</script>
<style scoped>button{color: red;}
</style>

3.13.props

/src/App.vue

<template><HelloWorld :pl="personList"/>
</template>
<script lang="ts" setup name="App">import { reactive } from 'vue';
import HelloWorld from './components/HelloWorld.vue'
import type { PersonArr } from './types';// Ts限制// const personList:PersonArr = reactive([const personList = reactive<PersonArr>([{id:"01",name:"張三",age:18},{id:"02",name:"李四",age:19,phone:"13888888888"}])</script>
<style scoped>
</style>

/src/components/HellowWorld.vue

<template><div><ul><li v-for="item in pl" :key="item.id">{{ item.id }} -- {{ item.name }}</li></ul></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { type PersonArr } from '@/types';//只接收pl(接收后頁面可使用,但js不能用)
// defineProps(['pl'])//接收pl+限制類型
// defineProps<{pl:PersonArr}>()//接收pl+限制類型+限制必要性+指定默認值
withDefaults(defineProps<{pl?:PersonArr}>(),{pl:()=>[{id:"03",name:"王五",age:18}]
})//接收后頁面可使用,js也要用
// const res = defineProps(['pl'])
// console.log(res.pl)</script>
<style scoped>
</style>

3.14.生命周期

  • 概念:Vue組件實例在創(chuàng)建時要經(jīng)歷一系例的祿始化步驟,在此過程中Vue會在合適的時機,調(diào)味用特定的函數(shù),從而讓開發(fā)者有機會在特定階段運行自己的代碼,這些特定的函數(shù)統(tǒng)稱為:生命周期鉤子

  • 規(guī)律: 生命周期整體分為四個階段,分別是:創(chuàng)建、掛載、更新、銷毀,第個階段都有兩個鉤子,一前一后。

  • Vue2的生命周期

     創(chuàng)建階段:beforeCreate、created掛載階段:beforeMount、mounted更新階段:beforeUpdate、updated銷毀階段:beforeDestory、destroyed
    
  • Vue3的生命周期

     創(chuàng)建階段:setup掛載階段:onBeforeMount、onMounted更新階段:onBeforeUpdate、onUpdated卸載階段:onBeforeUnmount
    
<!-- /src/App.vue -->
<template><HelloWorld ref="hw" v-if="showHelloWorld"/><button @click="changeHw">卸載HelloWorld</button>
</template>
<script lang="ts" setup name="App">import { ref } from "vue"import HelloWorld from './components/HelloWorld.vue'const showHelloWorld = ref(true)function changeHw(){showHelloWorld.value = !showHelloWorld.value}
</script>
<style scoped>
</style>

<!-- /src/components/HelloWorld.vue -->
<template><div><h1>{{ sum }}</h1><button @click="changeSum">更新</button></div>
</template>
<script lang="ts" setup name="HelloWorld">
import { ref,onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted } from 'vue';
onBeforeMount(()=>{console.log("掛載前")
})
onMounted(()=>{console.log("掛載后")
})
onBeforeUpdate(()=>{console.log("更新前")
})
onBeforeUpdate(()=>{console.log("更新后")
})
onBeforeUnmount(()=>{console.log("卸載前")
})
onUnmounted(()=>{console.log("卸載后")
})
const sum = ref(0)
function changeSum(){sum.value++
}
</script>
<style scoped>
</style>

3.15.自定義hook

  • 本質(zhì)是一個函數(shù),把setup函數(shù)使用的 Composition API 進行封裝,類似于 vue2.x 中的mixin
  • 優(yōu)勢:復(fù)用代碼,讓setup中邏輯更清楚易懂。

/src/components/HelloWorld.vue

<template><div><h1>{{ sum }}</h1><button @click="changeSum">sum++</button><br><img v-for="(item,index) in imgArr" :key="index" :src="item"></img><button @click="getImg">加載一張圖片</button></div>
</template>
<script lang="ts" setup name="HelloWorld">import useSum from '@/hooks/useSum';import useDog from '@/hooks/useDog';const {sum,changeSum} = useSum()const {imgArr,getImg} = useDog()
</script>
<style scoped>
</style>

/src/hooks/useDog.ts

<template><HelloWorld/>
</template>
<script lang="ts" setup name="App">import HelloWorld from './components/HelloWorld.vue'
</script>
<style scoped>
</style>

/src/hooks/useSum.ts

import {ref} from 'vue'
export default function(){const sum = ref(0)function changeSum(){sum.value++}return {sum,changeSum}
}

4.路由

4.1. 對路由的理解

4.2.基本切換效果

Vue3 中要使用 vue-router 的最新版本

/src/router/index.ts

// 創(chuàng)建一個路由器,并暴露出去
// 第一步:引入crateRouter
import {createRouter,createWebHashHistory} from 'vue-router'
// 引入組件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
// 第二步:創(chuàng)建路由器
const router = createRouter({history:createWebHashHistory(),//路由器工作模式routes:[{path:'/home',component:Home},{path:'/news',component:News},{path:'/about',component:About},]
})
export default router

/src/main.ts

import './assets/main.css'
// 引入createApp用于創(chuàng)建應(yīng)用
import { createApp } from 'vue'
// 引入App根組件
import App from './App.vue'
// 引入路由器
import router from './router'
// 創(chuàng)建一個應(yīng)用
const app = createApp(App)
// 使用路由器
app.use(router)
// 掛載整個應(yīng)用到app容器中
app.mount('#app')

/src/App.vue

<template><div class="app"><h1>vue3路由測試</h1><!-- 導(dǎo)航區(qū) --><div class="navigate"><RouterLink to="/home" active-class="aClass">首頁</RouterLink><RouterLink to="/news" active-class="aClass">新聞</RouterLink><RouterLink to="/about" active-class="aClass">關(guān)于</RouterLink></div><!-- 展示區(qū) --><div class="main-content"><RouterView></RouterView></div></div>
</template>
<script lang="ts" setup name="App">import { RouterView,RouterLink } from 'vue-router';
</script>
<style scoped>.aClass{color: red;}
</style>

/src/components/Home.vue

<template><div><h2>首頁</h2></div>
</template>
<script lang="ts" name="Home">
</script>
<style scoped></style>

/src/components/News.vue

<template><div><h2>新聞</h2></div>
</template>
<script lang="ts" name="News">
</script>
<style scoped></style>

/src/components/About.vue

<template><div><h2>關(guān)于</h2></div>
</template>
<script lang="ts" name="About">
</script>
<style scoped></style>

4.3.兩個注意點

  1. 路由組件通常放在pages 或 views 文件夾,一般組件通常放在components 文件夾。
  2. 通過點擊導(dǎo)航,視覺效果上“消失”了路由組件,默認是被銷毀掉的,需要的時候再去掛載。

4.4.路由器工作模式

  1. history 模式
    優(yōu)點:URL更加美觀,不帶有#,更接近傳的網(wǎng)站URL。
    缺點:后期項目上線,需要服務(wù)端配合處理路徑問題,否則刷新會有404錯誤
const router = createRouter({history:createWebHistory(),//history模式/******/
})
  1. hash 模式
    優(yōu)點:兼容性更好,因為不需要服務(wù)器端處理路徑
    缺點:URL帶有#不太美觀,且在SEO優(yōu)化方面相對較差
const router = createRouter({history:createWebHashHistory(),//hash模式/*******/
})

4.5.命名路由

作用:可以簡化路由跳轉(zhuǎn)及傳參
給路由規(guī)則命名:

routes:[{name:'home',path:'/home',component:Home},{name:'news',path:'/news',component:News},{name:'about',path:'/about',component:About},
]

4.6.to的兩種寫法

<!-- 1.to的字符串寫法 -->
<router-link active-class="active" to="/home">主頁</router-link>
<!-- 2.to的對象寫法 -->
<router-link active-class="active" :to="{path:'/home'}">主頁</router-link>
<router-link active-class="active" :to="{name:'home '}">主頁</router-link>

4.7.嵌套路由

routes:[{name:'home',path:'/home',component:Home},{name:'news',path:'/news',component:News,children:[ //嵌套路由{path:"detail",component:Detail}]},{name:'about',path:'/about',component:About},
]

4.8.路由傳參

query傳參

  1. 路由設(shè)置
routes:[{name:'home',path:'/home',component:Home},{name:'news',path:'/news',component:News,children:[ //嵌套路由{path:"detail",component:Detail}]},{name:'about',path:'/about',component:About},
]
  1. 傳遞參數(shù)
<RouterLink 
to="'/news/detail?id=${item.id}&title=${item.title}&detail=${item.detial}'">{{ item.title }}</RouterLink>//name:'detail',//用name也可以跳轉(zhuǎn)
path:'/news/detail',
query:{id:item.id,title:item.title,detail:item.detail}
}">{{ item.title }}</RouterLink>
<RouterLink 
:to="{
//name:'detail',//用name也可以跳轉(zhuǎn)
path:'/news/detail',
query:{id:item.id,title:item.title,detail:item.detail}
}">{{ item.title }}</RouterLink>
  1. 接收參數(shù)
import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute()
const { query } = toRefs(route)

params傳參

  1. 路由設(shè)置
routes:[{name:'home',path:'/home',component:Home},{name:'news',path:'/news',component:News,children:[{name:"newsDetail",//params傳參,必須使用namepath:"detail/:id/:title/:detail?",//占用,?表示該參數(shù)可選component:Detail}]},{name:'about',path:'/about',component:About},
]
  1. 傳遞參數(shù)
<RouterLink :to="`/news/detail/${item.id}/${item.title}/${item.detail}`">{{ item.title }}</RouterLink>
<RouterLink :to="{name:'newsDetail',params:{id:item.id,title:item.title,detail:item.detail}}">{{ item.title }}</RouterLink>
  1. 接收參數(shù)
import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute()
const { params } = toRefs(route)

4.9.路由的 props配置

作用:讓路由組件更方便的收到參數(shù)(可以將路由參數(shù)作為props傳給組件)

  1. 路由設(shè)置
{name:'news',path:'/news',component:News,children:[{name:"newsDetail",path:'detail/:id/:title/:detail',component:Detail,//props的布爾值寫法,把收到的params參數(shù),都作為props傳給組件props:true//props的函數(shù)寫法,把返回的對象中的每一組key-value作為props傳給組件// props(route){//   return route.query// }//props的對象寫法,把對象中的每一組key-value作為props傳給組件// props:{id:"01",title:"props標(biāo)題",detail:"props內(nèi)容"}}]},
  1. 參數(shù)接收
defineProps(['id','title','detail'])

4.10.replace屬性

  1. 作用:按制路由跳轉(zhuǎn)時操作瀏覽器歷史記錄的模式
  2. 瀏覽器的歷史記錄有兩種寫入方式:分別為push 和 replace:
    • push 是追加歷史記錄(默認值)
    • replace 是替換當(dāng)前記錄。
  3. 開啟 replace 模式:
<RouterLink replace :to="{path:'/news/detail'}">News</RouterLink>

4.11.編程式導(dǎo)航

路由組件的兩個重要的屬性:$route 和 $router 變成了兩個 hooks

import {useRoute,useRouter} from 'vue-router'
const route = useRoute()
const router = useRouter()
console.log(route.query)
console.log(route.params)
console.log(router.push)
console.log(router.replace)
import { useRouter } from 'vue-router';interface detailInter {id:string,title:string,detail:string
}
const router = useRouter();
function toPage(detail:detailInter){router.push({name:"newsDetail",params:{id:detail.id,title:detail.title,detail:detail.detail}})
})

4.12.重定向

routes:[{name:'home',path:'/home',component:Home},{name:'news',path:'/news',component:News,children:[{name:"newsDetail",path:'detail/:id/:title/:detail',component:Detail,props:true}]},{name:'about',path:'/about',component:About},{path:'/',//重定向redirect:'/home'}
]

5.pinia

5.1.準(zhǔn)備案例代碼

src/App.vue

<template><div class="app"><h1>APP</h1><Count/><News/></div>
</template>
<script lang="ts" setup name="App">
import Count from './components/Count.vue';
import News from './components/News.vue';
</script>
<style scoped>.aClass{color: red;}
</style>

src/components/Count.vue

<template><div><h2>求和:{{ sum }}</h2><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="add"></button><button @click="minus"></button></div>
</template>
<script lang="ts" name="Count" setup>
import { ref } from 'vue';
//數(shù)據(jù)
const sum = ref(1)//當(dāng)前求和
const n = ref(1)//選擇的數(shù)字
//方法
function add(){sum.value = sum.value + n.value
}
function minus(){sum.value = sum.value - n.value
}
</script>
<style scoped>
</style>

src/components/News.vue

<template><div><button @click="getNews">獲取一條新聞</button><ul><li v-for="news in newsList" :key="news.id">{{ news.title }}</li></ul></div>
</template>
<script lang="ts" name="News" setup>
import axios from 'axios';
import { reactive } from 'vue';
import {nanoid} from 'nanoid'
// 數(shù)據(jù)
const newsList = reactive([{id:'01',title:'新聞1'},{id:'02',title:'新聞2'},{id:'03',title:'新聞3'},
])async function getNews() {//連續(xù)解構(gòu),然后給解構(gòu)出來的content命名為title// const {data:{content:title}} = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json")// const obj = {id:nanoid(),title}const obj = {id:nanoid(),title:nanoid()}newsList.unshift(obj)
}
</script>
<style scoped>
</style>

5.2.搭建 pinia 環(huán)境

安裝:npm install pinia
src/main.ts

// 引入createApp用于創(chuàng)建應(yīng)用
import { createApp } from 'vue'
// 引入App根組件
import App from './App.vue'
// 創(chuàng)建一個應(yīng)用
const app = createApp(App)
// 1.引入pinia
import { createPinia } from 'pinia'
// 2.創(chuàng)建pinia
const pinia = createPinia()
// 3.安裝pinia
app.use(pinia)
// 掛載整個應(yīng)用到app容器中
app.mount('#app')

5.3.存儲+讀取數(shù)據(jù)

  1. Store 是一個保存:狀態(tài)、業(yè)務(wù)邏輯 的實體,每個組件都可以讀取、寫入它。
  2. 它有三個概念:state、getter、action,相當(dāng)于組件中的:data、computed、methods
  3. 具體代碼:
    src/store/count.ts
//引入defineStore用于創(chuàng)建store
import {defineStore} from 'pinia'
//定義并暴露一個store
export const useCountStore = defineStore('count',{//狀態(tài):真正存儲數(shù)據(jù)的地方state(){return {sum:6}},//動作actions:{},//計算getters:{}
})

src/store/news.ts

import {defineStore} from 'pinia'
export const useNewsStore = defineStore('news',{// 真正存儲數(shù)據(jù)的地方state(){return {newsList:[{id:'01',title:'新聞1'},{id:'02',title:'新聞2'},{id:'03',title:'新聞3'},  ]}}
})

src/components/Count.vue

//讀取
import { useCountStore } from "@/store/count"
const countStore = useCountStore() 
console.log(countStore.sum)

5.4.修改數(shù)據(jù)(三種方式)

  1. 直接修改
countStore.sum = 9  
  1. 批量修改
countStore.$patch({sum:999,school:'學(xué)校'
})
  1. 借助 action 修改(action 中可以編寫一些業(yè)務(wù)邏緝)
    src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{actions:{//記法increment(value:number){//操作countStore中的值 this.sum += value}}
})
countStore.increment(3 )

5.5.storeToRefs

  1. 借助 storeToRefs 將 store 中的數(shù)據(jù)轉(zhuǎn)為 ref 對象,方便在模板中使用。
  2. 注意:pinia 提供的 storeToRefs 只會將數(shù)據(jù)轉(zhuǎn)換,而 toRefs 會將所有東西轉(zhuǎn)換
// storeTorefs 只會對 store 中數(shù)據(jù),進行ref包裹
const countStore = useCountStore()
const {sum} = storeToRefs(countStore)

5.6.getters

概念:當(dāng)state中的數(shù)據(jù),需要經(jīng)過處理后再使用,可以使用getters配置。

//引入defineStore用于創(chuàng)建store
import {defineStore} from 'pinia'
//定義并暴露一個store
export const useCountStore = defineStore('count',{//狀態(tài):真正存儲數(shù)據(jù)的地方state(){return {sum:6,}},//動作actions:{},//計算getters:{//方式1minSum(state){return state.sum /10},//方式2bigSum:state=>state.sum * 10,//方式3//:number 返回的是number類型 addSum():number{return this.sum+10}}
})
const {sum,minSum,bigSum,addSum} = storeToRefs(countStore)

5.7.$subscribe

通過 store 的 $subscribe() 方法偵聽 state 及其變化

talkStore.$subscribe((mutate,state)=>{console.log('LoveTalk',mutate.state)localStorage.setItem('talk',JSON.stringify(talkList.value))
})

5.8.store組合式寫法

export const useCountStore = defineStore('count',()=>{const sum = ref(6)function add(){sum.value++}return {sum,add}
})

6.組件通信

Vue3 中移出了事件總線,可以使用pubsub代替。

  • vuex換成了pinia
  • 把 .sync 優(yōu)化到了 v-model 里面了
  • 把 $listeners 所有的東西,合并到 $attrs 中

常見搭配形式:

組件關(guān)系傳遞方式
父傳子1.props;2.v-model;3.$refs;4.默認插槽、具名插槽
子傳父1.props;2.自定義事件;3.v-model;4.$parent;5.作用域插槽
祖?zhèn)鲗O1.$attrs;2.provide、injetc
兄弟間、任意組件間1.mitt,2.pinia

6.1.props

概述:props是使用頻率最高的一種通方信方式,常用與:父<—>子
父傳子:屬性值是非函數(shù)
子傳父:屬性值是函數(shù)
父組件:src/pages/props/Father.vue

<template><div class="father"><h3>父組件</h3><h4>汽車:{{ car }}</h4><h4>子給的玩具:{{ toy }}</h4><Child :car="car" :sendToy="getToy"/></div>
</template>
<script setup lang="ts" name="Father">
import { ref } from 'vue';
import Child from '../Child.vue';
//數(shù)據(jù)
const car = ref("吉利")
const toy = ref("")
//方法
function getToy(value:string){toy.value = value
}
</script>
<style scoped>
</style>

子組件:src/pages/props/Child.vue

<!-- eslint-disable vue/multi-word-component-names -->
<template><div class="child"><h3>子組件</h3><h4>玩具:{{ toy }}</h4><h4>父給的車:{{ car }}</h4><button @click="sendToy(toy)">把玩具給父親</button></div>
</template>
<script setup lang="ts" name="Child">
import {ref} from 'vue'
//數(shù)據(jù)
const toy = ref("小汽車") 
//聲明接收props
defineProps(['car','sendToy'])
</script>
<style scoped>
</style>

6.2.自定義事件

父組件:src/customEvent/Father.vue

<template><div class="father"><h3>父組件</h3><h4>汽車:{{ car }}</h4><button @click="changeCar1">點我1</button><br><button @click="changeCar2(1,$event,2)">點我2</button><br><button @click="car = 'BYD'">點我3</button><br><!--也可以直接使用$event--><button @click="car = $event.toString()">點我3</button><br><h4>子給的玩具:{{ toy }}</h4><Child @send-toy="saveToy"/></div>
</template>
<script setup lang="ts" name="Father">
import { ref } from 'vue';
import Child from './Child.vue';
const car = ref('吉利')
const toy = ref('')
//調(diào)用方法不傳參,默認可以接收事件event
function changeCar1(x:Event){console.log(x)
}
//調(diào)用方法可以用 $event 占用
function changeCar2(a:number,event:Event,b:number){console.log(a,event,b)
}function saveToy(value:string){toy.value = value
}
</script>
<style scoped>
</style>

子組件:src/customEvent/Child.vue

<template><div class="child"><h2>子組件</h2><button @click="emit('send-toy',toy)">傳給父親</button></div>
</template>
<script setup lang="ts" name="Child">import {ref} from 'vue'const toy = ref('小汽車')const emit = defineEmits(['send-toy'])
</script>
<style scoped>
</style>

6.3.mitt

mitt簡單方法

//引入mitt
import mitt from "mitt";
//調(diào)用mitt得到emitter,emitter能綁定事件、觸發(fā)事件
const emitter = mitt()
//綁定事件
emitter.on('test1',()=>{console.log(3333)
})
setTimeout(()=>{//觸發(fā)事件emitter.emit('test1')
},2000)
//解綁事件
emitter.off('test1')
//全部解綁
emitter.all.clear()
//暴露emitter
export default emitter

案例
父組件:src/pages/mitt/Father.vue

<template><div class="father"><Child1/><Child2/></div>
</template>
<script setup lang="ts" name="Father">
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';
</script>
<style scoped>
</style>

子組件1:src/pages/mitt/Child1.vue

<template><div class="child1"><h3>子組件1</h3><h4>玩具:{{ toy }}</h4><button @click="emitter.emit('send-toy',toy)">給子組件2傳數(shù)據(jù)</button></div>
</template>
<script setup lang="ts" name="Child1">
import emitter from '@/utils/emitter';
import {ref} from 'vue'
const toy = ref('小汽車1')
</script>
<style scoped>
</style>

子組件2:src/pages/mitt/Child2.vue

<template><div class="child2"><h3>子組件2</h3><h4>child1給的玩具:{{ toy }}</h4></div>
</template>
<script setup lang="ts" name="Child2">
import {ref,onUnmounted} from 'vue'
import emitter from '@/utils/emitter';
const toy = ref('')
//給emitter綁定send-toy事件
emitter.on('send-toy',(value:string)=>{toy.value = value
})
//在組件卸載時解綁send-toy事件,減少內(nèi)存占用
onUnmounted(()=>{emitter.off('send-toy')
})
</script>
<style scoped>
</style>

6.4.v-model

父組件:src/pages/v-model/Father.vue

<template><div class="father"><h3>父組件</h3><!-- v-model用在html標(biāo)簽上:雙向綁定 --><input type="text" v-model="username"><br><!-- 底層原理:(<HTMLInputElement>$event.target) 斷言這是html元素,不會是null,防止TS紅--><input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value"><br><!-- v-model用在組件標(biāo)簽上 --><!-- <Diyinput v-model="username"/> --><!-- 底層原理 --><Diyinput:modelValue="username"@update:modelValue="username = $event"/><br><!-- v-model重新命名 --><DiyinputTwov-model:modelAccount="account"v-model:modelPassword="password"/><br></div>
</template>
<script setup lang="ts" name="Father">import {ref} from 'vue'import Diyinput from './Diyinput.vue';import DiyinputTwo from './DiyinputTwo.vue';const username = ref("張三")const account = ref("root")const password = ref("123456")
</script>
<style scoped>
</style>

子組件:src/pages/v-model/Diyinput.vue

<template><input type="text" :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)">
</template>
<script setup lang="ts" name="Diyinput">
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<style scoped>input{background-color: red;color: white;}
</style>

子組件:src/pages/v-model/DiyinputTwo.vue

<template><input type="text" :value="modelAccount" @input="emit('update:modelAccount',(<HTMLInputElement>$event.target).value)"><input type="text" :value="modelPassword" @input="emit('update:modelPassword',(<HTMLInputElement>$event.target).value)">
</template>
<script setup lang="ts" name="Diyinput">
defineProps(['modelAccount','modelPassword'])
const emit = defineEmits(['update:modelAccount','update:modelPassword'])
</script>
<style scoped>input{background-color: red;color: white;}
</style>

6.5.$attrs

  • 概述: $attrs 用于實現(xiàn)當(dāng)前組件的父組件,向當(dāng)前組件的子組件通信(祖—>孫
  • 說明:$atttrs 是一個對象,包含所有父組件傳入的標(biāo)簽屬性
注意:$attrs 會自動排除props中聲明的屬性(可以認為聲明的props被子組件自己“消費”了)

父組件:src/pages/Father.vue

<template><div class="father"><h3>父組件</h3><h4>b:{{ b }}</h4><!-- v-bind="{x:4,y:5}" 相當(dāng)于 :x="4" :y="5" --><Child :a="a" :b="b" v-bind="{x:4,y:5}" :updateB="updateB"/></div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import {ref} from 'vue'
const a = ref(1)
const b = ref(2)
function updateB(value:number){b.value += value
}
</script>
<style scoped>
div{border: 2px solid red;padding:10px;
}
</style>

子組件:src/pages/Child.vue

<template><div class="child"><h3>子組件</h3><!-- <h4>a:{{ a }}</h4> --><h4>其它:{{ $attrs }}</h4><GrandChild v-bind="$attrs"/></div>
</template>
<script setup lang="ts" name="Child">
import GrandChild from './GrandChild.vue';
defineProps(['a'])
</script>
<style scoped>
div{border: 2px solid red;padding:10px;
}
</style>

孫組件:src/pages/GrandChild.vue

<template><div class="grand-child"><h3>孫組件</h3><h4>a:{{ a }},a被子組件props消費掉了</h4><h4>b:{{ b }}</h4><h4>x:{{ x }}</h4><h4>y:{{ y }}</h4><button @click="updateB(2)">增加b</button></div>
</template>
<script setup lang="ts" name="GrandChild">
defineProps(['a','b','x','y','updateB'])
</script>
<style scoped>
div{border: 2px solid red;padding:10px;
}
</style>

6.6.$ refs、$parent

  1. 概述
    • $refs 用于:父—>子
    • $parent 用于:子—父
  2. 原理
屬性說明
$refs值為對象,包含所有被ref屬性標(biāo)識的DOM元素或組件實例
$parent值為對象,當(dāng)前組件的父組件實例對象
父組件:src/pages/refs-parent/Father.vue
<template><div class="father"><h3>父組件</h3><h4>資產(chǎn):{{ num }} 萬元</h4><button @click="editToy()">修改Child1_toy</button><br><button @click="editComputer()">修改Child2_computer</button><br><button @click="addBook($refs)">增加所有子組件的book</button><br><Child1 ref="c1"/><Child2 ref="c2"/></div>
</template>
<script setup lang="ts" name="Father">
import Child1 from './Child1.vue'
import Child2 from './Child2.vue'
import { ref } from 'vue'
const c1 = ref()
const c2 = ref()
const num = ref(4)
function editToy(){c1.value.toy = "挖掘機"
}
function editComputer(){c2.value.computer = "聯(lián)想"
}
function addBook(refs:{[key:string]:any}){for(const key in refs){refs[key].book +=3}
}
defineExpose({num})
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

子組件1:src/pages/refs-parent/Child1.vue

<template><div class="child1"><h3>子組件1</h3><h4>玩具:{{ toy }}</h4><h4>書本:{{ book }} 本</h4><button @click="minus($parent)">減少父親num</button></div>
</template>
<script setup lang="ts" name="Child1">
import { ref } from 'vue'
const toy = ref('小汽車')
const book = ref(4)
function minus(parent: { num: number}){parent.num --
}
//把數(shù)據(jù)交給外部
defineExpose({toy,book})
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

子組件2:src/pages/refs-parents/Child2.vue

<template><div class="child2"><h3>子組件2</h3><h4>電腦:{{ computer }}</h4><h4>書本:{{ book }} 本</h4></div>
</template>
<script setup lang="ts" name="Child2">
import { ref } from 'vue'
const computer = ref("小米")
const book = ref(5)
//把數(shù)據(jù)交給外部
defineExpose({computer,book})
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

6.7.provide、inject

  1. 概述:實現(xiàn)組孫組件直接通信
  2. 具體使用:
    • 在祖先組件中通過 provide 配置向后代組件提供數(shù)據(jù)
    • 在后代組件中通過 inject 配置來聲明接收數(shù)據(jù)

父組件:src/pages/provice-inject/Father.vue

<!-- eslint-disable vue/multi-word-component-names -->
<template><div class="father"><h3>父組件</h3><h4>錢:{{ money }}</h4><h4>車:{{ car }}</h4><Child/></div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { provide, reactive, ref } from 'vue'
const money = ref(100)
const car = reactive({brand:"BYD",price:20
})
function updateMoney(value:number){money.value -= value
}
//向后代提供數(shù)據(jù)
provide('moneyObject',{money,updateMoney})
provide('car',car)
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

子組件:src/pages/provide-inject/Child.vue

<template><div class="child"><h3>子組件</h3><GrandChild/></div>
</template>
<script setup lang="ts" name="Child">
import GrandChild from './GrandChild.vue';
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

孫組件:src/pages/provide-inject/Child.vue

<template><div class="child"><h3>孫組件</h3><h4>父的錢:{{ money }}</h4><h4>父的車:{{ car }}</h4><button @click="updateMoney(6)">花錢</button></div>
</template>
<script setup lang="ts" name="GrandChild">
import { inject } from 'vue';const {money,updateMoney} = inject('moneyObject',{money:0,updateMoney:(x: number)=>{}})
const car = inject('car')
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

6.8.slot

  • 默認插槽
  • 具名插槽
  • 作用域插槽

父組件:src/pages/slot/Father.vue

<template><div class="father"><h2>父組件</h2><div><!-- 默認插槽 --><Category><h3>默認插槽</h3></Category><!-- 具名插槽 --><Category1><template v-slot:slotOne><h3>具名插槽</h3></template></Category1><Category2><template v-slot="params"><h3>作用域插槽</h3><h4>{{ params.title }}</h4><h4>{{ params.games }}</h4></template></Category2></div></div>
</template>
<script setup lang="ts" name="Father">
import Category from './Category.vue'
import Category2 from './Category2.vue'
import Category1 from './Category1.vue'</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

子組件:src/pages/slot/Category.vue

<template><div class="category"><!-- 默認插槽 --><slot></slot><!-- 默認插槽 實際上是 <slot name="default"></slot> --></div>
</template>
<script setup lang="ts" name="Category">
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

子組件:src/pages/slot/Categroy1.vue

<template><div class="category1"><!-- 具名插槽 --><slot name="slotOne"></slot></div>
</template>
<script setup lang="ts" name="Category1">
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

子組件:src/pages/slot/Category2.vue

<!-- eslint-disable vue/multi-word-component-names -->
<template><div class="category2"><!-- 作用域插槽 --><slot :games="games" :title="title"></slot></div>
</template>
<script setup lang="ts" name="Category2">
import {ref,reactive} from 'vue'
const games = reactive(["游戲1","游戲2"
])
const title = ref("游戲標(biāo)題")
</script>
<style scoped>
div{border: 2px solid red;padding:10px;margin: 10px;
}
</style>

7.其它API

7.1. shallowRef 與 shallowReactive

shallow:淺的

shallowRef

  1. 作用:創(chuàng)建一個響應(yīng)式數(shù)據(jù),但只能頂層屬性進行響應(yīng)式處理。
  2. 用法
let myVar = shallowRef(initialValue);
  1. 特點:只跟蹤引用值的變化,不關(guān)心內(nèi)部的屬性變化。

shallowReactive

  1. 作用:創(chuàng)建一個淺層響應(yīng)式對象,只會使對象的最頂層屬性變成響應(yīng)式的,對象內(nèi)部的嵌套屬則不會變成響應(yīng)式折
  2. 用法:
const myObj = shallowReactive({....})
  1. 特點:對象的頂層屬性是響應(yīng)式的,但嵌套對象屬性不是。

總結(jié)

通過使用 shallowRef() 和 shallowReactive() 來繞開深度響應(yīng)。淺層式API創(chuàng)建的狀態(tài)只在其頂層是響應(yīng)式的,對所有深層的對象不會做出任何處理,避免對每一個內(nèi)部屬性做響應(yīng)式所帶的性能成功,這使得屬性的訪問變提更快,可提升性能。

7.2.readonly 與 shallowReadonly

readonly

  1. 作用:用于創(chuàng)建一個對象的深只讀副本。
  2. 用法:
const original = reactive({......});
const readOnlyCopy = readonly(original);
  1. 特點:
    • 對象的所有嵌套屬性都將變成只讀。
    • 任何嘗試修改這個對象的操作都會被阻止(在開發(fā)模式下,還會在控制臺發(fā)出警告)
  2. 應(yīng)用場景:
    • 創(chuàng)建不可變的狀態(tài)快照。
    • 保護全局狀態(tài)或配置不被修改。

shallowReadonly

  1. 作用:與 readonly 類似,但只作用于對象的頂屬屬性。
  2. 用法:
const original = reactive({...});
const shallowReadOnlyCopy = shallowReadonly(original)
  1. 特點:
    • 只將對象的頂層屬性設(shè)置為只讀,對象內(nèi)部的嵌套屬性仍然是可變的。
    • 適用于只需保護對象頂層屬性的場景。

7.3.toRaw 與 markRaw

raw:未經(jīng)加工的

toRaw

  1. 作用:用于獲取一個響應(yīng)式對象的原始對象,toRaw 返回的對象不再是響應(yīng)式的,不會觸發(fā)視圖更新。
官網(wǎng)描述:這是一個可以用于臨時讀取而不引起代理訪問/跟蹤開銷,或是寫入而不觸 發(fā)更改的特方法。不建議保存對原始對象的持久引用,請謹(jǐn)慎使用。
使用時機:在需要將響應(yīng)式對象傳遞給非Vuer的庫或外部孫統(tǒng)時,使用toRaw 可以確保它們收到的是普通對角
  1. 編碼:
import { reactive,toRaw,markRaw,isReactive } from 'vue';/* toRaw */
//響應(yīng)式對象
let person = reactive({name:'tony',age:18})
//原始對象
let rawPerson = toRaw(person)

markRaw

  1. 作用:標(biāo)記一個對象,使其永遠不會變成響應(yīng)式的。
例如使用mockjs時,為了防止把mockjs變成響應(yīng)式對象,可以使用markRaw 去標(biāo)記 mockjs
  1. 編碼:
/* markRaw */
let citys = markRaw({{id:'01',name:'上海'},{id:'02',name:'北京'}
})
//根據(jù)原始對象citys去創(chuàng)建響應(yīng)式對象citys2 --創(chuàng)建失敗,因為citys被markRaw 標(biāo)記了
let city2 = reactive(citys)

7.4.customRef

作用:創(chuàng)建一個自定義的ref,并對其依賴項跟蹤和更新觸發(fā)進行邏輯控制。
實現(xiàn)防抖效果(useMsgRef.ts)

import {customRef} from 'vue';
export default function(initValue:string,delay:number){//track:跟蹤;trigger:觸發(fā)let msg = customRef((track,trigger)=>{let timer:numberreturn {get(){track()//告訴vue要對msg持續(xù)關(guān)注,一旦變化就更新return initValue},set(value){clearTimeout(timer)timer = setTimeout(()=>{initValue = valuetrigger()//通知Vue數(shù)據(jù)msg變化了},delay)}}})return {msg}
}
import {useMsgRef} from './useMsgRef'
let { msg } = useMsgRef('hellow',2000)

8.Vue3新組件

8.1.teleport

teleport:傳送
Teleport 是一種能夠?qū)⑽覀兊慕M件html結(jié)構(gòu)移動到指定位置的技術(shù)。

<!-- to表示該內(nèi)容顯示在body標(biāo)簽下,也可以使to="#app" 等 -->
<teleport to="body"><div class="model" v-show="isShow"><h2>這是一個彈窗</h2><p>我是彈窗中的一些內(nèi)容</p><button @click="isShow = false">關(guān)閉彈窗</button></div>
</teleport>

8.2.Suspense

suspense:懸念
fallback:退路;應(yīng)變計劃

  • 等待異步組件時渲染一些額外內(nèi)容,讓應(yīng)用有更好的用戶體驗
  • 使用步驟:
    • 異步引入組件
    • 使用 Suspense 包裹組件,并配置好 defalut 與 fallback
//?defineAsyncComponent 是Vue 3中用于定義異步組件的API,主要用于實現(xiàn)懶加載(Lazy Loading),即在實際需要時才加載組件,優(yōu)化頁面的性能和提升用戶體驗,尤其適用于大型應(yīng)用和單頁面應(yīng)用(SPA)?
import { defineAsyncComponent,Suspense } from 'vue'
const Child = defineAsyncComponent(()=>import('./Child.vue'))
<template><div class="app"><h3>App組件</h3><Suspense><template v-slot:default><Child/></template></Suspense><template v-slot:fallback><h3>加載中。。。</h3></template></div>
</template> 

8.3.全局API轉(zhuǎn)移到應(yīng)用對象

  1. app.component
    定義全局組件
import {crateApp} from 'vue'
import App from './App.vue'
import Hello form './hello.vue'
const app = createApp(App)
app.component('Hello',Hello)
app.mount('#app')
  1. app.config
    定義全局配置
import {crateApp} from 'vue'
import App from './App.vue'
const app = createApp(App)
//任何地方都可以使用x
app.config.globalProperties.x = 9
//避免全局使用x時,編輯器警告
declare module 'vue' {interface ComponentCustomProperties {x:number}
}
app.mount('#app')
  1. app.directive
    定義全局指令
import {crateApp} from 'vue'
import App from './App.vue'
const app = createApp(App)
app.directive('beauty',(element,{value})=>{element.innerText += valueelement.style.color = 'green'element.style.backgroundColor = 'yellow'
})
app.mount('#app')
<h4 v-beauty="1">hellow</h4>
  1. app.mount
    掛載應(yīng)用
import {crateApp} from 'vue'
import App from './App.vue'
const app = createApp(App)
//掛載應(yīng)用
app.mount('#app')
  1. app.unmount
    卸載應(yīng)用
import {crateApp} from 'vue'
import App from './App.vue'
const app = createApp(App)
//掛載應(yīng)用
app.mount('#app')
setTimeout(()=>{卸載應(yīng)用app.unmount()
},2000)
  1. app.use
    安裝插件
import {crateApp} from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
//使用路器
app.use(router)
app.mount('#app')
http://www.risenshineclean.com/news/30162.html

相關(guān)文章:

  • 1.網(wǎng)站開發(fā)的詳細流程網(wǎng)站優(yōu)化靠譜seo
  • 網(wǎng)站建設(shè)接單吧大數(shù)據(jù)營銷經(jīng)典案例
  • 公安部門網(wǎng)站建設(shè)方案百度一下百度
  • 小影wordpress主題廈門seo排名公司
  • 外貿(mào)網(wǎng)站響應(yīng)式百度推廣代理怎么加盟
  • 仿網(wǎng)站源碼相親網(wǎng)站排名前十名
  • 網(wǎng)站制作服務(wù)價格今天的重要新聞
  • 做淘寶客網(wǎng)站要注意什么免費建站免費推廣的網(wǎng)站
  • 個人建立網(wǎng)站后怎么盈利百度最新收錄方法
  • 淳安縣千島湖建設(shè)集團網(wǎng)站市場調(diào)研報告800字
  • 購物網(wǎng)站創(chuàng)建網(wǎng)址導(dǎo)航大全
  • php網(wǎng)站開發(fā)要學(xué)什么軟件培訓(xùn)學(xué)校招生方案范文
  • 網(wǎng)站建設(shè)所操作是什么怎樣做企業(yè)推廣
  • wordpress 獲取文章時間seo優(yōu)化培訓(xùn)機構(gòu)
  • 私活接單平臺windows優(yōu)化大師的優(yōu)點
  • 鄧州網(wǎng)站建設(shè)免費廣告推廣
  • h5響應(yīng)式企業(yè)網(wǎng)站源碼百度推廣客戶端下載
  • 創(chuàng)可貼網(wǎng)站怎么做圖片大全北京網(wǎng)站優(yōu)化
  • 佛山網(wǎng)站優(yōu)化公司做百度推廣代運營有用嗎
  • 包裝在線設(shè)計網(wǎng)站谷歌seo靠譜嗎
  • 哈爾濱網(wǎng)站開發(fā)培訓(xùn)seo推廣代運營
  • 王者榮耀網(wǎng)站建設(shè)的步驟怎么弄一個自己的網(wǎng)站
  • 上海做公益活動有哪些好的網(wǎng)站黑帽seo是什么意思
  • 公司網(wǎng)站域名做郵箱自己做網(wǎng)站設(shè)計制作
  • 網(wǎng)站建設(shè)情況的報告國外引流推廣軟件
  • 北京h5網(wǎng)站建設(shè)報價網(wǎng)絡(luò)推廣合作協(xié)議范本
  • html入門視頻教程鄭州seo推廣優(yōu)化
  • 廈門網(wǎng)站建設(shè)培訓(xùn)百度推廣開戶多少錢
  • 找人做網(wǎng)站需要注意什么跨境電商靠譜嗎
  • 做爰網(wǎng)站下載免費網(wǎng)頁制作模板