初识rollup 打包、配置vue脚手架
rollup javascript 代码打包器,它使用了 es6 新标准代码模块格式。
特点:
- 面向未来,拥抱 es 新标准,支持标准化模块导入、导出等新语法。
- tree shaking 静态分析导入的代码。排除未实际引用的内容
- 兼容现有的 commonJS 模块,可通过插件导入
安装使用
创建示例项目
$> mkdir rollup-example
$> cd rollup-example
安装 rollup
$> npm init -y
$> npm i rollup
创建main.js
主入口文件。创建 libs
目录用于方式封装的功能函数。
在package.json
文件中定义执行脚本命令。
-
--file
编译后的文件名称 简写-o
-
--format
按什么标准去编译文件类型 iffe、cjs、es。 简写-f
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rollup main.js --file bundle.js --format iife"
}
}
在libs
创建arry.js
/**
* 数组类型校验
* @param {*} target
* @returns
*/
export const isArray = (target) => {
return Array.isArray(target);
};
/**
* 数组去重
* @param {*} target
* @returns
*/
export const duplicateKill = (target) => {
return [...new Set(target)];
};
然后在main.js
中调用
import { isArray } from "./libs/array";
//
console.log(isArray(3));
console.log(isArray("1,2,3,4"));
console.log(isArray([1, 2, 3, 4]));
执行npm run build
,得到一个编译文件bundle.js
(function () {
"use strict";
/**
* 数组类型校验
* @param {*} target
* @returns
*/
const isArray = (target) => {
return Array.isArray(target);
};
//
console.log(isArray(3));
console.log(isArray("1,2,3,4"));
console.log(isArray([1, 2, 3, 4]));
})();
通过配置文件定义编译配置
上面使用了rollup
命令,并通过命令行参数指定文件以及编译类型。
定义rollup.config.js
文件,定义编译输出
// rollup.config.js
export default {
input: "main.js",
output: {
file: "dist/bundle.js",
format: "iife",
},
};
然后修改package.json
-
--config
指定配置文件, 简写-c
-
--bundleConfigAsCjs
因为配置文件是以.js
结尾的,通常建议使用.cjs
.
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rollup --config rollup.config.js --bundleConfigAsCjs"
}
}
然后执行 npm run build
可以看到 生成的dis/bundle.js
也可以省略 配置文件,rollup 会自动加载根目录下的
rollup.config.js
,rollup --config --bundleConfigAsCjs
多入口打包文件,配置文件可以是数组对象
会有一些多入口页面打包,通过配置入口,删除不同目录文件的编译资源
定义rollup.config.js
文件
// rollup.config.js
export default [
{
input: "main.js",
output: {
file: "dist/bundle.js",
format: "iife",
}
},
{
input: "login.js",
output: {
file: "dist/login.js",
format: "iife",
}
}
],
如果想要输出多个类型的编译资源output
配置为数组
// rollup.config.js
export default [
{
input: "main.js",
output: [
{
file: "dist/bundle.js",
format: "iife",
},
{
file: "dist/bundle-es.js",
format: "es",
},
{
file: "dist/bundle-cjs.js",
format: "cjs",
}
]
},
],
可异步请求配置文件
如果是在线配置,存储在后端。可通过请求获取配置文件。
// rollup.config.js
import ajax from "libs/ajax";
export default ajax.get("/**/**/rolleup-config");
多接口、都入口配置,则改为
// rollup.config.js
import ajax from "libs/ajax";
export default Promise.all([
ajax.get("/**/**/rolleup-config-main"),
ajax.get("/**/**/rolleup-config-login"),
]);
通过命令行参数适用不同的配置文件
开发坏境和生产环境有不同的配置。通过命令行参数,使用对应的配置文件。
// rollup.config.js
import devConfig from "./build/rollup.dev.config.js";
import prdConfig from "./build/rollup.prd.config.js";
export default (commandLineArgs) => {
if (commandLineArgs.environment === "dev") {
return devConfig;
}
return prdConfig;
};
修改package.json
提供 dev、build 脚本
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "rollup --config --environment dev --bundleConfigAsCjs",
"build": "rollup --config --bundleConfigAsCjs --environment prd"
}
}
rollup.config.js
配置文件说明
// rollup.config.js
// can be an array (for multiple inputs)
export default {
// core input options
external,
input: "main.js", // 主入口文件配置路径
plugins, // 使用插件,
// advanced input options
cache,
onwarn,
preserveEntrySignatures,
strictDeprecations,
// danger zone
acorn,
acornInjectPlugins,
context,
moduleContext,
preserveSymlinks,
shimMissingExports,
treeshake,
// experimental
experimentalCacheExpiry,
perf,
// required (can be an array, for multiple outputs)
output: {
// core output options
dir: "dist", // 编译文件目录地址,多个编译文件则必须指定
file: "dist/bundle.js", // 编译后文件目录路径
format, // 文件编译类型 es cjs iife
globals,
name,
plugins, // 针对某些输出的插件
// advanced output options
assetFileNames,
banner,
chunkFileNames,
compact,
entryFileNames,
extend,
footer,
hoistTransitiveImports,
inlineDynamicImports,
interop,
intro,
manualChunks,
minifyInternalExports,
outro,
paths,
preserveModules,
preserveModulesRoot,
sourcemap,
sourcemapBaseUrl,
sourcemapExcludeSources,
sourcemapFile,
sourcemapPathTransform,
validate,
// danger zone
amd,
esModule,
exports,
externalLiveBindings,
freeze,
indent,
namespaceToStringTag,
noConflict,
preferConst,
sanitizeFileName,
strict,
systemNullSetters,
},
watch: {
buildDelay,
chokidar,
clearScreen,
skipWrite,
exclude,
include,
},
};
使用一个加载json
文件的插件
安装
$> npm i --save-dev @rollup/plugin-json
在配置中使用插件
// rollup.config.js prd
import PluginJson from "@rollup/plugin-json";
export default {
input: "main.js",
output: {
file: "dist/bundle.js",
format: "iife",
},
plugins: [PluginJson()],
};
然后在项目中可以导入 JSON 文件,按对象取值。
import { version } from "./package.json";
// import packageJson from "./package.json";
console.log(version);
可以针对输出output
配置插件
最小化构建代码,压缩代码,安装@rollup/plugin-terser
$> npm install --save-dev @rollup/plugin-terser
修改配置文件,在编译类型为 es 的输出使用插件
// rollup.config.js dev
import PluginJson from "@rollup/plugin-json";
import PluginTerser from "@rollup/plugin-terser";
export default {
input: "main.js",
output: [
{
file: "dist/bundle.js",
format: "iife",
},
{
file: "dist/bundle-es.js",
format: "es",
plugins: [PluginTerser()],
},
{
file: "dist/bundle-cjs.js",
format: "cjs",
},
],
plugins: [PluginJson()],
};
执行npm run build
可以看到bundle-es.js
文件代码被压缩,没有任何格式
动态加载已使用代码拆分
在以下情况会自动进行代码拆分
- 动态加载模块
- 多入口引入统一模块。
- 通过输出配置
output.manualChunks
指定需要拆分的模块
[!] RollupError: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
编译类型 UMD、IIFE 是不支持代码拆分的。改用 cjs
实现login
模块的拆分,创建 login 文件入口。然后在 main.js 文件中动态引入
import("./login.js").then(() => console.log("成功加载login..."));
需要修改配置指定编译目录dir:'dist'
,原来指定的 file
只是编译生成一个文件包。现在拆分代码,会生成多个编译包。
// rollup.config.js prd
import PluginJson from "@rollup/plugin-json";
export default {
input: "main.js",
output: {
// file: "dist/bundle.js",
format: "cjs",
dir: "dist",
},
plugins: [PluginJson()],
};
build-dir.png
定义插件,以满足定制化需求
编译时可能会需要一些定制化处理。通过自定义插件实现代码的转换。
示例实现一个移除console.log
的代码。一个插件约束
- 插件名需要以
rollup-plugin-**
开头 - package.json 中包含 rollup-plugin 关键字,构建后发布之前需要加前缀
- 经过全面的测试。
- 插件中尽可能的使用英文说明。
- 尽可能的使用异步方法读取文件,比如使用 fs.readFile 而不是 fs.readFileSync
- 插件是虚拟模块时,前缀追加
\0
.以便其他插件不去处理它。
项目目录下创建存储插件的位置 plugins/rollup-plugin-clear-console.js
// rollup-plugin-clear-console
import { createFilter } from "@rollup/pluginutils";
export default function clearConsole(options = {}) {
// filter file
var filter = createFilter(options.include, options.exclude);
return {
name: "clear-console",
transform(code, id) {
/**
* code 为加载的文件内容
* id 为当前内容文件的路径地址,通过filter判断是否符合处理的要求
*/
if (!filter(id)) return;
try {
return {
code: code.replace(/console\.log(\w*)/gi, ""),
map: { mappings: "" },
};
} catch (err) {
var message = "An error occurred during processing";
this.error({ message: message, id: id, cause: err });
return null;
}
},
};
}
@rollup/pluginutils
是针对编写 rollup 插件封装的一些功能性的函数,比如示例中使用了createFilter
,可以帮助过滤指定的文件、排除指定文件。
在rollup.config.dev.js
使用自定义插件,并制定处理目录下为main.js
的文件。
// rollup.config.js prd
import PluginJson from "@rollup/plugin-json";
// 自定义插件
import PluginClearConsole from "../plugins/rollup-plugin-clear-console";
export default {
input: "main.js",
output: {
// file: "dist/bundle.js",
format: "cjs",
dir: "dist",
},
plugins: [
PluginJson(),
PluginClearConsole({
include: ["**/main.js"],
}),
],
};
然后执行脚本,看看效果npm run dev
在dis/main.js
文件中可以看到所有的console.log
都被移除。而且由于移除这些代码,一些导入的变量未被使用,也被 tree shaking 了。
可以看到拆包编译的login.js
文件中还是包含有console.log
代码的。
配置一起使用vue
需要安装的 vue 相关的插件, 在 rollup-awesome 官方推荐的组件库
$> npm install rollup-plugin-vue vue --save-dev
安装的都是最新版本,vue 版本为3.2.45
创建 src 目录,用于 vue 视图组件的存放位置。
// index.js
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");
在项目目录下创建 index.html 文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>rollup-vue2</title>
</head>
<body>
<div id="app"></div>
<!-- 导入编译后的文件包 -->
<script src="./dist/index.js"></script>
</body>
</html>
修改 rollup 配置文件rollup.config.dev.js
// rollup.config.dev.js dev
import PluginJson from "@rollup/plugin-json";
import PluginVue from "rollup-plugin-vue";
// 自定义插件
import PluginClearConsole from "../plugins/rollup-plugin-clear-console";
export default {
input: "./src/index.js",
output: {
// file: "dist/bundle.js",
format: "es",
dir: "dist",
},
plugins: [
PluginVue(),
PluginJson(),
PluginClearConsole({
include: ["**/main.js"],
}),
],
};
然后执行 npm run dev ,打包编译后,通过live server
启动一个服务访问 index.html
服务启动页面不展示问题
- 页面打开后没有任何展示,控制台报错
Cannot use import statement outside a module
,查看编译包./dist/index.js
. 没有将 vue 一起打包进去。加载不到
解决,安装依赖@rollup/plugin-node-resolve
,将 vue 一起编译进去。
$> npm install @rollup/plugin-node-resolve --save-dev
修改配置,引入插件。 可以将 node_modules 中的组件包引入打包进编译包。
// rollup.config.dev.js dev
import PluginNodeResolve from "@rollup/plugin-node-resolve";
export default {
// ... other
plugins: [
// ... other
PluginNodeResolve(),
],
};
- 可以看到编译包变大了,vue 已经打包进去了,但是页面还不展示,报错
Uncaught ReferenceError: process is not defined
, 查看编译包./dist/index.js
,引用全局变量process.env.NODE_ENV
没有定义
解决,安装插件 @rollup/plugin-replace
$> npm install @rollup/plugin-replace --save-dev
修改配置,引入插件。 编译后替换编译包中的目标字符串。
// rollup.config.dev.js dev
import PluginReplace from "@rollup/plugin-replace";
export default {
// ... other
plugins: [
// ... other
PluginReplace({
"process.env.NODE_ENV": JSON.stringify("development"),
preventAssignment: true,
}),
],
};
页面打开展示正常。
生成 html 文件,npm run dev
创建一个服务
为了达到开发的目的,需要自动生成 html 文件,自动注入人编译后的资源包。还有其他资源的解析、加载等。
生成 html 文件
安装依赖插件,生成 index.html,并将所有编译的资源包添加到页面上。
$> npm install @rollup/plugin-html --save-dev
使用插件
// rollup.config.dev.js dev
import PluginHtml from "@rollup/plugin-html";
export default {
// ... other
plugins: [
// ... other
PluginHtml({
title: "rollup-vue3",
fileName: "index.html",
}),
],
};
开发模式,启动 serve 服务
安装依赖插件,启动一个服务
$> npm install rollup-plugin-serve --save-dev
使用插件
// rollup.config.dev.js dev
import PluginServe from "rollup-plugin-serve";
export default {
// ... other
plugins: [
// ... other
PluginServe({
// 运行在浏览器中
open: true,
// 运行成功后,打开的地址
openPage: "/",
// 打印服务地址
verbose: true,
// 地址,端口号
// host:"::",
host: "127.0.0.1",
port: 8009,
// https 协议配置
// https: {},
// 运行成功,事件
onListening: function (server) {
const address = server.address();
const host = address.address === "::" ? "localhost" : address.address;
// by using a bound function, we can access options as `this`
const protocol = this.https ? "https" : "http";
console.log(
`Server listening at ${protocol}://${host}:${address.port}/`
);
},
}),
],
};
运行成功后,未自动打开浏览器,serve 配置中的 open、openPage 暂时没有发现其作用
设置"::"不生效,设置为“127.0.0.1” 启动后正常打开。
--watch
监听模式,开发是文件变动重新编译
服务虽然启动了,但也仅仅是一个静态资源的服务,在开发的时候,通常需要实时编译改动的文件,希望立刻看到变化
修改启动脚本,在监听模式下,文件发生更改,就会重新编译。--watch 等同于 -w
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "dev": "rollup --config --environment dev --bundleConfigAsCjs -w",
+ "dev": "rollup --config --environment dev --bundleConfigAsCjs",
},
}
虽然重新编译了,但是浏览器中还是需要手动刷新才能看到变化。--需要唤起浏览器服务的插件--
解析.vue
中的样式 style
在 App.vue 中书写了样式,发现在页面中没有生效
<style>
.app p {
font-size: 32px;
}
</style>
增加插件postcss \ rollup-plugin-postcss
$> npm i postcss rollup-plugin-postcss -D
配置rollup.config.dev.js
import PluginPostCss from "rollup-plugin-postcss";
export default {
//...
plugins: [
//...
PluginPostCss(),
],
};
重新运行,可以看到样式生效了。默认编译后的 css 样式是注入到 html 的 head 中。
使用预编译 less
安装 less
$> npm i less -D
即可直接使用
<style lang="less">
.app {
p {
font-size: 32px;
color: aqua;
}
button {
color: red;
}
}
</style>
支持 JSX 语法
比较喜欢 jsx 语法的书写方式,实现相关配置;
import { defineComponent } from "vue";
export default defineComponent({
name: "IFunLazySelect",
render() {
return (
<div class="lazy-select">
{/* 相关代码 */}
</div>
})
安装 @vue/babel-plugin-jsx
, 还需要安装@babel/core 、@babel/preset-env
$> npm install @vue/babel-plugin-jsx @babel/core @babel/preset-env -D
新建.babelrc.js
,配置
module.exports = {
presets: [["@babel/env", { modules: false }]],
plugins: ["@vue/babel-plugin-jsx"],
};
这只是 babel 配置,还需要集成到 rollup 中,使之生效,安装@rollup/plugin-babel
则需要修改rollup.config.base.js
,增加插件配置。
// rollup.config.base.js
import PluginBabel from "@rollup/plugin-babel";
export default {
plugins: [
// ... other
PluginBabel({ babelHelpers: "bundled" }),
],
};
重新启动项目,完美运行。
其他插件
- 解析 commonJS 模块,转换成 es6 模块。安装依赖
$> npm install @rollup/plugin-commonjs --save-dev
修改配置
// rollup.config.dev.js dev
import PluginCommonJS from "@rollup/plugin-commonjs";
export default {
// ... other
plugins: [
// ... other
PluginCommonJS(),
],
};
- 全局注入,比如使用 jquery,安装依赖
$> npm install @rollup/plugin-inject --save-dev
修改配置
// rollup.config.dev.js dev
import PluginInject from "@rollup/plugin-inject";
export default {
// ... other
plugins: [
// ... other
PluginInject({
$: "jquery",
}),
],
};
- 定义别名路径,比如通常使用
@
代替./src
路径
$> npm install @rollup/plugin-alias --save-dev
修改配置
// rollup.config.dev.js dev
import PluginAlias from "@rollup/plugin-alias";
export default {
// ... other
plugins: [
// ... other
PluginAlias({
entries: {
"@": "../src",
libs: "../libs",
},
}),
],
};
- 加载图片资源,
.png\.jpeg\.svg
等
$> npm install @rollup/plugin-image --save-dev
修改配置
// rollup.config.dev.js dev
import PluginImage from "@rollup/plugin-image";
export default {
// ... other
plugins: [
// ... other
PluginImage(),
],
};