Taro组件库开发
rollup
本次使用的打包工具为rollup。
- 之所以选择rollup,是因为之前用webpack打包typescript校验一直报错,捣鼓半天没解决,果断脱坑另辟蹊径。
rollup的优势
rollup 打包体积小,代码精简,较少注入,动态加载路由代码不能用,所以单页项目不合适,项目使用人不多,支持的插件较少。
如有单独的js 基础类适合用rollup 打包,如有react/vue 打包建议用webpack,必要时两者都用,也不错。
Rollup最主要的优点是它是基于ES2015模块的,相比于webpack或Browserify所使用的CommonJS模块更加有效率,因为Rollup使用一种叫做tree-shaking的特性来移除模块中未使用的代码,这也就是说当我们引用一个库的时候,我们只用到一个库的某一段的代码的时候,它不会把所有的代码打包进来,而仅仅打包使用到的代码(webpack2.0 貌似也引入了tree-shaking) 。
注意:Rollup只会在ES6模块中支持tree-shaking特性。目前按照CommonJS模块编写的jquery不能被支持tree-shaking.
项目结构配置
作者用的项目结构是taro初始化的脚手架,建立src/compontents
文件夹存放组件,使用一个src/components/index.ts
暴露组件,最后在src/
index.ts中暴露全部组件。
所用到Rollup插件
@rollup/plugin-json
处理json文件的打包
@rollup/plugin-node-resolve
rollup项目中只支持相对路径的引用,而绝对路径需要插件支持。
比如你使用rollup打包,产生报错:
Cannot use import statement outside a module
这是因为rollup打包会处理相对路径,对于npm包的绝对路径引用是不会做任何处理的。这种情况可以用插件处理。
@rollup/plugin-commonjs
rollup默认不支持commonjs语法,需要插件支持
rollup-plugin-typescript2
支持typescript
@rollup/plugin-image
支持import引入图片
rollup-plugin-clear
每次打包清空前自动打包文件夹中的内容
rollup-plugin-babel
配置babel,rollup对taro的babel配置如下
RollupBabel({
runtimeHelpers: true,
"presets": [["taro", {
framework: 'react'
}]],
"plugins": ["@babel/plugin-transform-runtime"]
}),
rollup-plugin-copy
支持直接复制文件到包中
rollup-plugin-postcss
支持sass、less,并且实现移动端像素转换,配置如下
RollupPostCss({ // 可选:处理sass
plugins: [
autoprefixer(),
cssnano(),
pxtransform({
platform: 'h5'
}),
// constparse()
]
}),
rollup-plugin-dts
打包d.ts声明文件,提供typescript类型声明
rollup-plugin-terser
压缩代码
autoprefixer
自动管理浏览器前缀的插件,解析CSS文件并且添加浏览器前缀到CSS内容里,如--webkit-overflow
cssnano
压缩css
postcss-pxtransform
css像素单位转换
postcss-url
支持css内引入静态资源
postcss-import
支持sass的@import语法,在css内引用其他css文件
rollup-plugin-postcss-inject-to-css
支持将sass打包后的.scss.js后缀文件修改为.css后缀文件(解决组件打包小程序时不支持.scss.js内样式问题)
rollup完整配置
import RollupJson from '@rollup/plugin-json'
import RollupNodeResolve from '@rollup/plugin-node-resolve'
import RollupCommonjs from '@rollup/plugin-commonjs'
import RollupTypescript from 'rollup-plugin-typescript2'
import RollupBabel from 'rollup-plugin-babel'
import dts from 'rollup-plugin-dts'
import RollupImage from '@rollup/plugin-image'
import RollupClear from 'rollup-plugin-clear'
import RollupPostCss from 'rollup-plugin-postcss'
import autoprefixer from 'autoprefixer';
import pxtransform from 'postcss-pxtransform';
import postUrl from 'postcss-url';
import postImport from 'postcss-import';
import cssnano from 'cssnano';
import Uglifyjs from 'uglify-js';
import RollPostcssInject2Css from './copy-rollup-postcss-inject-to-css.js'; // 复制修改后的插件
const externalPackages = ['react', 'react-dom', '@tarojs/components', '@tarojs/runtime', '@tarojs/taro', '@tarojs/react']
export default [
{
input: './src/index.ts',
output: [
{
format: 'esm',
dir: './dist/lancooUI-Mobile',
exports: 'named', // 指定导出模式(自动、默认、命名、无)
preserveModules: true, // 保留模块结构
preserveModulesRoot: 'src', // 将保留的模块放在根级别的此路径下
}
],
external: externalPackages,
plugins: [
RollupClear({
targets: ['dist'], // 每次打包清空dist目录,从新生成
watch: true,
}),
RollupPostCss({ // 可选:处理sass
extract: false, // 非导出模式
inject: true, // 内联模式
plugins: [
autoprefixer(),
cssnano(),
postImport(),
pxtransform({
platform: 'h5'
}),
postUrl({
url: "inline",
maxSize: 70
})
]
}),
RollPostcssInject2Css(),
RollupNodeResolve({
customResolveOptions: {
moduleDirectory: 'node_modules',
},
}),
RollupCommonjs({
include: [//node_modules//],
}),
RollupJson(),
RollupBabel({
runtimeHelpers: true,
"presets": [
[
"taro", {
framework: 'react'
}
]
],
"plugins": ["@babel/plugin-transform-runtime"]
}),
RollupTypescript({
tsconfig: './tsconfig.json'
}),
RollupImage({
include: ['**/*.png', '**/*.jpg', '**/*.svg']
}),
// 自定义插件,用于压缩代码(使用rollup的terser插件压缩的话,rollup-postcss-inject-to-css失效,故自己引入UglifyJs压缩代码)
{
name: 'rollup-my-terser',
generateBundle(_, bundle) {
const bundleIdList = Object.keys(bundle)
bundleIdList.forEach((bundleId) => {
const bundleItem = bundle[bundleId];
const code = bundleItem.code;
// 压缩代码
if (/.*.js/.test(bundleId) && !/.*.scss.*/.test(bundleId) && !/.*.css.*/.test(bundleId) && typeof code === 'string') {
Uglifyjs.minify(code).code === undefined && console.log(bundleId);
bundleItem.code = Uglifyjs.minify(code).code;
}
})
},
}
]
},
{
input: './src/index.ts',
output: [
{
format: 'esm',
dir: './dist/lancooUI-Mobile',
exports: 'named', // 指定导出模式(自动、默认、命名、无)
preserveModules: true, // 保留模块结构
preserveModulesRoot: 'src', // 将保留的模块放在根级别的此路径下
}
],
external: id => /(node_modules|.*.scss|.*.css)/.test(id),
plugins: [
RollupPostCss(),
RollupNodeResolve({
customResolveOptions: {
moduleDirectory: 'node_modules',
},
}),
RollupCommonjs({
include: [//node_modules//],
}),
RollupJson(),
RollupBabel({
runtimeHelpers: true,
"presets": [
[
"taro", {
framework: 'react'
}
]
],
"plugins": ["@babel/plugin-transform-runtime"]
}),
RollupTypescript({
tsconfig: './tsconfig.json'
}),
dts()
]
}
]
配置解读:
- 配置项为数组,第一项打包源代码,故未配置dts插件,第二项配置了dts打包声明文件。
- 输出output配置采用
preserveModules: true
以保留项目结构,使用户能清晰看到所对应组件的声明文件。- postcss-pxtransfrom配置参考taro的配置,暂且使用了h5配置,后续考虑根据打包方式动态更改。
- package.json中需要配置
"type": "module"
,否则打包时会报错。- 输出格式选用
esm
,支持项目中es6语法引入。- 源代码进行了代码压缩,声明文件为了方便查阅,不采取压缩。
- 由于rollup-postcss-inject-to-css对.css.js不支持,且代码中code为undefined时仍然读取code[0]导致报错,复制代码并加以修改一份。
- 使用terser压缩代码,rollup-postcss-inject-to-css对.scss.js的转换出错,故引入UglifyJs自己完成代码压缩。
最后,运行以下命令进行打包即可
rollup -c
难点
打包后的组件库难点有JSX支持、类型声明、SASS支持、像素转换、多端支持。
解决思路:
JSX支持: jsx的支持依靠babel的配置,和react的jsx支持同理。
类型声明: 观察其他组件库,类型声明均是d.ts文件,了解rollup如何打包出d.ts文件即可。
SASS支持: 最初打包组件库时存在样式丢失的问题,后面增加postcss配置后完美解决(不能单单是引入postcss,其中还有像素转换、css内静态资源打包与@import语法,需要额外插件支持)。
像素转换: 打包后测试时发现移动端的像素仍保持与pc端一致,查阅了解后使用postcss-pxtransform插件,最初未配置option,仍未解决问题。查看tarojs的postcss.config.js
文件源码后,依葫芦画瓢增加option解决问题。
多端支持: 使用rollup-plugin-postcss-inject-to-css
解决小程序不支持.scss.js的问题。
总结
组件库打包磕磕绊绊,总结自身原因,一是对打包工具的不了解,对配置、插件作用的认知不清晰,二是对组件库结构的了解不足,不清楚其工作原理。三是国内网上组件库开发的资料甚少,尤其对taro组件库开发的资料几乎是荒漠。
本次打包收获如下:
- 对rollup以及其部分插件有了一定的了解。
- 了解如何编写rollup插件。
- 对组件库的打包以及结构有了自己的理解。
踩坑
- 打包完后发现字体未打包进去,但引入
rollup-plugin-font
没有起到作用。后面发现,不是该插件配置问题,而是cssurl()
方式引入的静态资源压根没打包进去(rollup-plugin-font和rollup-plugin-image只对js中import引入的静态资源生效),引入postcss-url解决问题。 - 打包后存在部分样式失效问题,发现该css文件中使用了sass的
@import
语法,结合postcss-url踩坑经验,引入postcss-import后解决问题。 - 打包sass后,产生的文件后缀为.scss.js,h5能正常显示样式,但打包成微信小程序无法正确识别,遂考虑到使用
rollup-plugin-copy
将sass文件直接复制到包中,跳过sass编译,但使用起来需一个个引入样式,太过于麻烦。后面寻找将sass文件打包为css文件的方法,找到了rollup-plugin-postcss-inject-to-css
插件,能将.scss.js转换为.css。然而使用起来时报空错误,简单修改了下源码后解决问题。