background

With IE out of the way and module support built into most browsers today, build is becoming an optimization step rather than a necessity. So, do we still need to keep the build steps that were originally derived to support some older versions of browsers? 🙈

What are the problems in the current development process?

  • Long construction process and high cost

Current front-end construction involves the following steps (dependency pulling, dependency graph analysis, packaging dependencies…) If, in an agile development scenario, the Release branch needs to be updated frequently, this will undoubtedly cause pipeline congestion; Changing a small amount of code at the same time leads to the entire build process and wastes resources.

  • Dependent packages release updates based on version numbers and are extremely slow

Because there is A one-way flow of information between dependencies, the UI Component is not actively notified of updates to A Dependencies. In the case of the top Web APP, it is often blocked at a point in the dependency package, so that the version cannot be updated quickly.

Let’s imagine a scenario: a business developerXiao MingIf you find A bug in A Dependencies that you cannot hack, you have to keep paying attention to the update of A Dependencies. After obtaining the updated version, you need to modify package.json to trigger the build process and fix the bug. If the dependencies of A dependencies package A-A dependencies also have the same bug? The blocking time for this process will increase exponentially. 😹

  • The construction and configuration is complicated

While the various build tools and scaffold preset configurations are as simple as possible, custom configurations are essential for large projects. Because of the complexity of Webpack, there are even special Webpack configuration engineers. Vite is optimized to a certain extent, but it doesn’t have the problem of bottom cutting.

Why is the ESM expected to solve these problems?

ESM, ECMA Script Modules, namely abstract: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/ModulesCopy the code

What magic happens if the project uses ESM? Take Xiaoming as an example. After the bug of A Dependencies is fixed, the user side will directly load the code after Fixed through import, without triggering the construction again (if the module of A dependencies is split properly, only the corresponding code of FIXED needs to be updated). Can complete the project update.

Talk is cheap, show me the code!

Next, let’s try to implement a no-build Hello World using ESM+ ESInstall. 💯

  • Set up the index. HTML

We declare type=”module” in the script tag and import the main js file main.js.

<! DOCTYPEhtml>
<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>Document</title>
    <script type="module" src=".. /js/main.js"></script>
</head>
<body>
    <h1>Hello World</h1>
    <my-element></my-element>
</body>
</html>
Copy the code
  • npm init

Create package.json as a normal project and list the dependencies.

{
  "name": "no-packaging"."version": "1.0.0"."description": ""."main": "index.js"."type": "module"."scripts": {
    "build": "node --experimental-modules build-modules.mjs"."postinstall": "yarn build"
  },
  "keywords": [
    "esinstall"]."author": ""."license": "ISC"."dependencies": {
    "dom-confetti": "^ 0.2.2"."lit": "^ 2.2.1." "
  },
  "devDependencies": {
    "esinstall": "^ 1.1.7"}}Copy the code
  • Esinstall set
Esinstall introduction: https://www.npmjs.com/package/esinstallCopy the code

As things stand, it is clearly irrational and unrealistic to abandon the nPM-rich ecosystem. So we use ESInstall to convert dependent packages to ESM.

We set a buildModules option to declare the dependencies we need to convert to ESM. And create a postinstall script that runs automatically after each install, which executes build-modules.mjs.

{
  "name": "no-packaging"."version": "1.0.0"."description": ""."main": "index.js"."type": "module"."scripts": {
    "build": "node --experimental-modules build-modules.mjs"."postinstall": "yarn build"
  },
  "keywords": [
    "esinstall"]."author": ""."license": "ISC"."dependencies": {
    "dom-confetti": "^ 0.2.2"."lit": "^ 2.2.1." "
  },
  "devDependencies": {
    "esinstall": "^ 1.1.7"
  },
  "buildModules": {
    "install": [
      "lit"."dom-confetti"]."installOptions": {
      "dest": "js/vendor"}}}Copy the code

What build-modules.mjs does is read the list of dependencies that need to be converted from package.json and hand them over to EsInstall for conversion.

import { install } from 'esinstall';
import { readFile } from 'fs/promises';

const json = JSON.parse(
  await readFile(
    new URL('./package.json'.import.meta.url)
  )
);
const moduleList = json.buildModules.install;
const installOptions = json.buildModules.installOptions;

await install(moduleList, installOptions);

Copy the code
  • Hello World

After yarn install is executed, we use lit to create a custom Web Components component in main.js. The component contains an input box, event listeners, and event handlers. And use dom-Confetti with a little bit of motion. 💥

import {LitElement, html} from './vendor/lit.js';
import { confetti } from "./vendor/dom-confetti.js";

confetti(document.body, { angle: 0.duration: 10000 });

class MyElement extends LitElement {
    static properties = {
      name: {}};constructor() {
      super(a);this.name = 'Your name here';
    }

    changeName(event) {
        const input = event.target;
        this.name = input.value;
    }
  
    render() {
      return html`
        <p>Hello, The ${this.name}</p>
        <input placeholder="Enter your name" @input=The ${this.changeName}
        >
      `;
    }
  }

customElements.define('my-element', MyElement);


Copy the code
  • test

With everything in place, we use the local server, access index.html, and see the application written without packaging.

Limitations of the current scheme

As we can see from Hello World, the current ESM solution is far from mature enough to be used in a production environment for the following reasons:

  • Many of the dependent libraries do not support ESM well and need to be converted using ESInstall
  • Without build packaging, multiple small files make browser decompression expensive, and large applications can have performance risks
  • Without Babel, cross-browser issues need to be handled manually
  • Unable to use the two main front-end frameworks (Vue, React) deeply bound to the build tool

The future of the ESM

Despite the current problems with the ESM, the trend towards faster iteration times and fewer build steps on the front end is unstoppable.

After all, the TypeScript team has been eager to make compiling types not the only step between TypeScript code and runtime, with a Stage0 proposal to support types directly in JavaScript.

On a deeper level, Webpack never expected Vite to be so unbeatable. And who is the next Vite? 😉