汇总:您已经可以构建应用程序

汇总是新一代的javascript应用程序和库生成器。 长期以来,许多人都知道他是一个很有前途的生成器,它非常适合于构建库,但不适用于构建应用程序。 但是,随着时间的流逝,该产品正在积极开发中。

我于2017年初首次尝试。 我立即喜欢它,因为它支持ES2015中的编译,树状交换,程序集中缺少模块,当然还有简单的配置。 但是那时它是一个粗糙的产品,具有少量的插件和非常有限的功能,我决定将其留待以后再继续通过browserify进行构建。 第二次尝试是在2018年,当时它已经有很多社区,插件和功能,但是在某些功能(包括观察程序)上仍然缺乏质量。 最后,在2019年初,我们可以放心地说-在Rollup的帮助下,您可以轻松便捷地构建现代应用程序。

为了了解其好处,让我们浏览一下主要功能并与Webpack进行比较(Browserify的情况相同)。

简单的配置


立即引起您注意的是一个非常简单且易于理解的配置:

export default [{ input: 'src/index.ts', output: [{ file: 'dist/index.min.js', format: 'iife' }], plugins: [ // todo:     ], }]; 

在cosnol中输入rollup -c ,您的捆绑包开始组装。 对于导出,您可以提供一组用于组装的包,例如,如果您分别收集多义词,几个程序,工作程序等。 在输入中,您可以提交文件数组,然后将收集大块。 在输出中,您可以提供一个输出文件数组,并将它们组合到不同的模块化系统中:iife,commonjs,umd。

Iife支持


支持组装成本身不带模块的函数 为了理解,让我们采用最著名的程序:

 console.log("Hello, world!"); 

通过汇总将其驱动为iife格式,然后查看结果:

 (function () { 'use strict'; console.log("Hello, world!"); }()); 

输出是非常紧凑的代码,只有69个字节。 如果您仍然不了解其优势,Webpack / Browserify将编译以下代码:

Webpack构建结果
 /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { console.log("Hello, world!"); /***/ }) /******/ ]); 


如您所见,由于Webpack / Browserify只能在CommonJS中构建,因此实际情况要多得多。 IIFE的最大优点是它的紧凑性和不同版本的CommonJS之间没有冲突。 但是有一个缺点,您不能收集块,因为您需要切换到CommonJS。

ES2015中的编译


由于能够在ES2015中进行组装,因此在2016年获得了“下一代收集器”的名称。 直到2018年底,它是唯一知道如何做的收藏家。
例如,如果采用以下代码:

 export class TestA { getData(){return "A"} } console.log("Hello, world!", new TestB().getData()); 

并进行汇总,然后在输出端得到相同的结果。 是的! 在2019年初,已经有87%的浏览器可以本地执行它。

然后在2016年,这似乎是一个突破,因为有许多应用程序不需要对旧浏览器的支持:管理员,信息亭,而不是Web应用程序,并且没有针对它们的构建工具。 现在,借助Rollup,我们可以一次性通过es3,es5,es2015,exnext收集多个捆绑软件,并根据浏览器下载必要的捆绑软件。

ES2015的一大优势是它的大小和执行速度。 由于缺少到较低层的转译,因此代码变得更加紧凑,并且由于缺少由转译器生成的辅助代码,因此该代码的运行速度也提高了3倍(根据我的主观测试)。

摇树


这是他发明的汇总芯片! Webpack多年来一直在尝试实现它,但是只有在第4版中才开始起作用。 Browserify的效果很差。
这是什么样的动物? 让我们以以下两个文件为例:

 // module.ts export class TestA { getData(){return "A"} } export class TestB { getData(){return "B"} } // index.ts import { TestB } from './module'; const test = new TestB(); console.log("Hello, world!", test.getData()); 

通过汇总运行并获得:

 (function () { 'use strict'; class TestB { getData() { return "B"; } } const test = new TestB(); console.log("Hello, world!", test.getData()); }()); 

作为TreeShaking的结果,即使在解决依赖项的阶段,死代码也被丢弃了。 借助Rollup组件,结果变得更加紧凑。 现在,让我们看看Webpack生成的内容:

Webpack构建结果
 /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); // CONCATENATED MODULE: ./src/module.ts class TestA { getData() { return "A"; } } class TestB { getData() { return "B"; } } // CONCATENATED MODULE: ./src/index.ts const test = new TestB(); console.log("Hello, world!", test.getData()); /***/ }) /******/ ]); 


在这里我们可以得出两个结论。 2018年底的第一个Webpack仍然学会了理解和构建ES2015。 第二,绝对所有代码都落入汇编中,但是无效代码已经使用Terser压缩程序(UglifyES的前叉和后继)删除了。 这种方法的结果是,比起Rollup更厚的捆绑包,在已经撰写过很多文章的habr上,我们将不会停止。

外挂程式


从汇总箱中只能组装裸露的ES2015 +。 为了教给他其他功能,例如连接commonjs,打字稿模块,加载html和scss等,您需要连接插件。

这非常简单地完成:
 import nodeResolve from 'rollup-plugin-node-resolve'; import commonJs from 'rollup-plugin-commonjs'; import typeScript from 'rollup-plugin-typescript2'; import postcss from 'rollup-plugin-postcss'; import html from 'rollup-plugin-html'; import visualizer from 'rollup-plugin-visualizer'; import {sizeSnapshot} from "rollup-plugin-size-snapshot"; import {terser} from 'rollup-plugin-terser'; export default [{ input: 'src/index.ts', output: [{ file: 'dist/index.r.min.js', format: 'iife' }], plugins: [ nodeResolve(), //   node commonJs(), //   commonjs postcss(), //   postcc,    scss  less html(), //  html  typeScript({tsconfig: "tsconfig.json"}), //  typescript sizeSnapshot(), //      terser(), //    ES2015+,    UglifyES visualizer() //   ] }]; 

如此简单,一个单词就能连接一个插件。 此配置可以收集任何复杂的应用程序,甚至在输出处生成捆绑分析。

总结


现在,有了新知识,我们就可以进行配置,该配置将分别收集polyphil,用于新旧浏览器的单独应用程序,用于pwa的服务工作者以及用于在brounund中进行复杂计算的Web工作者。

 import nodeResolve from 'rollup-plugin-node-resolve'; import commonJs from 'rollup-plugin-commonjs'; import typeScript from 'rollup-plugin-typescript2'; import postcss from 'rollup-plugin-postcss'; import html from 'rollup-plugin-html'; import visualizer from 'rollup-plugin-visualizer'; import { sizeSnapshot } from "rollup-plugin-size-snapshot"; import { terser } from 'rollup-plugin-terser'; const getPlugins = (options) => [ nodeResolve(), commonJs(), postcss(), html(), typeScript({ tsconfig: "tsconfig.json", tsconfigOverride: { compilerOptions: { "target": options.target } } }), sizeSnapshot(), terser(), visualizer() ]; export default [{ input: 'src/polyfills.ts', output: [{ file: 'dist/polyfills.min.js', format: 'iife' }], plugins: getPlugins({ target: "es5" }) },{ input: 'src/index.ts', output: [{ file: 'dist/index.next.min.js', format: 'iife' }], plugins: getPlugins({ target: "esnext" }) },{ input: 'src/index.ts', output: [{ file: 'dist/index.es5.min.js', format: 'iife' }], plugins: getPlugins({ target: "es5" }) },{ input: 'src/index.ts', output: [{ file: 'dist/index.es3.min.js', format: 'iife' }], plugins: getPlugins({ target: "es3" }) },{ input: 'src/serviceworker.ts', output: [{ file: 'dist/serviceworker.min.js', format: 'iife' }], plugins: getPlugins({ target: "es5" }) },{ input: 'src/webworker.ts', output: [{ file: 'dist/webworker.min.js', format: 'iife' }], plugins: getPlugins({ target: "es5" }) }]; 


所有简单的捆绑包和快速的Web应用程序!

Source: https://habr.com/ru/post/zh-CN440946/


All Articles