Rollup - 開(kāi)發(fā)UI庫(kù)入門

本文示例如何基于RollupVue編寫(xiě)一個(gè)HelloWorld的UI庫(kù)

一、初始化項(xiàng)目

生成package.json

mkdir base-ui
cd base-ui
npm init

安裝npm包

npm install --save-dev rollup rollup-plugin-vue vue vue-template-compiler 
npm install --save-dev rollup-plugin-babel @babel/core
npm install --save-dev rollup-plugin-node-resolve rollup-plugin-commonjs 
npm install --save-dev rollup-plugin-terser rollup-plugin-css-only clean-css

二、配置文件

增加rollup配置文件 rollup.config.js,支持umd es iiffe格式(分別用于node、es6、browser)

import commonjs from 'rollup-plugin-commonjs'
import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import vue from 'rollup-plugin-vue'
import { terser } from 'rollup-plugin-terser'
import css from 'rollup-plugin-css-only' // 提取css
import CleanCSS from 'clean-css'   // 壓縮css
import { writeFileSync } from 'fs' // 寫(xiě)文件

module.exports = {
  input: 'src/index.js', // 入口
  output: [
{
    file: 'dist/base-ui.umd.js', // 打包文件名
    name: 'base-ui',
    format: 'umd', // 打包模塊支持方案,可選 amd, cjs, es, iife, umd
  }, {
    file: 'dist/base-ui.es.js',
    format: 'es'
  }, {
    name: 'base-ui',
    file: 'dist/base-ui.min.js',
    format: 'iife'
  }],
  plugins: [
    // css({output: 'dist/base-ui.css'}),
    css({ output(style) {
      // 壓縮 css 寫(xiě)入 dist/base-ui.css
      writeFileSync('dist/base-ui.css', new CleanCSS().minify(style).styles)
    } }),
    vue({
      // css: false 將<style>塊轉(zhuǎn)換為導(dǎo)入語(yǔ)句,rollup-plugin-css-only可以提取.vue文件中的樣式
      css: false,
      normalizer : '~rollup-plugin-vue/runtime/normalize',
      // styleInjector : '~rollup-plugin-vue/runtime/browser',
    }),                    
    // terser(),
    resolve(),
    babel({
      exclude: ['node_modules/**']
    }),
    commonjs()
  ],
  external: [ // 不被打包的庫(kù)
    'vue'
  ]
}

插件說(shuō)明

  1. rollup-plugin-vue:負(fù)責(zé)處理vue文件,相當(dāng)于webpackvue-loader。可選參數(shù)見(jiàn)這里

5.0以后的rollup-plugin-vue需要明確指定normalizer : '~rollup-plugin-vue/runtime/normalize',,否則打包輸出文件有__vue_normalize__相關(guān)的引用錯(cuò)誤。

2 rollup-plugin-commonjs
該插件負(fù)責(zé)將Commonjs模塊轉(zhuǎn)化成ES6以便rollup打包。很多npm包都是commonjs模塊,需要結(jié)合此插件一起使用

  1. rollup-plugin-node-resolve
    rollup只能加載相對(duì)路徑模塊(即通過(guò)/,./, ../引入的模塊),而三方模塊(node_modules里模塊)有兩種方式打包:
    • rollup.config.js配置為external資源: 輸出bundle中不包含此模塊,作為external外部資源引入,由使用該bundle組件庫(kù)的客戶程序安裝
// rollup.config.js
export default {
  entry: 'src/index.js',
  dest: 'bundle.js',
  format: 'cjs',
  external: [ 'vue' ] // <-- suppresses the warning
};

此時(shí),需要在組件庫(kù)的package.json中增加外部依賴

"dependencies": {
    "vue": "^2.5.0"
  }

客戶端程序安裝該組件庫(kù)時(shí)會(huì)自動(dòng)安裝dependencies定義的模塊

  • rollup-plugin-node-resolve:該插件可以將三方模塊包含在輸出bundle中。具體見(jiàn)官網(wǎng)介紹
  1. rollup-plugin-babel: 在rollup項(xiàng)目中使用babel,詳細(xì)見(jiàn)這里

  2. rollup-plugin-terser: 壓縮輸出后js

  3. rollup-plugin-css-only: 提取css為獨(dú)立文件

  4. clean-css:壓縮css

package.json最終代碼為:

