自建vue組件 air-ui (5) -- 建立第一個組件 Button

前言

經過 自建vue組件 air-ui (4) -- air-ui 環境搭建和目錄結構 咱們已經搭好了 air-ui 項目,接下來就是開始寫組件了。javascript

ui 組件的三種類型

常見的 ui 組件有三種類型:css

標籤類型

這個是最多見的組件類型,好比 buttoncheckbox 等等, 使用的時候是這樣子的:html

<air-button>這是一個按鈕</air-button>
複製代碼

內置服務類型

內置服務類型的組件,其實就是綁定到 VUE 的全局對象中,好比 messagenotificationloading,使用方式是這樣子的:vue

this.$message({
  message: '警告哦,這是一條警告消息',
  type: 'warning'
});
複製代碼

指令方式類型

指令方式的組件,好比 loading, 使用方式是這樣子的 v-xxxjava

<air-button type="primary" @click="openFullScreen1" v-loading.fullscreen.lock="fullscreenLoading">
  指令方式
</air-button>
複製代碼

可是標籤方式的組件是最多見的。 因此本節就以標籤方式的 button 組件來講明。 另外兩種方式後面有時間再講。node

建立 button 組件

首先在建立一個組件的時候,必定要考慮清楚所要建立組件的表現形式,好比 button 按鈕,組件的表現方式可能有兩種:element-ui

  1. 單個按鈕呈現,標籤是 air-button
  2. 多個按鈕一塊兒的按鈕組,標籤是 air-button-group

這邊多個單詞要用中劃線_ 分隔開。 因此目錄結構就是這樣子:sass

components/
|    |--- button/
|    |     |--- src/
|    |     |     |--- button.vue
|    |     |     |--- button-group.vue
|    |     |--- index.js
複製代碼

目錄結構很簡單,事實上後面全部的組件的目錄結構都是這樣子的, 一個 index.js 用來導出對象, src 主要寫組件的邏輯和定義,以本例來講, button.vue 就是 air-button 標籤的實現文件, 而 button-group.vue 就是 air-button-group 標籤的實現文件。bash

具體代碼以下: src/components/button/src/button.vue:post

<template>
  <button class="air-button" @click="handleClick" :disabled="buttonDisabled || loading" :autofocus="autofocus" :type="nativeType" :class="[ type ? 'air-button--' + type : '', buttonSize ? 'air-button--' + buttonSize : '', { 'is-disabled': buttonDisabled, 'is-loading': loading, 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]" >
    <i class="air-icon-loading" v-if="loading"></i>
    <i :class="icon" v-if="icon && !loading"></i>
    <span v-if="$slots.default"><slot></slot></span>
  </button>
</template>
<script> export default { name: 'AirButton', inject: { elForm: { default: '' }, elFormItem: { default: '' } }, props: { type: { type: String, default: 'default' }, size: String, icon: { type: String, default: '' }, nativeType: { type: String, default: 'button' }, loading: Boolean, disabled: Boolean, plain: Boolean, autofocus: Boolean, round: Boolean, circle: Boolean }, computed: { _elFormItemSize () { return (this.elFormItem || {}).elFormItemSize }, buttonSize () { return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size }, buttonDisabled () { return this.disabled || (this.elForm || {}).disabled } }, methods: { handleClick (evt) { this.$emit('click', evt) } } } </script>
複製代碼

src/components/button/src/button-group.vue 的實現代碼:

<template>
  <div class="air-button-group">
    <slot></slot>
  </div>
</template>
<script> export default { name: 'AirButtonGroup' } </script>

複製代碼

事實上,整個邏輯的大部分實現幾乎跟 element-ui 如出一轍,只有一些小細節不同,好比:

  1. 將標籤名 el- 改爲 air-
  2. 將導出的nameEl 改爲 Air
  3. 將一些資源的絕對路徑改爲相對路徑 (button這個標籤組件沒有引入外部資源和第三方庫,因此這一點在本例看不出來)

ps: 並且 air-ui 最後完成的時候,從功能上來講,絕大部分組件的功能都跟element-ui的對應組件如出一轍,只有某些組件,好比 color-picker, table 等由於業務需求,有再進行了一些參數和方法的擴展。

並且 index.js 其實就是組件的導出,代碼以下,src/components/button/index.js

import AirButton from './src/button'

AirButton.install = function (Vue) {
  Vue.component(AirButton.name, AirButton)
}

export default AirButton
複製代碼

