如何在没有 Sapper 的情况下使用 Svelte 进行代码拆分
Posted
技术标签:
【中文标题】如何在没有 Sapper 的情况下使用 Svelte 进行代码拆分【英文标题】:How to do code splitting using Svelte without Sapper 【发布时间】:2020-05-18 08:01:59 【问题描述】:如何使用 Svelte 进行代码拆分?
(我看到你可以使用 Sapper 来做到这一点,但我不想依赖节点后端)
【问题讨论】:
【参考方案1】:代码拆分实际上是动态导入的一个花哨名称。以下是使用 Rollup 的方法(在此过程中您还将获得惊人的 tree-shaking!)。
动态导入提醒:
// "normal" static ES import
//
// - statically analytisable
// - must be called at top level
// - will be greedily resolved (and most often inlined) by your bundler
//
import Foo from './Foo.svelte'
// dynamic import
//
// - called like a function
// - returns a promise
// - default export is accessible on key `default` of the result
// - will be bundled into its own chunk by your bundler (hence code splitting)
//
import('./Foo.svelte').then(module =>
const cmp = module.default
console.log(module.myNamedExport)
)
请注意,动态导入是原生 ES 功能,就像正常导入一样。这意味着它们受到非过时浏览器的原生支持。
Rollup 支持“从动态导入中拆分代码”已有一段时间了(请参阅 docs)。
因此,如果您希望在项目中进行代码拆分,主要是配置 Rollup 以便将动态导入分块(另一种选择是解析并内联它们,这不会导致代码拆分)。
以下是执行此操作的步骤,从 Svelte 的 official template 开始。
-
将
output.format
更改为'es'
将output.file
更改为output.dir
(例如'public/build'
)
更改index.html
中的<script>
标签指向新的入口点/build/main.js
,并使用type="module"
使用动态导入编写一些代码
添加对旧版浏览器的支持
汇总配置:output.format
和 output.dir
并非汇总中可用的所有输出格式都支持动态导入。 Svelte 模板的默认设置,iife
没有,所以我们需要更改。
output.format: 'es'
不会重写代码中的 import
语句。这意味着我们将依赖浏览器的本地模块加载器。目前所有浏览器都支持 ES import
或动态 import(...)
,旧版浏览器可以进行 polyfill。
另一个选项可能是,例如,output.format: 'system'
,用于SystemJS,但这需要我们除了提供我们的代码之外还提供第三方模块加载器。
我们还需要将output.file
更改为output.dir
,因为代码拆分不会产生单个bundle.js
文件,而是多个块。 (而且你不能将单独的文件写入单个文件,显然......)
所以,现在是我们汇总配置的相关部分:
input: 'src/main.js', // not changed
output:
format: 'es',
dir: 'public/build/',
,
如果此时您运行yarn build
(或npm run build
),您会看到您的应用程序现在被拆分为`/public/build/ 目录中的多个.js
文件。
index.html
我们现在需要更改index.html
中的<script>
标记(位于`public/index.html,在Svelte 模板中)以使用它。
<script defer type="module" src="/build/main.js"></script>
首先,我们需要将src
从bundle.js
(这是我们的旧output.file
)更改为我们应用程序的新入口点。由于我们在 Rollup 配置中的入口点 (input
) 是 src/main.js
,因此我们应用的主要入口点将写入 main.js
(可通过 Rollup 的 entryFileNames
选项进行配置)。
由于我们的代码现在充满了 ES import
语句(因为我们使用的是 output.format='esm'
),我们还需要通过添加type="module"
属性到我们的脚本标签。
现代浏览器就是这样,您现在拥有完整的代码拆分支持!
实际拆分你的应用程序
代码拆分支持不足以获得实际的代码拆分。它只是让它成为可能。您仍然需要将动态块与应用程序的其余部分(主要)分开。
您可以通过在代码中编写动态导入来做到这一点。例如:
import('./Foo.svelte')
.then(module => module.default)
.then(Foo => /* do something with Foo */ )
.catch(err => console.error(err))
这将导致 Rollup 创建一个 Foo-[hash].js
块(可使用 chunkFileNames
选项配置),以及可能与其他组件共享的 Foo.svelte
依赖项的另一个块。
在浏览器中,只有在代码中遇到import('./Foo.svelte')
语句时才会加载此文件(延迟加载)。
(请注意,在瀑布中,Foo
和 Cmp
- 一个常见的 dep - 在页面加载后很长时间内被加载,由垂直红色条指示。)
旧版浏览器
Edge(最近成为 Chrome 之前)不支持动态导入。正常的 ES 导入,是的,但是动态的 import(...)
没有。这通常就是为什么你必须为过时的浏览器添加一些 polyfill。
一种解决方案,例如 rollup-starter-code-splitting 示例,是在浏览器中使用第三方模块加载器(例如 SytemJS)。
目前可用的另一个可能更简单的解决方案是使用dimport
包。它根据主机浏览器的需要填充了对 ES 导入和动态导入的支持。
为了使用它,我们将index.html
中的<script>
标签替换为以下内容:
<script defer type="module" src="https://unpkg.com/dimport?module"
data-main="/build/main.js"></script>
<script defer type="nomodule" src="https://unpkg.com/dimport/nomodule"
data-main="/build/main.js"></script>
然后瞧。完整的代码拆分。 (比你想象的要简单,不是吗?)
完整示例
这是一个 complete example 实现此答案中涵盖的所有不同位。你可能对this commit特别感兴趣。
注意!请注意,该示例位于存储库的example-code-splitting
分支上,而不是master
。如果你克隆 repo,你需要检查正确的分支!
示例用法:
# install
npx degit rixo/svelte-template-hot#example-code-splitting svelte-app
cd svelte-app
yarn # or npm install
# dev
yarn dev
# build
yarn build
# serve build
yarn start
【讨论】:
最佳答案!谢谢。 @rixo 非常感谢。我正在使用您的模板进行拆分。当进行生产缓存是问题所在。你有解决方案吗:github.com/sveltejs/template/issues/39main.js
被浏览器缓存。想要在 index.thml 中的 main.js 使用动态哈希。
我对 index.html 使用 1 天的 HTTP 到期值(前端/代理服务器配置),并且还使用缓存破坏策略(捆绑器配置),以便 main.js 脚本 src 每次部署都不同。【参考方案2】:
这个 repo 可能是一个很好的起点https://github.com/Rich-Harris/rollup-svelte-code-splitting
【讨论】:
以上是关于如何在没有 Sapper 的情况下使用 Svelte 进行代码拆分的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Google Adsense 添加到 Svelte/Sapper 网络应用程序?