Svelte 前端AOT解决方案

3,572 阅读4分钟

前端 AOT

首先开始之前我们要理清 AOT 与 JIT 的区别

  • AOT,Ahead Of Time,运行前编译
  • JIT,Just-in-time,动态编译,边运行边编译

类似于 REACT、VUE之类的框架都必须引入框架本身通过 Virtual DOM 的 diff/patch操作实现页面逻辑,Svelte 的产物是原生可执行代码,从速度、损耗、体积来说都优于前者,而且框架本身并无附加代码可理解为 0KB 的框架

题外话:根据框架特性、svelte 可以很好实现 web component,以及在react 与 vue 里混用

Svelte 工作原理

Svelte 将这个模板片段 AOT 编译成很精简的一段 JavaScript 模块。
这个模块主要的操作有四个部分:

  • create 把模板中的各个用 HTML 元素用 Vanilla API 创建出来
  • mount 可操作 dom 的时候
  • update 将数据绑定到视图的操作
  • unmount 注销 dom 之前

实战

本文使用 svelte 的ssr方案 sapper 进行业务实战,以及与 nextjs的直观对比

1、实例化

执行

  • npx degit "sveltejs/sapper-template#rollup" my-app 使用 rollup
  • npx degit "sveltejs/sapper-template#webpack" my-app 使用 webpack

可以拿到目录清单

├── README.md
├── __sapper__ 编译产物文件
├── cypress
├── cypress.json
├── efox_framework_style 自定义构建文件
├── node_modules
├── package.json
├── pm2.config.js
├── rollup.config.js 配置文件
├── src
│   ├── analysis
│   ├── client.js
│   ├── components 组件
│   ├── config 
│   ├── helper
│   ├── routes 页面 遵循类nextjs 的路由规则
│   ├── server.js 服务端配置
│   ├── service 自定义FAAS模型
│   ├── service-worker.js 
│   ├── store
│   └── template.html
└── static

2、开发

创建个人中心页面 根据uid 或者页面数据 src/routes/person/[uid].svelte

<script context="module">
  // <script context="module"> 只执行一次
  import { getUserInfo } from "~/service/person";
  import { formatNumber, toHttps } from "~/helper/until";
  import TopBanner from "~/components/person/TopBanner.svelte";
  import Info from "~/components/person/Info.svelte";
  import VideoList from "~/components/person/VideoList.svelte";
  import Header from "~/components/person/Header.svelte";
  import Footer from "~/components/person/Footer.svelte";
  import t from "~/store/langStore";
  // preload 只在 module 里面可以使用 page 为访问入参信息 session 可以自定义上下文 
  export async function preload(page, session) {
    let { language } = page.query;
    let { uid } = page.params;
    const { asPath } = session; //从 node server 获取的返回值 src/server.js
    language = language || "en";
    const [lang, userInfo] = await Promise.all([
      t.getlang("biugo_mobile", "person", language), // 通过 store 的方式进行数据更新
      getUserInfo(uid, session) 
    ]);
    const personInfo = userInfo ? userInfo.userDto : {};
    const videoList = userInfo ? userInfo.list : [];
    return { lang, personInfo, videoList, uid, asPath };
  }
</script>

<script>
  import { onMount } from "svelte";
  // 应用从 preload 返回的值
  export let lang;
  export let uid;
  export let asPath;
  export let personInfo = {};
  export let videoList = [];
  t.set(lang);//设置当前多语言 并且同步服务端与客户端


  onMount(() => {//出现页面后执行的操作});
</script>

<Header {title} />
<div class="person">
  <button on:click={() => t.getlang('en')}>
    en
  </button>
  <button on:click={() => t.getlang('pt')}>
    pt
  </button>
  <TopBanner {personInfo} />
  <Info {personInfo} />
  <VideoList {videoList} {personInfo} />
  <Footerrson} />
</div>

服务端配置 src/server.js

import sirv from 'sirv';
import polka from 'polka';
import compression from 'compression';
import * as sapper from '@sapper/server';
const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';
polka()
	.use(
		compression({ threshold: 0 }),
		sirv('static', { dev }),
		sapper.middleware({
		    //自定义服务端返回内容
			session: (req, res) => ({
				asPath: req.url,
				user: req.headers.cookie,
				agent: req.headers['user-agent'],
				lang: req.headers['accept-language']
			})
		})
	)
	.listen(PORT, err => {
		if (err) console.log('error', err);
	});

全局设置多语言Store src/store/langStore

import { writable } from 'svelte/store';

function langStore() {
  const { subscribe, set, update } = writable({});
  return {
    set,
    subscribe,
    update,
    getlang: async (lang) => {
      const d = await ......//获取多语言数据
      set(d)
      return d
    }
  }
}
export default langStore()

设置全局头部信息,便于seo src/components/person/Header.svelte

<script>
  export let title = "";
</script>

<svelte.head>
  <title>{title}</title>
  <meta name="description" content="" />
  <meta property="og:site_name" content="Site title" />
  <meta property="og:type" content="website" />
  <meta property="og:title" content="Site title" />
  <meta property="og:description" content="" />
  <meta property="og:image" content="" />
  <meta property="og:url" content="" />
  <meta name="twitter:title" content="Site title" />
  <meta name="twitter:description" content="" />
  <meta name="twitter:image" content="" />
  <meta name="twitter:url" content="" />
  <meta name="twitter:card" content="summary" />
  <!--引入全局css src/static/css/common.css -->
  <link rel="stylesheet" type="text/css" href="/css/common.css" /> 
  
</svelte.head>

引入Footer.svelte 代码具体多语言以及点击和静态文件引入的方法 src/components/person/Footer.svelte

<script>
  import t from "~/store/langStore";
  export let goto; //从外部注入的函数
</script>
<div class="footer" on:click={() =>  goto('6')}>
  //静态文件调用    
  <img src="/person/logo.png" class="logo" alt="" />
  //多语言调用
  <span class="content">{$t.logoInfo}</span>
  <button>{$t.openBtn}</button>
</div>

if、else 以及for 的调用

    {#if a && b}
      ......
    {/if}
    
    {#each videoList as item}
      item.title
    {/each}

3、调试

sapper dev or yarn dev 运行测试环境
http://localhost:{port}/person/${uid} 访问

4、对比

开发一样的业务逻辑,测试结果如下:

framework js spend time
nextjs 420kb 1856ms
sapperjs 36kb 939ms

Svelte 周边

1、Tailwind CSS

Tailwind CSS 是一个高度可定制的基础层 CSS 框架,它为您提供了构建定制化设计所需的所有构建块,而无需重新覆盖任何内建于框架中的设计风格。 tailwindcss.com/

2、Sapper

一个构建高性能通用Web应用程序的框架,实现Svelte SSR的解决方案 sapper.svelte.dev/

3、Svelte Native

使用 NativeScript 构建移动应用程序的框架。 svelte-native.technology/

Svelte vs Preact vs vs Vue vs React vs Rax

作者

Ken.xu

后续会持续关注并且选择性使用框架进行活动类开发;通过与QUIC的配合、提升海外市场在弱网环境下的整体效率