手把手教你创建一个 rollup 项目

3,647 阅读9分钟

创建 rollup 项目

  • 首先贴出项目的最终版本的地址:rollup-demo
  • 提示:下面的一些步骤可能你觉得不对或者不完美,那么请看下去,因为是我一步步探索的搭建步骤
  1. npm init
  • 创建 package.json 文件
  1. npm i typescript -D 添加 typescript
  2. npm install rollup --save-dev 添加 rollup
  • 在根目录添加一个文件 rollup.config.js
export default {
  input: "src/main.js",
  output: {
    file: "bundle.js",
    format: "es",
  },
};
  • 在 package.json 添加命令: "build": "rollup --config"
  1. npm install eslint --save-dev 添加 eslint
  2. ./node_modules/.bin/eslint --init 生成 eslint 配置文件
  1. rollup 集成 babel 等工具
  • rollup集成其他工具
  • 最后测试导入 the answer库 来判断是否集成完成
  • 注意 babel 的配置文件位置,官方说法是:我们将.babelrc.json文件放在src,而不是项目根目录中。这允许我们对.babelrc.json测试之类的事情有不同的看法
  1. rollup 安装 sass
  • npm install rollup-plugin-sass -D
// rollup.config.js
import sass from "rollup-plugin-sass";
// ...
plugins: [resolve(), sass(), babel({ babelHelpers: "bundled" }), commonjs()];
  • 添加 postcss,autoprefixer
import postcss from "postcss";
import autoprefixer from "autoprefixer";
// ...
plugins: [
  sass({
    output: "bundle.css",
    insert: true,
    processor: (css) =>
      postcss([autoprefixer])
        .process(css)
        .then((result) => result.css),
  }),
];
  • postcss 是一个转译 css 的工具,可以对 css 进行 lint,转换未来的语法等,可以和 scss 等预处理器一起使用
  • 结合文档
  1. 创建一个 ts 文件,执行 npm run build 报错
// Unexpected token (Note that you need plugins to import files that are not JavaScript)
// 因为使用了ts的static!
  • 安装:npm install @rollup/plugin-typescript --save-dev rollup的对等依赖项
  • 在 rollup.config.js 添加
import typescript from "@rollup/plugin-typescript";
// ...
plugins: [
  resolve(),
  sass(),
  typescript(),
  babel({ babelHelpers: "bundled" }),
  commonjs(),
];
  1. 安装运行插件,用于在 npm run build 的时候可以启动浏览器实现页面渲染
  • npm install --save-dev rollup-plugin-serve
  • 当对应的端口被其他进程占用了,直接就报错,而没有帮我们去占用下一个端口。。(源码逻辑就这样的)
// rollup.config.js
serve({
  open: true,
  contentBase: "public",
  host: "localhost",
  port: 10001,
});
  • 由于打包出现路径错误问题,暂时先把css,js输出到public
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";
import sass from "rollup-plugin-sass";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import typescript from "@rollup/plugin-typescript";
import serve from "rollup-plugin-serve";

export default {
  input: "src/main.js",
  output: {
    file: "public/bundle.js",
    name: "test",
    format: "umd",
  },
  plugins: [
    serve({
      open: true,
      contentBase: "public",
      host: "localhost",
      port: 10001,
    }),
    resolve(),
    sass({
      output: "public/bundle.css",
      insert: true, // 输出css到index.html的head标签
      processor: (css) =>
        postcss([autoprefixer])
          .process(css)
          .then((result) => result.css),
    }),
    typescript(),
    babel({ babelHelpers: "bundled" }),
    commonjs(),
  ],
};
  • 虽然这样是可以,但是存在一个问题!
  • sass中使用了insert把样式插入到head标签中,导致js文件中存在一份样式代码,css文件中也存在一份,冗余了

  1. 拆分为 dev,build 两个命令
  • build: 只生成 css,js 文件js文件没有对css文件的引用,需要外部额外通过import引入该css文件
  • dev: 在执行 build 的配置上添加一个 serve 配置,并且需要提前写好 index.html 文件
  • 因为使用import/export 方式没有办法设置process(node环境才有) ,所以修改为commonJs规范
  • 注意部分导入需要添加花括号,因为除了export default还有export function,识别有问题
  • 另外注意:dev是运行在public/index.html中,如果省略这部分无法加载bundle.css,bundle.js
// rollup.config.js
const { nodeResolve } = require("@rollup/plugin-node-resolve");
const commonjs = require("@rollup/plugin-commonjs");
const { babel } = require("@rollup/plugin-babel");
const sass = require("rollup-plugin-sass");
const postcss = require("postcss");
const autoprefixer = require("autoprefixer");
const typescript = require("@rollup/plugin-typescript");

