日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

TypeScript+Vue3

發(fā)布時(shí)間:2025/5/22 编程问答 20 如意码农
生活随笔 收集整理的這篇文章主要介紹了 TypeScript+Vue3 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

TypeScript

Any 類型 和 unknown 頂級(jí)類型

1.沒(méi)有強(qiáng)制限定哪種類型,隨時(shí)切換類型都可以 我們可以對(duì) any 進(jìn)行任何操作,不需要檢查類型

2.聲明變量的時(shí)候沒(méi)有指定任意類型默認(rèn)為any

3.弊端如果使用any 就失去了TS類型檢測(cè)的作用

4.TypeScript 3.0中引入的 unknown 類型也被認(rèn)為是 top type ,但它更安全。與 any 一樣,所有類型都可以分配給unknown

//unknown 可以定義任何類型的值
let value: unknown; value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = null; // OK
value = undefined; // OK
value = Symbol("type"); // OK //這樣寫(xiě)會(huì)報(bào)錯(cuò)unknow類型不能作為子類型只能作為父類型 any可以作為父類型和子類型
//unknown類型不能賦值給其他類型
let names:unknown = '123'
let names2:string = names //這樣就沒(méi)問(wèn)題 any類型是可以的
let names:any = '123'
let names2:string = names //unknown可賦值對(duì)象只有unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456' aaa = bbb

接口和對(duì)象類型

//這樣寫(xiě)是會(huì)報(bào)錯(cuò)的 因?yàn)槲覀冊(cè)趐erson定義了a,b但是對(duì)象里面缺少b屬性
//使用接口約束的時(shí)候不能多一個(gè)屬性也不能少一個(gè)屬性
//必須與接口保持一致
interface Person {
b:string,
a:string
}
const person:Person = {
a:"213"
}

對(duì)象的類型

在typescript中,我們定義對(duì)象的方式要用關(guān)鍵字interface(接口),我的理解是使用interface來(lái)定義一種約束,讓數(shù)據(jù)的結(jié)構(gòu)滿足約束的格式。定義方式如下:

//重名interface  可以合并
interface A{name:string}
interface A{age:number}
var x:A={name:'xx',age:20}
//繼承
interface A{
name:string
}
interface B extends A{
age:number
}
let obj:B = {
age:18,
name:"string"
}

可選屬性 使用?操作符

//可選屬性的含義是該屬性可以不存在
//所以說(shuō)這樣寫(xiě)也是沒(méi)問(wèn)題的
interface Person {
b?:string,
a:string
}
const person:Person = {
a:"213"
}

任意屬性 [propName: string]

//在這個(gè)例子當(dāng)中我們看到接口中并沒(méi)有定義C但是并沒(méi)有報(bào)錯(cuò)
//應(yīng)為我們定義了[propName: string]: any;
//允許添加新的任意屬性
interface Person {
b?:string,
a:string,
[propName: string]: any;
}
const person:Person = {
a:"213",
c:"123"
}

只讀屬性 readonly

//這樣寫(xiě)是會(huì)報(bào)錯(cuò)的
//應(yīng)為a是只讀的不允許重新賦值
interface Person {
b?: string,
readonly a: string,
[propName: string]: any;
}
const person: Person = {
a: "213",
c: "123"
}
person.a = 123

添加函數(shù)

interface Person {
b?: string,
readonly a: string,
[propName: string]: any;
cb():void
}
const person: Person = {
a: "213",
c: "123",
cb:()=>{
console.log(123)
}
}

數(shù)組類型

//類型加中括號(hào)
let arr:number[] = [123]
//這樣會(huì)報(bào)錯(cuò)定義了數(shù)字類型出現(xiàn)字符串是不允許的
let arr:number[] = [1,2,3,'1']
//操作方法添加也是不允許的
let arr:number[] = [1,2,3,]
arr.unshift('1') var arr: number[] = [1, 2, 3]; //數(shù)字類型的數(shù)組
var arr2: string[] = ["1", "2"]; //字符串類型的數(shù)組
var arr3: any[] = [1, "2", true]; //任意類型的數(shù)組

數(shù)組泛型

let arr:Array<number> = [1,2,3,4,5]

arguments類數(shù)組

function Arr(...args:any): void {
console.log(arguments)
//錯(cuò)誤的arguments 是類數(shù)組不能這樣定義
let arr:number[] = arguments
}
Arr(111, 222, 333) function Arr(...args:any): void {
console.log(arguments)
//ts內(nèi)置對(duì)象IArguments 定義
let arr:IArguments = arguments
}
Arr(111, 222, 333) //其中 IArguments 是 TypeScript 中定義好了的類型,它實(shí)際上就是:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}

函數(shù)的類型

//注意,參數(shù)不能多傳,也不能少傳 必須按照約定的類型來(lái)
const fn = (name: string, age:number): string => {
return name + age
}
fn('張三',18)

函數(shù)的可選參數(shù)?

//通過(guò)?表示該參數(shù)為可選參數(shù)
const fn = (name: string, age?:number): string => {
return name + age
}
fn('張三')

函數(shù)參數(shù)的默認(rèn)值


定義剩余參數(shù)

const fn = (array:number[],...items:any[]):any[] => {
console.log(array,items)
return items
} let a:number[] = [1,2,3] fn(a,'4','5','6')

函數(shù)重載

重載是方法名字相同,而參數(shù)不同,返回類型可以相同也可以不同。

如果參數(shù)類型不同,則參數(shù)類型應(yīng)設(shè)置為 any

參數(shù)數(shù)量不同你可以將不同的參數(shù)設(shè)置為可選。

function fn(params: number): void
function fn(params: string, params2: number): void
function fn(params: any, params2?: any): void {
console.log(params)
console.log(params2) } fn(123)
fn('123',456)

類型斷言 | 聯(lián)合類型 | 交叉類型

聯(lián)合類型

//例如我們的手機(jī)號(hào)通常是13XXXXXXX 為數(shù)字類型 這時(shí)候產(chǎn)品說(shuō)需要支持座機(jī)
//所以我們就可以使用聯(lián)合類型支持座機(jī)字符串
let myPhone: number | string = '010-820' //這樣寫(xiě)是會(huì)報(bào)錯(cuò)的應(yīng)為我們的聯(lián)合類型只有數(shù)字和字符串并沒(méi)有布爾值
let myPhone: number | string = true
const fn = (something:number | boolean):boolean => {
return !!something
}

交叉類型

