vue中this.$message的實(shí)現(xiàn)過(guò)程詳解
一、vue中為什么可以直接使用this.$message
1、Message在開(kāi)發(fā)中的使用頻率很高,也算是Element-UI組件庫(kù)中比較簡(jiǎn)單的,對(duì)于感興趣的朋友可以一起探討一下Message組件的實(shí)現(xiàn)
2、組件的使用
this.$message('這是一條消息提示');
this.$message({ message: '恭喜你,這是一條成功消息', type: 'success' });3、整體的執(zhí)行過(guò)程
Vue項(xiàng)目中的使用如下:
// main.js // 1.引入組件庫(kù) import ElementUI from 'element-ui'; // 2.使用組件庫(kù) Vue.use(ElementUI);
Element-UI組件庫(kù)中邏輯如下:
每次當(dāng)Vue.use的時(shí)候,在Element—UI內(nèi)部會(huì)觸發(fā)Element-UI的install方法,然后將組件注冊(cè)為全局組件,將方法放到Vue.prototype上,本次只看Message部分即可
// 文件目錄:node-modules/element-ui/src/index.js
// 1.引入Message對(duì)象
import Message from '../packages/message/index.js';
// 2. 定義 install函數(shù),
const install = function(Vue, opts = {}) {
// 將組件遍歷注冊(cè)為全局組件,例如Button組件
components.forEach(component => {
Vue.component(component.name, component);
});
// 將方法放到Vue原型上
Vue.prototype.$message = Message;
};
經(jīng)過(guò)上述兩步的處理,我們可以直接在項(xiàng)目中通過(guò)this.$message進(jìn)行組件的展示控制,接下來(lái)繼續(xù)探索Element-UI內(nèi)部如何處理的。
二、message組件的內(nèi)部實(shí)現(xiàn)原理
1、設(shè)計(jì)思路
Message的調(diào)用方式都是通過(guò)this.$message進(jìn)行調(diào)用,通過(guò)傳遞不同的options進(jìn)行組件樣式和內(nèi)容的控制,展示的html是動(dòng)態(tài)的插入到document中并在duration之后移除,組件的展示通過(guò)vue實(shí)例訪問(wèn)并控制。
組件的整體結(jié)構(gòu)分為展示部分和控制部分
展示部分(main.vue):?jiǎn)为?dú)抽離出一個(gè)組件,將組件的展示邏輯和交互封裝集中處理
控制部分(main.js):是承接vue實(shí)例和組件展示