module.exports = {
  input: "src/main.js",
  output: {
    file: "dist/bundle.js",
    name: "bundle",
    format: "umd",
    sourcemap: true,
  },
  plugins: [
    nodeResolve(),
    sass({
      output: "dist/bundle.css",
      // insert: true, // 不要插入head标签,冗余了
      processor: (css) =>
        postcss([autoprefixer])
          .process(css)
          .then((result) => result.css),
    }),
    typescript(),
    babel({ babelHelpers: "bundled", exclude: "**/node_modules/**" }),
    commonjs(),
  ],
};
// rollup.config.dev.js
const serve = require("rollup-plugin-serve");

const config = require("./rollup.config.js");
process.env.NODE_ENV = "development";

const indexPath = "public/index.html";
config.plugins = [
  ...config.plugins,
  serve({
    host: "localhost",
    port: 3000,
    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}/${indexPath}`
      );
    },
  }),
];

module.exports = config;
// rollup.config.prod.js
const config = require("./rollup.config.js");
process.env.NODE_ENV = "production";

config.output.sourcemap = false;
config.plugins = [...config.plugins];
module.exports = config;
  1. 编译代码混淆插件:rollup-plugin-uglify
  • npm i rollup-plugin-uglify --dev
  • 在 rollup.config.prod.js 修改
const { uglify } = require("rollup-plugin-uglify");
config.plugins = [...config.plugins, uglify({ sourcemap: false })];
  • 但是注意到rollup-plugin-uglify只能转译es5语法,但是项目会使用es6语法,为了转译简洁,改为使用推荐的tester
const config = require("./rollup.config.js");
const { terser } = require("rollup-plugin-terser");
process.env.NODE_ENV = "production";

config.output.sourcemap = false;
config.plugins = [...config.plugins, terser()];
module.exports = config;
  1. 监听文件变化:rollup-watch
  • 本来想下载的,但是下载后发现和直接使用-w 一样的效果。。。在github翻译可以看到已经弃用了
  1. 实时刷新页面(热更新):rollup-plugin-livereload
  • 参考
  • 在 rollup.config.dev.js 修改
const livereload = require("rollup-plugin-livereload");
config.plugins = [
  livereload(),
  // 省略
];
  • 但是现在只能监听到js文件的修改并实时生效,无法监听到scss文件的修改...(因为修改js文件才会重新编译,修改scss文件不会重新编译。。。)
  • serve和livereload都需要再看看源码,理清楚原理,看看怎么改进,如果是自己写这种插件的话
  1. 添加别名:@rollup/plugin-alias, github.com/rollup/plug…
  • 注意,添加了@,但是在编写的时候还是有黄色下划线,警告。但是运行成功
import { A } from "@/a.ts";
// todo 这里会有警告,因为@还是有问题,但是运行成功。。。
// 并且没法通过点击进入对应的文件
  • 在 rollup.config.js 添加(解决办法,添加src的别名
const alias = require("@rollup/plugin-alias");
const customResolver = nodeResolve({
  extensions: [".js", ".ts", ".json", ".scss"],
});
alias({
  entries: [
    {
      find: "@",
      replacement: path.resolve(projectRootDir, "..", "src"),
    },
    {
      find: "src",
      replacement: path.resolve(projectRootDir, "..", "src"),
    },
  ],
  customResolver,
});
  1. 在 ts 文件中使用接口报错,提示 no javascript,在网上查找之后找到 rollup-plugin-typescript2
  • 这是对 rollup-plugin-typescript 的重写,会比之前的慢一些,试一下是否可以解决接口 interface 报错问题

  • 但是发现了tslib,然后再看一遍rollup-plugin-typescript,找到问题是没有安装tslib

  • 安装 tslib,npm run dev,这时不会报错

  • 问题解决,暂时不使用rollup-plugin-typescript2(可以打印出语法错误和语义诊断信息)

  • 但是编译速度会变慢?[juejin.cn/post/693576…]

    16.rollup-plugin-prettier(格式化,代码美化)

  • npm install -D prettier rollup-plugin-prettier

  • 在 rollup.config.js 修改

const prettier = require("rollup-plugin-prettier");

prettier({
  printWidth: 100,
  singleQuote: true,
  trailingComma: "none",
  bracketSpacing: true,
  jsxBracketSameLine: true,
  tabWidth: 4,
  arrowParens: "always",
});
  • 考虑到 prettier 部分代码可能很多,所以修改一下,创建一个文件:.prettierrc.js
const prettierFile = path.resolve(projectRootDir, "..", ".prettierrc.js");
plugins: [prettier(prettierFile)];
  1. 添加忽略文件
  • 首先添加.eslintignore
dist/**
node_modules/**
  • 添加.gitignore,直接使用其他项目的配置文件,把没用的删除就可以了,例如 vue.config.js 就可以删除
  1. 添加 lint 命令
  • 并且需要在 dev,build 都添加 lint,在 package.json 修改
  "scripts": {
    "dev": "npm run lint && rollup -w -c ./config/rollup.config.dev.js",
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npm run lint && rollup -c ./config/rollup.config.prod.js",
    "lint": "eslint --fix --ext .ts,.js src"
  }
  1. babel-polyfill
  • 首先在代码使用 Map 等新的 api,(babel默认只转换新的js句法,例如class,箭头函数)
// main.js
let map = new Map([
  ["a", 1],
  ["name", 33],
]);
console.log(map);
// 打包后的bundle.js
var map = new Map([
  ["a", 1],
  ["name", 33],
]);
console.log(map);
  • 可以看到打包后还是 Map 数据结构,并没有进行转换,这样是不行的!因为 ie 不支持 map 数据结构
  • 官网说明
  • 另外注意 babel-polyfill 只是针对 js 的,对于 css,html 无法进行兼容,所以兼容性还是要考虑,同时 css 的兼容性是依靠 postcss 的

  • 注意:babel官网特意说明,在babel7.4.0之后,@babel/polyfill将被弃用,改为使用core-js和regenerator-runtime实现polyfill
  • 首先安装 npm install core-js regenerator-runtime -D
  • 在.babelrc.json 配置
{
  "presets": [
    [
      "@babel/env",
      {
        "useBuiltIns": "usage", // 按需加载polyfill
        "corejs": {
          "version": 2
        } // 使用3版本不行,默认是2.0
        // [rollup-plugin-prettier] This may take a moment (depends on the size of your bundle)
        // 会卡住
      }
    ]
  ]
}
  • 可以在 bundle.js 看到打包后的结果是导入了'core-js/modules/es6.map.js'这种的代码, 提供了 es6 api 的环境
  • prettier 格式化时间长是因为格式化前后需要进行 diff 对比,但是修改了配置发现还是会进行对比 prettier({ cwd: prettierFile, sourcemap: false });// 没用
  • 注意,使用 npm run dev 之后,回提示警告:Unresolved dependencies
  • 这是因为我们引入了 es6.string.iterator.js,web.dom.iterable.js 等文件,但是并没有使用(在 rollup 看来)
  1. 添加浏览器兼容目标
  • browserslist
  • 在根目录添加一个.browserslistrc 文件,babel/pretitter/postcss 等工具都会自动去获取浏览器兼容要求
> 0.25%
> not op_mini all
  • 使用npx browserslist查询当前项目支持的浏览器版本
  1. 修改打包后的文件名称
  • 首先把入口文件改为 index.ts;输出名称为 Demo;index.ts 代码为:
import Table from "./components/demo/Demo";
import "./assets/styles/index.scss";
const demo = new Demo().render;

export default demo;
  • 把 rollup.config.js 的 output 修改以下
output: {
        file: 'dist/Demo.js',
        name: 'demo', // 输出的模块名称,在iife时 global."xxx" = factory()
        format: 'umd',
        banner: banner, // 打包后的代码头部提示语
        sourcemap: true
    }
  1. 生成.d.ts声明文件
  • 由于@rollup/plugin-typescript 无法生成 声明文件,所以按照官方提示使用rollup-plugin-typescript2
  • 安装: npm i rollup-plugin-typescript2 -D
  • 修改rollup.config.js
const typescript2 = require('rollup-plugin-typescript2');
typescript2()
  • 生成声明文件,需要添加一个文件tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@/*": [ "src/*"]
    },
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "config", "dist", "public"]
}
  • declaration:true生成声明文件
  • 注意sourceMap必须是true,如果是false,那么因为我们在rollup.config.js开启了sourceMap,那么还是会生成.map文件
  • 但是生成的文件是不对的,mappings结果全部是逗号,。没法生成正确的map文件
  • 官网说明
  • 关于 sourcemap 的原理可以看阮一峰的博客。更进一步可以看mozilla做的sourcemap源码sourcemap
  1. 解决typescript文件使用别名失效问题
  • 这是因为在tsconfig.json中配置错误,修改为
  • baseUrl从src改为.;然后src路径修改
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@": [ "src"],
      "src": ["src"]
    },
    "declaration": true,
    "sourceMap": true,
  },
  "include": ["src/**/*.ts","src/*.ts"],
  "exclude": ["node_modules", "config", "dist", "public"]
}
  • 然后我们发现src可以找到别名了,但是使用@还是会报错。
  • 最后的解决方法是:"@/*": [ "src/*"],也就是需要通过@/*这种别名方式来解决!
  1. README.md文件美化
  • 首先找到了一个插件:readme-md-generator
  • 执行命令:npx readme-md-generator,自定义输出内容
  • 由于输出内容过少,所以根据提示修改package.json,添加字段
"engines": {
    "npm": ">=6.14.12",
    "node": ">=10.24.1"
  }
  • 并且把test命令删除,把多余的广告删除,补上dev命令
  1. commit 提交规范

项目的最终版本的地址:rollup-demo