interface People {
age: number,
height: number
}
interface Man{
sex: string
}
const xiaoman = (man: People & Man) => {
console.log(man.age)
console.log(man.height)
console.log(man.sex)
}
xiaoman({age: 18,height: 180,sex: 'male'});

類型斷言

語(yǔ)法:值 as 類型 或 <類型>值  value as string  <string>value
interface A {
run: string
} interface B {
build: string
} const fn = (type: A | B): string => {
return type.run
}
//這樣寫(xiě)是有警告的應(yīng)為B的接口上面是沒(méi)有定義run這個(gè)屬性的
interface A {
run: string
} interface B {
build: string
} const fn = (type: A | B): string => {
return (type as A).run
}
//可以使用類型斷言來(lái)推斷他傳入的是A接口的值

需要注意的是,類型斷言只能夠「欺騙」TypeScript 編譯器,無(wú)法避免運(yùn)行時(shí)的錯(cuò)誤,反而濫用類型斷言可能會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤:

使用any臨時(shí)斷言

window.abc = 123
//這樣寫(xiě)會(huì)報(bào)錯(cuò)因?yàn)閣indow沒(méi)有abc這個(gè)東西
(window as any).abc = 123
//可以使用any臨時(shí)斷言在 any 類型的變量上,訪問(wèn)任何屬性都是允許的。

as const

是對(duì)字面值的斷言,與const直接定義常量是有區(qū)別的

如果是普通類型跟直接const 聲明是一樣的

const names = '小滿'
names = 'aa' //無(wú)法修改 let names2 = '小滿' as const
names2 = 'aa' //無(wú)法修改
// 數(shù)組
let a1 = [10, 20] as const;
const a2 = [10, 20]; a1.unshift(30); // 錯(cuò)誤,此時(shí)已經(jīng)斷言字面量為[10, 20],數(shù)據(jù)無(wú)法做任何修改
a2.unshift(30); // 通過(guò),沒(méi)有修改指針
//unshift頭部插入

類型斷言是不具影響力的

function toBoolean(something: any): boolean {
return something as boolean;
} toBoolean(1);
// 返回值為 1
//

Class類

//定義類
class Person {
constructor () { }
run () { }
}

類的修飾符

總共有三個(gè) public private protected

使用public 修飾符 可以讓你定義的變量 內(nèi)部訪問(wèn) 也可以外部訪問(wèn) 如果不寫(xiě)默認(rèn)就是public

使用 protected 修飾符 代表定義的變量私有的只能在內(nèi)部和繼承的子類中訪問(wèn) 不能在外部訪問(wèn)

class Person {
public name:string
private age:number
protected some:any
constructor (name:string,ages:number,some:any) {
this.name = name
this.age = ages
this.some = some
}
run () { }
} class Man extends Person{
constructor () {
super("張三",18,1)
console.log(this.some)
}
create () {
console.log(this.some)
}
}
let xiaoman = new Person('小小',18,1)
let man = new Man()
man.some

ts interface 定義類 使用關(guān)鍵字 implements 后面跟interface的名字多個(gè)用逗號(hào)隔開(kāi) 繼承還是用extends


interface PersonClass {
get(type: boolean): boolean
} interface PersonClass2{
set():void,
asd:string
} class A {
name: string
constructor() {
this.name = "123"
}
} class Person extends A implements PersonClass,PersonClass2 {
asd: string
constructor() {
super()
this.asd = '123'
}
get(type:boolean) {
return type
}
set () { }
}

元組類型

如果需要一個(gè)固定大小的不同類型值的集合,我們需要使用元組。

元組就是數(shù)組的變種

元組(Tuple)是固定數(shù)量的不同類型的元素的組合。

元組與集合的不同之處在于,元組中的元素類型可以是不同的,而且數(shù)量固定。元組的好處在于可以把多個(gè)元素作為一個(gè)單元傳遞。如果一個(gè)方法需要返回多個(gè)值,可以把這多個(gè)值作為元組返回,而不需要?jiǎng)?chuàng)建額外的類來(lái)表示。

let excel: [string, string, number, string][] = [
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
]

枚舉類型

在javaScript中是沒(méi)有枚舉的概念的TS幫我們定義了枚舉這個(gè)類型(字典)

使用枚舉 通過(guò)enum關(guān)鍵字定義我們的枚舉

數(shù)字枚舉

例如 紅綠藍(lán) Red = 0 Green = 1 Blue= 2 分別代表紅色0 綠色為1 藍(lán)色為2

enum Types{
Red,
Green,
BLue
}

字符串枚舉

由于字符串枚舉沒(méi)有自增長(zhǎng)的行為,字符串枚舉可以很好的序列化。 換句話說(shuō),如果你正在調(diào)試并且必須要讀一個(gè)數(shù)字枚舉的運(yùn)行時(shí)的值,這個(gè)值通常是很難讀的 - 它并不能表達(dá)有用的信息,字符串枚舉允許你提供一個(gè)運(yùn)行時(shí)有意義的并且可讀的值,獨(dú)立于枚舉成員的名字。

enum Types{
Red = 'red',
Green = 'green',
BLue = 'blue'
}

異構(gòu)枚舉

enum Types{
No = "No",
Yes = 1,
}

反向映射

