如何使用 Webpack 代码拆分处理部署?

Posted

技术标签:

【中文标题】如何使用 Webpack 代码拆分处理部署?【英文标题】:How to handle deploys with Webpack code splitting? 【发布时间】:2018-01-11 14:24:57 【问题描述】:

这是我在野外使用 Webpack 代码拆分时遇到的一个意外问题:想象一下这种情况:

    用户加载一个带有 Webpack 代码拆分的 React 应用程序并加载了一些捆绑块 发生部署,用户可能从服务器接收到的任何未来块的内容都会更新(注意:在部署期间,以前的块会在服务器上被删除) 用户点击一个链接并加载一个新的路由,触发更多的捆绑块加载。除了这些新块与用户浏览器已经加载的块不兼容并且应用程序由于运行时错误而中断

如何防止这种情况发生?

一种可能的解决方案是维护多个版本化的块集,但我想知道大型应用程序是否使用更简单的解决方案。

如果使用preload-webpack-plugin,则可以预取所有块,但它们只会缓存一小段时间(在 Chrome 中为 5 分钟)。

【问题讨论】:

我和你有同样的考虑。你找到解决方案了吗?谢谢! @JordanEnev 我们最终维护了多个版本的部署,这样旧的块就不会失效。 谢谢!如果我发现更简单的东西,我会写。 对此解决方案感兴趣。是否有可能让 webpack 加载器代码响应某种警报,要求用户重新加载? 只是简单的解决方案,我们可以在部署后通过 websocket 使用推送,并显示页面应该重新加载的通知window.navigation.reload() 或使用长轮询并从某个地方检查最新版本,例如,从最新index.html 【参考方案1】:

作为 Max Stoiber writes on spectrum.chat:

ServiceWorkers 在进行代码拆分时非常方便!

我们使用@nekr 出色的离线插件在本地缓存所有当前包,因此无论服务器是否更新文件,ServiceWorker 将始终从本地缓存中提供文件。每小时它会检查服务器是否有更新,如果有可用更新,则从远程服务器下载所有新的捆绑包并在本地缓存它们。下次用户重新启动应用程序时,将使用新版本的应用程序! ?

https://github.com/NekR/offline-plugin

此解决方案意味着您的应用程序会预先下载所有块,这违背了在带宽方面进行代码拆分的目的,但至少您仍然保留了仅解析加载应用程序所需的块的好处,这对我来说在慢速设备上很重要。此外,浏览器刷新/缓存现在涉及 Service Worker 生命周期(请参阅 https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle 的“等待”)。

【讨论】:

我不同意这是一个解决方案。最好说“这是解决问题的方法”。我不明白为什么开发人员要隐藏这些问题。 是的,我同意,但我还没有找到更好的解决方案。而且添加一个服务器工作者会很复杂。 我不会说它是一种解决方法。问题是由于需要为用户加载更少的内容来满足他们的需求 - 通过这样做,我们需要确保更新不会破坏未使用新版本的用户。另一种方法可能是让服务人员以某种方式“监听”应用程序更改并相应地更新动态代码,但这可能令人沮丧且设置复杂【参考方案2】:

如果块文件名被散列,旧路由不会链接到旧散列块(大概仍然可用)并加载一切正常吗?

【讨论】:

但是在这种情况下,部署将删除旧的散列块,因此它们将不再可用。 您可以通过将旧块托管在 s3 中的 cdn / 上来保留旧块 如果它们被散列并上传到同一个地方,是的,这是一种完全有效的方法。只要这些文件是真正不可变的 :)【参考方案3】:

这个问题说得非常好。

我会补充说,“删除”可能不是正确的名称,具体取决于设置。

我对这个问题的最初反应是这是一个缓存问题。旧的块文件被拾取而不是新的。至少在我的情况下,它接近正在发生的事情,我有以下几点:

index.js

const Page1 = lazy(() => import('./page/Page1'));
const Page2 = lazy(() => import('./page/Page2'));

const main = () => 
  
    '/page1': Page1,
    '/page2': Page2,
  [window.location.href](); /* Some Render Router Implementation */
;
    V1 部署在 (https://my-domain/distribution_folder/*) 用户将加载 V1 index.js V2 部署在 (https://my-domain/distribution_folder/*) 用户(未刷新)将使用其缓存的 V1 index.js 文件动态加载分块路由。 请求将被发送到 (https://my-domain/distribution_folder/page_name.chunk_hash.js) 会发生块错误,因为该唯一块将不再存在。

这很有趣,因为正在使用的提供商正在将流量迁移到新版本。所以我认为这将是它的结束,但我没有意识到任何用户仍然可以使用以前部署的版本 - 他们怎么知道?他们已经在使用该应用程序。浏览器已经下载了应用程序 (index.js)。

解决方案实际上取决于您在哪里动态导入这些块。在上面的例子中,因为它们是页面路由,当我们找不到块时,当用户请求不同的页面时,我们可以进行硬刷新。但是,这假设您的 Cache-Control 标头设置正确。例如:

index.js -> Cache-Control: no-store page/page_name.chunk_hash.js -> Cache-Control: public,max-age=31536000,immutable

我们可以使这些块不可变,因为有时它们不会在版本之间更改,如果它们不更改,为什么不使用缓存版本。但是,index.js 不能存储在缓存中,因为这是动态加载内容的“路由器”,并且它总是会改变。

优点

不再出现块加载错误 我们不需要在首页加载时加载所有内容 没有 Service Worker,复杂性更低

缺点

这种方法会强制用户刷新

相关问题

Webpack Code Splitting 'Loading chunk failed' error wrong file path Angular 2 Error: Loading chunk failed many times Webpack - Loading chunk 0 failed ChunkLoadError: Loading chunk XY failed. - Randomly getting fatal on PRODUCTION

【讨论】:

【参考方案4】:

https://webpack.js.org/guides/caching/#output-filenames

确保浏览器选择更改文件的一种简单方法是使用 output.filename 替换。 [hash] 替换可用于在文件名中包含特定于构建的哈希,但是最好使用在文件名中包含特定于块的哈希的 [chunkhash] 替换。

【讨论】:

但在这种情况下,部署将删除旧的散列块,因此它们将不再可用。

以上是关于如何使用 Webpack 代码拆分处理部署?的主要内容,如果未能解决你的问题,请参考以下文章

使用 webpack 代码拆分,如何加载块和 HTML 布局?

如何使用 Webpack 拆分应用程序和供应商代码

如何通过 Angular、Karma 和 Webpack 的单独文件拆分代码覆盖率?

如何在 Angular 2 应用程序上使用 webpack 进行代码拆分?

如何让 webpack-dev-server 停止使用 React 延迟/Suspense 代码拆分在内容更改时下载不正确的块?

如何在 webpack 中使用 vuejs 禁用将块拆分为不同的文件?