若是排除掉那個 install, 這個文件簡直能夠去掉了,由於它就是把 button.vue 導出的對象,從新又導出了一次。 而 install 的存在才顯得這個文件有意義。 install 這個方法定義是由於後續若是這個組件是要容許單獨被 vue 引用的,也是單獨被打包成文件出來的。 vue 設置全局組件的話,是這個 API:

Vue.component(yourModule.name, yourModule)
複製代碼

可是若是 vue 加載第三方庫的話,是這個 API:

Vue.use(yourModule)
複製代碼

這時候就會去執行 yourModule 對象的 install 方法。 因此 index.js 文件實現 install 方法,只是爲了後面讓 vue 單獨設置全局組件的時候,除了能夠用標準 component 語法以外,還能夠用 use 語法,而 use 語法會觸發 install 方法,而後再在 install 方法去實現 component 語法。因此對於 button 組件來講,若是要用 vue 設置爲全局組件的話,有這兩種引用方式:

import Vue from 'vue';
import Button from './components/button'
Vue.component(Button.name, Button)
複製代碼

或者是

import Vue from 'vue';
import Button from './components/button'
Vue.use(Button);
複製代碼

這兩個效果實際上是同樣的。最後都實現了用 component 方法註冊成全局組件。

測試 button 組件

既然邏輯代碼寫好了,接下來是測試了。 因此修改首頁 hoome.vue 改爲這樣子

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <air-button disabled>默認按鈕</air-button>
    <air-button type="primary">主要按鈕</air-button>
    <br>
    <air-button-group>
      <air-button type="primary">主要按鈕</air-button>
      <air-button type="success">成功按鈕</air-button>
      <air-button type="info">信息按鈕</air-button>
      <air-button type="warning">警告按鈕</air-button>
      <air-button type="danger">危險按鈕</air-button>
    </air-button-group>
  </div>
</template>

<script> import AirButton from '../components/button/src/button' import AirButtonGroup from '../components/button/src/button-group' export default { data () { return { msg: `AIR-UI - 基於vue2.x,可複用UI組件` } }, components: { AirButton, AirButtonGroup }, } </script>
複製代碼

有效果了,可是發現並無樣式:

1

因此咱們寫好樣式: src/styles/button.scss, 寫好以後把樣式加上去:

import AirButton from '../components/button/src/button'
import AirButtonGroup from '../components/button/src/button-group'
import '../styles/button.scss'
複製代碼

可是發現編譯的時候,報錯:

1

看起來是 sass 模塊和對應的 loader 沒有裝的緣故, 這時候要手動安裝:

yarn add sass-loader node-sass --dev
複製代碼

可是發現仍是報錯:

1

網上查了一下資料,發現原來是sass-loader 的版本太高致使,因其最新版本爲 8.0.0,此會致使編譯出錯, 我從新換成 6.0.7 就能夠了:

yarn remove sass-loader
yarn add sass-loader@6.0.7 --dev
複製代碼

這樣子,編譯就能夠了。

1

這樣子這個組件就完成了。可是發現好像還能夠再優化:

註冊成全局組件對象

上面雖然組件已經作好了,可是能夠看到再引用的時候,是做爲局部組件來使用的,每次都要在 home.vue 引入 vue 文件和 css 文件。因此咱們要註冊成 vue 的全局組件,這樣子在寫 demo 的時候,就不用引入資源了。 因此 src/main.js 要改一下,加上如下代碼:

import Button from './components/button/src/button'
import ButtonGroup from './components/button/src/button-group'
import './styles/button.scss'

Vue.component(Button.name, Button)
Vue.component(ButtonGroup.name, ButtonGroup)
複製代碼

這樣子 buttonbutton-group 就會被註冊成全局組件了。 因此這時候我 home.vue 就能夠簡寫爲這樣子, src/views/home.vue:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <air-button disabled>默認按鈕</air-button>
    <air-button type="primary">主要按鈕</air-button>
    <br>
    <air-button-group>
      <air-button type="primary">主要按鈕</air-button>
      <air-button type="success">成功按鈕</air-button>
      <air-button type="info">信息按鈕</air-button>
      <air-button type="warning">警告按鈕</air-button>
      <air-button type="danger">危險按鈕</air-button>
    </air-button-group>
  </div>
</template>

<script> export default { data () { return { msg: `AIR-UI - 基於vue2.x,可複用UI組件` } }, } </script>
複製代碼

去掉了組件的導入 和 components 的聲明。直接默認使用全局組件。

再優化-兼容 vue.use 語法

