使用Webpack创建单独的SPA包 [英] Create individual SPA bundles with Webpack

查看:107
本文介绍了使用Webpack创建单独的SPA包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我的用户导航我的SPA时,如何使用Webpack创建可能会或可能不会立即加载的独立SPA套件?

How do I use Webpack to create independent SPA bundles that may or may not be loaded on the fly as my user navigates my SPA?

我有一个联系人模块和任务模块。两者都有两个依赖关系。我希望WebPack为每个需要时(以及如果需要)加载的包创建包。

I have a contacts module, and a tasks module. Both have two dependencies. I want WebPack to create bundles for each that are loaded when (and if) needed.

代码如下。问题似乎是这些条目中的每一个都被视为应用程序入口点,因此将webpack引导代码插入其中。

The code is below. The problem appears to be that each of these entries are being seen as application entry points, and so are getting webpack bootstrap code inserted therein.

我见过各种示例 CommonsChunkPlugin 但是我找不到它的API参考/文档,而且我可以推测,这不是我想要的。

I've seen various examples with CommonsChunkPlugin but I can't find an API reference / documentation for it, and from what I can surmise, that's not what I want anyway.

编辑 - 找到这些文档此处,并在我的编辑中添加了下面的插件尝试。

Edit - found those docs here, and added an attempt with that plugin below in my edit.

当前配置

module.exports = {
    entry: {
        contacts: './contacts',
        tasks: './tasks'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    }
};

Contacts.js

define(['./ca', './cb'], function(ca, cb){
    var name = 'Contacts';
    alert(ca + ' ' + cb);
});

Tasks.js

define(['./ta', './tb'], function(ta, tb){
    var name = 'TASKS Main';
    alert(ta + ' ' + tb);
});

tasks-bundle.js

/******/ (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] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = 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;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(3), __webpack_require__(4)], __WEBPACK_AMD_DEFINE_RESULT__ = function(ta, tb){
        var name = 'TASKS Main';
        alert(ta + ' ' + tb);
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

/***/ },
/* 1 */,
/* 2 */,
/* 3 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
        var name = 'TASKS - A';
        alert('ta');
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
        var name = 'TASKS - B';
        alert('tb');
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

/***/ }
/******/ ]);






编辑

这是我的CommonsChunkPlugin尝试号码2。我创建了一个虚拟的app.js

Here's my attempt number 2 with the CommonsChunkPlugin. I created a dummy app.js

app.js

var module = window.location.hash.split('/')[0];
alert(module);

然后我将所有联系人和任务文件移到组件文件夹下,但不管怎样。我的新配置:

Then I moved all of my contacts and tasks files under a components folder, but otherwise left them alone. My new configuration:

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/contacts',
            filename: 'contacts-component-bundle.js'
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/tasks',
            filename: 'tasks-component-bundle.js'
        })
    ]
};

很奇怪,现在 app-bundle.js 似乎没有任何Webpack引导代码

Bizarely, now app-bundle.js appears to not have any Webpack bootstrap code

webpackJsonp([0,1,2],[
/* 0 */
/***/ function(module, exports) {

    var module = window.location.hash.split('/')[0];
    alert(module);

/***/ }
]);

contacts-components-bundle.js 现在只有这个

webpackJsonp([1,2],[]);

tasks-components-bundle.js 似乎拥有我的全部webpack bootstrap代码

and tasks-components-bundle.js appears to have all of my webpack bootstrap code

/******/ (function(modules) { // webpackBootstrap
/******/    // install a JSONP callback for chunk loading
/******/    var parentJsonpFunction = window["webpackJsonp"];
/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
/******/        // add "moreModules" to the modules object,
/******/        // then flag all "chunkIds" as loaded and fire callback
/******/        var moduleId, chunkId, i = 0, callbacks = [];
/******/        for(;i < chunkIds.length; i++) {
/******/            chunkId = chunkIds[i];
/******/            if(installedChunks[chunkId])
/******/                callbacks.push.apply(callbacks, installedChunks[chunkId]);
/******/            installedChunks[chunkId] = 0;
/******/        }
/******/        for(moduleId in moreModules) {
/******/            modules[moduleId] = moreModules[moduleId];
/******/        }
/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);
/******/        while(callbacks.length)
/******/            callbacks.shift().call(null, __webpack_require__);
/******/        if(moreModules[0]) {
/******/            installedModules[0] = 0;
/******/            return __webpack_require__(0);
/******/        }
/******/    };

/******/    // The module cache
/******/    var installedModules = {};

/******/    // object to store loaded and loading chunks
/******/    // "0" means "already loaded"
/******/    // Array means "loading", array contains callbacks
/******/    var installedChunks = {
/******/        2:0,
/******/        1:0
/******/    };

/******/    // 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] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }

/******/    // This file contains only the entry chunk.
/******/    // The chunk loading function for additional chunks
/******/    __webpack_require__.e = function requireEnsure(chunkId, callback) {
/******/        // "0" is the signal for "already loaded"
/******/        if(installedChunks[chunkId] === 0)
/******/            return callback.call(null, __webpack_require__);

/******/        // an array means "currently loading".
/******/        if(installedChunks[chunkId] !== undefined) {
/******/            installedChunks[chunkId].push(callback);
/******/        } else {
/******/            // start chunk loading
/******/            installedChunks[chunkId] = [callback];
/******/            var head = document.getElementsByTagName('head')[0];
/******/            var script = document.createElement('script');
/******/            script.type = 'text/javascript';
/******/            script.charset = 'utf-8';
/******/            script.async = true;

/******/            script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"app","1":"./components/contacts"}[chunkId]||chunkId) + "-bundle.js";
/******/            head.appendChild(script);
/******/        }
/******/    };