它包含了正向映射( name -> value)和反向映射( value -> name

要注意的是 不會(huì)為字符串枚舉成員生成反向映射。

enum Enum {
fall
}
let a = Enum.fall;
console.log(a); //0
let nameOfA = Enum[a];
console.log(nameOfA); //fall

類型別名

type str = string | number
let s:str = "我"
console.log(s);
type str = () => string
let s: str = () => "我"
console.log(s);
type value = boolean | 0 | '213'
let s:value = true
//變量s的值 只能是上面value定義的值

never類型

// 返回never的函數(shù)必須存在無(wú)法達(dá)到的終點(diǎn)

// 因?yàn)楸囟⊕伋霎惓?,所?error 將不會(huì)有返回值
function error(message: string): never {
throw new Error(message);
} // 因?yàn)榇嬖谒姥h(huán),所以 loop 將不會(huì)有返回值
function loop(): never {
while (true) {
}
}

never 與 void 的差異

//void類型只是沒(méi)有返回值 但本身不會(huì)出錯(cuò)
function Void():void {
console.log();
} //只會(huì)拋出異常沒(méi)有返回值
function Never():never {
throw new Error('aaa')
}

never 類型的一個(gè)應(yīng)用場(chǎng)景

interface A {
type: "foo"
} interface B {
type: "bar"
}
type All = A | B ;
function handleValue(val: All) {
switch (val.type) {
case 'foo':
break;
case 'bar':
break
default:
//兜底邏輯 一般是不會(huì)進(jìn)入這兒如果進(jìn)來(lái)了就是程序異常了
const exhaustiveCheck:never = val;
break
}
}

比如長(zhǎng)時(shí)間后新增了一個(gè)C接口,我們必須手動(dòng)找到所有 switch 代碼并處理,否則將有可能引入 BUG 。

而且這將是一個(gè)“隱蔽型”的BUG,如果回歸面不夠廣,很難發(fā)現(xiàn)此類BUG。

interface A {
type: "foo"
} interface B {
type: "bar"
}
interface C {
type: "bizz"
}
type All = A | B | C;
function handleValue(val: All) {
switch (val.type) {
case 'foo':
break;
case 'bar':
break
default:
//兜底邏輯 一般是不會(huì)進(jìn)入這兒如果進(jìn)來(lái)了就是程序異常了 const exhaustiveCheck: never = val;
break
}
}

由于任何類型都不能賦值給 never 類型的變量,所以當(dāng)存在進(jìn)入 default 分支的可能性時(shí),TS的類型檢查會(huì)及時(shí)幫我們發(fā)現(xiàn)這個(gè)問(wèn)題

symbol類型

自ECMAScript 2015起,symbol成為了一種新的原生類型,就像numberstring一樣。

symbol類型的值是通過(guò)Symbol構(gòu)造函數(shù)創(chuàng)建的。

可以傳遞參做為唯一標(biāo)識(shí) 只支持 string 和 number類型的參數(shù)

let sym1 = Symbol();
let sym2 = Symbol("key"); // 可選的字符串key

Symbol的值是唯一的

const s1 = Symbol()
const s2 = Symbol()
// s1 === s2 =>false

用作對(duì)象屬性的鍵

let sym = Symbol();

let obj = {
[sym]: "value"
}; console.log(obj[sym]); // "value"

Symbol.iterator 迭代器 和 生成器 for of

for of 可以讀到value

for in 讀key

var arr = [1,2,3,4];
let iterator = arr[Symbol.iterator](); console.log(iterator.next()); //{ value: 1, done: false }
console.log(iterator.next()); //{ value: 2, done: false }
console.log(iterator.next()); //{ value: 3, done: false }
console.log(iterator.next()); //{ value: 4, done: false }
console.log(iterator.next()); //{ value: undefined, done: true }
interface Item {
age: number,
name: string
} const array: Array<Item> = [{ age: 123, name: "1" }, { age: 123, name: "2" }, { age: 123, name: "3" }] type mapTypes = string | number
const map:Map<mapTypes,mapTypes> = new Map() map.set('1','王爺')
map.set('2','陸北') const obj = {
aaa:123,
bbb:456
} let set:Set<number> = new Set([1,2,3,4,5,6])
// let it:Iterator<Item> = array[Symbol.iterator]()
const gen = (erg:any): void => {
let it: Iterator<any> = erg[Symbol.iterator]()
let next:any= { done: false }
while (!next.done) {
next = it.next()
if (!next.done) {
console.log(next.value)
}
}
}
gen(array)

泛型

函數(shù)泛型

function num (a:number,b:number) : Array<number> {
return [a ,b];
}
num(1,2)
function str (a:string,b:string) : Array<string> {
return [a ,b];
}
str('獨(dú)孤','求敗')

泛型優(yōu)化

function Add<T>(a: T, b: T): Array<T>  {
return [a,b]
}
Add<number>(1,2)
Add<string>('1','2')

我們也可以使用不同的泛型參數(shù)名,只要在數(shù)量上和使用方式上能對(duì)應(yīng)上就可以。

function Sub<T,U>(a:T,b:U):Array<T|U> {
const params:Array<T|U> = [a,b]
return params
}
Sub<Boolean,number>(false,1)

定義泛型接口

聲明接口的時(shí)候 在名字后面加一個(gè)<參數(shù)>

使用的時(shí)候傳遞類型

interface MyInter<T> {
(arg: T): T
} function fn<T>(arg: T): T {
return arg
} let result: MyInter<number> = fn result(123)

泛型約束

我們期望在一個(gè)泛型的變量上面,獲取其length參數(shù),但是,有的數(shù)據(jù)類型是沒(méi)有length屬性的

function getLegnth<T>(arg:T) {
return arg.length
}

于是,我們就得對(duì)使用的泛型進(jìn)行約束,我們約束其為具有length屬性的類型,這里我們會(huì)用到interface,代碼如下

interface Len {
length:number
} function getLegnth<T extends Len>(arg:T) {
return arg.length
} getLegnth<string>('123')

使用keyof 約束對(duì)象

其中使用了TS泛型和泛型約束。首先定義了T類型并使用extends關(guān)鍵字繼承object類型的子類型,然后使用keyof操作符獲取T類型的所有鍵,它的返回 類型是聯(lián)合 類型,最后利用extends關(guān)鍵字約束 K類型必須為keyof T聯(lián)合類型的子類型

function prop<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
} let o = { a: 1, b: 2, c: 3 } prop(o, 'a')
prop(o, 'd') //此時(shí)就會(huì)報(bào)錯(cuò)發(fā)現(xiàn)找不到

泛型類

聲明方法跟函數(shù)類似名稱后面定義<類型>

使用的時(shí)候確定類型new Sub()

class Sub<T>{
attr: T[] = [];
add (a:T):T[] {
return [a]
}
} let s = new Sub<number>()
s.attr = [1,2,3]
s.add(123) let str = new Sub<string>()
str.attr = ['1','2','3']
str.add('123')

Vue3

第一章、簡(jiǎn)單介紹

回顧vue2 對(duì)比 vue3

? 發(fā)現(xiàn)傳統(tǒng)的vue2 邏輯比較分散 可讀性差 可維護(hù)性差

? 對(duì)比vue3 邏輯分明 可維護(hù)性 高

Vue3 新特性介紹:

重寫(xiě)雙向綁定

Vue3 優(yōu)化Vdomvue3

允許我們支持多個(gè)根節(jié)點(diǎn)

第二章、配置環(huán)境

構(gòu)建vite項(xiàng)目

npm init vite@latest

npm install 安裝依賴包

npm run dev 啟動(dòng)

第三章、模板語(yǔ)法

