热门前端框架Astro在房产业务实践

01

 

 Astro介绍 


1.1 为什么使用Astro
随着业务不断地发展,对项目的要求也越来越高。加载要更快,性能要更好,资源消耗要更少,同时也要减少开发成本,并且还要满足业务的各种诉求。经过我们的调研最终我们锁定了 Astro 框架,Astro具有轻量级与高性能、卓越的SEO优化、兼容性与灵活性、简洁的开发体验、出色的社区支持、成本效益高等优势。能一站式解决了我们大部分诉求。

1.2 什么是Astro

Astro是一个现代化的静态站点生成器和前端框架。Astro由原Snowpack团队于21年4月创建,并于22年8月发布了1.0正式版本,当前已经发布到了4.x版本。从创建至今的3年时间里,Astro的Star数已经达到46k。对比创建3年时的Vue Star数是26k、React Star数是 37k。从下图可以看出来Astro Star 增长曲线还是很陡峭的,一直处于火热状态。


图1. Asro Star数变化趋势

1.3 Astro的特点

  • 群岛架构:一种基于组件针对内容驱动网站进行优化的 Web 架构。

Astro 开创并推广了一种叫做 “群岛” 的前端架构。群岛架构通过帮助你避免单体 JavaScript 模式并自动从页面中剥离所有非必需的 JavaScript,从而实现了更好的前端性能。开发者在使用 Astro 的同时,仍然可以继续使用他们最喜欢的 UI 组件和框架,并且从中得到受益。

“Etsy” 的前端架构师 Katie Sylor-Miller 在 2019 年首次创造了 “组件岛屿” 这个术语。随后,Preact 的创造者 Jason Miller 在 2020 年 8 月 11 日扩展了这个概念。
“群岛” 架构的总体思想不像看上去那么简单:在服务器上渲染 HTML 页面,并在高度动态的区域周围注入占位符或插槽 […] 这些区域随后可以在客户端 “激活” 成为小型独立的小部件,重用它们服务器渲染的初始 HTML。实践起来还是很有挑战性的。— Jason Miller, Preact 的创造者。
这种架构模式所依赖的技术也被称为 局部化或选择性激活。相比之下,大多数基于 JavaScript 的 Web 框架会将整个网站作为一个大型 JavaScript 应用程序(也被称为单页应用程序,或 SPA)进行激活和渲染。SPA 提供了简单性和强大的功能,但由于大量使用客户端 JavaScript 也导致存在一些页面加载的性能问题。SPA 在 嵌入 Astro 页面内 时也有其一席之地。但是,SPA 缺乏选择性和策略性激活的原生能力,使得它们对于当今大多数 Web 项目来说是一个笨重的选择。Astro 因为是第一个内置选择性激活的主流 JavaScript Web 框架而变得流行,它使用了由 Sylor-Miller 首次提出的组件岛屿模式。
在 Astro 中,“岛屿”指的是页面上的任何交互式 UI 组件。你可以将岛屿想象成一个个交互式小组件,漂浮在静态、轻量级、服务器渲染的 HTML 海洋中。

图2. 群岛架构示意图,来源:Jason Miller

岛屿始终独立于页面上的其他岛屿运行,且一个页面上可以存在多个岛屿。尽管岛屿在不同的组件中运行,它们仍然可以共享状态并相互通信。这种灵活性使得 Astro 能够支持多个 UI 框架,如 React、Preact、Svelte、Vue 和 SolidJS。由于它们是独立的,你甚至可以在每个页面上混合使用多种框架。
Astro 群岛的最明显的好处就是性能:你网站的大部分区域都被转换为了快速、静态的 HTML,JavaScript 只有在需要的时候才会加载到各个组件中。JavaScript 是一个加载得最慢的资源。每一个字节都影响着用户的体验!
另一个好处是并行加载。在上面的一些假想例子中,重要性更低的图像轮播不应该阻挡更重要的页头部分的加载。它俩并行加载但独自激活(hydrate),这表明用户并不需要等着更沉重的图像轮播加载完毕就可以与页头交互了。
更好的地方在于:你可以准确地告诉 Astro 如何以及何时渲染每个组件。如果该图像轮播的加载成本真的很高,你可以附加一个特殊的客户端指令,告诉 Astro 仅在轮播在页面上可见时才加载它。如果用户从未看到它,它永远不会被加载。
  • UI 无关 支持 React、Preact、Svelte、Vue、Solid、Lit、HTMX、Web 组件等等。
