Rollup: Sie können bereits Anwendungen erstellen

Rollup ist eine Javascript-Anwendung und ein Builder für Bibliotheken der neuen Generation. Viele Leute kennen ihn seit langem als vielversprechenden Sammler, der sich gut zum Erstellen von Bibliotheken eignet, aber schlecht zum Erstellen von Anwendungen. Mit der Zeit entwickelt sich das Produkt jedoch aktiv weiter.

Ich habe es zum ersten Mal Anfang 2017 versucht. Ich mochte es sofort, weil es die Kompilierung in ES2015, das Baumshaking, das Fehlen von Modulen in der Assembly und natürlich eine einfache Konfiguration unterstützte. Aber dann war es ein Rohprodukt mit einer kleinen Anzahl von Plugins und sehr eingeschränkter Funktionalität, und ich entschied mich, es für später zu belassen und baute weiter über browserify. Der zweite Versuch war im Jahr 2018, als die Community, die Plugins und die Funktionalität bereits deutlich übertroffen wurden, bei einigen Funktionen, einschließlich Watcher, jedoch noch keine Qualität bestand. Und schließlich können wir Anfang 2019 mit Sicherheit sagen: Mit Hilfe von Rollup können Sie einfach und bequem moderne Anwendungen erstellen.

Um die Vorteile zu verstehen, gehen wir die wichtigsten Funktionen durch und vergleichen sie mit Webpack (bei Browserify ist die Situation dieselbe).

Einfache Konfiguration


Was sofort auffällt, ist eine sehr einfache und verständliche Konfiguration:

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

Geben Sie rollup -c in das cosnol ein und Ihr Bundle beginnt sich zusammenzusetzen. Für den Export können Sie eine Reihe von Bundles zum Zusammenstellen bereitstellen, z. B. wenn Sie Polyphile, mehrere Programme, Worker usw. separat sammeln. Bei der Eingabe können Sie ein Array von Dateien senden, dann werden Chunks gesammelt. In der Ausgabe können Sie ein Array von Ausgabedateien einspeisen und zu verschiedenen modularen Systemen zusammenfügen: iife, commonjs, umd.

Iife Unterstützung


Unterstützung für die Montage in die Funktion selbst ohne Module aufgerufen. Nehmen wir zum Verständnis das bekannteste Programm:

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

Fahren Sie es durch Rollup in das iife-Format und sehen Sie das Ergebnis:

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

Die Ausgabe ist ein sehr kompakter Code, nur 69 Bytes. Wenn Sie den Vorteil immer noch nicht verstehen, kompiliert Webpack / Browserify den folgenden Code:

Webpack-Build-Ergebnis
 /******/ (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!"); /***/ }) /******/ ]); 


Wie Sie sehen, stellte sich heraus, dass Webpack / Browserify nur in CommonJS erstellt werden kann. Der große Vorteil von IIFE ist seine Kompaktheit und das Fehlen von Konflikten zwischen verschiedenen Versionen von CommonJS. Es gibt jedoch einen Nachteil: Sie können keine Chunks sammeln. Für diese müssen Sie zu CommonJS wechseln.

Zusammenstellung in ES2015


Das Rollup "Sammler der nächsten Generation" wurde 2016 für die Montage in ES2015 erhalten. Und bis Ende 2018 war es der einzige Sammler, der wusste, wie man das macht.
Zum Beispiel, wenn Sie den Code nehmen:

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

und durch Rollup fahren, dann bekommen wir am Ausgang das gleiche. Und ja! Zu Beginn des Jahres 2019 können bereits 87% der Browser es nativ ausführen.

Im Jahr 2016 sah es dann nach einem Durchbruch aus, da es eine große Anzahl von Anwendungen gab, die keine Unterstützung für alte Browser benötigten: Administratoren, Kioske, keine Webanwendungen, und es gab keine Build-Tools für sie. Und jetzt können wir mit Rollup mehrere Bundles in einem Durchgang sammeln, in es3, es5, es2015, exnext und je nach Browser das erforderliche herunterladen.

Ein großer Vorteil von ES2015 ist auch die Größe und Ausführungsgeschwindigkeit. Aufgrund des Fehlens der Transpilierung in eine niedrigere Schicht erweist sich der Code als viel kompakter, und aufgrund des Fehlens eines von Transpilern erzeugten Hilfscodes funktioniert dieser Code auch dreimal schneller (gemäß meinen subjektiven Tests).

Baum zittern


Dies ist ein Rollup-Chip, er hat ihn erfunden! Webpack hat viele Jahre hintereinander versucht, es zu implementieren, aber erst mit Version 4 begann etwas zu funktionieren. Browserify geht es sehr schlecht.
Was für ein Tier ist das? Nehmen wir als Beispiel die folgenden zwei Dateien:

 // 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()); 

Laufen Sie durch Rollup und erhalten Sie:

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

Infolge von TreeShaking wurde toter Code bereits beim Auflösen von Abhängigkeiten verworfen. Dank dessen, was Rollup-Baugruppen viel kompakter werden. Nun wollen wir sehen, was Webpack generiert:

Webpack-Build-Ergebnis
 /******/ (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()); /***/ }) /******/ ]); 


Und hier können wir zwei Schlussfolgerungen ziehen. Das erste Webpack Ende 2018 lernte noch, ES2015 zu verstehen und zu bauen. Der zweite, absolut vollständige Code fällt in die Assembly, aber der tote Code wird bereits mit dem Terser-Minifier (Gabel und Nachfolger von UglifyES) entfernt. Als Ergebnis dieses Ansatzes, dickere Bündel als Rollup, auf einem Habr bereits viel darüber geschrieben, werden wir nicht damit aufhören.

Plugins


Aus einer Rollup-Box kann nur nackter ES2015 + zusammengebaut werden. Um ihm zusätzliche Funktionen beizubringen, z. B. das Verbinden von CommonJs, Typoskript-Modulen, das Laden von HTML und SCSS usw., müssen Sie Plugins verbinden.

Dies geschieht ganz einfach:
 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() //   ] }]; 

So einfach ist ein Plug-In in einem Wort verbunden. Diese Konfiguration kann jede komplexe Anwendung erfassen und generiert sogar eine Bundle-Analyse am Ausgang.

Zusammenfassung


Und jetzt, bewaffnet mit neuem Wissen, erstellen wir eine Konfiguration, die separat Polyphile, eine separate Anwendung für alte und neue Browser, einen Service-Worker für pwa und einen Web-Worker für komplexe Berechnungen im Brounund sammelt.

 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" }) }]; 


Alle einfachen Bundles und schnellen Webanwendungen!

Source: https://habr.com/ru/post/de440946/


All Articles