<template>
<div>{{ message }}</div>
</template> <script setup lang="ts">
const message = "我是小滿"
</script> <style>
</style>
<template>
<div>{{ message == 0 ? '我是小滿0' : '我不是小滿other' }}</div>
</template> <script setup lang="ts">
const message:number = 1
</script> <style>
</style>
<template>
<div>{{ message.split(',') }}</div>
</template> <script setup lang="ts">
const message:string = "我,是,小,滿"
</script> <style>
</style>

指令

v- 開(kāi)頭都是vue 的指令

v-text 用來(lái)顯示文本

v-html 用來(lái)展示富文本

v-if 用來(lái)控制元素的顯示隱藏(切換真假DOM)

? (v-if會(huì)觸發(fā)組件的創(chuàng)建和銷毀鉤子)

v-else-if 表示 v-if 的“else if 塊”??梢枣?zhǔn)秸{(diào)用

v-else v-if條件收尾語(yǔ)句

v-show 用來(lái)控制元素的顯示隱藏(display none block Css切換)

v-on 簡(jiǎn)寫(xiě)@ 用來(lái)給元素添加事件

v-bind 簡(jiǎn)寫(xiě): 用來(lái)綁定元素的屬性Attr

v-model 雙向綁定

v-for 用來(lái)遍歷元素

v-on修飾符 冒泡案例

阻止表單提交案例

<template>
<form action="/">
<button @click.prevent="submit" type="submit">submit</button>
</form>
</template> <script setup lang="ts">
const submit = () => {
console.log('child');
} </script> <style>
</style>

.stop阻止冒泡

<template>
<div @click="parent">
<div @click.stop="child">child</div>
</div>
</template> <script setup lang="ts">
const child = () => {
console.log('child'); }
const parent = () => {
console.log('parent');
} </script>

第四章、響應(yīng)式對(duì)象

ref

接受一個(gè)內(nèi)部值并返回一個(gè)響應(yīng)式且可變的 ref 對(duì)象。ref 對(duì)象僅有一個(gè) .value property,指向該內(nèi)部值。

reactive

用來(lái)綁定復(fù)雜的數(shù)據(jù)類型 例如 對(duì)象 數(shù)組,使用reactive 去修改值無(wú)須.value

import { reactive } from 'vue'
let person = reactive({
name:"小"
})
person.name = "大"

toRef

如果原始對(duì)象是非響應(yīng)式的就不會(huì)更新視圖 數(shù)據(jù)是會(huì)變的

toRaw

將響應(yīng)式對(duì)象轉(zhuǎn)化為普通對(duì)象

第五章、計(jì)算屬性

computed用法

計(jì)算屬性就是當(dāng)依賴的屬性的值發(fā)生變化的時(shí)候,才會(huì)觸發(fā)他的更改,如果依賴的值,不發(fā)生變化的時(shí)候,使用的是緩存中的屬性值。

函數(shù)形式

import { computed, reactive, ref } from 'vue'
let price = ref(0) //$ 0 let m = computed<string>(()=>{
return `$` + price.value
}) price.value = 500

對(duì)象形式

<template>
<div>{{ mul }}</div>
<div @click="mul = 100">click</div>
</template> <script setup lang="ts">
import { computed, ref } from 'vue'
let price = ref<number | string>(1)//$ 0
let mul = computed({
get: () => {
return price.value
},
set: (value) => {
price.value = 'set' + value
}
})
</script> <style>
</style>

購(gòu)物車案例

<template>
<div>
<table style="width: 800px" border>
<thead>
<tr>
<th>名稱</th>
<th>數(shù)量</th>
<th>價(jià)格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key="index" v-for="(item, index) in data">
<td align="center">{{ item.name }}</td>
<td align="center">
<button @click="AddAnbSub(item, false)">-</button>
{{ item.num }}
<button @click="AddAnbSub(item, true)">+</button>
</td>
<td align="center">{{ item.num * item.price }}</td>
<td align="center">
<button @click="del(index)">刪除</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td></td>
<td align="center">總價(jià):{{ $total }}</td>
</tr>
</tfoot>
</table>
</div>
</template> <script setup lang="ts">
import { computed, reactive, ref } from "vue";
type Shop = {
name: string;
num: number;
price: number;
};
let $total = ref<number>(0);
const data = reactive<Shop[]>([
{
name: "襪子",
num: 1,
price: 100,
},
{
name: "褲子",
num: 1,
price: 200,
},
{
name: "衣服",
num: 1,
price: 300,
},
{
name: "毛巾",
num: 1,
price: 400,
},
]); const AddAnbSub = (item: Shop, type: boolean = false) => {
if (item.num > 1 && !type) {
item.num--;
total()
}
if (item.num <= 99 && type) {
item.num++;
total()
}
};
const del = (index: number) => {
data.splice(index, 1);
}; // $total = computed<number>(() => {
// return data.reduce((prev, next) => {
// return prev + next.num * next.price;
// },0);
// }); const total = () => {
$total.value = data.reduce((pre, cur) => {
return pre + cur.num * cur.price;
}, 0);
};
total()
</script> <style>
</style>

第六章、Watch監(jiān)聽(tīng)

watch

watch 需要偵聽(tīng)特定的數(shù)據(jù)源,并在單獨(dú)的回調(diào)函數(shù)中執(zhí)行副作用

watch第一個(gè)參數(shù)監(jiān)聽(tīng)源

watch第二個(gè)參數(shù)回調(diào)函數(shù)cb(newVal,oldVal)

watch第三個(gè)參數(shù)一個(gè)options配置項(xiàng)是一個(gè)對(duì)象{

immediate:true //是否立即調(diào)用一次

deep:true //是否開(kāi)啟深度監(jiān)聽(tīng)

}

import { ref, watch ,reactive} from 'vue'

let message = reactive({
nav:{
bar:{
name:""
}
}
}) watch(message, (newVal, oldVal) => {
console.log('新的值----', newVal);
console.log('舊的值----', oldVal);
})

watchEffect

立即執(zhí)行傳入的一個(gè)函數(shù),同時(shí)響應(yīng)式追蹤其依賴,并在其依賴變更時(shí)重新運(yùn)行該函數(shù)。

如果用到message 就只會(huì)監(jiān)聽(tīng)message 就是用到幾個(gè)監(jiān)聽(tīng)?zhēng)讉€(gè) 而且是非惰性 會(huì)默認(rèn)調(diào)用一次

let message = ref<string>('')
let message2 = ref<string>('')
watchEffect(() => {
//console.log('message', message.value);
console.log('message2', message2.value);
})

