javascript Webpack 带有小的初始脚本和所有其他脚本的异步加载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33001985/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 16:00:35  来源:igfitidea点击:

Webpack with small initial script and async loading of all other scripts

javascriptjqueryasynchronouswebpack

提问by erzki

I have started using Webpack when developing usual web sites consisting of a number pages and of different pages types. I'm used to the RequireJs script loader that loads all dependencies on demand when needed. Just a small piece of javascript is downloaded when page loads.

在开发由多个页面和不同页面类型组成的常用网站时,我已经开始使用 Webpack。我习惯了 RequireJs 脚本加载器,它在需要时按需加载所有依赖项。页面加载时只下载一小部分 javascript。

What I want to achieve is this:

我想要实现的是:

  • A small initial javascript file that loads dependencies asynchronous
  • Each page type can have their own javascript, which also may have dependencies.
  • Common modules, vendor scripts should be bundled in common scripts
  • 一个小的初始 javascript 文件,它异步加载依赖项
  • 每个页面类型都可以有自己的 javascript,也可能有依赖关系。
  • 通用模块,供应商脚本应该捆绑在通用脚本中

I have tried many configurations to achieve this but with no success.

我尝试了很多配置来实现这一点,但没有成功。

entry: {
    main: 'main.js', //Used on all pages, e.g. mobile menu
    'standard-page': 'pages/standard-page.js',
    'start-page': 'pages/start-page.js',
    'vendor': ['jquery']
},
alias: {
    jquery: 'jquery/dist/jquery.js'
},
plugins: [
    new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js"),
    new webpack.optimize.CommonsChunkPlugin('common.js')
]

In the html I want to load the javascripts like this:

在 html 中,我想像这样加载 javascripts:

<script src="/Static/js/dist/common.js"></script>
<script src="/Static/js/dist/main.js" async></script>

And on a specific page type (start page)

并在特定页面类型(起始页面)上

<script src="/Static/js/dist/start-page.js" async></script>

common.js should be a tiny file for fast loading of the page. main.js loads async and require('jquery') inside.

common.js 应该是一个用于快速加载页面的小文件。main.js 在里面加载 async 和 require('jquery') 。

The output from Webpack looks promising but I can't get the vendors bundle to load asynchronously. Other dependencies (my own modules and domReady) is loaded in ther autogenerated chunks, but not jquery.

Webpack 的输出看起来很有希望,但我无法让供应商包异步加载。其他依赖项(我自己的模块和 domReady)加载在这些自动生成的块中,但不是 jquery。

I can find plenty of examples that does almost this but not the important part of loading vendors asynchronously.

我可以找到很多几乎可以做到这一点的示例,但不是异步加载供应商的重要部分。

Output from webpack build:

webpack 构建的输出:

                  Asset       Size  Chunks             Chunk Names
            main.js.map  570 bytes    0, 7  [emitted]  main
                main.js  399 bytes    0, 7  [emitted]  main
       standard-page.js  355 bytes    2, 7  [emitted]  standard-page
c6ff6378688eba5a294f.js  348 bytes    3, 7  [emitted]
          start-page.js  361 bytes    4, 7  [emitted]  start-page
8986b3741c0dddb9c762.js  387 bytes    5, 7  [emitted]
              vendor.js     257 kB    6, 7  [emitted]  vendor
              common.js    3.86 kB       7  [emitted]  common.js
2876de041eaa501e23a2.js     1.3 kB    1, 7  [emitted]  

回答by Jamund Ferguson

The solution to this problem is two-fold:

这个问题的解决方法有两个:

  1. First you need to understand how code-splitting works in webpack
  2. Secondly, you need to use something like the CommonsChunkPluginto generate that shared bundle.
  1. 首先你需要了解代码拆分在 webpack 中是如何工作的
  2. 其次,您需要使用类似的东西CommonsChunkPlugin来生成共享包。

Code Splitting

代码拆分

Before you start using webpack you need to unlearn to be dependent on configuration. Require.js was all about configuration files. This mindset made it difficult for me to transition into webpack which is modeled more closely after CommonJS in node.js, which relies on no configuration.

在开始使用 webpack 之前,您需要忘记依赖配置。Require.js 是关于配置文件的。这种心态让我很难过渡到 webpack,它更接近于 node.js 中的 CommonJS 建模,它不依赖于配置。

With that in mind consider the following. If you have an app and you want it to asynchronously load some other parts of javascript you need to use one of the following paradigms.

考虑到这一点,请考虑以下事项。如果您有一个应用程序并且希望它异步加载 javascript 的其他一些部分,则需要使用以下范例之一。

Require.ensure

要求确保

require.ensureis one way that you can create a "split point" in your application. Again, you may have thought you'd need to do this with configuration, but that is not the case. In the example when I hit require.ensurein my file webpack will automatically create a second bundle and load it on-demand. Any code executed inside of that split-point will be bundled together in a separate file.

require.ensure是您可以在应用程序中创建“分割点”的一种方式。同样,您可能认为需要通过配置来执行此操作,但事实并非如此。在示例中,当我输入require.ensure文件时,webpack 将自动创建第二个包并按需加载。在该分割点内执行的任何代码都将捆绑在一个单独的文件中。

require.ensure(['jquery'], function() {
    var $ = require('jquery');
    /* ... */
});

Require([])

要求([])

You can also achieve the same thing with the AMD-version of require(), the one that takes an array of dependencies. This will also create the same split point:

