Vue組件的三種調用方式

最近在寫fj-service-system的時候,遇到了一些問題。那就是我有些組件,比如Dialog、Message這樣的組件,是引入三方組件庫,比如element-ui這樣的,還是自己實現一個?雖然它們有按需引入的功能,但是整體風格和我的整個系統不搭。于是就可以考慮自己手動實現這些簡單的組件了。

通常我們看Vue的一些文章的時候,我們能看到的通常是講Vue單文件組件化開發頁面的。單一組件開發的文章相對就較少了。我在做fj-service-system項目的時候,發現其實單一組件開發也是很有意思的。可以寫寫記錄下來。因為寫的不是什么ui框架,所以也只是一個記錄,沒有github倉庫,權且看代碼吧。


  • v-model或者.sync顯式控制組件顯示隱藏
  • 通過js代碼調用
  • 通過Vue指令調用

在寫組件的時候很多寫法、靈感來自于element-ui,感謝。

Dialog

我習慣把這個東西叫做對話框,實際上還有叫做modal(彈窗)組件的叫法。其實就是在頁面里,彈出一個小窗口,這個小窗口里的內容可以定制。通常可以用來做登錄功能的對話框。

1302312-20180114172825066-582482386.gif

這種組件就很適合通過v-model或者.sync的方式來顯式的控制出現和消失。它可以直接寫在頁面里,然后通過data去控制——這也是最符合Vue的設計思路的組件。

為此我們可以寫一個組件就叫做Dialog.vue

