前端組件化封裝及npm部署
簡介
組件化思想是軟件編程的一個重要思想。如汽車的生產,將輪子、燈、座椅等作為單獨的組件,由各自的工廠去生產維護,各個組件都做好后再拿到組裝廠統一組裝使用。組件化思想就是將一個項目拆分成若干個組件,分而治之。
組件化開發好處
- 高復用性:復用的好處可以得到 較高的生產效率以及隨之而來的成本降低、較高的軟件質量(錯誤可以更快的被糾正)以及 恰當的使用復用可以改善系統的可維護性。
- 低耦合性:低耦合就是指各模塊依賴程度,減少模塊間調用。
高內聚和低耦合目的是使程序模塊的可重用性、移植性大大增強。
組件封裝
github地址:https://github.com/MengFangui/iview-table-page
項目中經常使用表格和分頁組件,目前比較流行的element UI,iview UI和Ant Design 3個UI庫都有表格和分頁組件,但是沒有將表格和分頁組件組合;同時分頁組件都有一個bug,即電梯(快速跳轉到某一頁)沒有綁定跳轉事件。本文將以iview ui的Table和Page為例,簡述前端組件封裝和npm打包部署。
iview-table-page 組件實現
<template>
<div>
<Table
:columns="columns"
:data="data"
:stripe='stripe'
:border='border'
:show-header='showHeader'
:width='width'
:height='height'
:max-height='maxHeight'
:loading='loading'
:disabled-hover='disabledHover'
:row-class-name='rowClassName'
:size='size'
:no-data-tex='noDataText'
:no-filtered-data-text='noFilteredDataText'
:draggable='draggable'
:tooltip-theme='tooltipTheme'
:row-key='rowKey'
:highlight-row='highlightRow'
@on-current-change='onCurrentChange'
@on-select='onSelect'
@on-select-cancel='onSelectCancel'
@on-select-all='onSelectAll'
@on-select-all-cancel='onSelectAllCancel'
@on-selection-change='onSelectionChange'
@on-sort-change='onSortChange'
@on-filter-change='onFilterChange'
@on-row-click='onRowClick'
@on-row-dblclick='onRowDblclick'
@on-expand='onExpand'
@on-drag-drop='onDragDrop'>
<template slot="header">
<slot name='header'></slot>
</template>
<template slot="footer">
<slot name='footer'></slot>
</template>
<template slot="loading">
<slot name='loading'></slot>
</template>
</Table>
<div :style="pageStyle" v-if="paginationShow">
<Page
:total="total"
:current="current"
:page-size='pageSize'
:page-size-opts='pageSizeOpts'
:placement='placement'
:size='pageShapeSize'
:simple='simple'
:show-total='showTotal'
:show-elevator='showElevator'
:show-sizer='showSizer'
:class-name='pageClassName'
:styles='styles'
:transfer='transfer'
:prev-text='prevText'
:next-text='nextText'
@on-change='onChange'
@on-page-size-change='onPageSizeChange'>
<slot/>
</Page>
</div>
</div>
</template>
<script>
export default {
name: 'iviewTablePage',
props: {
// 表格數據
data: {
type: Array,
default: () => {
return []
}
},
// 表格列屬性
columns: {
type: Array,
default: () => {
return []
}
},
// 是否顯示間隔斑馬紋
stripe: {
type: Boolean,
default: false
},
// 是否顯示縱向邊框
border: {
type: Boolean,
default: false
},
// 是否顯示表頭
showHeader: {
type: Boolean,
default: true
},
// 表格寬度,單位 px
width: [String, Number],
// 表格高度,單位 px,設置后,如果表格內容大於此值,會固定表頭
height: [String, Number],
// 表格最大高度,單位 px,設置后,如果表格內容大於此值,會固定表頭
maxHeight: [String, Number],
// 表格是否加載中
loading: {
type: Boolean,
default: false
},
// 禁用鼠標懸停時的高亮
disabledHover: {
type: Boolean,
default: false
},
// 是否支持高亮選中的行,即單選
highlightRow: {
type: Boolean,
default: false
},
// 行的 className 的回調方法
rowClassName: {
type: Function,
default: () => {
return ''
}
},
// 表格尺寸,可選值為 large、small、default 或者不填
size: {
type: String
},
// 數據為空時顯示的提示內容
noDataText: {
type: String,
default: '暫無數據'
},
// 篩選數據為空時顯示的提示內容
noFilteredDataText: {
type: String,
default: '暫無篩選結果'
},
// 是否開啟拖拽調整行順序,需配合 @on-drag-drop 事件使用
draggable: {
type: Boolean,
default: false
},
// 列使用 tooltip 時,配置它的主題,可選值為 dark 或 light
tooltipTheme: {
type: String,
default: 'dark'
},
// 是否強制使用內置的 row-key,開啟后可能會影響性能
rowKey: {
type: Boolean,
default: false
},
// 當前頁碼,支持 .sync 修飾符
currentPage: {
type: Number,
default: 1
},
// 數據總數
total: {
type: Number,
default: 0
},
// 每頁條數
pageSize: {
type: Number,
default: 10
},
// 每頁條數切換的配置
pageSizeOpts: {
type: Array,
default: () => {
return [10, 20, 30, 40]
}
},
// 條數切換彈窗的展開方向,可選值為 bottom 和 top
placement: {
type: String,
default: 'bottom'
},
// 可選值為small(迷你版)或不填(默認)
pageShapeSize: {
type: String
},
// 簡潔版
simple: {
type: Boolean,
default: false
},
// 顯示總數
showTotal: {
type: Boolean,
default: false
},
// 顯示電梯,可以快速切換到某一頁
showElevator: {
type: Boolean,
default: false
},
// 顯示分頁,用來改變page-size
showSizer: {
type: Boolean,
default: false
},
// 自定義 class 名稱
pageClassName: {
type: String,
default: ''
},
// 自定義 style 樣式
styles: {
type: Object,
default: () => {
return {}
}
},
// 是否將彈層放置於 body 內,在 Tabs、帶有 fixed 的 Table 列內使用時,建議添加此屬性,它將不受父級樣式影響,從而達到更好的效果
transfer: {
type: Boolean,
default: false
},
// 替代圖標顯示的上一頁文字
prevText: {
type: String,
default: ''
},
// 替代圖標顯示的下一頁文字
nextText: {
type: String,
default: ''
},
// 是否顯示頁碼
paginationShow: {
type: Boolean,
default: true
},
paginationPosition: {
type: String,
default: 'right'
}
},
data () {
return {
pageStyle: {
'text-align': this.paginationPosition,
margin: '16px 0'
},
current: 1
}
},
methods: {
// 開啟 highlight-row 后有效,當表格的當前行發生變化的時候會觸發
onCurrentChange (...arg) {
this.$emit('on-current-change', ...arg)
},
// 在多選模式下有效,選中某一項時觸發
onSelect (...arg) {
this.$emit('on-select', ...arg)
},
// 在多選模式下有效,取消選中某一項時觸發
onSelectCancel (...arg) {
this.$emit('on-select-cancel', ...arg)
},
// 在多選模式下有效,點擊全選時觸發
onSelectAll (...arg) {
this.$emit('on-select-all', ...arg)
},
// 在多選模式下有效,點擊取消全選時觸發
onSelectAllCancel (...arg) {
this.$emit('on-select-all-cancel', ...arg)
},
// 在多選模式下有效,只要選中項發生變化時就會觸發
onSelectionChange (...arg) {
this.$emit('on-selection-change', ...arg)
},
// 排序時有效,當點擊排序時觸發
onSortChange (...arg) {
this.$emit('on-sort-change', ...arg)
},
// 篩選時有效,篩選條件發生變化時觸發
onFilterChange (...arg) {
this.$emit('on-filter-change', ...arg)
},
// 單擊某一行時觸發
onRowClick (...arg) {
this.$emit('on-row-click', ...arg)
},
// 雙擊某一行時觸發
onRowDblclick (...arg) {
this.$emit('on-row-dblclick', ...arg)
},
// 展開或收起某一行時觸發
onExpand (...arg) {
this.$emit('on-expand', ...arg)
},
// 拖拽排序松開時觸發,返回置換的兩行數據索引
onDragDrop (...arg) {
this.$emit('on-drag-drop', ...arg)
},
// 頁碼改變的回調,返回改變后的頁碼
onChange (...arg) {
this.$emit('on-change', ...arg)
},
// 切換每頁條數時的回調,返回切換后的每頁條數
onPageSizeChange (...arg) {
this.$emit('on-page-size-change', ...arg)
},
// 表格方法
// 清除高亮項,僅在開啟 highlight-row 時可用
clearCurrentRow (...arg) {
this.$children[0].clearCurrentRow(...arg)
},
// 表格方法
// 全選或者取消全選
selectAll (status) {
this.$children[0].selectAll(status)
},
// 表格方法
exportCsv (...arg) {
this.$children[0].exportCsv(...arg)
}
},
mounted () {
// 初始化頁碼
this.current = this.currentPage
let that = this
// 獲取跳轉頁碼
let dom = document.querySelector('.ivu-page-options-elevator input')
if (dom) {
// 定義事件onchange
dom.onchange = function () {
let pageNo = parseInt(dom.value, 10)
if (!Number.isNaN(pageNo) && pageNo > 0 && pageNo <= that.total) {
that.current = pageNo
}
}
}
}
}
</script>
API 說明
table 屬性
屬性 | 說明 | 類型 | 默認值 |
---|---|---|---|
data | 顯示的結構化數據,其中,字段 cellClassName 用於設置任意單元格的樣式名稱,因此數據不能使用該字段,詳見示例特定樣式。 | Array | [] |
columns | 表格列的配置描述,具體項見后文 | Array | [] |
stripe | 是否顯示間隔斑馬紋 | Boolean | false |
border | 是否顯示縱向邊框 | Boolean | false |
show-header | 是否顯示表頭 | Boolean | false |
width | 表格寬度,單位 px | Number, String | 自動 |
height | 表格高度,單位 px,設置后,如果表格內容大於此值,會固定表頭 | Number, String | - |
max-height | 表格最大高度,單位 px,設置后,如果表格內容大於此值,會固定表頭 | Number, String | - |
loading | 表格是否加載中 | Boolean | false |
disabled-hover | 禁用鼠標懸停時的高亮 | Boolean | false |
highlight-row | 是否支持高亮選中的行,即單選 | Boolean | false |
row-class-name | 行的 className 的回調方法,傳入參數:row:當前行數據, index:當前行的索引 | Function | - |
size | 表格尺寸,可選值為 large、small、default 或者不填 | String | - |
no-data-text | 數據為空時顯示的提示內容 | String | 暫無數據 |
no-filtered-data-text | 篩選數據為空時顯示的提示內容 | String | 暫無篩選結果 |
draggable | 是否開啟拖拽調整行順序,需配合 @on-drag-drop 事件使用 | Boolean | false |
tooltip-theme | 列使用 tooltip 時,配置它的主題,可選值為 dark 或 light | String | dark |
row-key | 是否強制使用內置的 row-key,開啟后可能會影響性能 | Boolean | false |
table事件
事件名 | 說明 | 返回值 |
---|---|---|
on-current-change | 開啟 highlight-row 后有效,當表格的當前行發生變化的時候會觸發 | currentRow:當前高亮行的數據, oldCurrentRow:上一次高亮的數據 |
on-select | 在多選模式下有效,選中某一項時觸發 | selection:已選項數據, row:剛選擇的項數據, 即接收的參數是(selection,row) |
on-select-cancel | 在多選模式下有效,取消選中某一項時觸發 | selection:已選項數據,row:取消選擇的項數據 |
on-select-all | 在多選模式下有效,點擊全選時觸發 | selection:已選項數據 |
on-select-all-cancel | 在多選模式下有效,點擊取消全選時觸發 | selection:已選項數據 |
on-selection-change | 在多選模式下有效,只要選中項發生變化時就會觸發 | selection:已選項數據 |
on-sort-change | 排序時有效,當點擊排序時觸發 | column:當前列數據,key:排序依據的指標,order:排序的順序,值為 asc 或 desc |
on-filter-change | 篩選時有效,篩選條件發生變化時觸發 | 當前列數據 |
on-row-click | 單擊某一行時觸發 | 當前行的數據,index |
on-row-dblclick | 雙擊某一行時觸發 | 當前行的數據,index |
on-expand | 展開或收起某一行時觸發 | row:當前行的數據,status:當前的狀態 |
on-drag-drop | 拖拽排序松開時觸發,返回置換的兩行數據索引 | index1, index2 |
page 屬性
屬性 | 說明 | 類型 | 默認值 | |
---|---|---|---|---|
currentPage | 當前頁碼 | Number | 1 | |
total | 數據總數 | Number | 0 | |
page-size | 每頁條數 | Number | 10 | |
page-size-opts | 每頁條數切換的配置 | Array | [10, 20, 30, 40] | |
placement | 條數切換彈窗的展開方向,可選值為 bottom 和 top | String | bottom | |
page-shape-size | 可選值為small(迷你版)或不填(默認) | String | - | |
simple | 簡潔版 | Boolean | false | |
show-total | 顯示總數 | Boolean | false | |
show-elevator | 顯示電梯,可以快速切換到某一頁 | Boolean | false | |
show-sizer | 顯示分頁,用來改變page-size | Boolean | false | |
page-class-name | 自定義 | class 名稱 | String | - |
styles | 自定義 style 樣式 | Object | - | |
transfer | 是否將彈層放置於 body 內,在 Tabs、帶有 fixed 的 Table 列內使用時,建議添加此屬性,它將不受父級樣式影響,從而達到更好的效果 | Boolean | false | |
prev-text | 替代圖標顯示的上一頁文字 | String | - | |
next-text | 替代圖標顯示的下一頁文字 | String | - |
新增屬性
屬性 | 說明 | 類型 | 默認值 |
---|---|---|---|
pagination-show | 是否顯示頁面 | Boolean | true |
pagination-position | 頁碼位置 | String 可選值為 left、center 和 right | right |
其中大多數都是ivew UI上的事件和屬性,新增了pagination-show和pagination-position屬性。
電梯(跳轉到某一頁)事件綁定實現
獲取跳轉頁面input dom,將dom綁定onchange事件。
// 獲取跳轉頁碼dom
let dom = document.querySelector('.ivu-page-options-elevator input')
if (dom) {
// 定義事件onchange
dom.onchange = function () {
let pageNo = parseInt(dom.value, 10)
if (!Number.isNaN(pageNo) && pageNo > 0 && pageNo <= that.total) {
that.current = pageNo
}
}
demo
- 服務端分頁以及自定義序號 https://github.com/MengFangui/iview-table-page/blob/master/example/table1.vue
- 服務端分頁並排序、過濾 https://github.com/MengFangui/iview-table-page/blob/master/example/table2.vue
- 可編輯單元格 https://github.com/MengFangui/iview-table-page/blob/master/example/table3.vue
- 可編輯行 https://github.com/MengFangui/iview-table-page/blob/master/example/table4.vue
- 快速切換到某一頁 https://github.com/MengFangui/iview-table-page/blob/master/example/table5.vue
iview-table-page 組件使用
安裝組件
$ npm i --save iview-table-page
or
$ yarn add iview-table-page
組件注冊
全局注冊組件
main.js中:
import Vue from 'vue'
import iviewTablePage from 'iview-table-page'
Vue.use(iviewTablePage)
局部注冊組件
<template>
<div>
<iviewTablePage
border
:columns="columns7"
:data="data6"
:total='total'
>
</iviewTablePage>
</div>
</template>
<script>
import iviewTablePage from 'iview-table-page'
export default {
components: { iviewTablePage },
data () {
return {
columns7: [
{
title: 'Name',
key: 'name',
render: (h, params) => {
return h('div', [
h('Icon', {
props: {
type: 'person'
}
}),
h('strong', params.row.name)
])
}
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
},
{
title: 'Action',
key: 'action',
width: 150,
align: 'center',
render: (h, params) => {
return h('div', [
h(
'Button',
{
props: {
type: 'primary',
size: 'small'
},
style: {
marginRight: '5px'
},
on: {
click: () => {
this.show(params.index)
}
}
},
'View'
),
h(
'Button',
{
props: {
type: 'error',
size: 'small'
},
on: {
click: () => {
this.remove(params.index)
}
}
},
'Delete'
)
])
}
}
],
data6: [
{
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 24,
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park'
}
],
total: 4
}
}
}
</script>
npm 打包
package.json核心配置
"build-bundle": "vue-cli-service build --target lib --name iviewTablePage ./src/components/index.js",
"main": "dist/iviewTablePage.umd.js",
- 打包為lib。
- 入口文件為umd規范的js。
- keywords:便於搜索npm 包。
- repository: 代碼存放地址(一般是git地址)。
- devDependencies: 你要發的包,所依賴的開發環境下的包。
- dependencies:你要發的包,所依賴的線上環境下的包。
package.json的完整配置見:https://github.com/MengFangui/iview-table-page/blob/master/package.json
npm注冊賬號
發包
在你將要發包的目錄下,執行
npm adduser
npm publish
到此完成前端組件化封裝和npm打包部署工作。