本文示例如何基于Rollup和Vue編寫(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ō)明
-
rollup-plugin-vue:負(fù)責(zé)處理vue文件,相當(dāng)于webpack的vue-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é)合此插件一起使用
-
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)介紹
rollup-plugin-babel: 在rollup項(xiàng)目中使用babel,詳細(xì)見(jiàn)這里rollup-plugin-terser: 壓縮輸出后jsrollup-plugin-css-only: 提取css為獨(dú)立文件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
參考文章