<template><div class="dialog"><div class="dialog__wrapper" v-if="visble" @clcik="closeModal"><div class="dialog"><div class="dialog__header"><div class="dialog__title">{{ title }}</div></div><div class="dialog__body"><slot></slot></div><div class="dialog__footer"><slot name="footer"></slot></div></div></div><div class="modal" v-show="visible"></div></div>
</template><script>export default {name: 'dialog',props: {title: String,visible: {type: Boolean,default: false}},methods: {close() {this.$emit('update:visible', false) // 傳遞關閉事件},closeModal(e) {if (this.visible) {document.querySelector('.dialog').contains(e.target) ? '' : this.close(); // 判斷點擊的落點在不在dialog對話框內,如果在對話框外就調用this.close()方法關閉對話框}}}}
</script>

CSS什么的就不寫了,跟組件本身關系比較小。不過值得注意的是,上面的dialog__wrapper這個class也是全屏的,透明的,主要用于獲取點擊事件并鎖定點擊的位置,通過DOM的Node.contains()方法來判斷點擊的位置是不是dialog本身,如果是點擊到了dialog外面,比如半透明的modal層那么就派發關閉事件,把dialog給關閉掉。

當我們在外部要調用的時候,就可以如下調用:

<template><div class="xxx"><dialog :visible.sync="visible"></dialog> <button @click="openDialog"></button></div>
</template><script>import Dialog from 'Dialog'export default {components: {Dialog},data() {return {visible: false}},methods: {openDialog() {this.visible = true // 通過data顯式控制dialog}}}
</script>

為了Dialog開啟和關閉好看點,你可試著加上<transition></transition>組件配合上過渡效果,簡單的一點過渡動效也將會很好看。

Notice

這個組件類似于element-ui的message(消息提示)。它吸引我的最大的地方在于,它不是通過顯式的在頁面里寫好組件的html結構通過v-model去調用的,而是通過在js里通過形如this.$message()這樣的方法調用的。這種方法雖然跟Vue的數據驅動的思想有所違背。不過不得不說在某些情況下真的特別方便。

1302312-20180114172835254-1510776768.gif

對于Notice這種組件,一次只要提示幾個文字,給用戶簡單的消息提示就行了。提示的信息可能是多變的,甚至可以出現疊加的提示。如果通過第一種方式去調用,事先就得寫好html結構,這無疑是麻煩的做法,而且無法預知有多少消息提示框。而通過js的方法調用的話,只需要考慮不同情況調用的文字、類型不同就可以了。

而之前的做法都是寫一個Vue文件,然后通過components屬性引入頁面,顯式寫入標簽調用的。那么如何將組件通過js的方法去調用呢?

這里的關鍵是Vue的extend方法。

文檔里并沒有詳細給出extend能這么用,只是作為需要手動mount的一個Vue的組件構造器說明了一下而已。

通過查看element-ui的源碼,才算是理解了如何實現上述的功能。

首先依然是創建一個Notice.vue的文件

<template><div class="notice"><div class="content">{{ content }}</div></div>
</template><script>export default {name: 'notice',data () {return {visible: false,content: '',duration: 3000}},methods: {setTimer() {setTimeout(() => {this.close() // 3000ms之后調用關閉方法}, this.duration)},close() {this.visible = falsesetTimeout(() => {this.$destroy(true)this.$el.parentNode.removeChild(this.$el) // 從DOM里將這個組件移除}, 500)}},mounted() {this.setTimer() // 掛載的時候就開始計時,3000ms后消失}}
</script>

上面寫的東西跟普通的一個單文件Vue組件沒有什么太大的區別。不過區別就在于,沒有props了,那么是如何通過外部來控制這個組件的顯隱呢?

所以還需要一個js文件來接管這個組件,并調用extend方法。同目錄下可以創建一個index.js的文件。

import Vue from 'vue'const NoticeConstructor = Vue.extend(require('./Notice.vue')) // 直接將Vue組件作為Vue.extend的參數let nId = 1const Notice = (content) => {let id = 'notice-' + nId++const NoticeInstance = new NoticeConstructor({data: {content: content}}) // 實例化一個帶有content內容的NoticeNoticeInstance.id = idNoticeInstance.vm = NoticeInstance.$mount() // 掛載但是并未插入dom,是一個完整的Vue實例NoticeInstance.vm.visible = trueNoticeInstance.dom = NoticeInstance.vm.$eldocument.body.appendChild(NoticeInstance.dom) // 將dom插入bodyNoticeInstance.dom.style.zIndex = nId + 1001 // 后插入的Notice組件z-index加一,保證能蓋在之前的上面return NoticeInstance.vm
}export default {install: Vue => {Vue.prototype.$notice = Notice // 將Notice組件暴露出去,并掛載在Vue的prototype上}
}

這個文件里我們能看到通過NoticeConstructor我們能夠通過js的方式去控制一個組件的各種屬性。最后我們把它注冊進Vue的prototype上,這樣我們就可以在頁面內部使用形如this.$notice()方法了,可以方便調用這個組件來寫做出簡單的通知提示效果了。

當然別忘了這個相當于一個Vue的插件,所以需要去主js里調用一下Vue.use()方法:

// main.js// ...
import Notice from 'notice/index.js'Vue.use(Notice)// ...

Loading

在看element-ui的時候,我也發現了一個很有意思的組件,是Loading,用于給一些需要加載數據等待的組件套上一層加載中的樣式的。這個loading的調用方式,最方便的就是通過v-loading這個指令,通過賦值的true/false來控制Loading層的顯隱。這樣的調用方法當然也是很方便的。而且可以選擇整個頁面Loading或者某個組件Loading。這樣的開發體驗自然是很好的。

1302312-20180114172844754-1386905862.gif

其實跟Notice的思路差不多,不過因為涉及到directive,所以在邏輯上會相對復雜一點。

平時如果不涉及Vue的directive的開發,可能是不會接觸到modifiers、binding等概念。參考文檔

簡單說下,形如:v-loading.fullscreen="true"這句話,v-loading就是directive,fullscreen就是它的modifier,true就是binding的value值。所以,就是通過這樣簡單的一句話實現全屏的loading效果,并且當沒有fullscreen修飾符的時候就是對擁有該指令的元素進行loading效果。組件通過binding的value值來控制loading的開啟和關閉。(類似于v-model的效果)

其實loading也是一個實際的DOM節點,只不過要把它做成一個方便的指令還不是特別容易。

首先我們需要寫一下loading的Vue組件。新建一個Loading.vue文件

<template><transitionname="loading"@after-leave="handleAfterLeave"><divv-show="visible"class="loading-mask":class={'fullscreen': fullscreen}><div class="loading">...</div><div class="loading-text" v-if="text">{{ text }}</div></div></transition>
</template>
<script>
export default {name: 'loading',data () {return {visible: true,fullscreen: true,text: null}},methods: {handleAfterLeave() {this.$emit('after-leave');}}
}
</script>
<style>
.loading-mask{position: absolute; // 非全屏模式下,position是absolutez-index: 10000;background-color: rgba(255,235,215, .8);margin: 0;top: 0;right: 0;bottom: 0;left: 0;transition: opacity .3s;
}
.loading-mask.fullscreen{position: fixed; // 全屏模式下,position是fixed
}
// ...
</style>

Loading關鍵是實現兩個效果:

????1.全屏loading,此時可以通過插入body下,然后將Loading的position改為fixed,插入body實現。
????2.對所在的元素進行loading,此時需要對當前這個元素的的position修改:如果不是absolute的話,就將其修改為relatvie,并插入當前元素下。此時Loading的position就會相對于當前元素進行絕對定位了。
所以在當前目錄下創建一個index.js的文件,用來聲明我們的directive的邏輯。

import Vue from 'vue'
const LoadingConstructor = Vue.extend(require('./Loading.vue'))export default {install: Vue => {Vue.directive('loading', { // 指令的關鍵bind: (el, binding) => {const loading = new LoadingConstructor({ // 實例化一個loadingel: document.createElement('div'),data: {text: el.getAttribute('loading-text'), // 通過loading-text屬性獲取loading的文字fullscreen: !!binding.modifiers.fullscreen }})el.instance = loading; // el.instance是個Vue實例el.loading = loading.$el; // el.loading的DOM元素是loading.$elel.loadingStyle = {};toggleLoading(el, binding);},update: (el, binding) => {el.instance.setText(el.getAttribute('loading-text'))if(binding.oldValue !== binding.value) {toggleLoading(el, binding)}   },unbind: (el, binding) => { // 解綁if(el.domInserted) {if(binding.modifiers.fullscreen) {document.body.removeChild(el.loading);}else {el.loading &&el.loading.parentNode &&el.loading.parentNode.removeChild(el.loading);}}}})const toggleLoading = (el, binding) => { // 用于控制Loading的出現與消失if(binding.value) { Vue.nextTick(() => {if (binding.modifiers.fullscreen) { // 如果是全屏el.originalPosition = document.body.style.position;el.originalOverflow = document.body.style.overflow;insertDom(document.body, el, binding); // 插入dom} else {el.originalPosition = el.style.position;insertDom(el, el, binding); // 如果非全屏,插入元素自身}})} else {if (el.domVisible) {el.instance.$on('after-leave', () => {el.domVisible = false;if (binding.modifiers.fullscreen && el.originalOverflow !== 'hidden') {document.body.style.overflow = el.originalOverflow;}if (binding.modifiers.fullscreen) {document.body.style.position = el.originalPosition;} else {el.style.position = el.originalPosition;}});el.instance.visible = false;}}}const insertDom = (parent, el, binding) => { // 插入dom的邏輯if(!el.domVisible) {Object.keys(el.loadingStyle).forEach(property => {el.loading.style[property] = el.loadingStyle[property];});if(el.originalPosition !== 'absolute') {parent.style.position = 'relative'}if (binding.modifiers.fullscreen) {parent.style.overflow = 'hidden'}el.domVisible = true;parent.appendChild(el.loading) // 插入的是el.loading而不是el本身Vue.nextTick(() => {el.instance.visible = true;});el.domInserted = true;}}}
}

同樣,寫完整個邏輯,我們需要將其注冊到項目里的Vue下:

// main.js// ...
import Loading from 'loading/index.js'Vue.use(Loading)// ...

至此我們已經可以使用形如

<div v-loading.fullscreen="loading" loading-text="正在加載中">

這樣的方式來實現調用一個loading組件了。

總結

在用Vue寫我們的項目的時候,不管是寫頁面還是寫形如這樣的功能型組件,其實都是一件很有意思的事情。本文介紹的三種調用組件的方式,也是根據實際情況出發而實際操作、實現的。不同的組件通過不同的方式去調用,方便了開發人員,也能更好地對代碼進行維護。當然也許還有其他的方式,我并沒有了解,也歡迎大家在評論里指出!

最后再次感謝element-ui的源碼給予的極大啟發。

文章作者: Molunerfinn
文章鏈接: https://molunerfinn.com/vue-components/
版權聲明: 本博客所有文章除特別聲明外,均采用 CC BY-NC-SA 4.0 許可協議。轉載請注明來自 MARKSZのBlog!

轉載于:https://www.cnblogs.com/wwhhq/p/8283769.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/278610.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/278610.shtml
英文地址,請注明出處:http://en.pswp.cn/news/278610.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

axios把post的RequestPayload格式轉為formdata

方法一&#xff1a;配置transformRequest&#xff0c;缺點&#xff1a;其他請求格式的數據也會被重新格式化&#xff08;PUT&#xff0c;PATCH&#xff09; const service axios.create({//設置axios為form-data 方法1// headers: {// post: {// "Content-T…

火狐打印預覽_將打印和打印預覽命令添加到Firefox的上下文菜單

火狐打印預覽Have you been thinking about how much easier it would be to having the Print & Print Preview commands in Firefox’s Context Menu? The Print Context Menu extension for Firefox allows you to avoid having to use the File Menu to access the pr…

每個人都要在自己的“時區”里找到自己的快樂

祝小妹和自己生日快樂&#xff0c;人人都想快樂&#xff0c;卻在平常的365天悶悶不樂&#xff0c;但愿家人朋友在平常的每一天都很夠健康快樂&#xff01; 在我那個開不了機的手機記事薄有句話還記得&#xff1a;你們不要刻意等我&#xff0c;因為可能現在的我還沒來得及去發現…

《2017 云計算評測報告》:帶你了解 AWS、阿里云、騰訊云等八家云計算服務提供商的綜合用戶體驗情況...

報告電子版至聽云官方博客下載&#xff1a;http://blog.tingyun.com/web/article/detail/1352 評測說明 評測目標&#xff1a;同一應用&#xff08;網站&#xff09;在不同云上的用戶訪問體驗&#xff0c;以及對云資源的使用 洞察周期及范圍&#xff1a;2017年4月-2017年9月 訪…

js以變量為鍵

let key "dynamic",obj{[key]:true }; obj[key2]key console.log(obj)一般在配置文件中應用較多

搭建jenkins實現自動化部署

參考&#xff1a; https://www.cnblogs.com/rslai/p/8135460.html轉載于:https://www.cnblogs.com/lihuanhuan/p/10612123.html

python 新聞摘要_每日新聞摘要:Microsoft內部禁止應用程序,這樣就可以了

python 新聞摘要Recently, a list of apps that Microsoft prohibits for internal employee use leaked, including Slack, Grammarly, and others. It’s tempting to think these are the actions of a company hating competition, but the truth is more complicated. 最近…

vue使用process.env搭建自定義運行環境

一、vue-cli項目下默認有三種模式&#xff1a; development&#xff1a;在 vue-cli-service serve 時使用。production&#xff1a;在 vue-cli-service build 和 vue-cli-service test:e2e 時使用。test&#xff1a;在 vue-cli-service test:unit 時使用。 對應的 process.env…

bootstrap評分插件 Bootstrap Star Rating Examples

http://www.jq22.com/demo/bootstrap-star-rating-master201708041812/ 轉載于:https://www.cnblogs.com/waw/p/8288951.html

http 請求報文

1、報文 2、http請求方法 restful接口 post&#xff1a;創建 put&#xff1a;更新 轉載于:https://www.cnblogs.com/mengfangui/p/10171559.html

chrome硬件加速_如何在Chrome中打開和關閉硬件加速

chrome硬件加速Google Chrome comes equipped with hardware acceleration, a feature which takes advantage of your computer’s GPU to speed up processes and free vital CPU time. However, sometimes driver incompatibilities can cause this feature to misbehave an…

春節您“搶票”到手了嗎,如果沒,請進來看看!

不是為了賣“廣告”!我與軟件作者從不認識&#xff01;我與軟件作者因為搶票認識&#xff0c;不&#xff0c;只認識他寫的軟件&#xff01;51CTO博客2.0后&#xff0c;我一直沒有寫博文&#xff01;主要原因&#xff1a;不能用Live Writer寫博文&#xff0c;復制&#xff0c;粘…

兩個矩陣相加 Exercise08_05

1 import java.util.Scanner;2 /**3 * author 冰櫻夢4 * 時間&#xff1a;2018年12月5 * 題目&#xff1a;兩個矩陣相加6 *7 */8 public class Exercise08_05 {9 public static void main(String[] args){ 10 Scanner inputnew Scanner(System.in); 11 …

vue element form中input等組件不能輸入值

<el-input v-model"form.inputVal " />由于data中form只是一個空對象{}&#xff0c;當主動設置 form.inputVal “” 后input卻仍無法輸入值&#xff0c;這是因為inputVal 屬性沒有get和set&#xff0c;需要用vue內置屬性設置&#xff1a;this.$set(this.form,…

如何在PowerPoint中制作三折

While Microsoft PowerPoint is almost exclusively used for presentation purposes, it’s also a great application for creating interesting and visually appealing brochures. Here’s how to create (and print out) a tri-fold using PowerPoint. 盡管Microsoft Powe…

徹底理解數據庫事物

事務 事務(Transaction)&#xff0c;一般是指要做的或所做的事情。在計算機術語中是指訪問并可能更新數據庫中各種數據項的一個程序執行單元(unit)。在計算機術語中&#xff0c;事務通常就是指數據庫事務。 概念 一個數據庫事務通常包含對數據庫進行讀或寫的一個操作序列。它的…

HttpRunner自動化框架學習筆記

一.簡單介紹 HttpRunner 是一款面向 HTTP(S) 協議的通用測試框架&#xff0c;只需編寫維護一份 YAML/JSON 腳本&#xff0c;即可實現自動化測試、性能測試、線上監控、持續集成等多種測試需求。 支持python2和python3 二.框架特點 繼承 Requests 的全部特性&#xff0c;輕松實現…

如何在Chrome中為Gmail啟用桌面通知

Last year Google rolled out desktop notifications for Google Calendar, now you can get Gmail and Gchat notifications on your desktop too. Read on as we walk you through configuring them both. 去年Google推出了Google日歷的桌面通知&#xff0c;現在您也可以在桌…

vue集成iconfont、fontawesome和圖標選擇器(含fontawesome、el-icon和加入的iconfont)

目錄&#xff08;一&#xff09;引入iconfont字體圖標庫將圖標加入購物車新建&#xff08;添加至&#xff09;項目下載后項目中引入&#xff08;二&#xff09;引入fontawesome&#xff08;三&#xff09;圖標選擇器效果圖結構使用源碼&#xff08;一&#xff09;引入iconfont字…

java之Synchronize

2019獨角獸企業重金招聘Python工程師標準>>> 實現原理&#xff1a;JVM 是通過進入、退出對象監視器( Monitor )來實現對方法、同步塊的同步的。 具體實現是在編譯之后在同步方法調用前加入一個 monitor.enter 指令&#xff0c;在退出方法和異常處插入 monitor.exit …