Svelte 前端开发笔记
近期用 Svelte 开发了一个小项目,记录在开发过程中遇到的一些问题。
逻辑块中的临时变量
通常为了简化代码,在 {#each}
或 {#if}
块中需要用到临时变量。比如:
svelte{#each items as item}
<a href="/user/{item.article.author.id}" title={item.article.author.fullName} use:link>
{item.article.author.name}
</a>
{/each}
上面代码中的 {item.article.author}
被多次引用,如果可以将其赋值给一个临时变量,那么代码可以简略些许。其实 Svelte 在某个版本中加入了 {@const}
指令可以实现临时变量,然而中文版的 Svelte 文档过于陈旧,我并没有找到相关的指令说明,直到后来翻阅官网的英文文档才找到。上面的代码可以简化为:
svelte{#each items as item}
{@const author = item.article.author}
<a href="/user/{author.id}" title={author.fullName} use:link>
{author.name}
</a>
{/each}
另外一种情况,循环块中的事件处理函数通常需要用到循环中的临时变量,可以利用闭包的特性将数据绑定到事件处理函数:
svelte<script lang="ts">
let items = loadItemsFromSomewhere();
function wrapArticleClick(article) {
return () => {
console.log(article.title);
};
}
</script>
{#each items as {article}}
<button on:click={wrapArticleClick(article)}>{article.title}</button>
{/each}
路由参数变化时不会重新渲染组件
我在项目中使用的路由组件是 svelte-spa-router。之所以选择这款第三方路由倒也没什么特别原因,仅仅是因为它在 NPM 上的下载次数比较多而已。其定义路由映射表的代码如下:
typescriptimport Home from './views/Hello.svelte';
const routes = {
'/hello/:hello': Hello,
};
export default routes;
该路由组件有个特性:就是当路由的参数变化时,它不会重新载入或重新渲染组件。虽然有点违背直觉,但是考虑到 Svelte 的响应式特性,这样的实现还是挺合理的。比如,下面的代码在路由参数变化时,页面内容不会变化:
svelte<script lang="ts">
export let params = {};
let hello: string = params['hello'];
</script>
<p>{hello}</p>
只要稍作修改就可以了:
svelte<script lang="ts">
export let params = {};
let hello: string;
$: hello = params['hello'];
</script>
<p>{hello}</p>
单页应用 (SPA) 的局限性
这并非是 Svelte 的问题,而是所有 SPA 前端项目的通病。除了 SEO 不友好外,更新客户端状态也是需要考虑的棘手问题。对于传统的 Web 项目,当用户点击链接、页面发生跳转时,就相当于和服务器进行了一次交互,这时可以完成客户端状态的更新;而 SPA 项目如果不是用户主动刷新页面或进行与服务器的交互操作,是无法更新客户端状态的。这些客户端状态包括但不限于:用户登录状态、有时效性的 CSRF 令牌等。
目前想到如下几种解决方案:
- 使用 WebSocket 或长轮询等方式和服务器端保持长连接,实时获取状态变化;
- 定时向服务器发起获取当前状态请求并更新;
- 每次和服务器产生交互时,服务器返回额外的状态信息;
- 当和服务器交互发生异常时,则向服务器请求当前状态并更新。
方法 1 的实现方式比较复杂,且服务器的开销也比较大,除非需求对客户端状态变化非常敏感,普通应用似无必要。方法 2 既可以更新客户端状态,又可以保持会话,实现原理也简单。但是要注意的是,对于移动端的浏览器,当页面切换到后台时,页面的 setInterval()
脚本会停止运行。方法 3 需要对服务器所有接口进行再封装,如果变动接口牵一发动全身的话则得不偿失。方法 4 的实现最简单,客户端和服务器交互时,如果服务器响应 401 状态码,则表示登录失效,客户端跳转到登录页或另作处理;如果服务器响应 CSRF 令牌校验失败,则请求对应接口,获取最新的 CSRF 令牌,等等。
下一步是否应该考虑迁移到 SvelteKit 的静态站点生成 (Static Site Generation) 呢?