正是由于Astro是群岛架构,每个组件是相互独立的,非常灵活。这种灵活性,使得Astro能够支持多个主流 UI 框架,如React、Preact、Svelte、Vue、Solid组件等。
下面从一个demo中来看Astro是如何去做多框架渲染的。
index.astro中引入React和Vue组件

---
import Header from '../components/Header/index.tsx'
import Footer from '../components/Footer/index.vue'
---

<div>
<Header client:idle />
<Footer client:idle />
</div>

React 文件内容

import React from 'react';
export default function() {
return (
<div>
<p>react header</p>
</div>
)
}

Vue 文件内容


   
   
   
   
   
<script setup>
import { ref, onMounted } from 'vue'
const words = ref('vue footer')
onMounted(() => {
console.log(123)
words.value = 'vue footer 2'
})
</script>

<template>
<div class="vue-footer-cont">
<p>{{ words }}</p>
</div>
</template>

SSR模式输出的 html 部分

<astro-island
uid="r30xB"
prefix="r0"
component-url="/_astro/index.db5e9ed3.js"
component-export="default"
renderer-url="/_astro/client.802dacf3.js"
props='{"data-astro-cid-j7pv25f6":[0,true]}'
ssr=""
client="idle"
opts='{"name";"Header", "value":true}'
await-children=""
>
<div>
<p>react header</p>
</div>
<!--astro:end-->
</astro-island>

<astro-island
uid="X0T3U"
component-url="/_astro/index.6bfab11a.js"
component-export="default"
renderer-url="/_astro/client.6f88b9f8.js"
props='{"data-astro-cid-j7pv25f6":[0,true]}'
ssr=""
client="idle"
opts='{"name":"Footer", "value":true}'
await-children=""
>
<div class="vue-footer-cont" data-astro-cid-j7pv25f6="true">
<p>vue footer</p>
</div>
<!--astro;end-->
</astro-island>

component-url:原始组件逻辑代码
renderer-url:组件渲染时需要的代码,如React的jsx-runtime。通过改写,支持.astro组件renderToString。

从输出的 html 部分内容与 React 和 Vue 源码可以简单观察到,Astro 是使用 React 和 Vue 的SSR模式协助渲染输出内容。外边包一层自定义标签,标签属性记录一些岛屿组件状态、渲染和逻辑 js 代码地址等,用来实现传递数据,群岛按需加载,状态标记等功能。

  • 服务器优先:将沉重的渲染移出访问者的设备。

在 Astro 中,大部分处理都是在服务器上进行,而非浏览器中。这通常使你的网站或应用在性能羸弱的设备或在较慢的互联网连接上查看时比客户端渲染更快

  • 默认无 JS:让客户端更少的执行 JS ,以提升网站速度。

Astro 默认不使用客户端渲染。它们在构建时或使用服务器端渲染(SSR)按需呈现为 HTML,默认情况下不用任何 JavaScript。当需要交互必须写js时,可以利用客户端指令或者inline script。

  • 可定制:Tailwind、MDX 和数百个集成可供选择。

Astro官方和社区提供了丰富的可定制的功能,比如多UI框架、多SSR托管平台适配器、Astro db、tailwind、MDX等。只需要运行一行或几行命令,就可以为你完成必要的配置更改。

02

 

 Web SSR类框架性能对比


通过CWV(Core Web Vitals,核心页面指标)的网站百分比:使用特定框架的网站,通过谷歌核心网络指标评估的占比,通过意味着性能更好。

指标

良好的

需要改进的

不良

LCP(最大内容绘制)

<=2.5s

<=4s

>4s

FID(首次输入延迟)

<=100ms

<=300ms

>300ms

CLS(累积布局偏移)

<=0.1

<=0.25

>0.25

Astro的CWV通过率最高,接近70%

图3. 通过CWV的站点百分比
LCP指标

图4. 通过LCP的站点百分比
FID指标

图5. 通过FID的站点百分比
CLS指标

图6. 通过CLS的站点百分比
通过谷歌的CWV测试,能看出来Astro的性能的确强劲。可以在我们的业务上做一些落地尝试。

03

 

 项目实践