雖然能夠註冊全局組件了。可是仍是不夠優雅(明明是兩個組件的聲明,可是引入的路徑居然是在 button 的src 目錄下,都沒有分開), 並且也不能使用 vue.use 語法,接下來咱們來兼容一下 use 語法,咱們知道要使用 use 語法,一個很重要的方式就是組件要有定義 install 方法,好比 buttonsrc/components/button/index.js 就有:

import AirButton from './src/button'

AirButton.install = function (Vue) {
  Vue.component(AirButton.name, AirButton)
}

export default AirButton
複製代碼

可是 button-group 沒有啊,因此咱們要爲 button-group 再建立一個獨屬於他的組件目錄:而且在目錄下建立一個 index.js 文件,完整的文件路徑就是 src/components/button-group/index.js, 內容以下:

import AirButtonGroup from '../button/src/button-group'

AirButtonGroup.install = function (Vue) {
  Vue.component(AirButtonGroup.name, AirButtonGroup)
}

export default AirButtonGroup
複製代碼

是的,咱們不須要再去寫 button-group 組件的邏輯代碼了,由於 button 組件的 src 裏面幫咱們寫好了,咱們只須要引用這個 vue 文件,而且定義好 install 方法,最後導出就好了。 最後再調整一下 main.js 的寫法:

import Button from './components/button'
import ButtonGroup from './components/button-group'
import './styles/button.scss'

Vue.use(Button)
Vue.use(ButtonGroup)
複製代碼

寫成這樣子,就更優雅了。

固然這邊還有一個細節,還能夠再優化一下,由於咱們從目錄結構上來講, buttonbutton-group 這兩個是單獨的組件,那麼 button-group 也應該也有本身的 scss 文件,可是事實上 button-group 相關的樣式都寫在 button.scss 上了,因此咱們爲了一致性,咱們就新建了一個空的 scss 文件 button-group.scss, 因此main.js 能夠再加上引入 button-group.scss:

import Button from './components/button'
import ButtonGroup from './components/button-group'
import './styles/button.scss'
import './styles/button-group.scss'

Vue.use(Button)
Vue.use(ButtonGroup)
複製代碼

導入全局組件庫

如今咱們有兩個組件了,buttonbutton-group, 後面咱們還會增長很是多的組件。總不可能咱們後面在註冊組件的時候,都要在 main.js 中導入這些組件的 js 和 css 文件吧,那但是太多了。因此這些組件合起來應該是一個組件庫,就像 element-ui 同樣,咱們只須要引入這個組件庫對象,那麼就可使用到這個組件庫所包含的全部組件。

air-ui 也是同樣的作法,所以咱們也要作本身的組件庫,而後將咱們寫的這些組件都放到這個組件庫裏面,而後再到 main.js 中去引用這個組件庫,就至關於註冊了這個組件庫的全部的組件了。

因此 src/components 增長了一個文件 src/components/index.js:

import Button from './button'
import ButtonGroup from './button-group'

const components = {
  Button,
  ButtonGroup
}

const install = function (Vue) {
  Object.keys(components).forEach(key => {
    Vue.component(components[key].name, components[key])
  })
}

export default {
  install
}
複製代碼

這個很好理解,由於要用 vue.use 來加載這個組件庫,因此導出的對象,要有 install 方法,而且由於要把這些組件都註冊成 vue 全局組件,因此這一步就在 install 方法裏面來處理。

而後在 main.js 將以前的去掉,改爲這樣子:

import AirUI from './components/index'

Vue.use(AirUI)
複製代碼

這樣就更完美了,不事後面調試的時候,發現沒有樣式,查了一下,發現樣式沒有導入, 因此咱們樣式的導入方式也應該改一下,換成導入組件庫樣式的方式,而不是在 main.js 中一個組件樣式一個組件樣式的導入。 因此咱們在 src/styles 目錄新增長了 index.scss, 而後把當前的組件的樣式都導入進去, src/styles/index.scss:

@import "./button.scss";
@import "./button-group.scss";
複製代碼

而後在 main.js 中導入這個總的組件庫的 scss 文件便可了。

import AirUI from './components/index'
import './styles/index.scss'

Vue.use(AirUI)
複製代碼

哇,這樣就真的很完美了。 基本上一個最小規模的組件庫的雛形就出來了。

總結

咱們如今已經能夠很好的寫一些標籤組件了,下節咱們講一下怎麼寫 指令組件和內置服務組件。


系列文章:

相關文章
相關標籤/搜索