Is it possible to let webpacks System.import use a

2019-04-10 23:48发布

So I've just updated to webpack 2 and have my first working setup where webpack automatically creates chunks by looking at System.import calls. Pretty sweet!

However, I load the initial chunk with an ajax call so that I can show the progress while loading

So my question is, can I overwrite or change the function of System.import somehow so that it will use an ajax request that I can listen to for events, instead of loading the chunk with a <script> tag?

1条回答
该账号已被封号
2楼-- · 2019-04-11 00:22

No, unfortunately not. webpack 2 translates System.import() to ordinary require.ensure() calls which just uses the <script> tag. Even the official WHATWG Loader Spec does not provide an API for this kind of event. I've created an issue for this question.

Regarding webpack: There is a way to implement your own require.ensure(). However, since chunk loading is an integral part of webpack, this requires to dive a little deeper. I'm not sure how important this is for you, but you might be interested how things work inside webpack, so let's take a look:

In webpack, all internal features are implemented as plugins. This way, webpack is able to support a lot of different features and environments. So, if you're interested how things are implemented in webpack, it's always a good idea to a) take a look at WebpackOptionsApply or b) search for a specific string/code snippet.

Chunk loading depends heavily on the given target, because you need different implementations for each environment. Webpack allows you to define custom targets. When you pass in a function instead of a string, webpack invokes the function with a compiler instance. There you can apply all the required plugins. Since our custom target is almost like the web target, we just copy all the stuff from the web target:

// webpack.config.js

const NodeSourcePlugin = require("webpack/lib/node/NodeSourcePlugin");
const FunctionModulePlugin = require("webpack/lib/FunctionModulePlugin");
const LoaderTargetPlugin = require("webpack/lib/LoaderTargetPlugin");
const JsonpChunkTemplatePlugin = require("webpack/lib/JsonpChunkTemplatePlugin");
const JsonpHotUpdateChunkTemplatePlugin = require("webpack/lib/JsonpHotUpdateChunkTemplatePlugin");

function customTarget(compiler) {
    compiler.apply(
        new JsonpTemplatePlugin(compiler.options.output),
        new FunctionModulePlugin(compiler.options.output),
        new NodeSourcePlugin(compiler.options.node),
        new LoaderTargetPlugin("web")
    );
}

module.exports = {
    entry:  require.resolve("./app/main.js"),
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js"
    },
    target: customTarget
};

If you take a look at each plugin, you will recognize that the JsonpTemplatePlugin is responsible for loading chunks. So let's replace that with out own implementation. We call it the XHRTemplatePlugin:

function customTarget(compiler) {
    compiler.apply(
        new XHRTemplatePlugin(compiler.options.output),
        new FunctionModulePlugin(compiler.options.output),
        new NodeSourcePlugin(compiler.options.node),
        new LoaderTargetPlugin("my-custom-target")
    );
}

Our XHRTemplatePlugin is responsible for providing the code in the main chunk, in each child chunk and for hot updates:

function XHRTemplatePlugin() {}

XHRTemplatePlugin.prototype.apply = function (compiler) {
    compiler.plugin("this-compilation", function(compilation) {
        compilation.mainTemplate.apply(new XHRMainTemplatePlugin());
        compilation.chunkTemplate.apply(new XHRChunkTemplatePlugin());
        compilation.hotUpdateChunkTemplate.apply(new XHRHotUpdateChunkTemplatePlugin());
    });
};

Maybe, you can also re-use the JsonpChunkTemplatePlugin and JsonpHotUpdateChunkTemplatePlugin plugin, but this depends on your use-case/implementation.

Your XHRMainTemplatePlugin now may look like this:

function XHRMainTemplatePlugin() {}

XHRMainTemplatePlugin.prototype.apply = function (mainTemplate) {
    mainTemplate.plugin("require-ensure", function(_, chunk, hash) {
        return this.asString([
            // Add your custom implementation here
            "fetch()"
        ]);
    });
};

I won't go any further here because I think this answer is already long enough. But I recommend to create a real small example project and to check the output created by webpack. The internal webpack plugins may look a little bit scary on first sight, but most of them are real short and do just one thing. You can also get some inspiration from them.

查看更多
登录 后发表回答