這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
在使用vue專案撰寫的時候,不可避免的會碰到需要時js api來呼叫組件進行顯示的情況
例如餓了么element ui 的 Notification 通知、Message 訊息提示等組件
雖然已經提供了,但是由于api的限制,我們只能通過特定的引數來有限的改變組件的樣式
之前的文章說過可以使用 new Vue()、Vue.extends等方法來進行改變這些api組件的樣式
但是同時它們也有個缺點,無法自動實時更新資料,也就是沒有雙向系結,只能靜態布局,為了解決這個痛點
我們自己動手封裝一個全域js api呼叫組件,然后再把需要的資料傳過去進去更新,自己動手豐衣足食
就以餓了么element-ui的通知組件Notification為例,實作一個全域通知彈窗下載進度條組件
正文
使用Vue.extend構造器來創建是最為方便的,不過和之前不同的是,這樣創建的實體組件只能創建單個,每一次呼叫都會重新創建一個新的實體,不會對原有的實體進行更新,所以我,我們要對實體進行快取,以便后續的資料更新
這里我以自定義創建一個下載進度彈窗通知為例
首先我們創建一個組件檔案夾下的js檔案
/components/DownLoadNotification/index.js
實作思路是用Vue.extend構造組件后,把api接收的引數直接傳入組件data使用,并為每個實體生成id,拿出dom插入到全域body中,當生成多個實體時動態計算定位的偏移量避免組件重疊
import Vue from 'vue'
import component from './index.vue'
?
const DownLoadNotification = Vue.extend(component)
?
const instances = [] // 實體快取串列
?
export const notify = (options) => {
let instance; // 單個實體
?
options.onClose = function() { // 把洗掉當前實體的方法傳入組件內
removeNotify(instance.id)
}
options.onCloseAll = () => { // 把洗掉所有實體的方法傳入組件內
removeNotifyAll()
}
?
// 直接控制實體的data,而不是通過propsData傳入
instance = new DownLoadNotification({
data: options,
})
instance.id = Date.now(); // 生成id
instance.$mount(); // 掛載,生成dom節點
document.body.appendChild(instance.$el) // 把dom節點添加到body全域
instance.visible = true // 先掛載節點再顯示節點里的內容,可以出現過渡影片,而不是一開始全部顯示
?
// 計算多個實體時的偏移量
// 第一個不需要計算,把push放到回圈下面,陣列為空時不會回圈,第二次開始則會進行計算
let verticalOffset = 0
instances.forEach((item) => {
verticalOffset += item.$el.offsetHeight + 16 // 每個組件高度間隔16px
})
?
verticalOffset += 16 // 首次最下面的組件底部距離最底部也有16px的間隙
?
instance.verticalOffset = verticalOffset // 計算出來的偏移量賦值到組件中data
?
instances.push(instance) // 快取實體
return instance
}
?
// 洗掉單個組件實體
function removeNotify(id) {
const index = instances.findIndex(item => item.id === id)
index !== -1 && instances.splice(index, 1)
}
?
// 洗掉所有組件實體
function removeNotifyAll() {
for (let i = instances.length - 1; i >= 0; i--) {
instances[i].close(); // 呼叫組件內的洗掉方法來同時洗掉實體和dom
}
}
洗掉時既要清空組件dom又要洗掉實體,所以把在js中定義的洗掉實體方法傳入組件,組件需要洗掉時呼叫即可
需要注意的是,當有多個全域組件,洗掉其中一個時,位置應當發生改變
所以洗掉其中的一個組件實體時要重新計算偏移量位置
重新改造一下 洗掉單個組件實體 的方法,大致做法就是,拿到被洗掉的當前實體的高度,然后從被洗掉實體的位置開始遍歷,后面的實體逐一洗掉被洗掉的實體高度和邊距
// 洗掉單個組件實體
function removeNotify(id) {
let index = -1;
const len = instances.length; // 未洗掉前陣列總長度
const instance = instances.filter((instance, i) => { // 獲取保存當前洗掉的實體
if (instance.id === id) {
index = i; // 保存索引
return true;
}
return false;
})[0];
instances.splice(index, 1); // 洗掉實體
?
if (len <= 1) return // 只有一個實體時不需要重新計算位置
const position = instance.position; // 獲取實體定位欄位
const removedHeight = instance.$el.offsetHeight; // 獲取實體高度
?
for (let i = index; i < len - 1 ; i++) { // 從被洗掉的位置開始遍歷
if (instances[i].position === position) { // 修改的位置定位是否一致
// 將后續元素的定位位置 減去 上一個洗掉的元素寬度 + 16px 的首次底部邊距
instances[i].$el.style[instance.verticalProperty] =
parseInt(instances[i].$el.style[instance.verticalProperty], 10) - removedHeight - 16 + 'px';
}
}
}
接下來在撰寫組件/components/DownLoadNotification/index.vue<template>
<el-collapse-transition>
<div v-if="visible" : :style="positionStyle">
<div >
<span>{{ fileName }}</span>
<span>{{ fileSize }}</span>
</div>
<el-progress
:percentage="downLoadProgress"
:status="downStatus"
:stroke-
></el-progress>
<el-button @click="close" size="mini">關 閉</el-button>
</div>
</el-collapse-transition>
</template>
<script>
export default {
data() {
return {
/* 自定義資料 */
fileName: "",
fileSize: "",
downLoadProgress: 0,
downStatus: "",
?
/* 組件基礎資料 */
id: null, // 實體id
visible: false, // 顯示控制按鈕
position: "bottom-left", // 顯示方位
verticalOffset: 0, // 位置偏移量
onClose: null, // js中傳入的洗掉當前組件方法
onCloseAll: null, // js中傳入的洗掉所有組件方法
};
},
computed: {
// 默認縱向布局,定位為 左 或者 右 時邊距為10px
horizontalClass() {
// 實體左偏移還是右偏移
return this.position.indexOf("right") > -1 ? "right" : "left";
},
verticalProperty() {
// 實體上還是下
return /^top-/.test(this.position) ? "top" : "bottom";
},
positionStyle() {
// 多個實體時的偏移量
return {
[this.verticalProperty]: `${this.verticalOffset}px`,
};
},
},
methods: {
// 銷毀當前組件
close() {
this.visible = false;
this.$el.addEventListener("transitionend", this.destroyElement); // 添加事件,在過渡效果結束后再銷毀組件
this.onClose(); // 呼叫外面js傳入組件的方法
},
// 銷毀所有組件
closeAll() {
this.onCloseAll();
},
// 銷毀組件方法
destroyElement() {
this.$el.removeEventListener("transitionend", this.destroyElement);
this.$destroy(true);
},
},
};
</script>
<style lang="less" scoped>
.DownLoadNotification {
width: 300px;
height: 60px;
background-color: #dcdfe6;
position: fixed;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
padding: 20px;
.header {
color: #606266;
margin-bottom: 10px;
}
}
.right {
right: 10px;
}
.left {
left: 10px;
}
</style>
?
data中的自定資料的就相當于使用api傳入的引數,當我們保存實體后,可以修改這個實體內的data,來達成實時更新的效果
多個實體使用案例參考
<template>
<div id="app">
<div >
<el-button @click="show(1)">顯示實體1</el-button>
<el-button @click="addBtn(1)">增加進度</el-button>
</div>
<div >
<el-button @click="show(2)">顯示實體1</el-button>
<el-button @click="addBtn(2)">增加進度</el-button>
</div>
<div >
<el-button @click="show(3)">顯示實體1</el-button>
<el-button @click="addBtn(3)">增加進度</el-button>
</div>
</div>
</template>
<script>
import { notify } from "./components/DownLoadNotification/index.js";
export default {
name: "App",
data() {
return {
instance1: null,
instance2: null,
instance3: null,
};
},
methods: {
show(index) {
this[`instance${index}`] = notify({
fileName: `測驗檔案${index}.zip`,
fileSize: "100mb",
downLoadProgress: 0,
downStatus: "success",
})
},
addBtn(index) {
this[`instance${index}`].downLoadProgress += 10
},
},
}
</script>
<style lang="less">
#app{
display: flex;
align-items: center;
}
.btn{
display: flex;
flex-direction: column;
}
</style>
本文轉載于:
https://juejin.cn/post/7243725204002209852
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/555475.html
標籤:其他
上一篇:JavaScript之Object.defineProperty()
下一篇:返回列表

