A fascinating technique that can greatly reduce your code

It's a common way in most web developers that write JavaScript with ES6+ and then bundle it to ES5, so that it can run in all browsers. However, the modern browsers support ES6 natively so it's unnecessary to shipping a lot of polyfills.html

I'm really excited to share you a technique that you can compile and serve two separate JavaScript bundles:webpack

  • One bundle you are definitely already generating, which serve for legacy browsers with polyfills.
  • Another bundle has less code with no polyfills, which serve for modern browsers.

That is <script type="module"> as a way to load ES modules. You can also load code with <script nomodule> for legacy browsers.git

The rest of this article explains how to implement this technique and the bundle solution for webpack.github

Concepts

How does it works? let's look at a example:web

<!-- For modern browsers -->
<script type="module" src="main.mjs"></script>

<!-- For legacy browsers -->
<script nomodule src="main.js"></script>
複製代碼

In modern browsers, script with type="module" will be loaded and executed, and script with nomodule will be ignored.npm

And in legacy browsers, script with type="module" will be ignored because they can't recognize this attrbute, script with nomodule has no effect, it will be treated as usual.json

Note: There's something you should know about <script type="module">.babel

  • It isn't executed until the document has been parsed, just like <script defer>.
  • Code is running in strict mode and top-level isn't window.

Warning: Safari 10 doesn’t support the nomodule attribute, but you can solve this by inlining a JavaScript snippet in your HTML (This has been fixed in Safari 11).app

Implementation

I really appreciate it that @babel/preset-env provides a convenient config for esmodules.less

babel for legacy:

{
  "presets": [
    [
      "@babel/preset-env", {
        "modules": false,
        "useBuiltIns": "entry",
        "targets": {
          "browsers": [
            "> 1%",
            "last 2 versions",
            "Firefox ESR"
          ]
        }
      }
    ]
  ]
}
複製代碼

babel for modern:

{
  "presets": [
    [
      "@babel/preset-env", {
        "modules": false,
        "useBuiltIns": false,
        "targets": {
          "esmodules": true
        }
      }
    ]
  ]
}
複製代碼

It's a bit complex for webpack to bundle two different JavaScript, there are some details you should know. So I wrote a esmodules-webpack-plugin to simplify configuration and bundle thest JavaScript just run webpack once.

Is it worth a try?

I think it's definitely worth a try, the size of polyfills that bundles by babel is more than what we think, and ES Modules code has an average reduction of 50% or even more, it all depends on your source code.

Besides, larger files not only take longer to download, but also take longer to parse and execute. So reduce file size is a efficient way to improve the performance of website.

Conclusion

<script type="module"> is really a fascinating technique that helps us shipping less code to users who use modern browsers and improve our website's performance.

However, there are also some limitations. for example, most module authors don’t publish ES6+ versions of their source code, a few browsers support <script type="module">, but will still download <script nomodule>(but won't execute).

Resources

A convenient webpack plugin for esmodules that I wrote: esmodules-webpack-plugin

相關文章
相關標籤/搜索