2、展示部分,即main.vue
首先看一下刪減版本之后展示部分的組件內(nèi)容,代碼刪除了容錯(cuò)和邊界值判斷的代碼,僅僅展示了基本功能。
<template>
<transition name="el-message-fade" @after-leave="handleAfterLeave">
<div
class="el-message"
:style="positionStyle"
v-show="visible">
<slot>
<p>{{ message }}</p>
</slot>
</div>
</transition>
</template>
<script type="text/javascript">
export default {
data() {
return {
visible: false,
message: '',
duration: 3000,
onClose: null,
closed: false,
verticalOffset: 20,
timer: null
};
},
computed: {
positionStyle() { // 控制當(dāng)前組件的顯示位置
return {
'top': `${ this.verticalOffset }px`
};
}
},
watch: {
// 監(jiān)聽(tīng)closed的變化,設(shè)置為true時(shí),將組件銷毀
closed(newVal) {
if (newVal) {
this.visible = false;
}
}
},
methods: {
// transtion組件的鉤子,觸發(fā)after-leave時(shí)執(zhí)行
handleAfterLeave() {
this.$destroy(true); // 銷毀組件
this.$el.parentNode.removeChild(this.$el); // 將組件的DOM移除
},
close() {
this.closed = true; // 組件隱藏
if (typeof this.onClose === 'function') {
this.onClose(this);
}
},
// 每次手動(dòng)啟動(dòng)編譯之后 設(shè)置其展示時(shí)間duration之后關(guān)閉
startTimer() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, this.duration);
}
}
},
mounted() {
this.startTimer();
}
};
</script>使用了Vue官方封裝的transition組件,不僅提供了良好的過(guò)渡效果,還提供了合適的鉤子便于開(kāi)發(fā)者控制,組件中使用after-leave鉤子,當(dāng)組件銷毀時(shí)進(jìn)行組件的銷毀和DOM的移除,visible用于控制組件的展示與銷毀,計(jì)算屬性positionStyle用于設(shè)置組件的展示位置,message為組件展示的內(nèi)容數(shù)據(jù),搞明白這些變量、計(jì)算屬性和方法的作用即可。
script部分可參考注釋進(jìn)行理解,需要注意兩個(gè)地方
(1)首先需要注意生命周期鉤子mount時(shí)做的事情,為何如此做?因?yàn)椴淮嬖趀l選項(xiàng),實(shí)例不會(huì)立即進(jìn)入編譯階段,需要顯示調(diào)用$mount 手動(dòng)開(kāi)啟編譯
(2)還需要注意的時(shí)close函數(shù)中做了兩件事,設(shè)置closed的值觸發(fā)對(duì)應(yīng)的watch,關(guān)閉組件,若是存在onClose方法則調(diào)用,注意這個(gè)onClose函數(shù)的定義是在控制部分定義,稍后會(huì)說(shuō)明
3、控制部分 至此已經(jīng)清楚Vue中是通過(guò)this.$message觸發(fā)組件的展示,而展示部分的組件內(nèi)容也已完成,現(xiàn)在就需要通過(guò)控制部分將兩者連接,達(dá)到期望的功能
與Vue關(guān)聯(lián)比較簡(jiǎn)單,僅僅是定義一個(gè)方法并將其導(dǎo)出即可
const Message = options => {
// 邏輯編寫(xiě)....
}
export default Message;這個(gè)時(shí)候通過(guò)this.$message即可調(diào)用,接下來(lái)便是將Message函數(shù)與組件關(guān)聯(lián),并控制展示部分
Message核心需要做那些事情
- 編譯組件,使用渲染并插入到
body中 - 控制組件內(nèi)的
visible變量,觸發(fā)組件的展示 - 控制組件內(nèi)的
verticalOffset變量,決定組件展示時(shí)的位置
手動(dòng)開(kāi)啟組件編譯,獲取其實(shí)例訪問(wèn)內(nèi)部data和渲染到頁(yè)面上
// 1. 使用基礎(chǔ) Vue 構(gòu)造器,創(chuàng)建一個(gè)“子類”
let MessageConstructor = Vue.extend(Main);
// 2. 組件實(shí)例, 可以通過(guò)instance訪問(wèn) visible和verticalOffset
instance = new MessageConstructor({
data: options
});整個(gè)Message方法其余部分就是在做容錯(cuò)和健壯處理,整體簡(jiǎn)潔版代碼如下
let MessageConstructor = Vue.extend(Main);
let instance; // 當(dāng)前組件
let instances = []; // 將所有的message組件收集,用于位置的判斷和銷毀等
let seed = 1; // 每個(gè)message實(shí)例都有一個(gè)唯一標(biāo)識(shí)
const Message = options => {
options = options || {
message: 'content' + Date.now(),
onClose(message){
console.log('關(guān)閉時(shí)的回調(diào)函數(shù), 參數(shù)為被關(guān)閉的 message 實(shí)例',message);
}
};
if (typeof options === 'string') {
options = {
message: options
};
}
// 關(guān)閉時(shí)的回調(diào)函數(shù), 參數(shù)為被關(guān)閉的 message 實(shí)例
let userOnClose = options.onClose;
let id = 'message_' + seed++;
// 增加 onClose 方法,組件銷毀時(shí),在組件內(nèi)部調(diào)用
options.onClose = function() {
Message.close(id, userOnClose);
};
// 組件實(shí)例,此時(shí)options與組件的data關(guān)聯(lián)
instance = new MessageConstructor({
data: options
});
instance.id = id; // 設(shè)置ID
instance.$mount(); // 因?yàn)椴淮嬖趀l選項(xiàng),實(shí)例不會(huì)立即進(jìn)入編譯階段,需要顯示調(diào)用$mount 手動(dòng)開(kāi)啟編譯
document.body.appendChild(instance.$el); // 將Message 組件插入到body中
// 設(shè)置組件距離頂部的距離,每個(gè)message組件會(huì)有16px的間距
let verticalOffset = options.offset || 20;
instances.forEach(item => {
verticalOffset += item.$el.offsetHeight + 16;
});
instance.verticalOffset = verticalOffset;
instance.visible = true; // 控制展示
instance.$el.style.zIndex = 99; // 控制層級(jí)
instances.push(instance);
return instance;
};
this.$message.error('')的實(shí)現(xiàn)
Message組件支持this.$message.error('錯(cuò)了哦,這是一條錯(cuò)誤消息');調(diào)用使用,到目前為止還不支持,代碼比較簡(jiǎn)單直接上代碼
// 為每個(gè) type 定義了各自的方法,如 Message.success(options),可以直接調(diào)用
['success', 'warning', 'info', 'error'].forEach(type => {
Message[type] = options => {
if (typeof options === 'string') {
options = {
message: options
};
}
options.type = type;
return Message(options);
};
});
展示組件內(nèi)部會(huì)調(diào)用this.onClose(this),組件內(nèi)部設(shè)置this.visible=false關(guān)閉彈框,并且移除其對(duì)應(yīng)的DOM結(jié)構(gòu),但是頁(yè)面展示多個(gè)組件時(shí)需要修改其余組件的位置
onClose函數(shù)是在Message函數(shù)中定義
// 關(guān)閉時(shí)的回調(diào)函數(shù), 參數(shù)為被關(guān)閉的 message 實(shí)例
let userOnClose = options.onClose;
// 增加 onClose 方法,組件銷毀時(shí),在組件內(nèi)部調(diào)用
options.onClose = function() {
Message.close(id, userOnClose);
};onClose函數(shù)最終調(diào)用的是Message上的靜態(tài)方法close
函數(shù)Message.close內(nèi)部主要做了幾件事情
- 在頁(yè)面顯示的組件數(shù)組中找到需要關(guān)閉的組件,將其移除
- 重新計(jì)算剩余組件的位置
三、總結(jié)
至此基礎(chǔ)版本的Message已經(jīng)完成,組件的代碼不到200行,通過(guò)源碼的簡(jiǎn)單閱讀和分析,知識(shí)點(diǎn)并不是很多,但是優(yōu)秀組件封裝的思路還是值得學(xué)習(xí)和借鑒
到此這篇關(guān)于vue中this.$message的實(shí)現(xiàn)過(guò)程詳解的文章就介紹到這了,更多相關(guān)vue中this.$message實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
TypeError:res.forEach?is?not?a?function報(bào)錯(cuò)解決辦法
這篇文章主要給大家介紹了關(guān)于TypeError:res.forEach?is?not?a?function報(bào)錯(cuò)的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-07-07
vue2中使用SSE(服務(wù)器發(fā)送事件)原因分析
SSE是圍繞只讀Comet交互推出的API或者模式,SSE 支持短輪詢、長(zhǎng)輪詢和HTTP 流,而且能在斷開(kāi)連接時(shí)自動(dòng)確定何時(shí)重新連接,本文重點(diǎn)給大家介紹2023-10-10
vue-seamless-scroll無(wú)縫滾動(dòng)組件使用方法詳解
這篇文章主要為大家詳細(xì)介紹了vue-seamless-scroll無(wú)縫滾動(dòng)組件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
html-webpack-plugin修改頁(yè)面的title的方法
這篇文章主要介紹了html-webpack-plugin修改頁(yè)面的title的方法 ,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
vue3?組合式api中?ref?和$parent?的使用方法
vue3中, 在 組件中添加一個(gè) component ref=“xxx” ,就可以在父組件中得到 子組件的 dom 對(duì)象, 以及 虛擬的 dom 對(duì)象, 有了虛擬 dom, 我們就可以在父組件中控制子組件的顯示了,這篇文章主要介紹了vue3組合式api中ref和$parent的使用,需要的朋友可以參考下2023-09-09
vue學(xué)習(xí)筆記五:在vue項(xiàng)目里面使用引入公共方法詳解
這篇文章主要介紹了在vue項(xiàng)目里面使用引入公共方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
ant design Vue 純前端實(shí)現(xiàn)分頁(yè)問(wèn)題
這篇文章主要介紹了ant design Vue 純前端實(shí)現(xiàn)分頁(yè)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
vue-cli history模式實(shí)現(xiàn)tomcat部署報(bào)404的解決方式
這篇文章主要介紹了vue-cli history模式實(shí)現(xiàn)tomcat部署報(bào)404的解決方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09

