摘要:教程如何使用打包通过这个系列教程一步一步学习如何使用更小更快的取代和打包文件。安装并且创建配置文件。提示是告诉我们实际需要哪些插件的集合。通过下面的命令安装两个插件更新然后,引入插件并添加进配置注意属性是为了帮助模块迁移到的一部分。
教程:如何使用Rollup打包JavaScript
通过这个系列教程一步一步学习如何使用更小更快的Rollup取代webpack和Browserify打包JavaScript文件。
这周,我们要使用Rollup构建我们的第一个项目,Rollup是一个打包JavaScript(和样式,不过下周才会做)的构建工具。
通过这个教程,我们的Rollup将能够:
合并scripts代码,
删除多余代码,
编译成对旧浏览器友好的代码,
支持在浏览器中使用Node模块,
能使用环境变量,
尽可能的压缩,减少文件大小。
准备工作至少懂一点JavaScript的话将会更好理解。
对ES2015 modules有基本了解,不过不了解也无妨。
在你的设备上要有npm。(还没有?在这下载Node.js)
Rollup是什么?用他们自己的话说:
Rollup是下一代JavaScript模块打包工具。开发者可以在你的应用或库中使用ES2015模块,然后高效地将它们打包成一个单一文件用于浏览器和Node.js使用。
和Browserify和webpack很像。
你也可以称Rollup是一个构建工具,可以和像Grunt和Gulp等一起配置使用。但是,需要注意的一点是当你使用Grunt和Gulp来处理像打包JavaScript这样的任务时,这些工具的底层还是使用了像Rollup,Browserify或webpack这些东西。
为什么应该关注Rollup?Rollup最令人激动的地方,就是能让打包文件体积很小。这么说很难理解,更详细的解释:相比其他JavaScript打包工具,Rollup总能打出更小,更快的包。
因为Rollup基于ES2015模块,比webpack和Browserify使用的CommonJS模块机制更高效。这也让Rollup从模块中删除无用的代码,即tree-shaking变得更容易。
当我们引入拥有大量函数和方法的三方工具或者框架时tree-shaking会变得很重要。想想lodash或者jQuery,如果我们只使用一个或者两个方法,就会因为加载其余内容而产生大量无用的开销。
Browserify和webpack就会包含大量无用的代码。但是Rollup不会 - 它只会包括我们真正用到的东西。
更新 (2016-08-22): 澄清一下,Rollup只能对ES模块上进行tree-shaking。CommonJS模块 - 像lodash和jQuery那样写的模块不能进行tree-shaking。然而,tree-shaking不是Rollup在速度/性能上唯一的优势。可以看Rich Harris的解释和Nolan Lawson的补充了解更多。
还有一个大新闻。
Part I: 如何使用Rollup处理并打包JavaScript文件注意: 由于Rollup很高效,webpack 2 也将支持tree-shaking。
为了展示Rollup如何使用,让我们通过构建一个简单的项目来走一遍使用Rollup打包JavaScript的过程。
STEP 0: 创建一个包含将被编译的JavaScript和CSS的项目.为了开始工作,我们需要一些用来处理的代码。这个教程里,我们将用一个小应用,可从GitHub获取。
目录结构如下:
learn-rollup/ ├── src/ │ ├── scripts/ │ │ ├── modules/ │ │ │ ├── mod1.js │ │ │ └── mod2.js │ │ └── main.js │ └── styles/ │ └── main.css └── package.json
你可以在终端执行下面的命令下载这个应用,我们将在这个教程中使用它。
# Move to the folder where you keep your dev projects. cd /path/to/your/projects # Clone the starter branch of the app from GitHub. git clone -b step-0 --single-branch https://github.com/jlengstorf/learn-rollup.git # The files are downloaded to /path/to/your/projects/learn-rollup/STEP 1: 安装Rollup并且创建配置文件。
第一步,执行下面的命令安装Rollup:
npm install --save-dev rollup
然后,在learn-rollup文件夹下新建rollup.config.js。在文件中添加如下内容。
export default { entry: "src/scripts/main.js", dest: "build/js/main.min.js", format: "iife", sourceMap: "inline", };
说说每个配置项实际上做了什么:
entry — 希望Rollup处理的文件路径。大多数应用中,它将是入口文件,初始化所有东西并启动应用。
dest — 编译完的文件需要被存放的路径。
format — Rollup支持多种输出格式。因为我们是要在浏览器中使用,需要使用立即执行函数表达式(IIFE)[注1]
sourceMap — 调试时sourcemap是非常有用的。这个配置项会在生成文件中添加一个sourcemap,让开发更方便。
测试Rollup配置NOTE: 对于其他的format选项以及你为什么需要他们,看Rollup’s wiki。
当创建好配置文件后,在终端执行下面的命令测试每项配置是否工作:
./node_modules/.bin/rollup -c
在你的项目下会出现一个build目录,包含js子目录,子目录中包含生成的main.min.js文件。
在浏览器中打开build/index.html可以看到打包文件正确生成了。
完成第一步后我们的示例项目的状态。
看看打包出来的文件注意:现在,只有现代浏览器下不会报错。为了能够在不支持ES2015/ES6的老浏览器中运行,我们需要添加一些插件。
事实上Rollup强大是因为它使用了“tree-shaking”,可以在你引入的模块中删除没有用的代码。举个例子,在src/scripts/modules/mod1.js中的sayGoodbyeTo()函数在我们的应用中并没有使用 - 而且因为它从不会被使用,Rollup不会将它打包到bundle中:
(function () { "use strict"; /** * Says hello. * @param {String} name a name * @return {String} a greeting for `name` */ function sayHelloTo( name ) { const toSay = `Hello, ${name}!`; return toSay; } /** * Adds all the values in an array. * @param {Array} arr an array of numbers * @return {Number} the sum of all the array values */ const addArray = arr => { const result = arr.reduce((a, b) => a + b, 0); return result; }; // Import a couple modules for testing. // Run some functions from our imported modules. const result1 = sayHelloTo("Jason"); const result2 = addArray([1, 2, 3, 4]); // Print the results on the page. const printTarget = document.getElementsByClassName("debug__output")[0]; printTarget.innerText = `sayHelloTo("Jason") => ${result1} ` printTarget.innerText += `addArray([1, 2, 3, 4]) => ${result2}`; }()); //# sourceMappingURL=data:application/json;charset=utf-8;base64,...
其他的构建工具则不是这样的,所以如果我们引入了一个像lodash这样一个很大的库而只是使用其中一两个函数时,我们的包文件会变得非常大。
比如使用webpack的话,sayGoodbyeTo()也会打包进去,产生的打包文件比Rollup生成的大了两倍多。
STEP 2: 配置babel支持JavaScript新特性。现在我们已经得到能在现代浏览器中运行的包文件了,但是在一些旧版本浏览器中就会崩溃 - 这并不理想。
幸运的是,Babel已发布了。这个项目编译JavaScript新特性(ES6/ES2015等等)到ES5, 差不多在今天的任何浏览器上都能运行。
如果你还没用过Babel,那么你的开发生涯要永远地改变了。使用JavaScript的新方法让语言更简单,更简洁而且整体上更友好。
那么让我们为Rollup加上这个过程,就不用担心上面的问题了。
INSTALL THE NECESSARY MODULES. 安装必要模块首先,我们需要安装Babel Rollup plugin和适当的Babel preset。
# Install Rollup’s Babel plugin. npm install --save-dev rollup-plugin-babel # Install the Babel preset for transpiling ES2015 using Rollup. npm install --save-dev babel-preset-es2015-rollup
创建.babelrc提示: Babel preset是告诉Babel我们实际需要哪些babel插件的集合。
然后,在项目根目录(learn-rollup/)下创建一个.babelrc文件。在文件中添加下面的JSON:
{ "presets": ["es2015-rollup"], }
它会告诉Babel在转换时哪些preset将会用到。
更新rollup.config.js为了让它能真正工作,我们需要更新rollup.config.js。
在文件中,importBabel插件,将它添加到新配置属性plugins中,这个属性接收一个插件组成的数组。
// Rollup plugins import babel from "rollup-plugin-babel"; export default { entry: "src/scripts/main.js", dest: "build/js/main.min.js", format: "iife", sourceMap: "inline", plugins: [ babel({ exclude: "node_modules/**", }), ], };
为避免编译三方脚本,通过设置exclude属性忽略node_modules目录。
检查输出文件全部都安装并配置好后,重新打包一下:
./node_modules/.bin/rollup -c
再看一下输出结果,大部分是一样的。但是有一些地方不一样:比如,addArray()这个函数:
var addArray = function addArray(arr) { var result = arr.reduce(function (a, b) { return a + b; }, 0); return result; };
Babel是如何将箭头函数(arr.reduce((a, b) => a + b, 0))转换成一个普通函数的呢?
这就是编译的意义:结果是相同的,但是现在的代码可以向后支持到IE9.
STEP 3: 添加ESLint检查常规JavaScript错误注意: Babel也提供了babel-polyfill,使得像Array.prototype.reduce()这些方法在IE8甚至更早的浏览器也能使用。
在你的项目中使用linter是个好主意,因为它强制统一了代码风格并且能帮你发现很难找到的bug,比如花括号或者圆括号。
在这个项目中,我们将使用ESLint。
安装模块为使用ESLint,我们需要安装ESLint Rollup plugin:
npm install --save-dev rollup-plugin-eslint生成一个.eslintrc.json
为确保我们只得到我们想检测的错误,首先要配置ESLint。很幸运,我们可以通过执行下面的命令自动生成大多数配置:
$ ./node_modules/.bin/eslint --init ? How would you like to configure ESLint? Answer questions about your style ? Are you using ECMAScript 6 features? Yes ? Are you using ES6 modules? Yes ? Where will your code run? Browser ? Do you use CommonJS? No ? Do you use JSX? No ? What style of indentation do you use? Spaces ? What quotes do you use for strings? Single ? What line endings do you use? Unix ? Do you require semicolons? Yes ? What format do you want your config file to be in? JSON Successfully created .eslintrc.json file in /Users/jlengstorf/dev/code.lengstorf.com/projects/learn-rollup
如果你按上面展示的那样回答问题,你将在生成的.eslintrc.json中得到下面的内容:
{ "env": { "browser": true, "es6": true }, "extends": "eslint:recommended", "parserOptions": { "sourceType": "module" }, "rules": { "indent": [ "error", 4 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "single" ], "semi": [ "error", "always" ] } }修改.eslintrc.json
然而我们需要改动两个地方来避免项目报错。
使用2空格代替4空格。
后面会使用到ENV这个全局变量,因此要把它加入白名单中。
在.eslintrc.json进行如下修改 — 添加globals属性并修改indent属性:
{ "env": { "browser": true, "es6": true }, "globals": { "ENV": true }, "extends": "eslint:recommended", "parserOptions": { "sourceType": "module" }, "rules": { "indent": [ "error", 2 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "single" ], "semi": [ "error", "always" ] } }更新rollup.config.js
然后,引入ESLint插件并添加到Rollup配置中:
// Rollup plugins import babel from "rollup-plugin-babel"; import eslint from "rollup-plugin-eslint"; export default { entry: "src/scripts/main.js", dest: "build/js/main.min.js", format: "iife", sourceMap: "inline", plugins: [ babel({ exclude: "node_modules/**", }), eslint({ exclude: [ "src/styles/**", ] }), ], };检查控制台输出
第一次,当执行./node_modules/.bin/rollup -c时,似乎什么都没发生。因为这表示应用的代码通过了linter,没有问题。
但是如果我们制造一个错误 - 比如删除一个分号 - 我们会看到ESLint是如何提示的:
$ ./node_modules/.bin/rollup -c /Users/jlengstorf/dev/code.lengstorf.com/projects/learn-rollup/src/scripts/main.js 12:64 error Missing semicolon semi ✖ 1 problem (1 error, 0 warnings)
一些包含潜在风险和解释神秘bug的东西立刻出现了,包括出现问题的文件,行和列。
但是它不能排除我们调试时的所有问题,很多由于拼写错误和疏漏产生的bug还是要自己花时间去解决。
STEP 4: 添加插件处理非ES模块如果你的依赖中有任何使用Node风格的模块这个插件就很重要。如果没有它,你会得到关于require的错误。
添加一个Node模块作为依赖在这个小项目中不引用三方模块很正常,但实际项目中不会如此。所以为了让我们的Rollup配置变得真正可用,需要保证在我们的代码中也能引用是三方模块。
举个简单的例子,我们将使用debug包添加一个简单的日志打印器到项目中。先安装它:
npm install --save debug
注意:因为它是会在主程序中引用的,应该使用--save参数,可以避免在生产环境下出现错误,因为devDependencies在生产环境下不会被安装。
然后在src/scripts/main.js中添加一个简单的日志:
// Import a couple modules for testing. import { sayHelloTo } from "./modules/mod1"; import addArray from "./modules/mod2"; // Import a logger for easier debugging. import debug from "debug"; const log = debug("app:log"); // Enable the logger. debug.enable("*"); log("Logging is enabled!"); // Run some functions from our imported modules. const result1 = sayHelloTo("Jason"); const result2 = addArray([1, 2, 3, 4]); // Print the results on the page. const printTarget = document.getElementsByClassName("debug__output")[0]; printTarget.innerText = `sayHelloTo("Jason") => ${result1} `; printTarget.innerText += `addArray([1, 2, 3, 4]) => ${result2}`;
到此一切都很好,但是当运行rollup时会得到一个警告:
$ ./node_modules/.bin/rollup -c Treating "debug" as external dependency No name was provided for external module "debug" in options.globals – guessing "debug"
而且如果在查看index.html,会发现一个ReferenceError抛出了:
默认情况下,三方的Node模块无法在Rollup中正确加载。
哦,真糟糕。完全无法运行。
因为Node模块使用CommonJS,无法与Rollup直接兼容。为解决这个问题,需要添加一组处理Node模块和CommonJS模块的插件。
安装模块围绕这个问题,我们将在Rollup中新增两个插件:
rollup-plugin-node-resolve,运行加载node_modules中的三方模块。
rollup-plugin-commonjs,将CommonJS模块转换成ES6,防止他们在Rollup中失效。
通过下面的命令安装两个插件:
npm install --save-dev rollup-plugin-node-resolve rollup-plugin-commonjs更新rollup.config.js.
然后,引入插件并添加进Rollup配置:
// Rollup plugins import babel from "rollup-plugin-babel"; import eslint from "rollup-plugin-eslint"; import resolve from "rollup-plugin-node-resolve"; import commonjs from "rollup-plugin-commonjs"; export default { entry: "src/scripts/main.js", dest: "build/js/main.min.js", format: "iife", sourceMap: "inline", plugins: [ resolve({ jsnext: true, main: true, browser: true, }), commonjs(), eslint({ exclude: [ "src/styles/**", ] }), babel({ exclude: "node_modules/**", }), ], };
检查控制台输出注意: jsnext属性是为了帮助Node模块迁移到ES2015的一部分。main和browser 属性帮助插件决定哪个文件应该被bundle文件使用。
执行./node_modules/.bin/rollup -c重新打包,然后再检查浏览器输出:
成功了!日志现在打印出来了。
STEP 5: 添加插件替换环境变量环境变量使开发流程更强大,让我们有能力做一些事情,比如打开或关闭日志,注入仅在开发环境使用的脚本等等。
那么让Rollup支持这些功能吧。
在main.js中添加ENV变量让我们通过一个环境变量控制日志脚本,让日志脚本只能在非生产环境下使用。在src/scripts/main.js中修改log()的初始化方式。
// Import a logger for easier debugging. import debug from "debug"; const log = debug("app:log"); // The logger should only be disabled if we’re not in production. if (ENV !== "production") { // Enable the logger. debug.enable("*"); log("Logging is enabled!"); } else { debug.disable(); }
然而,重新打包(./node_modules/.bin/rollup -c)后检查浏览器,会看到一个ENV的ReferenceError。
不必惊讶,因为我们没有在任何地方定义它。如果我们尝试ENV=production ./node_modules/.bin/rollup -c,还是不会成功。因为那样设置的环境变量只是在Rollup中可用,不是在Rollup打包的bundle中可用。
我们需要使用一个插件将环境变量传入bundle。
安装模块安装rollup-plugin-replace插件,它本质上只是做了查找-替换的工作。它能做很多事情,但现在我们只需要让它简单地找到出现的环境变量并将其替换成实际的值。(比如,所有在bundle出现的ENV变量都会被替换成"production" )。
npm install --save-dev rollup-plugin-replace更新rollup.config.js
在rollup.config.js中引入插件并且添加到插件列表中。
配置非常简单:只需添加一个键值对的列表,key是将被替换的字符串,value是应该被替换成的值。
// Rollup plugins import babel from "rollup-plugin-babel"; import eslint from "rollup-plugin-eslint"; import resolve from "rollup-plugin-node-resolve"; import commonjs from "rollup-plugin-commonjs"; import replace from "rollup-plugin-replace"; export default { entry: "src/scripts/main.js", dest: "build/js/main.min.js", format: "iife", sourceMap: "inline", plugins: [ resolve({ jsnext: true, main: true, browser: true, }), commonjs(), eslint({ exclude: [ "src/styles/**", ] }), babel({ exclude: "node_modules/**", }), replace({ exclude: "node_modules/**", ENV: JSON.stringify(process.env.NODE_ENV || "development"), }), ], };
在我们的配置中,将找打所有出现的ENV并且替换成process.env.NODE_ENV - 在Node应用中最普遍的设置环境变量的方法 - 或者 "development"中的一个。使用JSON.stringify()确保值被双引号包裹,如果ENV没有的话。
为了确保不会和三方代码造成问题,同样设置exclude属性来忽略node_modules目录和其中的全部包。(幸亏@wesleycoder先在这上面踩坑了。)
检查结果首先,重新打包然后在浏览器中检查。控制台日志会显示,就像之前一样。很棒 - 这意味着我们的默认值生效了。
为了展示新引入的能力,我们在production模式下运行命令:
NODE_ENV=production ./node_modules/.bin/rollup -c
注意: 在Windows上,使用SET NODE_ENV=production ./node_modules/.bin/rollup -c防止在设置环境变量时报错。
当刷新浏览器后,控制台没有任何日志打出了:
不改变任何代码的情况下,使用一个环境变量禁用了日志插件。
STEP 6: 添加UglifyJS压缩减小生成代码体积这个教程中最后一步是添加UglifyJS来减小和压缩bundle文件。可以通过移除注释,缩短变量名和其他压缩换行等方式大幅度减少bundle的大小 - 会让文件的可读性变差,但提高了网络间传输的效率。
安装插件我们将使用UglifyJS压缩bundle,通过rollup-plugin-uglify插件。
通过下面命令安装:
npm install --save-dev rollup-plugin-uglify更新rollup.config.js
然后添加Uglify到Rollup配置。为了开发环境下可读性更好,设置代码丑化仅在生产环境下使用:
// Rollup plugins import babel from "rollup-plugin-babel"; import eslint from "rollup-plugin-eslint"; import resolve from "rollup-plugin-node-resolve"; import commonjs from "rollup-plugin-commonjs"; import replace from "rollup-plugin-replace"; import uglify from "rollup-plugin-uglify"; export default { entry: "src/scripts/main.js", dest: "build/js/main.min.js", format: "iife", sourceMap: "inline", plugins: [ resolve({ jsnext: true, main: true, browser: true, }), commonjs(), eslint({ exclude: [ "src/styles/**", ] }), babel({ exclude: "node_modules/**", }), replace({ ENV: JSON.stringify(process.env.NODE_ENV || "development"), }), (process.env.NODE_ENV === "production" && uglify()), ], };
我们使用了短路运算,很常用(虽然也有争议)的条件性设置值的方法。[注4]
在我们的例子中,只有在NODE_ENV是"production"时才会加载uglify()。
检查压缩过的bundle保存配置文件,让我们在生成环境下运行Rollup:
NODE_ENV=production ./node_modules/.bin/rollup -c
注意: 在Windows上,使用SET NODE_ENV=production ./node_modules/.bin/rollup -c防止在设置环境变量时报错。
输出内容并不美观,但是更小了。这有build/js/main.min.js的截屏,看起来像这样:
丑化过的代码确实能更高效地传输。
之前,我们的bundle大约42KB。使用UglifyJS后,减少到大约29KB - 在没做其他优化的情况下节省了超过30%文件大小。
接下来的内容在这个系列的下一节,我们将了解通过Rollup和PostCSS处理样式,并且添加live reloading来实时看见我们的修改。
Further Reading 扩展阅读The cost of small modules - 这篇文章让我开始对Rollup感兴趣,因为它展示了一些Rollup相比webpack和Browserify的优势。
Rollup’s getting started guide
Rollup’s CLI docs
A list of Rollup plugins
注1: 这是一个非常难理解的概念,所以没全理解也不要有压力。简单来说,我们希望我们的代码在他们自己的作用域中,防止和其它脚本的冲突。IIFE是一个包括我们的代码在自身作用域的一个[闭包]。
注2:It’s important to keep in mind, though, that when we’re dealing with such a small example app it doesn’t take much to double a file size. The comparison at this point is ~3KB vs. ~8KB.
注3:作为曾经花数小时找bug然后发现拼错一个变量名的人,不需要夸大使用linter带来的效率提升。
注4:举个例子,使用这种方法来赋默认值时非常常见的。(比如var foo = maybeThisExists || "default";)
这篇文章的代码放在GitHub上。你可以fork 这个仓库进行修改或测试,开issue或者报告bug,或者新建pull request进行建议或者修改。
原文链接
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80976.html
摘要:通过这个教程学习如何使用打包工具配合来取代或处理样式文件。使用这个命令安装插件更新。如果你没有项目的副本,你可以通过这条命令克隆在结束这个状态下的项目为添加监听插件。在代码块内,添加如下内容简单起见我省略了文件的大部分内容 通过这个教程学习如何使用JavaScript打包工具Rollup配合PostCSS来取代Grunt或Gulp处理样式文件。 上一篇文章中,我们完成了使用Rollup...
摘要:前端日报精选了解中的全局对象和全局作用域张鑫旭鑫空间鑫生活子进程你应该知道的一切直出内存泄露问题的追查实践我他喵的到底要怎样才能在生产环境中用上模块化腾讯前端大会大咖说大咖干货,不再错过发布发布中文翻译在使用进行本地开发代码 2017-07-07 前端日报 精选 了解JS中的全局对象window.self和全局作用域self « 张鑫旭-鑫空间-鑫生活Node.js 子进程:你应该知道...
摘要:既可以通过一个配置文件使用命令行接口来调用,也可以他自己的使用。使用最简单的方法就是通过命令行接口。命令缩写会以监视模式运行。这时运行下将不会有错误抛出,包含导入的组件。 介绍 概览 rollup是一个js打包器,用来将很细碎的js编译打包成大的复杂的东西,像是一个库或者一个应用。其使用了ES6自带的新标准来格式化和打包js代码,而不是原先的Commonjs或者AMD这类解决方案。ES...
摘要:所以,打包工具就出现了,它可以帮助做这些繁琐的工作。打包工具介绍仅介绍款主流的打包工具,,,,以发布时间为顺序。它定位是模块打包器,而属于构建工具。而且在其他的打包工具在处理非网页文件比如等基本还是需要借助它来实现。 本文当时写在本地,发现换电脑很不是方便,在这里记录下。 前端的打包工具 打包工具可以更好的管理html,css,javascript,使用可以锦上添花,不使用也没关系...
摘要:为何有查阅了的文档,并没有找到字段的定义,直到才知道它是中最早就提出的概念。况且目前大部分仍是采用,所以便使用了另一个字段。所以目前主流的打包工具都是支持的,鉴于其优点,字段很有可能加入的规范之中。 引入 最近团队的一个同学在搞 npm library 源码的调试插件,因为内部的一个组件库含有大量的逻辑,在某个项目中不经意就出现一个磨人的 bug,但是组件库发布都是打包编译后的代码,而...
阅读 2471·2021-10-08 10:04
阅读 2710·2021-09-06 15:02
阅读 728·2019-08-30 13:50
阅读 1517·2019-08-30 13:21
阅读 2567·2019-08-30 11:15
阅读 2088·2019-08-29 17:19
阅读 1551·2019-08-26 13:55
阅读 1246·2019-08-26 10:15