清除副作用

就是在觸發(fā)監(jiān)聽(tīng)之前會(huì)調(diào)用一個(gè)函數(shù)可以處理你的邏輯例如防抖

oninvalidate

import { watchEffect, ref } from 'vue'
let message = ref<string>('')
let message2 = ref<string>('')
watchEffect((oninvalidate) => {
//console.log('message', message.value);
oninvalidate(()=>{ })
console.log('message2', message2.value);
})

停止跟蹤 watchEffect

返回一個(gè)函數(shù) 調(diào)用之后將停止更新

const stop =  watchEffect((oninvalidate) => {
//console.log('message', message.value);
oninvalidate(()=>{ })
console.log('message2', message2.value);
},{
flush:"post",
onTrigger () { }
})
stop()

第七章、生命周期

nBeforeMount()

在組件DOM實(shí)際渲染安裝之前調(diào)用。在這一步中,根元素還不存在。

onMounted()

在組件的第一次渲染后調(diào)用,該元素現(xiàn)在可用,允許直接DOM訪問(wèn)

onBeforeUpdate()

數(shù)據(jù)更新時(shí)調(diào)用,發(fā)生在虛擬 DOM 打補(bǔ)丁之前。

onUpdated()

DOM更新后,updated的方法即會(huì)調(diào)用。

onBeforeUnmount()

在卸載組件實(shí)例之前調(diào)用。在這個(gè)階段,實(shí)例仍然是完全正常的。

onUnmounted()

卸載組件實(shí)例后調(diào)用。調(diào)用此鉤子時(shí),組件實(shí)例的所有指令都被解除綁定,所有事件偵聽(tīng)器都被移除,所有子組件實(shí)例被卸載。

第八章、組件

父組件通過(guò)v-bind綁定一個(gè)數(shù)據(jù),然后子組件通過(guò)defineProps接受傳過(guò)來(lái)的值,

給Menu組件 傳遞了一個(gè)title 字符串類型是不需要v-bind

<template>
<div class="layout">
<Menu title="我是標(biāo)題"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>

傳遞非字符串類型需要加v-bind 簡(jiǎn)寫(xiě) 冒號(hào)

<template>
<div class="layout">
<Menu v-bind:data="data" title="我是標(biāo)題"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template> <script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue'; const data = reactive<number[]>([1, 2, 3])
</script>

子組件接受值

通過(guò)defineProps 來(lái)接受

defineProps

如果我們使用的TypeScript,可以使用傳遞字面量類型的純類型語(yǔ)法做為參數(shù)

如 這是TS特有的

<template>
<div class="menu">
菜單區(qū)域 {{ title }}
<div>{{ data }}</div>
</div>
</template> <script setup lang="ts">
defineProps<{
title:string,
data:number[]
}>()
</script>

withDefaults

是個(gè)函數(shù)也是無(wú)須引入開(kāi)箱即用接受一個(gè)props函數(shù)第二個(gè)參數(shù)是一個(gè)對(duì)象設(shè)置默認(rèn)值

type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(), {
title: "張三",
data: () => [1, 2, 3]
})

子組件給父組件傳參

defineEmits

派發(fā)一個(gè)事件

<template>
<div class="menu">
<button @click="clickTap">派發(fā)給父組件</button>
</div>
</template> <script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([4, 5, 6]) const emit = defineEmits(['on-click'])
const clickTap = () => {
emit('on-click', list)
}
</script>

我們?cè)谧咏M件綁定了一個(gè)click 事件 然后通過(guò)defineEmits 注冊(cè)了一個(gè)自定義事件

點(diǎn)擊click 觸發(fā) emit 去調(diào)用我們注冊(cè)的事件 然后傳遞參數(shù)

父組件接受子組件的事件

<template>
<div class="layout">
<Menu @on-click="getList"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template> <script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue'; const data = reactive<number[]>([1, 2, 3]) const getList = (list: number[]) => {
console.log(list,'父組件接受子組件');
}
</script>

defineExpose

子組件暴露給父組件內(nèi)部屬性

 <Menu ref="menus"></Menu>
const menus = ref(null)

然后打印menus.value 發(fā)現(xiàn)沒(méi)有任何屬性

這時(shí)候父組件想要讀到子組件的屬性可以通過(guò) defineExpose暴露

const list = reactive<number[]>([4, 5, 6])

defineExpose({
list
})

全局組件

組件使用頻率非常高(table,Input,button,等)這些組件 幾乎每個(gè)頁(yè)面都在使用便可以封裝成全局組件

import { createApp } from 'vue'
import App from './App.vue'
import './assets/css/reset/index.less'
import Card from './components/Card/index.vue' createApp(App).component('Card',Card).mount('#app')

遞歸組件

原理跟我們寫(xiě)js遞歸是一樣的 自己調(diào)用自己 通過(guò)一個(gè)條件來(lái)結(jié)束遞歸 否則導(dǎo)致內(nèi)存泄漏

在父組件配置數(shù)據(jù)結(jié)構(gòu) 數(shù)組對(duì)象格式 傳給子組件

type TreeList = {
name: string;
icon?: string;
children?: TreeList[] | [];
};
const data = reactive<TreeList[]>([
{
name: "no.1",
children: [
{
name: "no.1-1",
children: [
{
name: "no.1-1-1",
},
],
},
],
},
{
name: "no.2",
children: [
{
name: "no.2-1",
},
],
},
{
name: "no.3",
},
]);

子組件接收值 第一個(gè)script

type TreeList = {
name: string;
icon?: string;
children?: TreeList[] | [];
}; type Props<T> = {
data?: T[] | [];
}; defineProps<Props<TreeList>>(); const clickItem = (item: TreeList) => {
console.log(item)
}

子組件增加一個(gè)script 定義組件名稱為了 遞歸用

<script lang="ts">
export default {
name:"TreeItem"
}
</script>

template

TreeItem 其實(shí)就是當(dāng)前組件 通過(guò)import 把自身又引入了一遍 如果他沒(méi)有children 了就結(jié)束

  <div style="margin-left:10px;" class="tree">
<div :key="index" v-for="(item,index) in data">
<div @click='clickItem(item)'>{{item.name}}
</div>
<TreeItem @on-click='clickItem' v-if='item?.children?.length' :data="item.children"></TreeItem>
</div>
</div>

動(dòng)態(tài)組件

