npm 部署自己的組件庫

基于 vue-cli 打造屬于自己的 UI 庫

寫在前面

知識前置

我們可能習慣了在一個 vue 里面引入組件的方式,所以先這里要鞏固一下全局引入組件的方式。舉個栗子??,一般我們的用法是這樣的:

import Loading from '../components/loading'
// 方法一:name 是組件的名字
Vue.component(Loading.name, Loading)
// 方法二:前提是 Loading 有提供 install 這個方法
Vue.use(Loading);

基于vue-cli3打造組件庫

搭建目錄

快速創(chuàng)建項目

  • vue-cli3
vue create projectName

修改目錄結構

  • 把 src 目錄名字改成 examples,這是用于展示組件示例的
  • 在根目錄下新建一個 packages 文件夾,這是用來放組件的


    1ll.png

    你可能會問為什么要建這樣的目錄結構,問得好,原因很簡單,因為別人是這樣做的,所以借鑒(模仿)羅。。。我們可以看到 Element 的源碼也是這樣的結構:
    當我們水平不夠的時候,模仿是一種強大的學習能力??。

添加配置文件

小改了一下目錄之后,你會驚奇的發(fā)現(xiàn)項目運行不了了。沒關系,這很正常,畢竟 src 都不見了,路徑啥的肯定得報錯。所以現(xiàn)在我們來解決這個問題。 在根目錄下新建一個 vue.config.js 文件(新項目是沒有這個文件的),并寫入以下內容:

const path = require('path')
module.exports = {
  // 修改 pages 入口
  pages: {
    index: {
      entry: 'examples/main.js', // 入口
      template: 'public/index.html', // 模板
      filename: 'index.html' // 輸出文件
    }
  },
  // 擴展 webpack 配置
  chainWebpack: config => {
    // @ 默認指向 src 目錄,這里要改成 examples
    // 另外也可以新增一個 ~ 指向 packages
    config.resolve.alias
      .set('@', path.resolve('examples'))
      .set('~', path.resolve('packages'))

    // 把 packages 和 examples 加入編譯,因為新增的文件默認是不被 webpack 處理的
    config.module
      .rule('js')
      .include.add(/packages/).end()
      .include.add(/examples/).end()
      .use('babel')
      .loader('babel-loader')
      .tap(options => {
        // 修改它的選項...
        return options
      })
  }
}

上面的注釋應該都寫的挺明了,主要就是修改別名、修改入口文件以及把新文件加入 webpack 編譯這幾個步驟。然后我們再運行一下程序就可以跑得通了。至于為什么這么配置、或者怎么配置,不了解的同學可以去 Vue Cli 官網看下,上面寫的是清清楚楚、明明白白,然而我也只是懂那么一兩個配置而已??????,還沒學會 webpack 的套路,因為常常是用的時候看一眼,一陣子不用就又忘了,沒辦法???♀?腦子不行。

編寫組件

