【Vue進階】青銅選手,如何自研一套UI庫?

_安歌發表於2019-06-10

前言

更新

  1. 本地跑ange-ui專案的時候需要全域性安裝vuepress,目前vuepress有0.x和1.x版本(剛釋出,檢視1.x文件)系列,安裝最新的vupress無法正常執行專案(感謝 @_嗚啦啦啦火車笛 提出),本專案使用1.x vuepress會有以下問題:
  • 授權拒絕錯誤
  • 與最新版sas-loader(7.x)不相容
  1. 使用0.x可以避免npm install vuepress@0.14.11 -g 幾天前官方釋出的vuepress 1.x版本:

【Vue進階】青銅選手,如何自研一套UI庫?

謝謝大家支援!

即便是一個青銅,也要用王者的心去編碼!

output

Github上關於Vue的UI庫,大大小小不計其數,即便是已經被推廣使用的成熟庫,也有很多。很多時候,我們自研一套UI庫,不是想要做得多牛逼,競爭過別人(事實我們也幹不過人家,除非你不是一個人在戰鬥。畢竟這不僅是個技術活,還是個體力活),我們僅僅是源自一個青銅對王者的仰望或者是為滿足內心的需求。

這裡跟大家講一個一步一步自研UI庫的故事。

原文地址:github.com/qiud...

專案地址:Ange UI

開發一套UI庫,做成不難(這裡指的是半半半成品,全品也好難。。。),做好很難。但不慌,我們有祕籍

  1. 一定的內功修為。所謂打鐵還需自身硬,要做高複用元件的開發工作,對Vue.jsCSS3還是有一定的技術要求。那我要多牛逼才能寫好這個UI庫啊?這取決於你這套UI庫的實現高度。
  2. 一些招式套路。藏經閣(Github)上有很多關於Vue的UI庫:六脈神劍、獨孤九劍應有盡有。人家怎麼寫的,我們們跟著來就好了。有人可能意見很大,那不是在模仿嗎?這裡要嚴正宣告,我們不是在模仿,我們只是向標準靠攏。因為牛逼的就是標準的

UI庫的必要架構

一套成熟的開源UI庫一般都具備以下幾個特點:

  • 包含了元件原始碼
  • 完備的說明文件
  • 符合Eslint校驗標準
  • 全面的單元測試
  • 完整的構建生態

因此,它們的目錄架構也出現了主要的兩種形式:

image
&
image
有點大同小異,這裡我們按照第一種架構去開發。

  • build:存放構建配置檔案
  • docs:官方說明文件
  • src:元件原始碼
  • test:單元測試用例(這裡不作闡述)
  • eslintrc:基於eslint-plugin-vue的開發規範標準

搭建UI開發環境

觀察社群幾大UI庫發現,它們都是基於webpack搭建了自己的構建配置,包括本地開發、生產環境構建、UI庫的打包等,建立一套自己的構建生態。我們就不同了,業餘一點(其實是善於應用開源工具),我們的docs文件是基於vuepress的,vuepress有自己一套的構建體系,所以我們只需要針對UI元件原始碼寫一份打包配置就好了。

下面開始搭建打包配置(其實就是很久以前我們做的基於webpack的構建,現在cli用得多了,配置也不會寫了,碼耶): 首先在根目錄建立configbuild資料夾,然後往兩個目錄分別新建檔案,如下:

image

  • build-lib.js:node執行的指令碼,讀取並執行lib的配置進行構建;
  • webpack.base.conf.js:公共的構建配置;
  • webpack.lib.conf.js:針對UI元件打包的配置;
  • index.js:可變的配置資訊;
  • prod.env.js:宣告當前構建環境為生產環境的配置;

不是說只有一份打包的配置檔案麼?咋還多了那麼多檔案呢?是這樣,雖然我們是業餘的,但我們也想做得專業一點對吧(有利於對配置進行擴充套件管理)!

我們看 config/index.js有什麼?

image
好像註釋也很清楚?這些定義最終都被應用在 webpack.lib.conf.js中。

再看看build/webpack.base.conf.js裡面的關鍵配置:

image
基礎配置裡面的entryoutput會被 webpack.lib.conf.js覆寫;rules定義一系列loader的轉換規則,其中eslint的校驗就是在這裡定義了一個eslint-loader

最後看一眼build/webpack.lib.conf.js配置:

image
lib裡面重寫entry的規則是,根據傳參值(components)分別走不同的入口檔案,一種只有一個./src/index.js,這個是UI對外註冊的入口檔案(這使得我們可以引入整個UI);另一種是各個UI元件的註冊入口檔案(這使得我們可以按需引入元件);

先給大家貼圖直觀感受下原始碼的目錄結構:

image

那實際打包的時候走的是哪一種呢?真相是都走!在build-lib.js中,執行了三次打包,打包輸出效果如下:

image
第一次的打包的輸出是經過壓縮的(壓縮css樣式表檔案,可以看到輸出檔案帶了min字尾),第二次是未壓縮的,最後一次是對各元件分別打包。(PS:我很想給大家用紅圈圈在圖上標記下,奈何裝置不允許。。。數度哽咽......Ubuntu系統下大家有好用的截圖示記工具可以推薦下嗎?)

開發一個元件

前面紮好了馬步,終於到修煉招式的階段了! 我們都知道,在應用某個外掛的時候需要經過下面程式碼的呼叫:

import Ange from 'ange-ui'
Vue.use(Ange)
複製程式碼

好奇下Vue.use在做什麼處理呢?它其實就是註冊/安裝這個外掛,根據use內部的定義,它通過呼叫install方法去註冊外掛,那麼,Ange就必須是一個Function(會被use當做是install方法呼叫)或者是一個包含了install方法的Object

道理我都懂,可是install方法裡面到底寫些什麼?

試想一下,我們希望在專案的任意位置都能引用這個外掛,那我們的每一個元件是不是要在全域性註冊?比如通過下面這種方式全域性註冊元件:

Vue.component('pagination', pagination)
複製程式碼

沒錯,install方法內部就是批量地全域性註冊元件。

搭好目錄架構

首先我們按照下圖的方式新建目錄和檔案:

image
src目錄的index.js檔案中定義install方法:

import './scss/ange.scss'  // 引入元件樣式表,也可以讓使用者在使用的時候自行引入
import components from './components'

function install(Vue, opts = {}) {
    Object.values(components).forEach((each) => {
        Vue.component(each.name, each)
    })
}

if (typeof window !== 'undefined' && window.Vue) {
    install(window.Vue)
}
export default {
    version: '1.0.0',
    install,
    ...components
}
複製程式碼

核心邏輯install就是對所有的元件迴圈註冊在全域性。components目錄的 index.js 則是逐個對外暴露元件物件,其次每一個元件也有一個 index.js ,它的作用是為當前元件注入install方法。理所當然地,install裡面是將該元件註冊在全域性,於是我們可以按需引用元件。

開發Button元件

元件開發的通用模板

<template>
    <component :is="'button'"></component>
</template>

<script>
export default {
    name: 'ag-button',
    props: {}
}
</script>
複製程式碼

component是vue的內建元件,is引數設定成button,表明最終渲染的html是button標籤,我們也可以直接使用button標籤,但我們的按鈕元件不一定是button,還可能是a標籤,為了更好的擴充,這裡使用component。

宣告元件引數

export default {
    name: 'ag-button',
    props: {
        // 按鈕類別
        primary: Boolean,
        secondary: Boolean,
        dashed: Boolean,
        link: Boolean,
        // 按鈕狀態
        color: {
            type: String,
            validator (val) {
                return new Set(['success', 'warn', 'danger']).has(val)
            }
        },
        // 按鈕尺寸
        size: {
            type: String,
            validator (val) {
                return new Set(['large', 'normal', 'small']).has(val)
            }
        },
        // 圖示按鈕
        icon: String,
        // 圓形按鈕(一般結合圖示按鈕使用)
        circle: Boolean,
        // 外鏈按鈕
        external: Boolean,
        // 非同步按鈕
        loading: Boolean
    }
}
複製程式碼

完善元件模板

<template>
    <component
        :is="tag"
        class="ange-btn"
        :class="[ btnSize, color, {
            'default': isDefault,
            'primary': primary,
            'secondary': secondary,
            'dashed': dashed,
            'link': link,
            'icon': icon,
            'circle': circle
        }]"
        @click="$emit('click', $event)"
        :disabled="loading">
        <span class="ange-btn-content">
            <!-- 圖示按鈕依賴 ag-icon 元件 -->
            <ag-icon
                v-if="icon"
                :icon="icon" />
            <slot />  <!-- 插槽接收按鈕文字 -->
        </span>
    </component>
</template>

<script>
export default {
    // ...
    computed: {
        tag () {
            return this.external ? 'a' : 'button'
        },
        isDefault() {
            const type = [this.primary, this.secondary, this.dashed, this.link]
            return type.every((each) => !each)
        },
        btnSize() {
            return this.size || 'normal'
        }
    }
}
</script>

複製程式碼

剩下的工作就是寫好樣式表了,你可以選擇直接寫在vue檔案裡面,也可以新建_scss/scss_樣式表。

元件應用及效果線上檢視

image

以上,便開發好一個button元件了,執行一下node build-lib.js或者npm run build:lib(現在package.json宣告script)就可以打包這個UI框架,然後再將其釋出到npm平臺(如果你想...)

寫好一份文件

線上檢視 Ange UI Docs 寫好文件是一個庫不可或缺的部分,寫的過程通過實際應用各元件,還可以對其進行測試校驗。前面說到,我們的文件要基於vuepress開發,簡潔的Markdown寫法,很是方便。這裡一篇 指南 可以很好地幫助你,它告訴了你如何搭建架構,寫好配置以及部署上線,或者參考我這個 倉庫 的配置。

假設docs/views/button.md是你的button元件的文件頁面,要如何引用你的元件?

image
Ange UI內建引入了樣式表,這裡可以不用再引入,也可以按需只引入button元件:

import '@scss/ange.scss'
import Button from '@component/button'
Vue.use(Button)
複製程式碼

至此,文件也就寫好了!

最後的最後,按照 vuepress doc 部署到你的github倉庫上就可以了!

本文通過button元件從0到1的開發,深入淺出闡述了Vue UI框架的開發流程,你對Vue.js的理解越深,元件的功能越複雜,你就會用到更多的高階用法。通過自研UI框架,我們也有很大的收穫:

  • 重新溫習webpack配置
  • 深入理解Vue內部機制,掌握更多的vue高階用法
  • 夯實js和css3基礎
  • 掌握程式設計正規化和設計模式

PS:CSS其實是UI開發中佔比很重的部分,大家按照自己的風格元件化開發就好。給大家推薦幾個很棒的配色網站

最後,希望大家也能多多去嘗試,這個青銅自研UI庫的故事到這裡就結束了。

The end.

相關文章