動(dòng)態(tài)組件就是:讓多個(gè)組件使用同一個(gè)掛載點(diǎn),并動(dòng)態(tài)切換,這就是動(dòng)態(tài)組件。

在掛載點(diǎn)使用component標(biāo)簽,然后使用v-bind:is=”組件”

import A from './A.vue'
import B from './B.vue' <component :is="A"></component>

注意事項(xiàng)

1.在Vue2 的時(shí)候is 是通過(guò)組件名稱切換的 在Vue3 setup 是通過(guò)組件實(shí)例切換的

2.如果你把組件實(shí)例放到Reactive Vue會(huì)給你一個(gè)警告runtime-core.esm-bundler.js:38 [Vue warn]: Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef instead of ref.

Component that was made reactive:

這是因?yàn)閞eactive 會(huì)進(jìn)行proxy 代理 而我們組件代理之后毫無(wú)用處 節(jié)省性能開(kāi)銷 推薦我們使用shallowRef 或者 markRaw 跳過(guò)proxy 代理

修改為如下

const tab = reactive<Com[]>([{
name: "A組件",
comName: markRaw(A)
}, {
name: "B組件",
comName: markRaw(B)
}])

第九章、插槽

插槽就是子組件中的提供給父組件使用的一個(gè)占位符,用 表示,父組件可以在這個(gè)占位符中填充任何模板代碼,如 HTML、組件等,填充的內(nèi)容會(huì)替換子組件的標(biāo)簽。

匿名插槽

在子組件放置一個(gè)插槽

<template>
<div>
<slot></slot>
</div>
</template>

父組件使用插槽

在父組件給這個(gè)插槽填充內(nèi)容

<Dialog>
<template v-slot>
<div>2132</div>
</template>
</Dialog>

具名插槽

具名插槽其實(shí)就是給插槽取個(gè)名字。一個(gè)子組件可以放多個(gè)插槽,而且可以放在不同的地方,而父組件填充內(nèi)容時(shí),可以根據(jù)這個(gè)名字把內(nèi)容填充到對(duì)應(yīng)插槽中

<div>
<slot name="header"></slot>
<slot></slot> <slot name="footer"></slot>
</div>

父組件使用需對(duì)應(yīng)名稱

<Dialog>
<template v-slot:header>
<div>1</div>
</template>
<template v-slot>
<div>2</div>
</template>
<template v-slot:footer>
<div>3</div>
</template>
</Dialog>

插槽簡(jiǎn)寫(xiě)

  <Dialog>
<template #header>
<div>1</div>
</template>
<template #default>
<div>2</div>
</template>
<template #footer>
<div>3</div>
</template>
</Dialog>

作用域插槽

在子組件動(dòng)態(tài)綁定參數(shù) 派發(fā)給父組件的slot去使用

<div>
<slot name="header"></slot>
<div>
<div v-for="item in 100">
<slot :data="item"></slot>
</div>
</div> <slot name="footer"></slot>
</div>

通過(guò)解構(gòu)方式取值

 <Dialog>
<template #header>
<div>1</div>
</template>
<template #default="{ data }">
<div>{{ data }}</div>
</template>
<template #footer>
<div>3</div>
</template>
</Dialog>

動(dòng)態(tài)插槽

插槽可以是一個(gè)變量名

  <Dialog>
<template #[name]>
<div>
23
</div>
</template>
</Dialog> const name = ref('header')

(附加)異步組件&代碼分包&suspense

(附加)Teleport傳送組件

第十章、keep-alive緩存組件

有時(shí)候我們不希望組件被重新渲染影響使用體驗(yàn);或者處于性能考慮,避免多次重復(fù)渲染降低性能。而是希望組件可以緩存下來(lái),維持當(dāng)前的狀態(tài)。這時(shí)候就需要用到keep-alive組件。

開(kāi)啟keep-alive 生命周期的變化

初次進(jìn)入時(shí): onMounted> onActivated

退出后觸發(fā) deactivated

再次進(jìn)入:只會(huì)觸發(fā) onActivated

事件掛載的方法等,只執(zhí)行一次的放在 onMounted中;組件每次進(jìn)去執(zhí)行的方法放在 onActivated中

<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive> <!-- 多個(gè)條件判斷的子組件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive> <!-- 和 `<transition>` 一起使用 -->
<transition>
<keep-alive>
<component :is="view"></component>
</keep-alive>
</transition>

include 和 exclude

 <keep-alive :include="" :exclude="" :max=""></keep-alive>

max

<keep-alive :max="10">
<component :is="view"></component>
</keep-alive>

第十一章、transition動(dòng)畫(huà)組件

Vue 提供了 transition 的封裝組件,在下列情形中,可以給任何元素和組件添加進(jìn)入/離開(kāi)過(guò)渡:

條件渲染 (使用 v-if)

條件展示 (使用 v-show)

動(dòng)態(tài)組件

組件根節(jié)點(diǎn)

自定義 transition 過(guò)度效果,你需要對(duì)transition組件的name屬性自定義。并在css中寫(xiě)入對(duì)應(yīng)的樣式

過(guò)渡 class

在進(jìn)入/離開(kāi)的過(guò)渡中,會(huì)有 6 個(gè) class 切換。

v-enter-from:定義進(jìn)入過(guò)渡的開(kāi)始狀態(tài)。在元素被插入之前生效,在元素被插入之后的下一幀移除。

v-enter-active:定義進(jìn)入過(guò)渡生效時(shí)的狀態(tài)。在整個(gè)進(jìn)入過(guò)渡的階段中應(yīng)用,在元素被插入之前生效,在過(guò)渡/動(dòng)畫(huà)完成之后移除。這個(gè)類可以被用來(lái)定義進(jìn)入過(guò)渡的過(guò)程時(shí)間,延遲和曲線函數(shù)。

v-enter-to:定義進(jìn)入過(guò)渡的結(jié)束狀態(tài)。在元素被插入之后下一幀生效 (與此同時(shí) v-enter-from 被移除),在過(guò)渡/動(dòng)畫(huà)完成之后移除。

v-leave-from:定義離開(kāi)過(guò)渡的開(kāi)始狀態(tài)。在離開(kāi)過(guò)渡被觸發(fā)時(shí)立刻生效,下一幀被移除。

v-leave-active:定義離開(kāi)過(guò)渡生效時(shí)的狀態(tài)。在整個(gè)離開(kāi)過(guò)渡的階段中應(yīng)用,在離開(kāi)過(guò)渡被觸發(fā)時(shí)立刻生效,在過(guò)渡/動(dòng)畫(huà)完成之后移除。這個(gè)類可以被用來(lái)定義離開(kāi)過(guò)渡的過(guò)程時(shí)間,延遲和曲線函數(shù)。