3.1 首先确定实践方向
  • 在APP内的H5:CSR转向SSR,减少前端的js体积。目前的前端业务,采用React和Vue,大部分都是CSR渲染模式,js体积庞大,性能并不是最优。SSR和SSG能大幅减少体积,提升性能。

  • SEO页面优化:当前主站SEO页面是基于Vue的SSR+JQuery模式,没有用框架。项目配置较复杂,上手成本较高。可以利用Astro的服务器优先策略,默认支持SSR模式。配置简单,上手成本低。UI无关,可以不用框架,或者少用框架,即可快速搭建页面。

  • 活动类型的业务:当前业务中,存在多技术栈的组件,Vue 和 React 组件均有,可以利用 UI 无关的特点,利用已有组件快速搭建活动页面。

3.2 示例项目

下面以业主认证页面为例子,介绍一下一些实践细节。以下是一个内容展示页面,用于介绍业主认证的一些流程和好处。

图7. 内容展示页面示例图

3.3 项目示意代码

3.3.1 主文件

   
   
   
   
   
---
export const prerender = false
import Layout from '../layouts/Layout.astro'
import Page from '../components/BrokerOwnerCheck/posterToC/index'
import { url } from './api'

const data = await fetch(url)
---

<script src="lib-flexible/flexible"></script>
<Layout title="业主品牌馆">
<Page data={data} />
</Layout>