一個組件庫沒有組件怎么行呢,所以我們要先寫個 test 組件(你可以隨便寫,這不重要)。ok??,我們先在 packages 目錄下新建一個 test 文件夾,再在 test 文件夾下下面新建一個 src 文件夾,在 src 文件夾下面新建一個 test.vue 組件,大概長下面這樣子??:
![](https://upload-images.jianshu.io/upload_images/11846892-72c3c6fb69c57a58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
<!--test.vue-->
<template>
  <div class="echojoy-test" @click="handleClick">{{ num }}</div>
</template>

<script>
export default {
  name: 'EchojoyTest',
  data () {
    return {
      num: 0
    }
  },
  methods: {
    handleClick () {
      this.num++
    }
  }
}
</script>

<style >
.echojoy-test {
  width: 100px;
  height: 100px;
  line-height: 100px;
  border-radius: 50%;
  font-size: 30px;
  text-align: center;
  background: #24292e;
  color: white;
}
</style>

應該都能看懂吧,不過多解釋。??這里主要強調一點,就是 name 這個名字尤為重要,我就在這個坑里呆了挺久。首先它是必須要寫的,為啥呢,你可以把它理解為 id,具有唯一標識組件的作用,將來我們可是要通過這個 name 來找到和判定這是什么組件,所以你寫的所有組件應該是不重名的;其次這個 name 就是我們最終的標簽名,比如這里我們的 name 是 EchojoyTest,到時候我們寫的標簽就長這樣 <echojoy-test></echojoy-test>,就像 Element 一樣,name 是 ElButton,用的時候就是 <el-button></el-button>

暴露組件

讓我們在 packages/test 下面新建一個 index.js 文件,具體代碼如下:

// 對外提供對組件的引用,注意組件必須聲明 name
import EchojoyTest from './src/test'
// 為組件提供 install 安裝方法,供按需引入
EchojoyTest.install = Vue => {
  Vue.component(EchojoyTest.name, EchojoyTest)
}
export default EchojoyTest

這步的精髓就在于給組件擴展一個 install 方法,至于為什么要擴展這個方法,文章開頭已經說到了,是因為 Vue.use() 的需要,use 會默認調用 install 方法安裝,僅此而已。接著我們在 packages 下面也新建一個 index.js 文件,注意和上面那個 index.js 區(qū)別開,上面那個是針對單個組件安裝的,這個是針對所有組件全局安裝的,先看代碼:

import EchojoyTest from './test'
// 存儲組件列表
const components = [
  EchojoyTest
]
// 定義 install 方法,接收 Vue 作為參數(shù)。如果使用 use 注冊插件,則所有的組件都將被注冊
const install = function (Vue) {
  // 判斷是否安裝
  if (install.installed) return
  install.installed = true
  // 遍歷注冊全局組件
  components.map(component => Vue.component(component.name, component))
  // 下面這個寫法也可以
  // components.map(component => Vue.use(component))
}

// 判斷是否是直接引入文件
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

export default {
  // 導出的對象必須具有 install,才能被 Vue.use() 方法安裝
  install,
  // 以下是具體的組件列表
  ...components
}

這步的主要作用就是統(tǒng)一導出所有組件及暴露 install 方法。之前的 index.js 只是安裝單個組件,而現(xiàn)在這個 index.js 是循環(huán)安裝所有組件,具體使用就看你是不是要按需引用了。這里給個目錄結構方便大家觀看:

4ll.png

因為這步挺重要的,所以建議好好停下來理解消化一下??。。。
當然你可能會問道,為什么這樣建目錄?還能什么原因,因為 Element 是這樣(如下圖),所以我們這樣寫,僅此而已。

組件測試

ok,組件寫完了,接下來我們就在 examples 下面測試一下,看看能不能引用成功。 首先在 examples 下的 main.js 中引入剛剛寫好的包,就像下面這樣:

import EchojoyUI from './../packages'
Vue.use(EchojoyUI);

然后把 examples/components下面的 HelloWorld里面的內容刪了,寫入自己標簽組件,就像下面這樣:

image.png

好了,最后讓我們運行一下項目 npm run serve,看看效果,嗯,還湊合吧。

庫模式打包

在 vue-cli3 中我們通過以下命令可以將一個單獨的入口打包成一個庫:

// target: 默認為構建應用,改為 lib 即可啟用構建庫模式
// name: 輸出文件名
// dest: 輸出目錄,默認為 dist,這里我們改為 lib
// entry: 入口文件路徑
vue-cli-service build --target lib --name lib [entry]

要注意的是在庫模式中,打包出來的庫中是不包含 Vue 的。 然后我們修改一下 package.json 文件,就像下面這樣:

xxxxx.png

接著執(zhí)行 npm run lib 就能生成庫啦,看看左側的目錄是不是多了個 lib 文件夾,那個就是我們要發(fā)布的東西。

cccc.png

補充下,lib 目錄下面的 js 之所以有好幾種,是因為有兩種規(guī)范(common 和 umd)、是否壓縮(min)和映射(map)的區(qū)別,暫且知道有這么回事就行,不用深究。

發(fā)布到npm

萬事俱備,只欠發(fā)布。

  1. 完善一下 README.md 文檔,這個隨便寫兩句就好
  2. 修改一下 package.json 文件:
{ 
  "name": "echojoy-ui",
  "version": "0.1.0",
  "private": true,
  "description": "基于 vue-cli3 的 UI 組件庫",
  "main": "lib/xr-ui.umd.min.js",
  "keywords": "echojoy-ui",
  "private": false,
  "license": "MIT"
}

  1. 在根目錄下新建一個 .npmignore 文件,內容和 .gitignore 差不多:
# 這是復制 .gitignore 里面的
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

# 以下是新增的
# 要忽略目錄和指定文件
examples/
packages/
public/
vue.config.js
babel.config.js
*.map
*.html

最后執(zhí)行 npm login 登入 npm 賬號,再執(zhí)行 npm publish 發(fā)布即可,就這么簡單的兩步就可以,過一會在 npm 上就能搜到了。當然前提是你有個 npm 賬號,沒有的話去注冊一個吧,很 easy 的,然后還要搜下你的 npm 包名是否有人用,有的話就換一個。

小試牛刀

終于,歷盡千辛萬苦,我們可以引用自己寫的庫拉,想想就牛叉。別激動,讓我們試驗一下,用 vue create echojoy-ui-use 另起一個新項目,然后 npm i echojoy-ui -S,可以在 node_modules 里面看到我們的包大概長這樣:

zzzz.png

然后在 main.js 引入:

import EchojoyUI from "echojoy-ui";
import "echojoy-ui/lib/echojoy-ui.css";
Vue.use(EchojoyUI);

這樣我們就能在頁面中引入組件啦,哈哈哈哈,賊開心,喜上眉梢。。。

<echojoy-test></echojoy-test>

! vue-cli3(vue-cli2)

 vue init webpack projectName
1111.png

啟動項目

npm run dev

4444.png

瀏覽器輸入

http://localhost:8080

編寫組件

在src的components新建組件limit:即文件夾EchoLimit->文件index.vue + 文件index+css

// index.vue
<template>
  <div class="echo-limit-container" v-if="visible === false">
    <div class="echo-limit-container-inner">
      <span class="echo-limit-text">
        {{limitTip}}
      </span>
    </div>
  </div>
</template>

<script>

export default {
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    limitTip: {
      type: String,
      default: '沒有權限!!'
    },
  },
  name: "EchoLimit" // 這個名字很重要,它就是未來的標簽名<echo-limit></echo-limit>
};
</script>
<style>
  @import "./index"
