没接触过前端模块化概念的同学可先参考:JS & Node 模块化解释:AMD、UMD、CommonJS、 ESM
Rollup
工具可以将代码转成不同模块,实现一套代码多端(浏览器/Node)引入。比如通过一句命令将代码打包为:
rollup main.js --file bundle.js --format iife
rollup main.js --file bundle.js --format amd
rollup main.js --file bundle.js --format cjs
rollup main.js --file bundle.js --format umd --name "myBundle"
后面会提到每个命令的作用,这里了解即可。
如果你的项目是以插件或库给用户引入使用,Rollup 是最佳选择,它可以将代码转成不同的模块,然后用户根据自身项目模块语法来引入你的代码。
Rollup 支持 Tree-shaking ,只打包引用代码,无引用的一律甩掉。
你的项目可以肆无忌惮地使用 ESM 写法,Rollup 将会自动编译,再结合 Babel 美滋滋。
Rollup
将 JS 代码转成不同的引入模块形式,如果要处理其它语言需要借助插件。
Webpack
将 JS 代码进行打包压缩,同时拥有着 CSS/JS/HTML/Font 等其它语言插件的生态丰富圈。
组件库或插件
适合用 Rollup 打包、大型项目
适合用 Webpack 或 Vite 。
Rollup
可将 JS 代码变成多种模块来引入运行。
Babel
可将高级 JS 语法代码转成低级版本兼容运行。
新建项目: rollup-example
src/main.js
代码:
const user = () => {
return {
name: 'Jack'
}
}
export { user };
全局安装 Rollup:
npm install rollup -g
rollup ./src/main.js --file lib.js --format cjs
解释:
./src/main.js
是目标即入口文件。--file lib.js
是输出的产物文件名。--format cjs
是需要转换的模块格式,这里我们采用 CJS
,表示只用于 Node.js-o
=== --file
=== output
输出产物的文件名。
-d
=== --dir
=== 输出产物的目录名,一般在开启 code spliting 代码分割技术后才用到。
-f
=== --format
=== 打包模块格式。
-w
=== --watch
=== 启动监听模式,当项目代码有变动时,自动执行打包命令。
-c
=== --config
=== rollup.config.js
指向配置文件。
其它参数命令可参考官方文档。
当频繁使用打包命令且参数量越来越多时,Rollup 支持配置文件的方式,例如:
// rollup.config.js
import json from '@rollup/plugin-json';
import terser from '@rollup/plugin-terser';
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';
export default {
input: 'src/main.js', // 目标入口
// 同一个目标入口可以编译多种产物形式。
output: [
{
file: 'bundle.js', // 产物文件名。
format: 'cjs', // 产物模块格式。
},
{
file: 'bundle.min.js',
format: 'iife',
// output-plugin(构建完成后的插件):
// 做一些收尾工作,比如 terser 插件可将代码变量/函数名全部压缩转义为 a,b,c,d 的精简版本形式,俗称“混淆”,防止外部人员窥探源码。
plugins: [terser()]
}
],
// compliling-plulgin(rollup 执行编译时的插件):
// 帮助我们在写代码的过程中享受一些特殊语法糖和特殊处理。
plugins: [
// 支持识别 import json 数据。
json(),
// 支持 Babel (记得要在 babel.config.js 中配置下 preset )
resolve(),
babel({ babelHelpers: 'bundled' })
],
// 告诉哪个 NPM 模块打包过程种直接全量 import,不采用 tree-shaking 。
external: ['lodash']
};
最后执行 rollup -c
即可,更多配置选项可参考官方文档 。
提示:命令行参数的优先级比配置选项更高。
构建一个组件库时,通常需要借助框架来快速完成,比如我想构建一套 Vue 语法的组件库,能够给其它 Vue 项目引入使用。接下来我将以 Vue3 的两种写法来作为演示。
新建 rollup-vue-example
项目并使用 npm init -y
初始化。
为了使项目全面支持 ESM 模块语法,在 package.json
中新增:"type": "module"
,以便本地运行项目时 node 不会报错。
main.js
入口代码如下:
import Vue from 'vue';
import App from './src/App.vue';
new Vue({
render: (h) => h(App),
}).$mount('#app');
src/App.vue
代码如下
<template>
<div>Hello,worlddiv>
template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
setup(props) {
return {}
}
})
script>
<style lang="scss" scoped>
style>
rollup.config.js
配置文件如下:
export default {
input: 'main.js',
output: {
file: 'dist/index.js',
format: 'esm',
},
plugins: [],
};
要想让 Rollup
识别 .vue
文件,得分几个步骤完成:
npm i rollup-plugin-vue @vue/compiler-sfc
其中 rollup-plugin-vue
是我们想要的插件,@vue/compiler-sfc
是 rollup-plugin-vue 需要的依赖(后面会讲它的用处)。
rollup.config.js
配置:import vue from 'rollup-plugin-vue';
export default {
input: 'main.js',
output: {
file: 'dist/index.js',
format: 'esm',
},
plugins: [
vue({
css: true, // 提取 css 为单个文件。
compileTemplate: true, // 编译 vue 模板。
})
],
};
rollup -c
你可能会问,为什么 .vue 源码会被编译,而不像 js 代码那样原封不动的输出?
这是因为 .vue
语法本身就不能直接在浏览器或 node 上面跑,于是刚刚的 @vue/compiler-sfc
依赖就排上用场了,它可以识别 .vue
文件并编译成可执行的 JS 对象代码。
不信可以在产物里找找看哪个不是 JS 能识别的,若能找到;我请你喝可口可乐。
你可能还会问,那为啥本地项目就能跑起 vue 模板的代码呢?这是因为编译期间,如 vue-cli or vite 偷偷将它们转为可执行的 JS 脚本,只是我们没感知到而已~
Vue3 除了语法上发生了变化,它还支持 JSX 写法,我们又不得不继续卷到底!
与 Vue 模板相比,Rollup 支持 Vue JSX 需要多点配置量,但问题不大。
我们继续沿用刚刚的 rollup-vue-example
项目来作为演示,这里稍微改动下即可:
src/App.tsx
文件,内容如下:import { defineComponent } from 'vue';
const App = defineComponent({
setup() {
const Greet = () => <div>Hello,world</div>
return <Greet />
}
});
export default App
main.
引入文件方式:import Vue from 'vue';
import App from './src/App.tsx'; // 引入 tsx 。
new Vue({
render: (h) => h(App),
}).$mount('#app');
npm i @rollup/plugin-babel @babel/core @vue/babel-preset-jsx
解释:
@rollup/plugin-babel
能够让 Rollup 支持 Babel
。@babel/core
引入 babel
的核心编译包。@vue/babel-preset-jsx
Vue 自家实现的 Vue JSX
babel 插件 。对 Babel
概念不熟的可以阅读前面写过的 JS & 介绍 Babel 的使用及 presets & plugins 的概念
。
rollup.config.js
配置:import vue from 'rollup-plugin-vue';
// 新增
import { babel } from '@rollup/plugin-babel';
export default {
input: 'main.js',
output: {
file: 'dist/index.js',
format: 'esm',
},
plugins: [
vue({
css: true, // 提取 css 为单个文件。
compileTemplate: true, // 编译 vue 目标。
}),
// 新增
babel({
extensions: ['.js', '.jsx', '.ts', '.tsx'],
presets: ['@vue/babel-preset-jsx'], // 让 babel 加载 Vue JSX babel 插件。
})
],
};
roolup -c
编译后的 dist/index.js
产物如下:
你可能会问,.tsx
采用的就是 JS 语法呀,产物为什么也会发生变化呢?
错!!!TSX 不是 JS,它们是 JS + HTML 的混合体,看看里面的标签便知,浏览器是无法执行这段代码的,依然需要转成可执行的JS 对象。
========compiling plugin=========
@rollup/plugin-commonjs 支持在 ESM 中引入 CJS
代码。
@rollup/plugin-typescript 支持识别 .ts
文件。
@rollup/plugin-json 能够导入 import .json
文件。
@rollup/plugin-babel 支持 Babel
@rollup/plugin-node-resolve 提供更全面的路径解析
========output plugin=========
@rollup/plugin-terser 将代码变量混淆,转成 a,b,c,d, …的形式。
查看更多插件
----------------------------------------------------------------结尾----------------------------------------------------------------
参考文献:
https://rollup-plugin-vue.vuejs.org/#what-does-rollup-plugin-vue-do
https://rollupjs.org/introduction/
https://rollupjs.org/tutorial/
https://adostes.medium.com/authoring-a-javascript-library-that-works-everywhere-using-rollup-f1b4b527b2a9