Vue3 Library模式构建私有组件库的实战经验分享

南宫佳杰 工具 阅读 1,663
赞 10 收藏
二维码
手机扫码查看
反馈

先来个完整的Demo跑起来

别整那些虚的,直接看代码跑起来什么效果。Library模式就是把你的代码打包成一个可以直接引用的库文件,比如jQuery那种,在script标签里引入就能用了。

Vue3 Library模式构建私有组件库的实战经验分享

// 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模式确实是个好东西,特别是做组件库或者工具库的时候。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论