</style>


??這里主要強調一點,就是 name 這個名字尤為重要,你可以把它理解為 id,具有唯一標識組件的作用,將來我們可是要通過這個 name 來找到和判定這是什么組件,所以你寫的所有組件應該是不重名的;其次這個 name 就是我們最終的標簽名,比如這里我們的 name 是 EchoLimit,到時候我們寫的標簽就長這樣 <echo-limit></echo-limit>,就像 Element 一樣,name 是 ElButton,用的時候就是 <el-button></el-button>。

暴露組件

  • 注冊單個組件

在 components 的 EchoLimit下面新建一個 index.js 文件,具體代碼如下:
// 為組件提供 install 方法,供組件對外按需引入

import EchoLimit from "./EchoLimit/index.vue";
EchoLimit.install = Vue => {
  Vue.component(EchoLimit.name, EchoLimit);
};
export default EchoLimit;

這步的精髓就在于給組件擴展一個 install 方法,至于為什么要擴展這個方法,文章開頭已經說到了,是因為 Vue.use() 的需要,use 會默認調用 install 方法安裝。

  • 注冊全局組件
    讓我們在 components 下面新建一個 index.js 文件,具體代碼如下:
// index.js
import EchoLimit from './EchoLimit'
// 所有組件列表
const components = [
  EchoLimit
]
// 定義 install 方法,接收 Vue 作為參數(shù)
const install = function (Vue) {
  // 判斷是否安裝,安裝過就不繼續(xù)往下執(zhí)行
  if (install.installed) return
  install.installed = true
  // 遍歷注冊所有組件
  components.map(component => Vue.component(component.name, component))
  // 下面這個寫法也可以
  // components.map(component => Vue.use(component))
}

// 檢測到 Vue 才執(zhí)行,畢竟我們是基于 Vue 的
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

console.log(components[0].name) // 控制臺輸出組件的name,方便引用組件

export default {
  install,
  // 所有組件,必須具有 install,才能使用 Vue.use()
  ...components
}

目錄結構方便大家觀看:
6666.png

組件測試

ok,組件寫完了,接下來我們就在 HelloWorld.vue下面測試一下,看看能不能引用成功。 首先在 src下的 main.js 中引入剛剛寫好的包,如下所示:

import EchoUI from './components'
Vue.use(EchoUI)

在 HelloWorld.vue中引入組件EchoLimit.

<template>
  <div>
    <EchoLimit></EchoLimit>
     <echo-limit></echo-limit>
  </div>
</echo-limit>

運行項目npm run dev,會發(fā)現(xiàn)成功了??!

999.png

過程同上?。。。。。。。?!

參考 鏈接:https://juejin.im/post/5c95c61f6fb9a070c40acf65

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容