少女風vue組件庫製做全攻略~~

預覽

組件庫官網
github地址
若是喜歡各位小哥哥小姐姐給個小星星鼓勵一下哈, 請勿在生產環境中使用,供學習&交流~~

css

完整項目目錄結構

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

├─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 因爲這是 DOM2 規範的基本內容,幾乎全部瀏覽器都支持這個,並且不須要特殊的跨瀏覽器兼容代碼。
  • Node.contains()返回的是一個布爾值,來表示傳入的節點是否爲該節點的後代節點。多用於事件監聽判斷是否點擊了目標區域。
  • window.scrollY 獲取文檔垂直方向的滾動距離。
  • Element​.get​Bounding​Client​Rect() 返回元素的大小及其相對於視口的位置,返回一個對象,包括width/height/left/right/top/bottom。多用於計算定位。

技術點總結

組件設計的思想包括單數據流/ eventBus事件中心,核心是組件通訊。vue

  • 單數據流: 數據的改變是單向的,即經過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開發思路node

  1. 在原有的popover組件上開發
    點擊一個元素A(輸入框)後能夠彈出元素B(日期面板)git

  2. 生成日期面板
    生成7*6=42個日期,6行是爲了確保一個月都能在面板上完整顯示。這裏計算最方便的作法是用時間戳,計算出這個月第一天時間戳和這一天周幾,就能夠一次性計算出這42個日期。不用算上個月下個月分三段算,這樣的問題是還要考慮邊界狀況,如恰好出現上一年下一年等,麻煩容易出bug。這42個日期咱們在computed用visibleDays表示。github

    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',進行樣式上的區分vue-cli

  4. 實現選中日期
    告訴父組件修改數據意圖讓父組件修改傳入的props,對應使用咱們組件的時候使用, 這裏的基礎知識是組件上的v-model是個語法糖,v-model="x"會被解析成:value="a" @input="a=$event"。同時面板上輸入框顯示的數據也要跟着變化,因此這裏用計算屬性,如在computed中用formattedValue表示。npm

    formattedValue: {
         return this.value instanceof Date ? helper.getFormatDate(this.value) : ''
     }
    複製代碼
  5. 實現點擊上一年/月,下一年/月
    咱們須要知道當前展現的是哪一年哪個月,這個數據是組件內部維護的,因此在data申明一個display對象json

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

    點擊的時候即修改display對象的year/month,由於visibleDays也是計算屬性,依賴display對象,因此點擊上一年/月,下一年/月,渲染的日期也跟着變。windows

  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的類名命名我沒作的很規範,大部分組件都是本身測試沒有測到複雜或特殊場景,還有不少功能還沒支持。經過這段時間製做組件庫,本身的技術有了必定提高,官網的展現融入了本身的一點想法和設計,但願你們喜歡~~ 謝謝!

相關文章
相關標籤/搜索