v-leave-to:離開(kāi)過(guò)渡的結(jié)束狀態(tài)。在離開(kāi)過(guò)渡被觸發(fā)之后下一幀生效 (與此同時(shí) v-leave-from 被移除),在過(guò)渡/動(dòng)畫(huà)完成之后移除。

 <button @click='flag = !flag'>切換</button>
<transition name='fade'>
<div v-if='flag' class="box"></div>
</transition>
//開(kāi)始過(guò)度
.fade-enter-from{
background:red;
width:0px;
height:0px;
transform:rotate(360deg)
}
//開(kāi)始過(guò)度了
.fade-enter-active{
transition: all 2.5s linear;
}
//過(guò)度完成
.fade-enter-to{
background:yellow;
width:200px;
height:200px;
}
//離開(kāi)的過(guò)度
.fade-leave-from{
width:200px;
height:200px;
transform:rotate(360deg)
}
//離開(kāi)中過(guò)度
.fade-leave-active{
transition: all 1s linear;
}
//離開(kāi)完成
.fade-leave-to{
width:0px;
height:0px;
}

自定義過(guò)渡 class 類名

trasnsition props

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

自定義過(guò)度時(shí)間 單位毫秒

你也可以分別指定進(jìn)入和離開(kāi)的持續(xù)時(shí)間:

<transition :duration="1000">...</transition>

<transition :duration="{ enter: 500, leave: 800 }">...</transition>

通過(guò)自定義class 結(jié)合css動(dòng)畫(huà)庫(kù)

animate css

安裝庫(kù) npm install animate.css

引入 import 'animate.css'

使用方法

官方文檔 Animate.css | A cross-browser library of CSS animations.

<transition
leave-active-class="animate__animated animate__bounceInLeft"
enter-active-class="animate__animated animate__bounceInRight"
>
<div v-if="flag" class="box"></div>
</transition>

transition 生命周期8個(gè)

  @before-enter="beforeEnter" //對(duì)應(yīng)enter-from
@enter="enter"http://對(duì)應(yīng)enter-active
@after-enter="afterEnter"http://對(duì)應(yīng)enter-to
@enter-cancelled="enterCancelled"http://顯示過(guò)度打斷
@before-leave="beforeLeave"http://對(duì)應(yīng)leave-from
@leave="leave"http://對(duì)應(yīng)enter-active
@after-leave="afterLeave"http://對(duì)應(yīng)leave-to
@leave-cancelled="leaveCancelled"http://離開(kāi)過(guò)度打斷

當(dāng)只用 JavaScript 過(guò)渡的時(shí)候,在 enter 和 leave 鉤子中必須使用 done 進(jìn)行回調(diào)

結(jié)合**gsap **動(dòng)畫(huà)庫(kù)使用 GreenSock

const beforeEnter = (el: Element) => {
console.log('進(jìn)入之前from', el);
}
const Enter = (el: Element,done:Function) => {
console.log('過(guò)度曲線');
setTimeout(()=>{
done()
},3000)
}
const AfterEnter = (el: Element) => {
console.log('to');
}

appear

通過(guò)這個(gè)屬性可以設(shè)置初始節(jié)點(diǎn)過(guò)度 就是頁(yè)面加載完成就開(kāi)始動(dòng)畫(huà) 對(duì)應(yīng)三個(gè)狀態(tài)

appear-active-class=""
appear-from-class=""
appear-to-class=""
appear

transition-group過(guò)度列表

單個(gè)節(jié)點(diǎn)

多個(gè)節(jié)點(diǎn),每次只渲染一個(gè)

那么怎么同時(shí)渲染整個(gè)列表,比如使用 v-for?在這種場(chǎng)景下,我們會(huì)使用 組件。

默認(rèn)情況下,它不會(huì)渲染一個(gè)包裹元素,但是你可以通過(guò) tag attribute 指定渲染一個(gè)元素。

過(guò)渡模式不可用,因?yàn)槲覀儾辉傧嗷デ袚Q特有的元素。

內(nèi)部元素總是需要提供唯一的 key attribute 值。

CSS 過(guò)渡的類將會(huì)應(yīng)用在內(nèi)部的元素中,而不是這個(gè)組/容器本身

