在專案中,一般我們經常會基于一套現有組件庫進行快速開發,但是現實中往往需要對組件庫進行定制化改造二次封裝
混入(mixin)
vue 官方介紹
混入 (mixin) 提供了一種非常靈活的方式,來分發 Vue 組件中的可復用功能,一個混入物件可以包含任意組件選項,當組件使用混入物件時,所有混入物件的選項將被“混合”進入該組件本身的選項,
簡單來說就是將組件的物件的屬性,方法,鉤子函式等等進行提取封裝,以便達到可以多出復用,來看一個簡單例子
<template>
<div>
<el-table :v-loading='isLoading' :data="https://www.cnblogs.com/nextl/archive/2021/04/13/tableData" style="width: 100%" ref="table">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="name" label="規則名稱"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column prop="count" label="服務呼叫次數(萬)"></el-table-column>
<el-table-column prop="state" label="狀態">
<template slot-scope="{row}">
<span :></span>
{{ row.state=='close'?'關閉':'運行中' }}
</template>
</el-table-column>
<el-table-column prop="time" label="上次調度時間"></el-table-column>
<el-table-column label="操作">
<template>
<el-button type="text">編輯</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text">訂閱警報</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page.sync="page.pageIndex" :page-sizes="[20, 30, 40, 50]" @size-change="(e)=>page.pageSize=e" :total="total" layout="total, sizes, prev, pager, next, jumper"> </el-pagination>
</div>
</template>
<script>
export default {
data () {
return {
tableData:[],
isLoading: false,
total: 0,
page: {
pageSize: 20,
pageIndex: 1
}
};
},
watch: {
'page':{
deep: true,
immediate: true,
handler(){
this.getList()
}
}
},
methods: {
getList(pageIndex = this.page.pageIndex,pageSize = this.page.pageSize){
//獲取串列資料
this.isLoading = true;
setTimeout(() => {
this.tableData = https://www.cnblogs.com/nextl/archive/2021/04/13/MockData();
this.total=300;
this.isLoading = false;
}, 2000);
}
}
};
</script>
上面是個常見報表分頁使用場景,假如有很多個表報,那就需要寫很多次分頁的邏輯,正常開發中當然不可能這么處理的, 這種情況就可以使用mixins來提取分頁的邏輯
// mixins.js
<script>
export default {
data () {
return {
isLoading: false,
total: 0,
page: {
pageSize: 20,
pageIndex: 1
}
};
},
watch: {
'page':{
deep: true,
immediate: true,
handler(){
this.getList()
}
}
}
};
</script>
<template>
<div>
<el-table :v-loading='isLoading' :data="https://www.cnblogs.com/nextl/archive/2021/04/13/tableData" style="width: 100%" ref="table">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="name" label="規則名稱"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column prop="count" label="服務呼叫次數(萬)"></el-table-column>
<el-table-column prop="state" label="狀態">
<template slot-scope="{row}">
<span :></span>
{{ row.state=='close'?'關閉':'運行中' }}
</template>
</el-table-column>
<el-table-column prop="time" label="上次調度時間"></el-table-column>
<el-table-column label="操作">
<template>
<el-button type="text">編輯</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text">訂閱警報</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page.sync="page.pageIndex" :page-sizes="[20, 30, 40, 50]" @size-change="(e)=>page.pageSize=e" :total="page.total" layout="total, sizes, prev, pager, next, jumper"> </el-pagination>
</div>
</template>
<script>
import PageMixins from "./mixins.js";
export default {
mixins:[PageMixins],
data () {
return {
tableData:[]
};
},
methods: {
getList(pageIndex = this.page.pageIndex,pageSize = this.page.pageSize){
//獲取串列資料
this.isLoading = true;
setTimeout(() => {
this.tableData = https://www.cnblogs.com/nextl/archive/2021/04/13/MockData();
this.total=300;
this.isLoading = false;
}, 2000);
}
}
};
</script>
這樣就將分頁的邏輯分離出來了,也可以被其他組件混入使用,大大的減少了代碼量,當然mixin過度濫用也是存在缺點的
- 命名沖突
使用mixins是將兩個組件物件合并的,當兩個組件屬性名重復時候,vue默認會將本地組件的屬性覆寫mixin的,雖然vue提供了合并策略配置,但是同時存在多個mixin存在命名沖突時候就會變得處理起來非常麻煩了 - 隱含的依賴關系
很明顯上面的組件是依賴于mixin的,這種情況會存在潛在問題,如果我們以后想重構一個組件,改變了mixin需要的變數的名稱,就會影響現有的組件的使用了,而且當專案中使用了很多這個mixin的時候,就只能去手動搜索修改了,因為不知道哪些組件使用了這些mixin
組件封裝
上面表格還有中處理方法,就是將el-table和el-pagination封裝成一個組件去是使用,也能提高復用性
template封裝
使用template創建組件,來對el-table進行二次封裝滿足上面需求,增加一個total引數
提供是一個分頁改變事件,再把m-table的\(attrs和\)listeners 系結到el-table上,然后把el-table 方法暴露出去,這樣就可像使用 el-table 一樣使用 m-table
<template>
<div>
<el-table ref="table" v-bind="$attrs" v-on="$listeners">
<slot></slot>
</el-table>
<el-pagination :current-page.sync="page.pageIndex" :page-sizes="[20, 30, 40, 50]" @size-change="(e)=>page.pageSize=e" :total="total" layout="total, sizes, prev, pager, next, jumper"> </el-pagination>
</div>
</template>
<script>
export default {
name: 'm-table',
data() {
return {
page: {
pageSize: 20,
pageIndex: 2
}
}
},
props: {
total: {
type: Number,
default: 0
}
},
watch: {
page: {
deep: true,
handler: function () {
this.$emit("page-chagne")
}
}
},
methods: {
// 將el-table 方法暴露出去
...(() => {
let methodsJson = {};
['reloadData', 'clearSelection', 'toggleRowSelection', 'toggleAllSelection', 'setCurrentRow', 'clearSort', 'clearFilter', 'doLayout', 'sort']
.forEach(key => {
methodsJson = {
...methodsJson, [key](...res) {
this.$refs['table'][key].apply(this, res);
}
};
});
return methodsJson;
})()
}
}
</script>
使用 m-table
<m-table @page-chagne="GetTableDataList()" :total="page.total" :data="https://www.cnblogs.com/nextl/archive/2021/04/13/tableData">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="name" label="規則名稱"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column prop="count" label="服務呼叫次數(萬)"></el-table-column>
<el-table-column prop="state" label="狀態">
<template slot-scope="{row}">
<span :></span>
{{ row.state=='close'?'關閉':'運行中' }}
</template>
</el-table-column>
<el-table-column prop="time" label="上次調度時間"></el-table-column>
<el-table-column label="操作">
<template>
<el-button type="text">編輯</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text">訂閱警報</el-button>
</template>
</el-table-column>
</m-table>
一般情況下這樣使用 template 封裝就滿足了需求,但是總有些時候這樣封裝是滿足不了需求的,比如現在m-table現在需要動態支持修改配置顯示列,并且不希望修改m-table的基本使用方式, 這個時候就需要使用 render 了,
render 函式
Vue 的模板實際上都會被編譯成了渲染函式,render 函式有一個 createElement 引數,用來創建一個VNode,
要滿足上面的需求,首先是的獲得el-table的插槽(slot)中的內容,根據插槽的內容生成每列資訊,根據配置的資訊動態創建插槽的內容就可以實作了,簡單示例代碼入下
<script>
import mSetting from './setting'
export default {
components: { mSetting },
name: 'm-table',
data() {
return {
showTable: true,
setShow: false,
config: [],
copySlots: [], // 展示solt資料
page: {
pageSize: 20,
pageIndex: 1
}
}
},
props: {
total: {
type: Number,
default: 0
}
},
watch: {
page: {
deep: true,
handler: function () {
this.$emit("page-chagne")
}
}
},
created() {
this.initConfig()
},
render() {
return (
<div>
<el-table ref="table" {...{ attrs: this.$attrs }} {...{ on: this.$listeners }}>
{
this.copySlots
}
</el-table>
{this.showTable ? (<el-pagination layout="total, sizes, prev, pager, next, jumper"
{...{
on: {
'size-change': (e) => this.page.pageSize = e,
'current-change': (e) => this.page.pageIndex = e
}
}}
{...{
attrs: {
'current-page': this.page.pageIndex,
'page-sizes': [20, 30, 40, 50],
'total': this.total
}
}}
> </el-pagination>) : null}
<m-setting {...{
on: {
'update:show': e => this.setShow = e,
'change': this.initConfig
}
}} show={this.setShow} config={this.config}></m-setting>
</div >
)
},
methods: {
initConfig(config = []) {
if (config.length === 0) {
config = this.$slots.default
.filter(item => item.componentOptions && item.componentOptions.tag === "el-table-column")
.map(item => {
if (item.componentOptions.propsData.prop === 'index') {
if (!item.data.scopedSlots) {
item.data.scopedSlots = {};
}
item.data.scopedSlots.header = () => (
<i onClick={() => this.setShow = true} />
);
}
return { ...item.componentOptions.propsData };
})
this.sourceConfig = JSON.parse(JSON.stringify(config))
this.copySlots = this.$slots.default;
this.sourceSlots = this.$slots.default;
} else {
let arr = []
this.sourceSlots.forEach(item => {
let temp = config.find(subItem =>
(subItem.prop && subItem.prop === item.componentOptions.propsData.prop) ||
(subItem.type && subItem.type === item.componentOptions.propsData.type)
);
if (temp && temp.isShow) {
Object.assign(item.componentOptions.propsData, temp);
arr.push(item)
}
})
this.copySlots = arr;
this.showTable = false
this.$nextTick(() => {
this.showTable = true
})
}
this.config = config;
},
...(() => {
let methodsJson = {};
['reloadData', 'clearSelection', 'toggleRowSelection', 'toggleAllSelection', 'setCurrentRow', 'clearSort', 'clearFilter', 'doLayout', 'sort']
.forEach(key => {
methodsJson = {
...methodsJson, [key](...res) {
this.$refs['table'][key].apply(this, res);
}
};
});
return methodsJson;
})()
}
}
</script>
<template>
<el-dialog title="表格設定" :visible.sync="shows" width="600px" size="small" :before-close="handleClose">
<el-table :data="https://www.cnblogs.com/nextl/archive/2021/04/13/list" size='mini'>
<el-table-column prop="showLabel" label="名稱"></el-table-column>
<el-table-column prop="label" label="顯示名稱">
<template slot-scope="{row}">
<el-input size="mini" v-model="row.label"></el-input>
</template>
</el-table-column>
<el-table-column prop="isShow" label="是否顯示" width="100" align="center">
<template slot-scope="{row}">
<el-switch v-model="row.isShow"></el-switch>
</template>
</el-table-column>
</el-table>
<span slot="footer" >
<el-button @click="shows = false" size="small">取 消</el-button>
<el-button type="primary" @click="handleClose()" size="small">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: 'm-setting',
data() {
return {
list: []
};
},
computed: {
shows: {
get() {
return this.show;
},
set() {
this.$emit("update:show")
}
}
},
props: {
show: {
type: Boolean,
required: true,
default: false
},
config: {
type: Array,
default() {
return []
}
}
},
created() {
this.init()
},
methods: {
init() {
this.list = this.config.map(item => {
return {
...item,
showLabel: item.showLabel || item.label, // 名稱
isShow: item.isShow || true // 是否顯示
}
})
},
handleClose() {
this.$emit('change', this.list);
this.shows = false
}
}
};
</script>
這樣就簡單實作了可以動態顯示列,而且不需要去修改原組件的使用方式了
hoc 高階組件
vue高階組件可以參考這篇
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/275717.html
標籤:其他
上一篇:前端開發面試題 — html篇
下一篇:css如何將元素水平居中