您还可以使用 AMD 版本实现相同的功能,该版本require()采用一系列依赖项。这也将创建相同的分割点:

require(['jquery'], function($) {
    /* ... */
});

Shared Bundles

共享包

In your example above you use entryto create a vendorbundle which has jQuery. You don't need to manually specify these dependency bundles. Instead, using the split points above you webpack will generate this automatically.

在上面的示例中,您用于entry创建一个vendor包含 jQuery的包。您不需要手动指定这些依赖包。相反,使用 webpack 上方的分割点会自动生成它。

Use entryonly for separate <script>tags you want in your pages.

entry仅用于<script>您希望在页面中使用的单独标签

Now that you've done all of that you can use the CommonsChunkPluginto additional optimize your chunks, but again most of the magic is done for you and outside of specifying which dependencies should be shared you won't need to do anything else. webpackwill pull in the shared chunks automatically without the need for additional <script>tags or entryconfiguration.

现在您已经完成了所有这些,您可以使用CommonsChunkPlugin来额外优化您的块,但是大部分的魔法已经为您完成了,除了指定应该共享哪些依赖项之外,您不需要做任何其他事情。webpack将自动拉入共享块,无需额外的<script>标签或entry配置。

Conclusion

结论

The scenario you describe (multiple <script>tags) may not actually be what you want. With webpack all of the dependencies and bundles can be managed automatically starting with only a single <script>tag. Having gone through several iterations of re-factoring from require.js to webpack, I've found that's usually the simplest and best way to manage your dependencies.

您描述的场景(多个<script>标签)实际上可能不是您想要的。使用 webpack,所有依赖项和包都可以从一个<script>标签开始自动管理。经历了从 require.js 到 webpack 的多次重构迭代后,我发现这通常是管理依赖项的最简单和最好的方法。

All the best!

一切顺利!

回答by mpen

Here's the solution I came up with.

这是我想出的解决方案。

First, export these two functions to window.*-- you'll want them in the browser.

首先,将这两个函数导出到window.*-- 您需要在浏览器中使用它们。

export function requireAsync(module) {
    return new Promise((resolve, reject) => require(`bundle!./pages/${module}`)(resolve));
}

export function runAsync(moduleName, data={}) {
    return requireAsync(moduleName).then(module => {
        if(module.__esModule) {
            // if it's an es6 module, then the default function should be exported as module.default
            if(_.isFunction(module.default)) {
                return module.default(data);
            }
        } else if(_.isFunction(module)) {
            // if it's not an es6 module, then the module itself should be the function
            return module(data);
        }
    })
}

Then, when you want to include one of your scripts on a page, just add this to your HTML:

然后,当您想在页面上包含其中一个脚本时,只需将其添加到您的 HTML 中:

<script>requireAsync('script_name.js')</script>

Now everything in the pages/directory will be pre-compiled into a separate chunk that can be asynchronously loaded at run time, only when needed.

现在pages/目录中的所有内容都将被预编译成一个单独的块,可以在运行时异步加载,仅在需要时。

Furthermore, using the functions above, you now have a convenient way of passing server-side data into your client-side scripts:

此外,使用上述函数,您现在可以方便地将服务器端数据传递到客户端脚本中:

<script>runAsync('script_that_needs_data', {my:'data',wow:'much excite'})</script>

And now you can access it:

现在您可以访问它:

// script_that_needs_data.js
export default function({my,wow}) {
    console.log(my,wow);
}

回答by Ted Fitzpatrick

I've recently travelled this same road, I'm working on optimizing my Webpack output since I think bundles are too big, HTTP2 can load js files in parallel and caching will be better with separate files, I was getting some dependencies duplicated in bundles, etc. While I got a solution working with Webpack 4 SplitChunksPlugin configuration, I'm currently moving towards using mostly Webpack's dynamic import() syntax since just that syntax will cause Webpack to automatically bundle dynamically imported bundles in their own file which I can name via a "magic comment":

我最近走过同样的路,我正在优化我的 Webpack 输出,因为我认为包太大了,HTTP2 可以并行加载 js 文件并且缓存会更好地使用单独的文件,我在包中复制了一些依赖项等虽然我得到了一个使用 Webpack 4 SplitChunksPlugin 配置的解决方案,但我目前主要使用 Webpack 的动态 import() 语法,因为正是这种语法会导致 Webpack 自动将动态导入的包捆绑在他们自己的文件中,我可以命名通过“神奇评论”:

import(/* webpackChunkName: "mymodule" */ "mymodule"); // I added an resolve.alias.mymodule entry in Webpack.config

回答by Grzegorz T.

Some time ago I made such a small "Proof of concept" to check how importlazy will work in IE11. I have to admit it works :) After clicking the button, the code responsible for changing the background color of the page is loaded - full example

前段时间我做了这么一个小的“概念证明”来检查 importlazy 在 IE11 中的工作方式。我不得不承认它有效:) 单击按钮后,负责更改页面背景颜色的代码被加载 -完整示例

Js:

JS:

// polyfils for IE11
import 'core-js/modules/es.array.iterator';

const button = document.getElementById('background');

button.addEventListener('click', async (event) => {
  event.preventDefault();
  try {
    const background = await import(/* webpackChunkName: "background" */ `./${button.dataset.module}.js`);
    background.default();
  } catch (error) {
    console.log(error);
  }
})

Html:

网址:

<button id="background" class="button-primary" data-module="background">change the background</button>