Vue3 Library模式构建私有组件库的实战经验分享
先来个完整的Demo跑起来
别整那些虚的,直接看代码跑起来什么效果。Library模式就是把你的代码打包成一个可以直接引用的库文件,比如jQuery那种,在script标签里引入就能用了。
// webpack.config.js
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: require('path').resolve(__dirname, 'dist'),
filename: 'my-library.js',
library: 'MyLib', // 全局变量名
libraryTarget: 'umd', // 支持多种模块系统
globalObject: 'this'
}
};
// src/index.js
class MyLibrary {
constructor(options) {
this.options = options || {};
}
sayHello(name) {
return Hello ${name}!;
}
getData(url) {
return fetch(url).then(res => res.json());
}
}
// 暴露给全局
if (typeof window !== 'undefined') {
window.MyLib = MyLibrary;
}
export default MyLibrary;
这样打包出来的文件就可以这样用:
<script src="./dist/my-library.js"></script>
<script>
const myLib = new MyLib();
console.log(myLib.sayHello('World')); // "Hello World!"
</script>
几种常见的libraryTarget选择
这里我踩过坑,不同的target适用不同场景。亲测有效的几种:
- umd:兼容性最好的,支持AMD、CommonJS和全局变量
- var:只支持全局变量
- window:专门给浏览器用的
- commonjs2:给Node.js用
// 针对不同环境的配置
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-library.umd.js',
library: 'MyLib',
libraryTarget: 'umd', // 推荐这个,兼容性强
umdNamedDefine: true
},
externals: { // 外部依赖处理
lodash: {
commonjs: 'lodash',
commonjs2: 'lodash',
amd: 'lodash',
root: '_'
}
}
};
externals这个配置很重要,不然会把lodash整个打包进去,文件变得很大。这里注意我踩过好几次坑,如果不设置externals,使用者还得装一遍你库里的依赖,就很尴尬。
Vue组件库打包实战
最近做了一个Vue组件库,Library模式真的香。直接看代码:
// vue组件库的webpack配置
module.exports = {
mode: 'production',
entry: {
'my-components': './src/index.js',
'button': './src/components/Button.vue',
'input': './src/components/Input.vue'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: ['MyComponents', '[name]'],
libraryTarget: 'umd',
globalObject: 'this'
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
}
};
// src/index.js - 组件库入口
import Button from './components/Button.vue';
import Input from './components/Input.vue';
const components = [Button, Input];
const install = function(Vue) {
components.forEach(component => {
Vue.component(component.name, component);
});
};
// 支持按需引入
export { Button, Input };
// 支持全局注册
export default {
install,
Button,
Input
};
这个场景最好用
Library模式最爽的地方是做工具函数库。比如我写的一个日期处理库,只需要这么配置:
// 日期处理库配置
module.exports = {
mode: 'production',
entry: './src/date-utils.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'date-utils.min.js',
library: 'DateUtils',
libraryTarget: 'umd',
libraryExport: 'default' // 导出默认值
}
};
// date-utils.js
class DateUtils {
static format(date, fmt) {
// 格式化逻辑...
return formattedDate;
}
static addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
static isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
}
}
export default DateUtils;
使用的时候就是:npm install your-date-lib 或者直接script引入,非常方便。
踩坑提醒:这三点一定注意
搞Library模式最容易翻车的就是这三个地方:
第一,externals配置。如果不小心配置错误,会把不必要的依赖打包进来。比如你的库用了moment.js,但使用者可能已经引入过了,这时就应该external掉:
externals: {
moment: 'moment',
vue: 'Vue',
react: 'React'
}
第二,版本兼容性。Library要特别注意兼容性,不能用太新的ES语法,否则老项目引入会报错。Babel配置要设置目标环境:
// .babelrc
{
"presets": [
["@babel/preset-env", {
"targets": {
"browsers": ["> 1%", "last 2 versions"]
}
}]
]
}
第三,Tree Shaking支持。现代前端框架都支持按需加载,你的库也要考虑这一点:
// package.json
{
"sideEffects": false,
"module": "dist/my-lib.esm.js"
}
然后同时输出ES Module版本和UMD版本,让用户自己选择。
高级玩法:同时输出多个版本
实际项目中经常需要同时输出几个版本满足不同需求。这是我的配置:
// 多入口打包配置
const config = [
// UMD版本
{
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-lib.umd.js',
library: 'MyLib',
libraryTarget: 'umd'
}
},
// ES Module版本
{
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-lib.esm.js',
libraryTarget: 'module'
},
experiments: {
outputModule: true
}
}
];
module.exports = config;
这样用户可以根据自己的构建工具选择合适的版本,灵活性提升不少。
以上是我踩坑后的总结,Library模式确实是个好东西,特别是做组件库或者工具库的时候。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

暂无评论