主文件是个 Astro 文件:

  • prerender 是配置当前页面预渲染状态,因为项目是配置的 hybrid 模式,所prerender=false 才能启动实时渲染(文档地址:https://docs.astro.build/zh-cn/getting-started/);

  • Layout 是 Astro 组件,用来放置公共 Layout 代码,像页面 head 标签内一些公共内容和公共底部等;

  • Page 是 React 组件,大部分逻辑都放在 React 组件中;

3.3.2 Page组件

   
   
   
   
   
import styles from './index.module.less';

export default ({ data }) => {
const renderTitleBox = (title) => {
return <div className={styles.titleBox}>
<span className={styles.titleText}>{title}</span>
</div>
};

const renderTextBox = (role, text) => {
return <div className={styles.textBox}>
<div className={styles.textIcon}>A</div>
<div className={styles.text}>
<span style={{ fontWeight: 700, color: '#333' }}>{role}</span>
<span>{text}</span>
</div>
</div>
};
return (
<div className={styles.posterContainer}>
<img className={styles.headPoster} src={data.img1} />
<div className={styles.moduleBox}>
{ renderTitleBox(data.title1) }
{ renderTextBox(data.desc1) }
{ renderTextBox(data.desc2) }
</div>
<div className={styles.moduleBox}>
{ renderTitleBox(data.title1) }
<img className={styles.poster} src={data.img2} />
</div>
</div>
)
};

很标准的React写法,数据从上层传过来,拿到数据进行填充到对应位置。整体写法采用的是以React组件为主,Astro入口文件为辅。从代码层面可以看出来对前端同学开发非常友好,上手几乎零成本。还可以兼容各个UI框架的组件。

3.4 性能对比

在页面资源方面,使用Astro开发,页面 js, css, html 资源约占6.6KB,且没有js渲染。如果使用传统React客户端渲染开发,页面js, css, html 资源约占 150KB,且还需要js渲染。两者资源体积相差20+倍。

借助Lighthouse进行评估,Astro框架也整体优于React

图8. Astro开发页面 Lighthouse评估效果

图9. React开发页面 Lighthouse评估效果
可以看出整体性能Astro开发是优于传统React客户端渲染开发。

3.5 最佳实践

开发Astro项目和我们用React Vue等开发客户端渲染形式页面开发思路还是有区别的,如果还用常规思路开发,Astro性能效果会打一定折扣。下面介绍一下常见“误区”及如何解决。
在引入react vue 等组件的时候通常会遇到如下图报错

图10. 报错示例图

这个报错提示:浏览器API不能在 node 中使用。或者使用指令更改渲染方式。这个时候查看文档链接通常都会使用文档中 client:only="react" 这个指令来解决这个报错。但是使用这个指令Astro会将我们的组件进行客户端渲染,这样就失去Astro的SSR部分优势。
报错原因:因为渲染 React 组件会在 node 端运行,node 端不支持浏览器特有的API。所以当我们运行一些浏览器特有API会报异常。那么如何发挥Astro最大优势去做开发?最好的方式是调整开发逻辑,规避在 node 中使用浏览器API。下面推荐代码结构来讲解一下。

图11. 整体推荐代码结构示例图

图12. React文件示例推荐代码结构

小结:React、 Vue 分别在各自的 useEffect onMounted 方法中和在交互事件绑定的方法中才能使用浏览器API。

总结:区分场景,按场景来使用浏览器或 node 特有API。

目前在我们保障专区、安心租PC/APP的活动邀请、业主认证C端引导页已经完成了项目落地实践。相对于之前的开发模式,页面性能和开发者体验都提升不少。
但实践过程也发现Astro不足之处:Astro静态资源前缀地址只支持一种前缀地址配置。但是我们业务中通常不止一个CDN地址。

在 build.assetsPrefix 选项中:


图12. build.assetsPrefix选项展示

如果有多种情况,比如css 文件CDN地址前缀是 https://css.58.com/static/css, js 文件CDN地址前缀是 https://js.58.com/static/js。其它文件地址是 https://imgs.58.com/static/imgs。Astro 就不支持了。此时对 Astro 比较熟悉的同学会提出疑问:Astro 支持vite配置扩展,vite 里不是有针对多CDN地址前缀的配置嘛?没错是的,但是,配置后你会发现几乎不起作用。提了 issues(https://github.com/withastro/astro/issues/9700#issuecomment-1907226632) 之后与核心开发者确认沟通,Astro 确实无法支持多CDN配置。

Pull Request

不支持那么就开始尝试提PR来支持这个功能。

一些小技巧

提PR有些小技巧,要循序渐进的参与到开源项目中,何为循序渐进呢?先提issues与作者讨论,先混个“脸熟”,再提少量代码的PR,逐渐的代码量可以随着PR次数逐渐增多而增多。通过不断增多的沟通次数,逐渐提升你与作者之间的“亲密度”。这样的好处是能逐渐的了解整体架构和作者的设计思路,在做改动的时候不会破坏整体的设计,作者也更容易接受你的改动。不提倡突然大量代码的PR!

API设计 & 开发

开发第一步,先设计API,首先要兼容原有的API使用形式,而且灵活性要好,能适用各种场景。所以最终受vite配置启发,也采用函数的形式。
也使用 build.assetsPrefix 选项,当前 assetsPrefix 只支持字符串类型。添加函数类型,返回值为前缀字符串。

// assetsPrefix function
import { defineConfig } from 'astro/config';
export default defineConfig({
build: {
assetsPrefix: (fileType) => {
if (fileType === 'js') {
return 'https://js.example.com/'
} else if (fileType === 'css') {
return 'https://css.example.com/'
}
return ''
}
}
});

按这种 function 类型开发完并提交PR进行初次审核与讨论 PR1 (PR地址:https://github.com/withastro/astro/pull/9898#issuecomment-1951878995)
通过PR1作者反馈在SSR场景下会不生效,建议使用对象的形式:

图13. PR1作者的反馈
但我还是想用函数的形式,于是尝试修复SSR场景不生效问题。
经排查,SSR场景构建时候会对配置进行 JSON.stringify 导致方法丢失。

图14. SSR场景构建时对配置进行 JSON.stringify 源码位置


Astro 构建项目的时候SSR场景会将配置部分内容进行打包到构建产物中,这种形式如果我们使用函数配置选项的话会对项目改动比较大,所以最终采纳作者建议使用对象的形式。最终经过多人反复讨论使用方法如下最新文档。(PR地址:https://github.com/withastro/astro/pull/10189#event-12055572178)。 


图15. assetsPrefix最新文档

04

 

 展望规划


解决完这个问题,在后续项目实践中就没有什么障碍了。
后续充分发挥Astro框架能力,优化业务页面体验。
  • 以活动专题页开始试点Astro框架应用
  • 搭建标准化开发框架,更多应用场景试点
  • 基于Astro搭建一种“高效能服务端渲染平台”,可以快捷地生产高性能页面

本文分享自微信公众号 - 58技术(architects_58)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

微软开源基于 Rust 的 OpenHCL 字节跳动商业化团队模型训练被“投毒”,内部人士称未影响豆包大模型 华为正式发布原生鸿蒙系统 OpenJDK 新提案:将 JDK 大小减少约 25% Node.js 23 正式发布,不再支持 32 位 Windows 系统 Linux 大规模移除疑似俄开发者,开源药丸? QUIC 在高速网络下不够快 RustDesk 远程桌面 Web 客户端 V2 预览 前端开发框架 Svelte 5 发布,历史上最重要的版本 开源日报 | 北大实习生攻击字节AI训练集群;Bitwarden进一步脱离开源;新一代MoE架构;给手机装Linux;英伟达真正的护城河是什么?
{{o.name}}
{{m.name}}

猜你喜欢

转载自my.oschina.net/u/5359019/blog/16310432