javascript - 使用 Webpack 创建单独的 SPA 包

标签 javascript webpack

我如何使用 Webpack 创建独立的 SPA 包,当我的用户浏览我的 SPA 时,这些包可能会或可能不会动态加载?

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

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

我看过各种关于 CommonsChunkPlugin 的例子,但我找不到它的 API 引用/文档,据我推测,这不是我想要的。

编辑 - 找到那些文档 here ,并在我的编辑中添加了使用该插件的尝试。


当前配置

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

app.js

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

然后我将我所有的联系人和任务文件都移到一个组件文件夹下,但除此之外别管它们。我的新配置:

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 引导代码

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 代码

/******/ (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 有多棒。


编辑 2

根据下面 bebraw 的回答,我尝试了以下操作:

应用程序.js

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

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

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,其中包含我所有的 Bootstrap 代码和我的 app.js 代码,然后是 1.1-bundle.js,其中包含所有我的任务和联系人代码。

试过这个

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
        })
    ]
};

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

同样,我只是希望能够以一种或另一种方式告诉 Webpack 捆绑各个模块及其依赖项,以便在需要时进行后续的延迟异步加载。

最终答案由 Tobias(Webpack 的创建者)在下面给出,我将其放在这里供后人引用。

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 语句(require.ensure 或 AMD require([]))创建一个分割点。因此,您需要为应用的每个延迟加载部分编写一个 require([])

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

webpack.config.js:

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

应用程序.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;
}

备选方案:使用“上下文”。

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

应用程序.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。检查正确的大小写,区分大小写。

关于javascript - 使用 Webpack 创建单独的 SPA 包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31617955/

相关文章:

JavaScript:使用来自单独 HTML 文档的输入写入当前 HTML 文档?

javascript - webpack 4.1.1 -> configuration.module 有一个未知的属性 'loaders' 。

node.js - 什么是 node-libs-browser 以及为什么在我安装 babel-loader 时安装它

node.js - 无服务器错误代码 10021 : Uncaught ReferenceError: require is not defined

javascript - jQuery 插件反馈

javascript - 如何在页面加载后 10 秒显示弹出窗口?

javascript - 使用javascript或jquery使用数组中的多个值过滤数组对象

javascript - 在 ajax 请求中发送带有 JSON 数据的文件

javascript - 如何使用 Symfony Webpack Encore 编译多个 javascript 文件?

node.js - Redis 的 WebPack 问题