不同于js库的打包搭建 组件库还需要打包出css,因此单独讲
组件库对外是编译后(外部应用配置loader不编译)还是编译前(跟外部应用一起编译)
如果是编译后的组件,可以脱离技术栈吗?
我们先来大概看一下市面上组件库搭建的方式
umd-组件库打包产物是纯js
- 打包组件库不抽离css,通过js动态插入css,组件库产物会是js 组件库入口文件
// src/index.js
import Hello from "./components/Hello.vue"
// 外部的Vue.use(MyLib)会执行该方法,完成组件的全局注册。
function install(Vue){
Vue.component(Hello.name, Hello)
}
// 通过`script`标签引入的情况,在组件内部完成组件注册。
if(window && window.Vue) {
Vue.use(install)
}
export default install
👆 打包成umd后 支持
- webpack 工程
import
并vue.use()
- html直接
<script src="">
👆 这种只打包js的形式其实和打包普通js工具库一样,不过是打包配置需要支持css和vue
webpack.config.js
//webpack.config.js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
path: path.join(__dirname,"/dist"),
filename: 'my-ui.js',
libraryTarget: 'umd', //用到的模块定义规范
library: 'myUi', //库的名字
libraryExport: 'default'
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: ['style-loader','css-loader','postcss-loader']
},
{
test: /\.s[ac]ss$/i,
use: ['style-loader','css-loader','postcss-loader','sass-loader']
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
}
]
},
plugins: [
new VueLoaderPlugin()
],
externals : {
// 只有当libraryTarget: 'umd'时,才可以配置如上那样的包含 { root, amd, commonjs,... } 的对象,其它的libraryTarget的值不能这样配置。
vue: {
root: "Vue", //通过 script 标签引入,此时全局变量中可以访问的是 Vue
commonjs: "vue", //可以将vue作为一个 CommonJS 模块访问
commonjs2: "vue", //和上面的类似,但导出的是 module.exports.default
amd: "vue" //类似于 commonjs,但使用 AMD 模块系统
}
}
}
组件库和引用方共同的外部资源库,由引用方提供,组件库不打包进去,如vue、lodash
打包结果会是umd的结构,参考从0到1构建符合标准的公共库
//官方示例。 'MyUi'就是library中定义的库名称, 对应我们自己demo中的myUi。
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
// Commonjs
module.exports = factory();
else if(typeof define === 'function' && define.amd)
// AMD
define([], factory);
else if(typeof exports === 'object')
// ESM???
exports['MyUi'] = factory();
else
// window
root['MyUi'] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return _entry_return_; // 此模块返回值,是入口 chunk 返回的值
});
umd-手动按需加载
导出文件也是一个立即执行函数,一点都不符合es6 模块化方式。按照上面那样的配置是没有办法按需加载的。
有很多库为了按需引用,把打包脚本做成多入口打包的形式,实现单个组件一份js,然后通过文件路径进行引用:
import 'echarts/lib/chart/pie'
import 'echarts/lib/component/title'
这样写不能一次引入多个组件,也不够优雅。element-ui就专门开发了babel插件babel-plugin-component,使我们能像下面这样按需引入:
import { Button, Select } from 'element-ui'
Vue.use(Button)
Vue.use(Select)
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
mode: 'none',
entry: {
'hello': './src/components/Hello/index.js',
'test': './src/components/Test/index.js',
'my-ui': './src/index.js'
},
output: {
path: path.join(__dirname,"/lib"),
filename: '[name].js',
libraryTarget: 'umd',
library: '[name]',
libraryExport: 'default'
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: ['style-loader','css-loader','postcss-loader']
},
{
test: /\.s[ac]ss$/i,
use: ['style-loader','css-loader','postcss-loader','sass-loader']
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
}
]
},
plugins: [
new VueLoaderPlugin()
],
externals : {
// 只有当libraryTarget: 'umd'时,才可以配置如上那样的包含 { root, amd, commonjs,... } 的对象,其它的libraryTarget的值不能这样配置。
vue: {
root: "Vue", //通过 script 标签引入,此时全局变量中可以访问的是 Vue
commonjs: "vue", //可以将vue作为一个 CommonJS 模块访问
commonjs2: "vue", //和上面的类似,但导出的是 module.exports.default
amd: "vue" //类似于 commonjs,但使用 AMD 模块系统
}
}
}
webpack抽离css style-loader
替换为 mini-css-extract-plugin
配置示例工程的babel
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "my-ui",
"styleLibrary": {
"name": "lib-style", // same with styleLibraryName
"base": false // if theme package has a base.css
}
}
]
]
}
ESM-组件库打包纯js
使用rollup rollup从入门到打包一个按需加载的组件库
rollup.config.js
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import vue from 'rollup-plugin-vue'
import autoprefixer from 'autoprefixer'
export default {
input: "./src/index.js",
// 一个入口打包出多种产物
output: [
// umd产物
{
file: './dist/my-lib-umd.js',
format: 'umd',
name: 'myLib'
},
// esm产物
{
file: './dist/my-lib-es.js',
format: 'es'
},
// cjs产物
{
file: './dist/my-lib-cjs.js',
format: 'cjs'
}
],
plugins:[
babel({
exclude: 'node_modules/**'
}),
// vue编译配置
vue({
style: {
postcssPlugins: [
autoprefixer()
]
}
}),
// TODO: css抽离配置
// rollup自身不支持cjs语法(自己写的代码尽量不使用cjs,外部库则没办法),需要插件支持
commonjs()
],
external:[ //外部库, 使用'umd'文件时需要先引入这个外部库
'vue'
]
}
package.json
{
"main": "dist/my-lib-cjs.js",
"module": "dist/my-lib-es.js",
}
excemple
写示例调试页面时,可以通过npm link来直接引入资源,而不需要先写成./dist/xx
首先在组件库 package.json
(配置好main指向打包后路径) 目录下初始化link npm link
再再excemle工程下 npm link my-ui
package.json中定义的"name": "my-lib"
热更新问题,引入的都是打包后的库,修改源码不会实时编译,因此没有热更新效果