標籤:

談談 Vue 業務組件

春節的假期剛剛過去不久,大腦還沒有從假期綜合症中緩過來,就迎來了開工的日子,不知道各位有沒有收到開工大紅包?有沒有被虐狗?

什麼是組件

Web 頁面上的每個獨立的可視/可交互區域視為一個組件,組件就好像我們的 PC 組裝機一樣,整個機器(應用)由不同的部件組成,例如顯示器、主板、內存、顯卡、硬碟等等。頁面就是由一個個類似這樣的部分組成的,比如導航、列表、彈窗、級聯、下拉菜單等。頁面只不過是這些組件的容器,組件自由組合形成功能完整的界面,當不需要某個組件,或者想要替換某個組件時,可以隨時進行替換和刪除,而不影響整個應用的運行。

項目結構約定

  1. 每個 Vue 組件的代碼建議不要超出 200 行,如果超出建議拆分組件
  2. 拆分文件目錄,將頁面、通用組件、工具、通用樣式、路由等單獨放在一個文件夾中。
  3. 統一 Vue 文件的書寫格式參考 Vue 組件風格 或 風格指南(官方)。

例如我的項目結構:

src ├── assets # 圖片、icon 等靜態資源 ├── common # state、工具函數、公共常量 ├── components # 公共組件 ├── mixins # 公共 mixin 文件 ├── pages # 頁面組件 ├── router # 路由 └── styles # 公共樣式文件

業務組件設計

我們在項目開發中必然會使用到一些通用的組件,例如彈窗、級聯、下拉菜單等。好在有很多 UI 組件庫例如 element-ui、Mint UI、framework7,這些 UI 組件庫已經幫我們實現了最基礎的功能,我們只需要拿來用就可以了。然而現實開發中,產品總是會腦洞大開,提出這樣或那樣的需求,很顯然這些 UI 庫並不能實現所有的業務需求。所以這個時候我們就需要自己設計一個組件,或是在某個 UI 組件的基礎上再進行一層封裝。

封裝組件

譬如下拉多選,element-ui 的 select 組件雖然可以實現這個功能,但是 select 組件多選會將選中項作為標籤放在 input 中,當選項過多會導致標籤換行,並不美觀。所以我們需要自己封裝一個 dropdown-list 組件,我們只需要將 element-ui 的 dropdown 組件做一個簡單的封裝就可以實現這個功能。

// dropdown-list.vue<template> <el-dropdown class="cpt-dropdown-list" trigger="click" :hide-on-click="false" @command="commandHandler"> <span class="dropdown-link">{{ text }}</span> <el-dropdown-menu slot="dropdown" class="cpt-dropdown-list-wrap"> <el-dropdown-item v-for="item in options" :key="item.key" :command="item" :class="{ active: isActive(item) }"> {{ item.label }} <i v-show="isActive(item)" class="el-icon-check"></i> </el-dropdown-item> </el-dropdown-menu> </el-dropdown></template><script>export default { props: { text: String, options: Array, selected: Array }, methods: { isActive (data) { return this.selected.some(item => item.key === data.key) }, commandHandler (data) { const temp = this.selected.slice() const index = temp.findIndex(item => item.key === data.key) if (index > -1) { temp.splice(index, 1) } else { temp.push(data) } this.$emit(update:selected, temp) } }}</script><style lang="scss">.cpt-dropdown-list { display: inline-block; .dropdown-link { color: #409eff; cursor: pointer; }}</style>// base.scss.cpt-dropdown-list-wrap { max-height: 300px; overflow: auto; .el-dropdown-menu__item { display: flex; justify-content: space-between; padding: 0 10px; &, &:hover { color: #606266; background-color: #fff; } .el-icon-check { margin-left: 10px; line-height: unset; } &.active { color: #409eff; } }}

查看在線示例

樣式作用域空間

使用 componet- 或 page- 作為前綴加上組件名稱作為組件樣式作用域空間。為什麼要有樣式作用域空間?因為隨著項目不斷的擴大,不同組件相同類名出現的可能性也越來越大,在某些場景下會導致樣式衝突。譬如下面的例子:

// primary-button.vue<template functional> <div class="button"> blue </div></template><style lang="css">.button { color: blue;}</style>// warning-button.vue<template functional> <div class="button"> red </div></template><style lang="css">.button { color: red;}</style>//home.vue<template> <div class="page-home"> <button @click="isRed = !isRed">click me</button> <warning-button v-if="isRed"></warning-button> <primary-button v-else></primary-button> </div></template><script>import WarningButton from ./warning-button.vueimport PrimaryButton from ./primary-button.vueexport default { data () { return { isRed: true } }, components: { WarningButton, PrimaryButton }}</script>

查看在線示例

像上面的例子,我們期望通過切換 isRed 的值來切換不同背景色的按鈕組件,初始化的時候 warning-button 組件背景色是紅色,連續點擊兩次按鈕切換回來 ,這時 warning-button 組件的背景色卻變成了藍色。這是因為切換 isRed 的時候載入了 primary-button 組件的樣式,primary-button 組件的樣式在 warning-button 組件之後載入,導致 warning-button 組件的樣式被覆蓋了。上面的例子過於簡單,但實際開發中是有可能遇到這種問題的,所以我們應該為組件添加樣式作用域空間來避免這種情況。

第三方庫

在實際開發中,我們會使用一些第三方的庫,例如 Lodash、ECharts、高德地圖 等。但產品可能提出一些第三方庫本身不支持的功能,我們就需要在原有的基礎上解析封裝。例如我之前接手一個高德地圖的項目,大部分頁面都有高德地圖功能,所以我在多個頁面中共享一個地圖實例。產品提出新需求,在不同頁面中縮放的級別可能不同,而高德地圖本身的 Zoom 組件不能實現該功能,所以我們需要自己封裝這個功能。

<template> <div class="cpt-zoom"> <button @click="zoomIn">+</button> <span>{{ zoom }}</span> <button @click="zoomOut">-</button> </div></template><script>export default { props: { min: Number, max: Number, map: { type: Object, required: true } }, data() { return { zoom: this.map.getZoom() }; }, methods: { zoomIn() { // 處理 }, zoomOut() { // 處理 } }};</script>

查看在線示例

最後

在日常的業務組件的開發中,個人總結了幾點:

  1. 這個功能是否通用(最少會被兩個或以上頁面引用)?如果功能只會在一個頁面中使用,建議將該組件放在當前頁面的文件夾中。
  2. 這個功能是否是獨立,如果不是可以將這個組件拆封成多個組件,如果這些子組件沒用被單獨引用可以將它們放在同一個文件夾中。還有一種情況,如果這個組價代碼過多超出 200 行,這時我們也應該將功能拆分出去,這樣會方便閱讀也便於維護。
  3. 為組件添加樣式作用域空間,避免樣式衝突。
  4. 只在業務需要該功能的時候添加,在開發中我時常會犯一個錯誤「以後可能會用到」。實際上我們認為這個功能可能會用上,所以過早的編寫了該功能,最後發現它們只會靜靜的待在哪裡,為組件添加了很多無用的代碼。
  5. 如果組件本身沒有 data,且沒有複雜的計算屬性,建議寫成函數式組件。

上面提到的一些封裝組件的建議,只是我個人在開發中做的一些總結,如果有什麼不對的地方歡迎指出,最後祝大家 2018 開工大吉,狗年汪汪汪。

推薦閱讀:

TAG:組件 |