/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/ })
/************************************************************************/
/******/ ([]);

同样,我只是试图使用Webpack来获取SPA概念证明并运行,使用某种根app.js入口点,然后按需加载一些任意数量的模块/组件。这对于requirejs来说非常简单,所以我必须想象我在这里缺少一些关键,特别是我所见过的所有文章都谈到了Webpack对于SPA的好处。

Again, I'm just trying to use Webpack to get an SPA proof of concept up and running, with some sort of root app.js entry point, and then some arbitrary number of modules / components which are loaded on demand. This is trivially easy with requirejs, so I have to imagine I'm missing something key here, especially with all the articles I've seen talking about how great Webpack is for SPAs.

编辑2

根据以下bebraw的回答我尝试了以下内容:

Per bebraw's answer below I tried the following:

app.js

var mod = window.location.hash.split('/')[0];
alert(mod);

require.ensure([], function() {
    require('./components/' + mod).show();
});

webpack.config.js

webpack.config.js

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    }
};

然后在我的构建文件夹中,我留下app-bundle.js,其中包含我的所有引导程序代码和我的app.js代码,然后1.1-bundle.js,其中包含所有我的任务和联系人代码。

And then in my build folder I'm left with app-bundle.js which has all my bootstrap code, and my app.js code, and then 1.1-bundle.js which has all of my tasks and contacts code.

我' ve 试了这个

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/contacts',
            filename: 'contacts-component-bundle.js',
            children: true
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/tasks',
            filename: 'tasks-component-bundle.js',
            children: true
        })
    ]
};

产生的结果与上述相同,但现在有任务 - component-bundle.js和contacts-component-bundle.js,它们都只有 一些webpack引导程序代码;任务和联系人代码都仍在1.1-bundle中。

Which yields the same as the above, but now also has tasks-component-bundle.js and contacts-component-bundle.js, both of which have only some webpack bootstrap code; the tasks and contacts code are all still in 1.1-bundle.

同样,我只是希望能够以某种方式告诉Webpack将各个模块捆绑在一起以及它们在需要时进行后续延迟异步加载的依赖关系。

Again, I simply want to be able to tell Webpack, one way or another, to bundle up individual modules and their dependencies for subsequent lazy, async loading when needed.

最终的答案由Tobias-Webpack创建者提供 - 我将在此为后人提供。

The final answer was given by Tobias—Webpack creator—below, which I'll put here for posterity.


真正的动态是不可能的。 webpack(在require.js中的构造)在执行之前编译您的应用程序,并且无权访问运行时信息。动态需要webpack潜入每个可能的文件夹,只要你的动态表达不包含...你甚至应该能够配置它使用mod +'/'+ mod与ContextReplacementPlugin和一点RegExp魔术(使用后面的引用) RegExp)。默认情况下,它将包含太多模块。

Truly dynamic is not possible. webpack (in constract to require.js) compiles your app before executing it, and don't have access to runtime information. Dynamic requires in webpack dive in every possible folder as long your dynamic expression don't contain ... You should even be able to configure it to use mod + '/' + mod with the ContextReplacementPlugin and a little RegExp magic (use backreferences in the RegExp). By default it would include too many modules.


推荐答案

webpack为每个异步要求创建一个分割点声明( require.ensure 或AMD require([]))。所以你需要为你的应用程序的每个延迟加载部分写一个 require([])

webpack creates a split point per async require statement (require.ensure or AMD require([])). So you need to write a require([]) per lazy-loaded part of your app.

你的SPA只有一个入口点:(客户端)路由器。我们称之为 app.js 。这些页面是按需加载的,不是入口点。

Your SPA only has a single entry point: the (client-side) router. Let's call it app.js. The pages are loaded on demand and ain't entry points.

webpack.config.js:

webpack.config.js:

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    }
}

app.js:

var mod = window.location.hash.split('/')[0].toLowerCase();
alert(mod);

switch(mod) {
    case "contacts":
        require(["./pages/contacts"], function(page) {
            // do something with "page"
        });
        break;
    case "tasks":
        require(["./pages/tasks"], function(page) {
            // do something with "page"
        });
        break;
}






替代方案:使用上下文。


Alternative: Using a "context".

使用动态依赖时i。 e require(./ pages /+ mod)你不能为每个文件写一个分割点。对于这种情况,有一个加载器在 require.ensure 块中包装文件:

When using a dynamic dependency i. e require("./pages/" + mod) you can't write a split point per file. For this case there is a loader that wrapps a file in a require.ensure block:

app.js

var mod = window.location.hash.split('/')[0].toLowerCase();
alert(mod);

require("bundle!./pages/" + mod)(function(page) {
    // do something with "page"
});

这是特定于webpack的。不要忘记 npm install bundle-loader --save 。检查正确的外壳,它区分大小写。

This is webpack-specific. Don't forget to npm install bundle-loader --save. Check the correct casing, it's case-sensitive.

这篇关于使用Webpack创建单独的SPA包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