【发布时间】:2020-05-18 08:01:59
【问题描述】:
如何使用 Svelte 进行代码拆分?
(我看到你可以使用 Sapper 来做到这一点,但我不想依赖节点后端)
【问题讨论】:
标签: svelte
如何使用 Svelte 进行代码拆分?
(我看到你可以使用 Sapper 来做到这一点,但我不想依赖节点后端)
【问题讨论】:
标签: svelte
代码拆分实际上是动态导入的一个花哨名称。以下是使用 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 中的<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
【讨论】:
main.js被浏览器缓存。想要在 index.thml 中的 main.js 使用动态哈希。
这个 repo 可能是一个很好的起点https://github.com/Rich-Harris/rollup-svelte-code-splitting
【讨论】: