少女風vue元件庫製作全攻略~~

goji發表於2019-05-14

預覽

元件庫官網
github地址
如果喜歡各位小哥哥小姐姐給個小星星鼓勵一下哈 ~~

少女風vue元件庫製作全攻略~~

少女風vue元件庫製作全攻略~~

完整專案目錄結構

git clone到本地安裝依賴後,執行npm run serve進行本地元件庫開發,npm run docs:dev進行元件庫官網開發。一般在src/demo.vue進行單個元件測試通過後,再引入到.vuepress/components中放入元件庫官網。

├─docs               // vuepress開發目錄
│  ├─.vuepress
│  │  ├─components   // 在markdown中可以使用的vue元件
│  │  ├─dist         // vuepress打包目錄
│  │  │  ├─assets
│  │  │  │  ├─css
│  │  │  │  ├─img
│  │  │  │  └─js
│  │  │  ├─components
│  │  │  │  ├─basic
│  │  │  │  ├─form
│  │  │  │  ├─navigation
│  │  │  │  ├─notice
│  │  │  │  └─other
│  │  │  └─guide
│  │  │
│  │  ├─config.js    // vurepess配置修改入口,包括左邊sidebar,右上方nav導航選單,favicon等
│  │  ├─style.style  // 覆蓋vuerpress預設主題樣式
│  │  └─public       //公共資源入口,如favicon
│  ├─static
│  │  ├─img
│  │  └─js
│  └─views          // vuepress檢視檔案,格式是markdown
│      ├─components
│      │  ├─basic
│      │  ├─form
│      │  ├─navigation
│      │  ├─notice
│      │  └─other
│      ├─design
│      │  └─color
│      └─guide
├─src              // 元件庫原始碼目錄
│  ├─button
│  ├─cascader
│  ├─collapse
│  ├─container
│  ├─datepicker
│  ├─form
│  ├─icon
│  ├─layout
│  ├─notice
│  ├─plugins
│  ├─slide
│  ├─tab  
│  ├─step
│  ├─sticky
│  └─index.js    // 元件庫原始碼元件入口檔案,執行npm run build的目標檔案
├─package.json   // 與npm釋出相關,記錄版本號,包入口檔案地址
複製程式碼

學習元件庫製作會收穫

  • 學習元件封裝技能,良好的介面設計, 掌握元件設計套路
  • 夯實js/css基礎
  • 深入對vue的理解

製作流程

  1. 元件設計/開發
  2. 釋出npm
  3. 製作官網展示

元件設計/開發

頻繁涉及到的vue api包括

  • $children : 獲取當前元件子元件。
  • $parent: 獲取當前元件父元件。
  • $options: 用於當前 Vue 例項的初始化選項, 可以用此選項獲得元件的name。
  • $refs: 一個物件,持有註冊過 ref 特性 的所有 DOM 元素和元件例項。
  • $el: Vue 例項使用的根 DOM 元素。
  • provide & inject :這對選項需要一起使用,允許一個祖先元件向其所有子孫後代注入一個依賴,注意不是響應式的。注入的物件可以是個vue例項的eventBus。
  • $on: 元件監聽自定義事件。
  • $emit: 元件觸發自定義事件。
  • .sync:語法糖,單向資料流中,父元件監聽到子元件修改props的意圖後父元件修改傳入的props,用了.sync不需要顯式在父元件監聽元件內部觸發的自定義事件去修改值, 父元件只要寫:x.sync="bindValue", 注意此時子元件觸發的事件必須是"update:x"此語法糖才生效。
  • updated 生命週期鉤子函式,由於資料更改導致的虛擬 DOM 重新渲染和打補丁,在這之後會呼叫該鉤子, 在父子元件通訊可能用到。
  • beforeDestoryed/ destory 生命週期鉤子函式,destory後元件的所有的事件監聽器會被移除。注意:如果是自己在元件內部對dom增加了事件監聽,元件銷燬的時候需要自己手動接觸自己另外新增上去的監聽程式。而且元件銷燬,dom元素還被保留在頁面,需要手動清除,可以呼叫原生js api, node.remove()清除dom節點。

原生js api包括:

  • target.addEventListener(type, listener[, useCapture])/removeEventListener 由於這是 DOM 0 規範的基本內容,幾乎所有瀏覽器都支援這個,而且不需要特殊的跨瀏覽器相容程式碼。
  • Node.contains()返回的是一個布林值,來表示傳入的節點是否為該節點的後代節點。多用於事件監聽判斷是否點選了目標區域。
  • window.scrollY 獲取文件垂直方向的滾動距離。
  • Element​.get​Bounding​Client​Rect() 返回元素的大小及其相對於視口的位置,返回一個物件,包括width/height/left/right/top/bottom。多用於計算定位。

技術點總結

元件設計的思想包括單資料流/ eventBus事件中心,核心是元件通訊。

  • 單資料流: 資料的改變是單向的,即通過props的方式,只能讓父元件來修改資料,子元件不能主動修改props。這樣的例子如在collapse/tab/slide元件中,讓父元件來控制選中的值。單向資料流的思想讓資料修改更好設計,邏輯更加清晰。
  • vue外掛開發:什麼時候用外掛開發?當元件不是顯式在程式碼中被呼叫,不是直接寫在template中,而是通過呼叫Vue原型鏈上的方法被掛載到文件中。 比如modal模態框/toast彈窗。外掛設計的基本思路是暴露一個install方法,這個方法中在vue原型鏈上增加一個自定義的方法X, X中引入元件a,通過Vue.extend(a)獲得元件構造器Constructor, 在通過new Constructor({propsData})獲得元件例項vm, 再掛載元件例項到文件中。
import modal from '../notice/modal'

export default {
  install (vue, options) {
    const Construtor = vue.extend(modal)

    let modalVm // 保證全域性只有一個modal例項
    let lastOption
    vue.prototype.$modal = (options) => {

    if (lastOption !== JSON.stringify(options)) { //! modalVm
        modalVm = new Construtor({ propsData: options })
        modalVm.$mount()

        document.body.append(modalVm.$el)
      }
      lastOption = JSON.stringify(options)
      modalVm.isVisible = true
    }
  }
}

複製程式碼
  • eventBus:什麼時候用eventBus?當狀態的變化需要多個子元件被通知。如tab元件中,當選中的值發生變化,tab-head需要感知變化讓提示的短線做個動畫滑到選中的標籤下,tab-item需要感知變化讓文字變成選中樣式,tab-pane需要感知變化讓選中的皮膚出現。
  • 遞迴:在級聯元件的設計中用到。類似函式fn中用setTimout(fn,millseconds)呼叫自己實現setInterval的遞迴效果。元件只要內部提供name屬性,就可以遞迴地呼叫自身。允許元件模板遞迴地呼叫自身。通過提供 name 選項,便於除錯,在控制檯可以看到可以獲得更有語義資訊的元件標籤。
  • 媒體查詢 &flex佈局:響應式佈局的原理是媒體查詢和百分比佈局,介於某個尺寸的時候某個類名生效;跟佈局相關的大部分用到flex,非常好用。詳細看阮一峰老師教程

| 元件型別 | 元件|單資料流 | vue外掛開發 | eventBus| 原生js操作dom & 事件| 遞迴|媒體查詢&flex佈局| | :------| :------ | :------: |:------| :------| :------: |:------|:------| | 基礎| button按鈕 |- |-| -|- |- |- | | 基礎| icon圖示 |- |-| -|- |- |- | | 基礎| grid網格 |- |-| -|- |- | yes | | 基礎| layout佈局 |- |-| -|- |- | yes | | 表單| input輸入框 |- |-| -|- |- |- | | 表單| cascader級聯選擇器 | yes |-| -|- | yes |- | | 表單| form表單 |- |-| -|- |- |- | | 表單| datepicker日期選擇器 |- |-| -| yes |- |- | | 導航| tab標籤頁 |- |-| yes|- |- |- | | 導航| step步驟調 |- |-| -| - |- |- | | 通知| toast提示 |- |yes| -|yes |- |- | | 通知| popover彈出框 |- |-| -| yes | - |- | | 通知| modal模態框 |- |yes| -|yes | - |- | | 其他| collapse摺疊皮膚 | yes |-| yes|- |- |- | | 其他| slide輪播圖 | yes |-| -|- |- |- | | 其他| sticky粘滯 |- |-| -|- |- |- |

元件設計三要素

  1. props:可以參考餓了麼或者antd, 需要從使用者的角度考慮怎麼使用方便和擴充套件性好,一般需要校驗型別和有效值,設定預設值。
  2. slot:插槽內容分發,使用作用域插槽讓slot也可以獲得元件內部方法,讓使用者自定義的內容頁能呼叫元件內部方法,比如popover彈出框中使用者想自己加個按鈕手動呼叫關閉。
  3. event: 元件事件。從使用者角度考慮,比如datepicker元件中使用者想在日期皮膚被開啟或這關閉的時候進行操作。這種一般用在互動類UI元件。

舉個例子

複雜元件datepicker開發思路

少女風vue元件庫製作全攻略~~

  1. 在原有的popover元件上開發
    點選一個元素A(輸入框)後可以彈出元素B(日期皮膚)

  2. 生成日期皮膚
    生成7*6=42個日期,6行是為了確保一個月都能在皮膚上完整顯示。這裡計算最方便的做法是用時間戳,計算出這個月第一天時間戳和這一天周幾,就可以一次性計算出這42個日期。不用算上個月下個月分三段算,這樣的問題是還要考慮邊界情況,如剛好出現上一年下一年等,麻煩容易出bug。這42個日期我們在computed用visibleDays表示。

    visibleDays () {
       let { year, month } = this.display
       let defaultObj = new Date(year, month, 28)
       var curMonthFirstDay = helper.getMonthFirstDay(defaultObj)
       var curMonthFirstDayDay = helper.getDay(curMonthFirstDay) === 0 ? 7 : helper.getDay(curMonthFirstDay)
       let x = curMonthFirstDayDay - 1 // 前面需要補多少位
       var arr = []
       for (let i = 0; i < 42; i++) {
         arr.push(new Date(curMonthFirstDay.getTime() + (-x + i) * 3600 * 24 * 1000))
       }
       return arr
     },
    複製程式碼
  3. props接受value, 型別是date
    日期皮膚上的日期渲染的時候加上一個計算的class, 分別加上'today','selected-date','available','prev-month','next-month',進行樣式上的區分

  4. 實現選中日期
    告訴父元件修改資料意圖讓父元件修改傳入的props,對應使用我們元件的時候使用, 這裡的基礎知識是元件上的v-model是個語法糖,v-model="x"會被解析成:value="a" @input="a=$event"。同時皮膚上輸入框顯示的資料也要跟著變化,所以這裡用計算屬性,如在computed中用formattedValue表示。

     formattedValue: {
         return this.value instanceof Date ? helper.getFormatDate(this.value) : ''
     }
    複製程式碼
  5. 實現點選上一年/月,下一年/月
    我們需要知道當前展示的是哪一年哪一個月,這個資料是元件內部維護的,所以在data申明一個display物件

    display: {
         year: (this.value && this.value.getFullYear()) || new Date().getFullYear(),
         month: (this.value && this.value.getMonth()) || new Date().getMonth()
       }
    複製程式碼

    點選的時候即修改display物件的year/month,因為visibleDays也是計算屬性,依賴display物件,所以點選上一年/月,下一年/月,渲染的日期也跟著變。

  6. 實現選擇年
    年皮膚的製作,生成12個年,點選第1(12)個年渲染出上(下)12個年。這裡只需要給渲染出來的年的第一個和最後一個dom元素繫結事件,事件監聽程式傳入當前點選的元素的值,即可計算出上或下一個12年。 同理點選年的時候用$emit通知父元件修改value

  7. 實現選擇月
    直接寫死12個月份,同理點選月的時候用$emit通知父元件修改value

  8. 增加住皮膚上【今天】和【清空】的按鈕
    點選的時候用$emit通知父元件修改value,new Date()和''

  9. 細節處理
    使用者選中完日期後要關閉皮膚 使用者選了年後點選周圍空白區域日期皮膚關閉,第二次點選進來應該預設展示日皮膚

  10. 使用者可以修改輸入框裡面的值,需要判斷有效性
    有效的話$emit通知父元件改值,無效的話當失去焦點的時候變回原來的值,這裡需要用原生js去給input修改value。注意這裡直接改formattedValue的話無效,雖然輸入框的值繫結了:value="formattedValue",但是因為formattedValue是計算屬性,依賴於this.value,在使用者輸入無效值的情況下this.value不會改變,因此介面不會被更新,所以需要手動改value的值。

    setValueManually ($event) {
       if (!helper.isValidDate($event)) {
         this.$refs.inputWrapper.$refs.input.value = this.isDate(this.value) ? helper.getFormatDate(this.value) : ''
         return
       }
       this.$emit('input', new Date($event))
     }
    複製程式碼
  11. 完善
    給彈出日期皮膚和關閉日期皮膚增加元件自定義事件, 即呼叫$emit觸發'showDatepicker'和'closeDatepicker'事件。