{
 "name": "base-ui",
 "version": "1.0.0",
 "description": "A demo for how to create a UI library with Rollup",
 "main": "index.js",
 "scripts": {
   "build": "rollup -c",
   "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "",
 "license": "ISC",
 "devDependencies": {
  "@babel/core": "^7.7.4",
   "clean-css": "^4.2.1",
   "node-sass": "^4.13.0",
   "rollup": "^1.27.8",
   "rollup-plugin-babel": "^4.3.3",
   "rollup-plugin-commonjs": "^10.1.0",
   "rollup-plugin-css-only": "^1.0.0",
   "rollup-plugin-node-resolve": "^5.2.0",
   "rollup-plugin-terser": "^5.1.2",
   "rollup-plugin-vue": "^5.1.2",
   "vue-template-compiler": "^2.6.10"
 }
}

三、開(kāi)發(fā)組件庫(kù)

3.1 開(kāi)發(fā)組件

src/button/index.vue 編寫(xiě)UI組件

<template>
  <h1>Button</h1>
</template>

<script>
export default {
  name: 'my-buttom',
  created() {
    console.log('hello world')
  }
}
</script>

3.2 打包全部組件

src/index.js 輸出組件。其中install函數(shù)可使組件庫(kù)當(dāng)做Vue插件使用,全局注入組件庫(kù)。請(qǐng)參考VUE插件

import Button from './button/Index.vue'

const components = [Button];
// 全局注入組件
const install = function(Vue) {
  if (!Vue || install.installed) return
  components.forEach(component => {
     Vue.component(component.name, component);
  })
}

 if (typeof window != 'undefined' && window.Vue) {
    install(window.Vue)
  }

export {
  Button
}

export default {
  install
};
npm run build

輸出文件dist/base-ui.umd.js如下

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global['base-ui'] = factory());
}(this, (function () { 'use strict';

  var script = {
    name: 'my-button',
    created() {
      console.log('hello world');
    }

  };

  ...

  var index = {
    install
  };

export default index;
export {  __vue_component__ as Button};
})));

3.3 輸出單獨(dú)組件

輸出單獨(dú)組件以支持按需加載
rollup.component.config.js增加用于輸出組件的配置文件

import commonjs from 'rollup-plugin-commonjs'
import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import vue from 'rollup-plugin-vue'
import { terser } from 'rollup-plugin-terser'
import css from 'rollup-plugin-css-only' // 提取css
import CleanCSS from 'clean-css'   // 壓縮css
import { writeFileSync } from 'fs' // 寫(xiě)文件

module.exports = {
  input: 'src/Button/index.js', // 入口
  output: {
    file: 'dist/button/index.js',
    format: 'es'
  },
  plugins: [
    css({ output(style) {
      // 壓縮 css 寫(xiě)入 dist/base-ui.css
      writeFileSync('dist/button/index.css', new CleanCSS().minify(style).styles)
    } }),
    vue({
      // css: false 將<style>塊轉(zhuǎn)換為導(dǎo)入語(yǔ)句,rollup-plugin-css-only可以提取.vue文件中的樣式
      css: false,
      normalizer : '~rollup-plugin-vue/runtime/normalize',
      // styleInjector : '~rollup-plugin-vue/runtime/browser',
    }),                    
    // terser(),
    resolve(),
    babel({
      exclude: ['node_modules/**']
    }),
    commonjs()
  ]
}

執(zhí)行命令rollup -c rollup.component.config.js輸出組件

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global['base-ui'] = factory());
}(this, (function () { 'use strict';

  var script = {
    name: 'my-button',
    created() {
      console.log('hello world');
    }

  };

  ...

export {  __vue_component__ as Button};
})));

四、測(cè)試組件庫(kù)

4.1 創(chuàng)建測(cè)試項(xiàng)目

vue create examples 創(chuàng)建項(xiàng)目,用于模擬實(shí)際項(xiàng)目引入組件庫(kù)

全局引入
examples/src/main.js中引入組件,全局注入組件

import MyComponent from '../../dist/base-ui.es'
import MyComponent from '../../dist/base-ui.css'
Vue.use(MyComponent)

examples/src/components/HelloWorld.vue中使用組件

<template>
  <div id="app">
    <my-index></my-index>
  </div>
</template>

按需引入
src/components/HelloWorld.vue中引入Button

...
import { Button } from '../../../dist/button/index.es'

export default {
  components: {
    [Button.name]: Button
  },
}
...

bable-plugin-import
bable-plugin-import可以幫助用戶方便地實(shí)現(xiàn)按需加載
bable.config.js配置文件中增加配置(如下為mand-mobile配置):

{
  "plugins": [
    ["import", {
      "libraryName": "mand-mobile",  // 組件庫(kù)名稱
      "libraryDirectory": "lib"  // 組件所在目錄
    }]
  ]
}

import { Button } from 'mand-mobile'
babel-plugin-import會(huì)將以上引入語(yǔ)句轉(zhuǎn)換為下面寫(xiě)法
import Button from 'mand-mobile/lib/button'

4.2 測(cè)試

組件項(xiàng)目package,json下增加執(zhí)行命令, 啟動(dòng)組件庫(kù)和示例項(xiàng)目的開(kāi)發(fā)模式

"scripts": {
    "dev": "rollup -c --watch",
    "example": "cd examples && npm run serve",
    ...
}

組件項(xiàng)目 package.json里增加入口文件

...
"main": "dist/base-ui.umd.js",
"module": "dist/base-ui.es.js",
"unpkg": "dist/base-ui.min.js",
...

Issues

examples項(xiàng)目里從組件輸出目錄(/dist)引入組件時(shí)默認(rèn)會(huì)啟動(dòng)eslint,編譯會(huì)報(bào)語(yǔ)法錯(cuò)誤。暫時(shí)取消lint


源碼:https://github.com/guolixincl/examples/tree/master/rollup/base-ui


參考文章

  1. https://jsprogram.cn/blog/rollup-config-for-vue-ui-library/
  2. http://www.itdecent.cn/p/0c42ae0f1f2b
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容