记组件库工程搭建

Posted by Yeoman on 2017-12-10

最近团队内准备维护一套vue的组件库,为了和nek-ui保持一样的交互设计。在搭建一个vue的组件库(nek-vue)的工程,其中有一个平时业务工程中不会遇到的需求,就是组件的按需引入。

需求:组件按需引入

功能点拆分:JavaScript和CSS分别需要按需引入,也就意味着每一个组件必须单独打包成对应的脚本和CSS

JavaScript打包

对于开发环境来讲,webpack的entry可以配成单入口,也就是SPA模式:

1
2
3
4
5
6
7
8
9
10
entry: {
main: './examples/main',
vendors: ['vue', 'vue-router']
},
output: {
path: path.join(__dirname, '../examples/lib'),
publicPath: '',
filename: '[name].js',
chunkFilename: '[name].chunk.js'
}

而对于生成环境,为了满足分开打包的需求,则需要配成多入口,也就是MPA模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
entry: components, // 组件列表config文件
output: {
path: path.resolve(__dirname, '../lib'),
publicPath: '/lib/',
filename: '[name].js',
// 公开出来的包名
library: 'nek-vue',
libraryTarget: 'umd',
umdNamedDefine: true //会对 UMD 的构建过程中的 AMD 模块进行命名。否则就使用匿名的 define。
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
}

这里会使用到两个配置参数,librarylibraryTargetlibrary是暴露的变量名,而libraryTarget可以理解为library暴露的方式。上面的配置的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["nek-vue"] = factory();
else
root["nek-vue"] = factory();
})(this, function() {
// 这个模块会返回你的入口文件返回的对象
});

还有一点要注意的是,因为在组件开发阶段我们会引入Vue,但是在生产环境中我们并不希望将Vue打包进bundle中,而是让用户在运行时(runtime)去获取,这时候可以配置externals选项,这个选项支持从多个方式从外部引入这个依赖。

CSS打包

CSS部分使用gulp来处理打包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var gulp = require('gulp');
var postcss = require('gulp-postcss');
var cssmin = require('gulp-cssmin');
var salad = require('postcss-salad')(require('./salad.config.json'));
gulp.task('compile', function() {
return gulp.src(['../components/**/*.css', '!{style}/**'])
.pipe(postcss([salad]))
.pipe(cssmin())
.pipe(gulp.dest('../lib'));
});
// 拷贝字体文件
gulp.task('copyfonts', function () {
gulp.src('../components/style/iconfonts/fonts/*.*')
.pipe(gulp.dest('../lib/style/fonts'));
});
// 拷贝base
gulp.task('copyfonts', function () {
gulp.src('../components/style/core/base.css')
.pipe(gulp.dest('../lib/'));
});
gulp.task('default', ['compile', 'copyfonts']);

总结

最后打包的结果如下图:

  1. 在项目中使用的时候可以借助babel-plugin-component这个babel插件,来import { Button } from 'nek-vue'这样引入,他的实际引用其实会转换成这样:var button = require('nek-vue/lib/button'); require('nek-vue/lib/styles/button.css')
  2. 在工程搭建的时候,参考了iView和element-ui两个工程,老版本的iView对按需引入的处理方式是直接在工程中引入源码,通过babel编译。然后CSS并没有拆分引入,这种做法显然不够优雅。而我上述讲的方式正是参考的element-ui的处理方式,区别在于我把每个组件的样式和脚本打包在一起,这样更方便维护。