釋出npm

  1. 使用vue cli3 的庫模式打包程式碼,修改package.json 中的"build": "vue-cli-service build --target lib --name sakura src/index.js",打包後輸出umd構建版本, 參考vue cli。 什麼是umd? 統一模組定義,可以相容common.js(node端規範)/ AMD(瀏覽器端規範)/ ES6(node端不完全支援)等多種模組化方案,確保程式碼在各種環境下能被執行。
    File                     Size                     Gzipped
    
    dist/sakura.umd.min.js    13.28 kb                 8.42 kb
    dist/sakura.umd.js        20.95 kb                 10.22 kb
    dist/sakura.common.js     20.57 kb                 10.09 kb
    dist/sakura.css           0.33 kb                  0.23 kb
    複製程式碼
  2. 在package.json指明模組入口"main":"dist/sakura.umd.min.js"
     "name": "heian-sakura-ui",
      "version": "0.0.6",
      "private": false,
      "main":"dist/sakura.umd.min.js",
      "description": "an UI framework based on Vue.js",
    複製程式碼
  3. 在npm 上註冊一個使用者
  4. 在命令列輸入,注意每次釋出都要修改package.json中的 "version": "0.0.x","private"必須設定成false才能釋出
    npm adduser // 提示輸入註冊的使用者名稱
    npm publish
    複製程式碼

官網製作

使用vue press

  1. 在原有專案中使用

    # 安裝依賴
    npm install -D vuepress
    # 建立一個 docs 目錄
    mkdir docs
    複製程式碼

    在package.json中進行指令碼配置

    {
    "scripts": {
      "docs:dev": "vuepress dev docs",
      "docs:build": "vuepress build docs"
    }
    }
    複製程式碼

    然後執行npm run docs:dev即可訪問

  2. 簡單配置
    在docs/.vuepress下新建檔案config.js

    module.exports = {
        base:'/sakura-ui/',
        title: 'Sakura UI',
        description: 'Inspiration from heian sakura',
        head: [
          ['link', { rel: 'icon', href: '/favicon.ico' }]
        ],
        themeConfig: {
          nav: [
            { text: 'Home', link: '/' },
            { text: 'Github', link: 'https://github.com/Firenzia/sakura-ui/' },
          ],
          sidebar: [
              {
                  title: '開發指南',
                  collapsable: true,
                  children: [
                    'views/guide/install.md',
                    'views/guide/get-started.md'
                  ]
                },
                {
                  title: '設計',
                  collapsable: true,
                  children: [
                    'views/design/color/',
                  ]
                },
                {
                  title: '元件',
                  collapsable: true,
                  children: [
                    'views/components/basic/',
                    'views/components/form/',
                    'views/components/navigation/',
                    'views/components/notice/',
                    'views/components/other/'
                  ]
                },
            ]
          }
      }
    複製程式碼
  3. 使用vue元件
    官網中提到,所有在 .vuepress/components 中找到的 *.vue 檔案將會自動地被註冊為全域性的非同步元件,可以在markdown中引用, vue檔案中的程式碼高亮我用的是vue-highlightjs 檢視這裡

  4. 編寫文件
    由於所有的頁面在生成靜態 HTML 時都需要通過 Node.js 服務端渲染,對於SSR 不怎麼友好的元件(比如包含了自定義指令),你可以將它們包裹在內建的 ClientOnly 元件中,而且注意因為是ssr,元件內部beforeCreate, created生命週期鉤子函式訪問不到瀏覽器 / DOM 的 API,只能在beforeMount和mounted中呼叫。

    ---
    title: 'Basic 基礎'
    sidebarDepth: 2
    ---
    ## Icon 圖示
    <ClientOnly>
      <sakura-icon/>
    <font size=5>Attributes</font>
    | 引數| 說明 | 型別 | 可選值 | 預設值 |
    | :------ | ------ | ------ | ------ | ------ |
    | name | 圖示名稱 | string |- | - |
    | color | 圖示顏色, 支援常見顏色和十六進位制顏色 | string |- | - |
    
    </ClientOnly>
    
    複製程式碼
  5. 覆蓋預設主題樣式
    在.vuepress下新增style.styl進行覆蓋。

  6. 部署到github
    官網上介紹的很清楚,點這裡。 在專案根目錄下新增deploy.sh,windows下直接命令列執行./deploy.sh即可釋出到github pages上。

結語

如果你能看到這裡,非常感謝,第一次寫文章,希望大家多多提出意見。元件庫還有很多細節需要完善,比如裡面css的類名命名我沒做的很規範,大部分元件都是自己測試沒有測到複雜或特殊場景,還有很多功能還沒支援。通過這段時間製作元件庫,自己的技術有了一定提升,官網的展示融入了自己的一點想法和設計,希望大家喜歡~~ 謝謝!

相關文章