產(chǎn)品經(jīng)理身旁過(guò),需求變更逃不過(guò)。 測(cè)試姐姐瞇眼笑,今晚bug必然多。
據(jù)悉Vue3.0
的正式版將要在本月(8月)發(fā)布,從發(fā)布到正式投入到正式項(xiàng)目中,還需要一定的過(guò)渡期,但我們不能一直等到Vue3
正式投入到項(xiàng)目中的時(shí)候才去學(xué)習(xí),提前學(xué)習(xí),讓你更快一步掌握Vue3.0
,升職加薪迎娶白富美就靠它了。不過(guò)在學(xué)習(xí)Vue3
之前,還需要先了解一下Proxy
,它是Vue3.0
實(shí)現(xiàn)數(shù)據(jù)雙向綁定的基礎(chǔ)。
了解代理模式
一個(gè)例子
作為一個(gè)單身鋼鐵直男程序員,小王最近逐漸喜歡上了前端小妹,不過(guò)呢,他又和前臺(tái)小妹不熟,所以決定委托與前端小妹比較熟的UI
小姐姐幫忙給自己搭橋引線。小王于是請(qǐng)UI
小姐姐吃了一頓大餐,然后拿出一封情書(shū)委托它轉(zhuǎn)交給前臺(tái)小妹,情書(shū)上寫(xiě)的 我喜歡你,我想和你睡覺(jué)
,不愧鋼鐵直男。不過(guò)這樣寫(xiě)肯定是沒(méi)戲的,UI
小姐姐吃人嘴短,于是幫忙改了情書(shū),改成了我喜歡你,我想和你一起在晨輝的沐浴下起床
,然后交給了前臺(tái)小妹。雖然有沒(méi)有撮合成功不清楚啊,不過(guò)這個(gè)故事告訴我們,小王活該單身狗。
其實(shí)上面就是一個(gè)比較典型的代理模式的例子,小王想給前臺(tái)小妹送情書(shū),因?yàn)椴皇焖晕?code>UI小姐姐,UI
小姐姐相當(dāng)于代理人,代替小王完成了送情書(shū)的事情。
引申
通過(guò)上面的例子,我們想想Vue
的數(shù)據(jù)響應(yīng)原理,比如下面這段代碼
const xiaowang = {
love: '我喜歡你,我想和你睡覺(jué)'
}
// 送給小姐姐情書(shū)
function sendToMyLove(obj) {
console.log(obj.love)
return '流氓,滾'
}
console.log(sendToMyLove(xiaowang))
如果沒(méi)有UI
小姐姐代替送情書(shū),顯示結(jié)局是悲慘的,想想Vue2.0
的雙向綁定,通過(guò)Object.defineProperty
來(lái)監(jiān)聽(tīng)的屬性 get
,set
方法來(lái)實(shí)現(xiàn)雙向綁定,這個(gè)Object.defineProperty
就相當(dāng)于UI
小姐姐
const xiaowang = {
loveLetter: '我喜歡你,我想和你睡覺(jué)'
}
// UI小姐姐代理
Object.defineProperty(xiaowang,'love', {
get() {
return xiaowang.loveLetter.replace('睡覺(jué)','一起在晨輝的沐浴下起床')
}
})
// 送給小姐姐情書(shū)
function sendToMyLove(obj) {
console.log(obj.love)
return '小伙子還挺有詩(shī)情畫(huà)意的么,不過(guò)老娘不喜歡,滾'
}
console.log(sendToMyLove(xiaowang))
雖然依然是一個(gè)悲慘的故事,因?yàn)樗捅捡Y的成功率可能會(huì)更高一些。但是我們可以看到,通過(guò)Object.defineproperty
可以對(duì)對(duì)象的已有屬性進(jìn)行攔截,然后做一些額外的操作。
存在的問(wèn)題
在Vue2.0
中,數(shù)據(jù)雙向綁定就是通過(guò)Object.defineProperty
去監(jiān)聽(tīng)對(duì)象的每一個(gè)屬性,然后在get
,set
方法中通過(guò)發(fā)布訂閱者模式來(lái)實(shí)現(xiàn)的數(shù)據(jù)響應(yīng),但是存在一定的缺陷,比如只能監(jiān)聽(tīng)已存在的屬性,對(duì)于新增刪除屬性就無(wú)能為力了,同時(shí)無(wú)法監(jiān)聽(tīng)數(shù)組的變化,所以在Vue3.0
中將其換成了功能更強(qiáng)大的Proxy
。
(推薦教程:Vue 2教程)
了解Proxy
Proxy
是ES6
新推出的一個(gè)特性,可以用它去攔截js
操作的方法,從而對(duì)這些方法進(jìn)行代理操作。
用Proxy重寫(xiě)上面的例子
比如我們可以通過(guò)Proxy
對(duì)上面的送情書(shū)情節(jié)進(jìn)行重寫(xiě):
const xiaowang = {
loveLetter: '我喜歡你,我想和你睡覺(jué)'
}
const proxy = new Proxy(xiaowang, {
get(target,key) {
if(key === 'loveLetter') {
return target[key].replace('睡覺(jué)','一起在晨輝的沐浴下起床')
}
}
})
// 送給小姐姐情書(shū)
function sendToMyLove(obj) {
console.log(obj.loveLetter)
return '小伙子還挺有詩(shī)情畫(huà)意的么,不過(guò)老娘不喜歡,滾'
}
console.log(sendToMyLove(proxy))
再看這樣一個(gè)場(chǎng)景
請(qǐng)分別使用Object.defineProperty
和Proxy
完善下面的代碼邏輯.
function observe(obj, callback) {}
const obj = observe(
{
name: '子君',
sex: '男'
},
(key, value) => {
console.log(`屬性[${key}]的值被修改為[${value}]`)
}
)
// 這段代碼執(zhí)行后,輸出 屬性[name]的值被修改為[妹紙]
obj.name = '妹紙'
// 這段代碼執(zhí)行后,輸出 屬性[sex]的值被修改為[女]
obj.name = '女'
看了上面的代碼,希望大家可以先自行實(shí)現(xiàn)以下,下面我們分別用Object.defineProperty
和Proxy
去實(shí)現(xiàn)上面的邏輯.
- 使用
Object.defineProperty
/**
* 請(qǐng)實(shí)現(xiàn)這個(gè)函數(shù),使下面的代碼邏輯正常運(yùn)行
* @param {*} obj 對(duì)象
* @param {*} callback 回調(diào)函數(shù)
*/
function observe(obj, callback) {
const newObj = {}
Object.keys(obj).forEach(key => {
Object.defineProperty(newObj, key, {
configurable: true,
enumerable: true,
get() {
return obj[key]
},
// 當(dāng)屬性的值被修改時(shí),會(huì)調(diào)用set,這時(shí)候就可以在set里面調(diào)用回調(diào)函數(shù)
set(newVal) {
obj[key] = newVal
callback(key, newVal)
}
})
})
return newObj
}
const obj = observe(
{
name: '子君',
sex: '男'
},
(key, value) => {
console.log(`屬性[${key}]的值被修改為[${value}]`)
}
)
// 這段代碼執(zhí)行后,輸出 屬性[name]的值被修改為[妹紙]
obj.name = '妹紙'
// 這段代碼執(zhí)行后,輸出 屬性[sex]的值被修改為[女]
obj.name = '女'
- 使用
Proxy
function observe(obj, callback) {
return new Proxy(obj, {
get(target, key) {
return target[key]
},
set(target, key, value) {
target[key] = value
callback(key, value)
}
})
}
const obj = observe(
{
name: '子君',
sex: '男'
},
(key, value) => {
console.log(`屬性[${key}]的值被修改為[${value}]`)
}
)
// 這段代碼執(zhí)行后,輸出 屬性[name]的值被修改為[妹紙]
obj.name = '妹紙'
// 這段代碼執(zhí)行后,輸出 屬性[sex]的值被修改為[女]
obj.name = '女'
通過(guò)上面兩種不同實(shí)現(xiàn)方式,我們可以大概的了解到Object.defineProperty
和Proxy
的用法,但是當(dāng)給對(duì)象添加新的屬性的時(shí)候,區(qū)別就出來(lái)了,比如
// 添加編程網(wǎng)站
obj.gzh = 'W3Cschool編程獅'
使用Object.defineProperty
無(wú)法監(jiān)聽(tīng)到新增屬性,但是使用Proxy
是可以監(jiān)聽(tīng)到的。對(duì)比上面兩段代碼可以發(fā)現(xiàn)有以下幾點(diǎn)不同
Object.defineProperty
監(jiān)聽(tīng)的是對(duì)象的每一個(gè)屬性,而Proxy
監(jiān)聽(tīng)的是對(duì)象自身- 使用
Object.defineProperty
需要遍歷對(duì)象的每一個(gè)屬性,對(duì)于性能會(huì)有一定的影響 Proxy
對(duì)新增的屬性也能監(jiān)聽(tīng)到,但Object.defineProperty
無(wú)法監(jiān)聽(tīng)到。
初識(shí)Proxy
概念與語(yǔ)法
在MDN
中,關(guān)于Proxy
是這樣介紹的: Proxy
對(duì)象用于定義基本操作的自定義行為(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。什么意思呢?Proxy
就像一個(gè)攔截器一樣,它可以在讀取對(duì)象的屬性,修改對(duì)象的屬性,獲取對(duì)象屬性列表,通過(guò)for in
循環(huán)等等操作的時(shí)候,去攔截對(duì)象上面的默認(rèn)行為,然后自己去自定義這些行為,比如上面例子中的set
,我們通過(guò)攔截默認(rèn)的set
,然后在自定義的set
里面添加了回調(diào)函數(shù)的調(diào)用
Proxy
的語(yǔ)法格式如下
/**
* target: 要兼容的對(duì)象,可以是一個(gè)對(duì)象,數(shù)組,函數(shù)等等
* handler: 是一個(gè)對(duì)象,里面包含了可以監(jiān)聽(tīng)這個(gè)對(duì)象的行為函數(shù),比如上面例子里面的`get`與`set`
* 同時(shí)會(huì)返回一個(gè)新的對(duì)象proxy, 為了能夠觸發(fā)handler里面的函數(shù),必須要使用返回值去進(jìn)行其他操作,比如修改值
*/
const proxy = new Proxy(target, handler)
在上面的例子里面,我們已經(jīng)使用到了handler
里面提供的get
與set
方法了,接下來(lái)我們一一看一下handler
里面的方法。
handler 里面的方法列表
handler
里面的方法可以有以下這十三個(gè),每一個(gè)都對(duì)應(yīng)的一種或多種針對(duì)proxy
代理對(duì)象的操作行為
handler.get
當(dāng)通過(guò)proxy
去讀取對(duì)象里面的屬性的時(shí)候,會(huì)進(jìn)入到get
鉤子函數(shù)里面
handler.set
當(dāng)通過(guò)proxy
去為對(duì)象設(shè)置修改屬性的時(shí)候,會(huì)進(jìn)入到set
鉤子函數(shù)里面
handler.has
當(dāng)使用in
判斷屬性是否在proxy
代理對(duì)象里面時(shí),會(huì)觸發(fā)has
,比如
const obj = {
name: '子君'
}
console.log('name' in obj)
handler.deleteProperty
當(dāng)使用delete
去刪除對(duì)象里面的屬性的時(shí)候,會(huì)進(jìn)入deleteProperty`鉤子函數(shù)
handler.apply
當(dāng)proxy
監(jiān)聽(tīng)的是一個(gè)函數(shù)的時(shí)候,當(dāng)調(diào)用這個(gè)函數(shù)時(shí),會(huì)進(jìn)入apply
鉤子函數(shù)
handle.ownKeys
當(dāng)通過(guò)Object.getOwnPropertyNames
,Object.getownPropertySymbols
,Object.keys
,Reflect.ownKeys
去獲取對(duì)象的信息的時(shí)候,就會(huì)進(jìn)入ownKeys
這個(gè)鉤子函數(shù)
handler.construct
當(dāng)使用new
操作符的時(shí)候,會(huì)進(jìn)入construct
這個(gè)鉤子函數(shù)
handler.defineProperty
當(dāng)使用Object.defineProperty
去修改屬性修飾符的時(shí)候,會(huì)進(jìn)入這個(gè)鉤子函數(shù)
handler.getPrototypeOf
當(dāng)讀取對(duì)象的原型的時(shí)候,會(huì)進(jìn)入這個(gè)鉤子函數(shù)
handler.setPrototypeOf
當(dāng)設(shè)置對(duì)象的原型的時(shí)候,會(huì)進(jìn)入這個(gè)鉤子函數(shù)
handler.isExtensible
當(dāng)通過(guò)Object.isExtensible
去判斷對(duì)象是否可以添加新的屬性的時(shí)候,進(jìn)入這個(gè)鉤子函數(shù)
handler.preventExtensions
當(dāng)通過(guò)Object.preventExtensions
去設(shè)置對(duì)象不可以修改新屬性時(shí)候,進(jìn)入這個(gè)鉤子函數(shù)
handler.getOwnPropertyDescriptor
在獲取代理對(duì)象某個(gè)屬性的屬性描述時(shí)觸發(fā)該操作,比如在執(zhí)行 Object.getOwnPropertyDescriptor(proxy, "foo")
時(shí)會(huì)進(jìn)入這個(gè)鉤子函數(shù)
Proxy
提供了十三種攔截對(duì)象操作的方法,本文主要挑選其中一部分在Vue3
中比較重要的進(jìn)行說(shuō)明,其余的建議可以直接閱讀MDN
關(guān)于Proxy
的介紹。
詳細(xì)介紹
get
當(dāng)通過(guò)
proxy
去讀取對(duì)象里面的屬性的時(shí)候,會(huì)進(jìn)入到get
鉤子函數(shù)里面
當(dāng)我們從一個(gè)proxy
代理上面讀取屬性的時(shí)候,就會(huì)觸發(fā)get
鉤子函數(shù),get
函數(shù)的結(jié)構(gòu)如下
/**
* target: 目標(biāo)對(duì)象,即通過(guò)proxy代理的對(duì)象
* key: 要訪問(wèn)的屬性名稱
* receiver: receiver相當(dāng)于是我們要讀取的屬性的this,一般情況
* 下他就是proxy對(duì)象本身,關(guān)于receiver的作用,后文將具體講解
*/
handle.get(target,key, receiver)
示例
我們?cè)诠ぷ髦薪?jīng)常會(huì)有封裝axios
的需求,在封裝過(guò)程中,也需要對(duì)請(qǐng)求異常進(jìn)行封裝,比如不同的狀態(tài)碼返回的異常信息是不同的,如下是一部分狀態(tài)碼及其提示信息:
// 狀態(tài)碼提示信息
const errorMessage = {
400: '錯(cuò)誤請(qǐng)求',
401: '系統(tǒng)未授權(quán),請(qǐng)重新登錄',
403: '拒絕訪問(wèn)',
404: '請(qǐng)求失敗,未找到該資源'
}
// 使用方式
const code = 404
const message = errorMessage[code]
console.log(message)
但這存在一個(gè)問(wèn)題,狀態(tài)碼很多,我們不可能每一個(gè)狀態(tài)碼都去枚舉出來(lái),所以對(duì)于一些異常狀態(tài)碼,我們希望可以進(jìn)行統(tǒng)一提示,如提示為系統(tǒng)異常,請(qǐng)聯(lián)系管理員
,這時(shí)候就可以使用Proxy
對(duì)錯(cuò)誤信息進(jìn)行代理處理
// 狀態(tài)碼提示信息
const errorMessage = {
400: '錯(cuò)誤請(qǐng)求',
401: '系統(tǒng)未授權(quán),請(qǐng)重新登錄',
403: '拒絕訪問(wèn)',
404: '請(qǐng)求失敗,未找到該資源'
}
const proxy = new Proxy(errorMessage, {
get(target,key) {
const value = target[key]
return value || '系統(tǒng)異常,請(qǐng)聯(lián)系管理員'
}
})
// 輸出 錯(cuò)誤請(qǐng)求
console.log(proxy[400])
// 輸出 系統(tǒng)異常,請(qǐng)聯(lián)系管理員
console.log(proxy[500])
set
當(dāng)為對(duì)象里面的屬性賦值的時(shí)候,會(huì)觸發(fā)
set
當(dāng)給對(duì)象里面的屬性賦值的時(shí)候,會(huì)觸發(fā)set
,set
函數(shù)的結(jié)構(gòu)如下
/**
* target: 目標(biāo)對(duì)象,即通過(guò)proxy代理的對(duì)象
* key: 要賦值的屬性名稱
* value: 目標(biāo)屬性要賦的新值
* receiver: 與 get的receiver 基本一致
*/
handle.set(target,key,value, receiver)
示例
某系統(tǒng)需要錄入一系列數(shù)值用于數(shù)據(jù)統(tǒng)計(jì),但是在錄入數(shù)值的時(shí)候,可能錄入的存在一部分異常值,對(duì)于這些異常值需要在錄入的時(shí)候進(jìn)行處理, 比如大于100
的值,轉(zhuǎn)換為100
, 小于0
的值,轉(zhuǎn)換為0
, 這時(shí)候就可以使用proxy
的set
,在賦值的時(shí)候,對(duì)數(shù)據(jù)進(jìn)行處理
const numbers = []
const proxy = new Proxy(numbers, {
set(target,key,value) {
if(value < 0) {
value = 0
}else if(value > 100) {
value = 100
}
target[key] = value
// 對(duì)于set 來(lái)說(shuō),如果操作成功必須返回true, 否則會(huì)被視為失敗
return true
}
})
proxy.push(1)
proxy.push(101)
proxy.push(-10)
// 輸出 [1, 100, 0]
console.log(numbers)
對(duì)比Vue2.0
在使用Vue2.0
的時(shí)候,如果給對(duì)象添加新屬性的時(shí)候,往往需要調(diào)用$set
, 這是因?yàn)?code>Object.defineProperty只能監(jiān)聽(tīng)已存在的屬性,而新增的屬性無(wú)法監(jiān)聽(tīng),而通過(guò)$set
相當(dāng)于手動(dòng)給對(duì)象新增了屬性,然后再觸發(fā)數(shù)據(jù)響應(yīng)。但是對(duì)于Vue3.0
來(lái)說(shuō),因?yàn)槭褂昧?code>Proxy, 在他的set
鉤子函數(shù)中是可以監(jiān)聽(tīng)到新增屬性的,所以就不再需要使用$set
const obj = {
name: '子君'
}
const proxy = new Proxy(obj, {
set(target,key,value) {
if(!target.hasOwnProperty(key)) {
console.log(`新增了屬性${key},值為${value}`)
}
target[key] = value
return true
}
})
// 新增 公眾號(hào) 屬性
// 輸出 新增了屬性gzh,值為前端有的玩
proxy.gzh = '前端有的玩'
has
當(dāng)使用
in
判斷屬性是否在proxy
代理對(duì)象里面時(shí),會(huì)觸發(fā)has
/**
* target: 目標(biāo)對(duì)象,即通過(guò)proxy代理的對(duì)象
* key: 要判斷的key是否在target中
*/
handle.has(target,key)
示例
一般情況下我們?cè)?code>js中聲明私有屬性的時(shí)候,會(huì)將屬性的名字以_
開(kāi)頭,對(duì)于這些私有屬性,是不需要外部調(diào)用,所以如果可以隱藏掉是最好的,這時(shí)候就可以通過(guò)has
在判斷某個(gè)屬性是否在對(duì)象時(shí),如果以_
開(kāi)頭,則返回false
const obj = {
publicMethod() {},
_privateMethod(){}
}
const proxy = new Proxy(obj, {
has(target, key) {
if(key.startsWith('_')) {
return false
}
return Reflect.get(target,key)
}
})
// 輸出 false
console.log('_privateMethod' in proxy)
// 輸出 true
console.log('publicMethod' in proxy)
deleteProperty
當(dāng)使用
delete
去刪除對(duì)象里面的屬性的時(shí)候,會(huì)進(jìn)入deleteProperty`攔截器
/**
* target: 目標(biāo)對(duì)象,即通過(guò)proxy代理的對(duì)象
* key: 要?jiǎng)h除的屬性
*/
handle.deleteProperty(target,key)
示例
現(xiàn)在有一個(gè)用戶信息的對(duì)象,對(duì)于某些用戶信息,只允許查看,但不能刪除或者修改,對(duì)此使用Proxy
可以對(duì)不能刪除或者修改的屬性進(jìn)行攔截并拋出異常,如下
const userInfo = {
name: '子君',
gzh: '前端有的玩',
sex: '男',
age: 22
}
// 只能刪除用戶名和公眾號(hào)
const readonlyKeys = ['name', 'gzh']
const proxy = new Proxy(userInfo, {
set(target,key,value) {
if(readonlyKeys.includes(key)) {
throw new Error(`屬性${key}不能被修改`)
}
target[key] = value
return true
},
deleteProperty(target,key) {
if(readonlyKeys.includes(key)) {
throw new Error(`屬性${key}不能被刪除`)
return
}
delete target[key]
return true
}
})
// 報(bào)錯(cuò)
delete proxy.name
對(duì)比Vue2.0
其實(shí)與$set
解決的問(wèn)題類似,Vue2.0
是無(wú)法監(jiān)聽(tīng)到屬性被刪除的,所以提供了$delete
用于刪除屬性,但是對(duì)于Proxy
,是可以監(jiān)聽(tīng)刪除操作的,所以就不需要再使用$delete
了
其他操作
在上文中,我們提到了Proxy
的handler
提供了十三個(gè)函數(shù),在上面我們列舉了最常用的三個(gè),其實(shí)每一個(gè)的用法都是基本一致的,比如ownKeys
,當(dāng)通過(guò)Object.getOwnPropertyNames
,Object.getownPropertySymbols
,Object.keys
,Reflect.ownKeys
去獲取對(duì)象的信息的時(shí)候,就會(huì)進(jìn)入ownKeys
這個(gè)鉤子函數(shù),使用這個(gè)我們就可以對(duì)一些我們不像暴露的屬性進(jìn)行保護(hù),比如一般會(huì)約定_
開(kāi)頭的為私有屬性,所以在使用Object.keys
去獲取對(duì)象的所有key
的時(shí)候,就可以把所有_
開(kāi)頭的屬性屏蔽掉。關(guān)于剩余的那些屬性,建議大家多去看看MDN
中的介紹。
Reflect
在上面,我們獲取屬性的值或者修改屬性的值都是通過(guò)直接操作target
來(lái)實(shí)現(xiàn)的,但實(shí)際上ES6
已經(jīng)為我們提供了在Proxy
內(nèi)部調(diào)用對(duì)象的默認(rèn)行為的API
,即Reflect
。比如下面的代碼
const obj = {}
const proxy = new Proxy(obj, {
get(target,key,receiver) {
return Reflect.get(target,key,receiver)
}
})
大家可能看到上面的代碼與直接使用target[key]
的方式?jīng)]什么區(qū)別,但實(shí)際上Reflect
的出現(xiàn)是為了讓Object
上面的操作更加規(guī)范,比如我們要判斷某一個(gè)prop
是否在一個(gè)對(duì)象中,通常會(huì)使用到in
,即
const obj = {name: '子君'}
console.log('name' in obj)
但上面的操作是一種命令式的語(yǔ)法,通過(guò)Reflect
可以將其轉(zhuǎn)變?yōu)楹瘮?shù)式的語(yǔ)法,顯得更加規(guī)范
Reflect.has(obj,'name')
除了has
,get
之外,其實(shí)Reflect
上面總共提供了十三個(gè)靜態(tài)方法,這十三個(gè)靜態(tài)方法與Proxy
的handler
上面的十三個(gè)方法是一一對(duì)應(yīng)的,通過(guò)將Proxy
與Reflect
相結(jié)合,就可以對(duì)對(duì)象上面的默認(rèn)操作進(jìn)行攔截處理,當(dāng)然這也就屬于函數(shù)元編程的范疇了。
(推薦微課:Vue 2.x 微課)
總結(jié)
有的同學(xué)可能會(huì)有疑惑,我不會(huì)Proxy
和Reflect
就學(xué)不了Vue3.0
了嗎?其實(shí)懂不懂這個(gè)是不影響學(xué)習(xí)Vue3.0
的,但是如果想深入 去理解Vue3.0
,還是很有必要了解這些的。比如經(jīng)常會(huì)有人在使用Vue2
的時(shí)候問(wèn),為什么我數(shù)組通過(guò)索引修改值之后,界面沒(méi)有變呢?當(dāng)你了解到Object.defineProperty
的使用方式與限制之后,就會(huì)恍然大悟,原來(lái)如此。
來(lái)源公眾號(hào):前端有點(diǎn)玩
文章來(lái)源鏈接:mp.weixin.qq.com/s/JQDA6bP805xuN-tzuimtAA
作者:前端進(jìn)擊者
以上就是關(guān)于學(xué)習(xí)Vue3.0,你需要先了解一下Proxy的相關(guān)介紹了,希望對(duì)大家有所幫助。