首页 文章

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

提问于
浏览
14

我开始使用Webpack开发包含多个页面和不同页面类型的常用网站 . 我已经习惯了RequireJs脚本加载器,它根据需要加载所有依赖项 . 页面加载时只下载一小段javascript .

我想要实现的是:

  • 一个小的初始javascript文件,它异步加载依赖项

  • 每种页面类型都有自己的javascript,也可能有依赖关系 .

  • 通用模块,供应商脚本应捆绑在通用脚本中

我已经尝试了很多配置来实现这一目标,但没有成功 .

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')
]

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

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

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

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

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

Webpack的输出看起来很有希望,但我不能让供应商捆绑异步加载 . 其他依赖项(我自己的模块和domReady)在自动生成的块中加载,但不加载jquery .

我可以找到很多几乎可以做到的例子,但不是异步加载供应商的重要部分 .

webpack build的输出:

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]

2 回答

  • 12

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

    代码拆分

    在开始使用webpack之前,您需要忘记依赖于配置 . Require.js就是配置文件 . 这种心态使我很难转换到webpack,后者在node.js中的CommonJS之后更加紧密地建模,它依赖于没有配置 .

    考虑到这一点,请考虑以下内容 . 如果你有一个应用程序,并且你希望它异步加载javascript的其他部分,你需要使用以下范例之一 .

    Require.ensure

    require.ensure 是您可以在应用程序中创建"split point"的一种方法 . 同样,您可能认为您需要使用配置执行此操作,但事实并非如此 . 在我的文件中点击 require.ensure 的示例中,webpack将自动创建第二个包并按需加载它 . 在该分裂点内执行的任何代码将捆绑在一个单独的文件中 .

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

    Require([])

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

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

    共享包

    在上面的示例中,您使用 entry 创建一个包含jQuery的 vendor 包 . 您无需手动指定这些依赖关系包 . 相反,使用上面的分割点,webpack将自动生成 .

    仅将 entry 用于页面中所需的单独 <script> 标记 .

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

    结论

    您描述的场景(多个 <script> 标签)可能实际上并不是您想要的 . 使用webpack,可以从只有一个 <script> 标记开始自动管理所有依赖项和包 . 经过几次从require.js重新分解到webpack的迭代,我通常是管理依赖关系的最简单,最好的方法 .

    祝一切顺利!

  • 1

    这是我提出的解决方案 .

    首先,将这两个函数导出到 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);
            }
        })
    }
    

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

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

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

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

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

    现在您可以访问它:

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

相关问题