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 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 个解决方案
#1
12
The solution to this problem is two-fold:
这个问题的解决方案有两个方面:
- First you need to understand how code-splitting works in webpack
- Secondly, you need to use something like the
CommonsChunkPlugin
to generate that shared bundle.
首先,您需要了解代码分割在webpack中的工作原理
其次,您需要使用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.ensure
is 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.ensure
in 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将自动创建第二个bundle并按需加载它。在该分裂点内执行的任何代码将捆绑在一个单独的文件中。
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 entry
to create a vendor
bundle 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来创建具有jQuery的供应商包。您无需手动指定这些依赖关系包。相反,使用上面的分割点,webpack将自动生成它。
Use entry
only for separate <script>
tags you want in your pages.
仅将条目用于页面中所需的单独
Now that you've done all of that you can use the CommonsChunkPlugin
to 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. webpack
will pull in the shared chunks automatically without the need for additional <script>
tags or entry
configuration.
现在你已经完成了所有这些,你可以使用CommonsChunkPlugin来进一步优化你的块,但是大部分魔法都是为你完成的,除了指定应该共享哪些依赖项之外你不需要做任何其他事情。 webpack将自动拉入共享块,而无需其他
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.
您描述的场景(多个
All the best!
祝一切顺利!
#2
1
Here's the solution I came up with.
这是我提出的解决方案。
First, export these two functions to window.*
-- you'll want them in the browser.
首先,将这两个函数导出到窗口。* - 您将需要它们在浏览器中。
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);
}
#1
12
The solution to this problem is two-fold:
这个问题的解决方案有两个方面:
- First you need to understand how code-splitting works in webpack
- Secondly, you need to use something like the
CommonsChunkPlugin
to generate that shared bundle.
首先,您需要了解代码分割在webpack中的工作原理
其次,您需要使用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.ensure
is 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.ensure
in 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将自动创建第二个bundle并按需加载它。在该分裂点内执行的任何代码将捆绑在一个单独的文件中。
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 entry
to create a vendor
bundle 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来创建具有jQuery的供应商包。您无需手动指定这些依赖关系包。相反,使用上面的分割点,webpack将自动生成它。
Use entry
only for separate <script>
tags you want in your pages.
仅将条目用于页面中所需的单独
Now that you've done all of that you can use the CommonsChunkPlugin
to 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. webpack
will pull in the shared chunks automatically without the need for additional <script>
tags or entry
configuration.
现在你已经完成了所有这些,你可以使用CommonsChunkPlugin来进一步优化你的块,但是大部分魔法都是为你完成的,除了指定应该共享哪些依赖项之外你不需要做任何其他事情。 webpack将自动拉入共享块,而无需其他
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.
您描述的场景(多个
All the best!
祝一切顺利!
#2
1
Here's the solution I came up with.
这是我提出的解决方案。
First, export these two functions to window.*
-- you'll want them in the browser.
首先,将这两个函数导出到窗口。* - 您将需要它们在浏览器中。
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);
}