<transition-group>
<div style="margin: 10px;" :key="item" v-for="item in list">{{ item }</div>
</transition-group> const list = reactive<number[]>([1, 2, 4, 5, 6, 7, 8, 9])
const Push = () => {
list.push(123)
}
const Pop = () => {
list.pop()
}

transition-group列表的移動(dòng)過(guò)渡

組件還有一個(gè)特殊之處。除了進(jìn)入和離開(kāi),它還可以為定位的改變添加動(dòng)畫(huà)。只需了解新增的 v-move 類就可以使用這個(gè)新功能,它會(huì)應(yīng)用在元素改變定位的過(guò)程中。像之前的類名一樣,它的前綴可以通過(guò) name attribute 來(lái)自定義,也可以通過(guò) move-class attribute 手動(dòng)設(shè)置

<template>
<div>
<button @click="shuffle">Shuffle</button>
<transition-group class="wraps" name="mmm" tag="ul">
<li class="cell" v-for="item in items" :key="item.id">{{ item.number }}</li>
</transition-group>
</div>
</template> <script setup lang='ts'>
import _ from 'lodash'
import { ref } from 'vue'
let items = ref(Array.apply(null, { length: 81 } as number[]).map((_, index) => {
return {
id: index,
number: (index % 9) + 1
}
}))
const shuffle = () => {
items.value = _.shuffle(items.value)
}
</script> <style scoped lang="less">
.wraps {
display: flex;
flex-wrap: wrap;
width: calc(25px * 10 + 9px);
.cell {
width: 25px;
height: 25px;
border: 1px solid #ccc;
list-style-type: none;
display: flex;
justify-content: center;
align-items: center;
}
} .mmm-move {
transition: transform 0.8s ease;
}
</style>

transition-group狀態(tài)過(guò)渡

Vue 也同樣可以給數(shù)字 Svg 背景顏色等添加過(guò)度動(dòng)畫(huà) ,下面演示數(shù)字變化

<template>
<div>
<input step="20" v-model="num.current" type="number" />
<div>{{ num.tweenedNumber.toFixed(0) }}</div>
</div>
</template> <script setup lang='ts'>
import { reactive, watch } from 'vue'
import gsap from 'gsap'
const num = reactive({
tweenedNumber: 0,
current:0
}) watch(()=>num.current, (newVal) => {
gsap.to(num, {
duration: 1,
tweenedNumber: newVal
})
}) </script> <style>
</style>

第十二章、組件傳參

Provide / Inject

通常,當(dāng)我們需要從父組件向子組件傳遞數(shù)據(jù)時(shí),我們使用 props。想象一下這樣的結(jié)構(gòu):有一些深度嵌套的組件,而深層的子組件只需要父組件的部分內(nèi)容。在這種情況下,如果仍然將 prop 沿著組件鏈逐級(jí)傳遞下去,可能會(huì)很麻煩。

provide 可以在祖先組件中指定我們想要提供給后代組件的數(shù)據(jù)或方法,而在任何后代組件中,我們都可以使用 inject 來(lái)接收 provide 提供的數(shù)據(jù)或方法。

父組件傳遞數(shù)據(jù)

<template>
<div class="App">
<button>我是App</button>
<A></A>
</div>
</template> <script setup lang='ts'>
import { provide, ref } from 'vue'
import A from './components/A.vue'
let flag = ref<number>(1)
provide('flag', flag)
</script> <style>
.App {
background: blue;
color: #fff;
}
</style>

子組件接受

<template>
<div style="background-color: green;">
我是B
<button @click="change">change falg</button>
<div>{{ flag }}</div>
</div>
</template> <script setup lang='ts'>
import { inject, Ref, ref } from 'vue' const flag = inject<Ref<number>>('flag', ref(1)) const change = () => {
flag.value = 2
}
</script> <style>
</style>

兄弟組件傳參和Bus

借助父組件傳參

例如父組件為App 子組件為A 和 B他兩個(gè)是同級(jí)的

A 組件派發(fā)事件通過(guò)App.vue 接受A組件派發(fā)的事件然后在Props 傳給B組件 也是可以實(shí)現(xiàn)的

缺點(diǎn)就是比較麻煩 ,無(wú)法直接通信,只能充當(dāng)橋梁

<template>
<div>
<A @on-click="getFalg"></A>
<B :flag="Flag"></B>
</div>
</template> <script setup lang='ts'>
import A from './components/A.vue'
import B from './components/B.vue'
import { ref } from 'vue'
let Flag = ref<boolean>(false)
const getFalg = (flag: boolean) => {
Flag.value = flag;
}
</script> <style>
</style>

Event Bus(方法)

我們?cè)赩ue2 可以使用$emit 傳遞 $on監(jiān)聽(tīng) emit傳遞過(guò)來(lái)的事件

這個(gè)原理其實(shí)是運(yùn)用了JS設(shè)計(jì)模式之發(fā)布訂閱模式

type BusClass<T> = {
emit: (name: T) => void
on: (name: T, callback: Function) => void
}
type BusParams = string | number | symbol
type List = {
[key: BusParams]: Array<Function>
}
class Bus<T extends BusParams> implements BusClass<T> {
list: List
constructor() {
this.list = {}
}
emit(name: T, ...args: Array<any>) {
let eventName: Array<Function> = this.list[name]
eventName.forEach(ev => {
ev.apply(this, args)
})
}
on(name: T, callback: Function) {
let fn: Array<Function> = this.list[name] || [];
fn.push(callback)
this.list[name] = fn
}
}
export default new Bus<number>()

然后掛載到Vue config 全局就可以使用

Mitt

在vue3中$on,$off 和 $once 實(shí)例方法已被移除,組件實(shí)例不再實(shí)現(xiàn)事件觸發(fā)接口,因此熟悉的EventBus便無(wú)法使用了。然而我們習(xí)慣了使用EventBus,對(duì)于這種情況我們可以使用Mitt庫(kù)

npm install mitt -S

main.ts 初始化

全局總線,vue 入口文件 main.js 中掛載全局屬性

import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt' const Mit = mitt() //TypeScript注冊(cè)
// 由于必須要拓展ComponentCustomProperties類型才能獲得類型提示
declare module "vue" {
export interface ComponentCustomProperties {
$Bus: typeof Mit
}
} const app = createApp(App) //Vue3掛載全局API
app.config.globalProperties.$Bus = Mit app.mount('#app')

使用方法通過(guò)emit派發(fā), on 方法添加事件,off 方法移除,clear 清空所有

A組件派發(fā)(emit)

<template>
<div>
<h1>我是A</h1>
<button @click="emit1">emit1</button>
<button @click="emit2">emit2</button>
</div>
</template> <script setup lang='ts'>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance();
const emit1 = () => {
instance?.proxy?.$Bus.emit('on-num', 100)
}
const emit2 = () => {
instance?.proxy?.$Bus.emit('*****', 500)
}
</script> <style>
</style>

B組件監(jiān)聽(tīng)(on)

<template>
<div>
<h1>我是B</h1>
</div>
</template> <script setup lang='ts'>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
instance?.proxy?.$Bus.on('on-num', (num) => {
console.log(num,'===========>B')
})
</script> <style>
</style>

監(jiān)聽(tīng)所有事件( on("*") )

instance?.proxy?.$Bus.on('*',(type,num)=>{
console.log(type,num,'===========>B')
})

移除監(jiān)聽(tīng)事件(off)

const Fn = (num: any) => {
console.log(num, '===========>B')
}
instance?.proxy?.$Bus.on('on-num',Fn)//listen
instance?.proxy?.$Bus.off('on-num',Fn)//unListen

清空所有監(jiān)聽(tīng)(clear)

instance?.proxy?.$Bus.all.clear()

Pinia

Store 是一個(gè)保存狀態(tài)和業(yè)務(wù)邏輯的實(shí)體,可以自由讀取和寫(xiě)入,并通過(guò)導(dǎo)入后在 setup 中使用Getters 作用與 Vuex 中的 Getters 相同,但使用略有差異,其直接在 Store 上讀取,形似 Store.xx,就和一般的屬性讀取一樣Pinia 沒(méi)有 Mutations,統(tǒng)一在 actions 中操作 state,通過(guò)this.xx 訪問(wèn)相應(yīng)狀態(tài),雖然可以直接操作 Store,但還是推薦在 actions 中操作,保證狀態(tài)不被意外改變

總結(jié)

以上是生活随笔為你收集整理的TypeScript+Vue3的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。