前端简史
- 【静态】在最早的时候是根本没有前端或者后端的概念的。当时就是用 Dreamweaver 写 html 静态页面,然后部署到一台电脑的 IIS (Internet Information Services) 上。当请求这个页面时,返回这个 html 文件。
- 【模版】再后面一点,服务端变得复杂了一些,html 页面开始使用各种模板来写,譬如 Java 系列的 FreeMarker,还有 ASP 、 php 等等。此时,前后端开发是一体的,最多也就是模板的编写算是最初的前端范畴,但那个时候,这个活儿往往都是现在的后端开发去干的。
- 【Web2.0】随着 2005年 Ajax (Asynchronous JavaScript and XML) 的诞生,彻底得改变了这一切。JS 脚本可以独立向服务器请求数据,拿到数据后,进行处理并更新网页,这个过程中,后端只负责提供数据,其他事情都由前端来做。不夸张的说,这一年算得上是 Web 开发技术发展的元年。Web也从 1.0 的时代,步入 2.0 的时代。促进了前后端的分离。
- 【MVC】前端可以通过 Ajax 获取数据,因此也就有了处理数据的需求,于是就促使了前端 MVC 的诞生。在这个阶段的后期,前端逐渐开始有了一点工程化的影子,并且开始受 CommonJS 的影响,有了模块化编程的概念,诞生了相应的 CMD 和 AMD 的规范。开始有了构建工具 Grunt/Gulp,开始有了编码的规范 JsLint。
- 【MVVM】MVVM 同样是一种软件架构模式,它是在 MVC 的基础上演进过来的,去掉了 MVC 中的 Controller,增加了数据的双向绑定。最有代表性的框架就是 Google 公司推出的 Angular, 它的风格属于 HTML 语言的增强,核心概念就是数据双向绑定。
- 【SPA】用户第一次发起页面请求时,后端返回HTML、 CSS 和 JS文件。JS 文件包括了页面切换逻辑的处理,这是单页应用实现的关键,它利用 Hash 或者 History 的技术,实现了当切换页面时,首先通过 Ajax 获取到新页面需要的数据,然后由 JS 根据要切换到的网址,使用获取到的数据来拼接出要展示页面的 HTML。整个切换页面的动作全部由前端来完成了。这就是单页应用,所有的资源只在第一次页面请求时被加载,后面都只会发起 Ajax 请求获取数据而已。
- 【SSR】SPA 让 web 变成了应用的形态,它是客户端渲染(client side render)。客户端渲染有它的弊端,譬如没法做 SEO(Search Engine Optimization),由于所有的 JS 和 CSS会在首次访问时被全部加载,并且 HTML 是在前端组装的,就势必导致首屏加载以及渲染的时间会增加,影响用户体验(不过有了代码分割,只要不需要SEO,SPA现在依然是最流行的方案)。目前NextJS是个不错的SSR框架。此外umi也支持SSR,也支持SSR失败时自动降级为CSR。
背景
Good & Cheap & Fast
Dan在视频开头,提到了Project Management中的概念:一个项目中,Good、Cheap、Fast三种属性很难同时具备,通常只能追求其二的极致。这同样适用于React开发:好的用户体验、低成本的代码维护、快的性能。 举个例子:一个典型的展示组件的例子。 给一个作曲家id,渲染他的详情页,里面可以看到他的TopTracks,Discography。当然,TopTracks和Discography不一定非要在详情页可以看到(其他地方也可以引用,希望它是公共的,不是详情页私有的组件)
好的用户体验、快的性能
如果能够在父组件统一获取数据,再传递数据给子组件,那么性能是快的,避免了多个API请求。用户体验也好。但是维护成本又高,每个数据需要一层一层传递下去,如果API做了改动,每个子组件都要改;此外要复用TopTracks和Discography的话,就需要在其它地方重新写获取数据的逻辑。
快的性能、低成本的代码维护
如果我们只允许用户在详情页能看到TopTracks和Discography,这样这俩组件不必是公用组件,我们可以以低成本、快性能的渲染他们。但是用户必须进入详情页才能看到这些组件,用户体验不好。
低成本的代码维护、好的用户体验
我们把每个数据获取逻辑放在各个组件内,维护起来很方便,用户体验也不错。但是性能差,发送了3个请求,而且这些请求是「瀑布流」: 先渲染ArtistDetails,等它拿到数据后,才会继续渲染children,children才会开始获取数据。
这只是前端项目中一个例子,但其实很常见。(只是他们产生的原因可能不同,很多项目中产生的原因是后者请求的API依赖于前者的结果。上面例子只是单纯因为渲染的顺序导致了瀑布流)
已有的解决方案
已有的问题,主要是Client和Server之间来回的数据获取逻辑,一来一回,再来再回,形成了瀑布流,损耗了很多时间。 GraphQL通过一次性请求数据,拿到了所有,解决了这个问题。 GraphQL + Relay (React中用于GraphQL获取数据的库) 这在Facebook内部带来了很多益处,但是难以推广普及:
- GraphQL并不普遍,不是所有人都会用
- 已有的庞大项目改造为GraphQL成本太高(“明明是解决的前端痛点,却非要改造后端”)
- 不是所有人都喜欢它
新的解决方案
而React团队提出的新的解决方案,则是这样的:
Client发送请求,Server直接渲染组件,并在Server本地获取数据,不管瀑布有多长,都可以很快的拿到所有数据,然后在Server渲染出组件,一次性返回给Client。 在Server端运行的React Component就是React Server Component。 在过去,前端都是Client Component:
现在,引入Server Component后,组件树可能会是这样:
怎么用React Server Component?
参考demo:github.com/reactjs/ser…
获取数据
NoteList.server.js
import {fetch} from 'react-fetch';
export default function NoteList({searchText}) {
const notes = fetch('http://localhost:4000/notes').json();
return notes.length > 0 ? (
<ul className="notes-list">
{notes.map((note) => (
<li key={note.id}>
<SidebarNote note={note} />
</li>
))}
</ul>
) : (
<div className="notes-empty">
{searchText
? `Couldn't find any notes titled "${searchText}".`
: 'No notes created yet!'}{' '}
</div>
);
}
Server Component里可以直接获取数据(不需要像Client Component一样,要在useEffect中执行)。而且它还可以操作本地文件、甚至可以直接执行数据库查询语句。Server Component在Server执行,后端能做的,它基本都可以做到。
引用其它组件
Server Component里可以引用Client Component。以指令的形式返回给Client。 Server Component会将组件及其从IO请求到的数据序列化为特定的数据结构(称之为指令),以流的形式传递给前端:
客户端在运行时直接获取到填充了数据的流,并借助Concurrent Mode执行流式渲染。 指令含义:
- M:下载Client组件,收到后会立即下载该模块
- J:序列化的Server组件,收到后该组件可以直接渲染
- S:Symbol,suspense是其中一种
服务端组件的依赖,不会被打包
Note.server.js中,引用了date-fns
,但是在浏览器Sources中看不到这个资源。而且Server Component全都不在里面。
Amazing!
约束
Server Component不能有状态、事件监听器。如果需要交互,只能使用Client Component。(见SidebarNote.js,它把“数据”(其实是JSX)传递给了Client Component,由Client Component控制不同状态的显示内容) Server Component不能传递函数参数给Client Component,因为函数无法被序列化。而JSX可序列化,所以也能传递JSX。 如果传递的JSX也是Server Component,那么它会在Server先渲染完毕,再发送给Client。(这意味着这部分Server Component也不会被Client下载)
混用的同时,保持Client Component的状态
左侧列表是个Server Component,引用的Client Component保存了展开/收起的状态,如果你新增、删除一个项目,他们展开/收起的状态会被保持! Amazing! 这在CSR时代,是需要花一定成本才能实现的功能,在Server Component中,re-render直接可以保持Client Component的状态,非常优秀!
Shared Component组件
有的组件以.js结尾,而非.server.js或.client.js,则它既可以在Server渲染,又可以在Client渲染。 如果Server Component里用到了Shared Component或Server Component,那么将会在Server渲染后,以指令的形式返回给Client,他们不会被下载到Client。 如果Client Component里用到了Shared Component或Client Component,那么会在浏览器渲染,他们会被下载到Client。 如果Client Component里用到了Server Component,会发送请求。 Amazing! 很多系统是区分多角色的,不同角色应该看到不同的内容。这样组件级别的按需加载,正是我们想要的!
这在当前,是非常常见的React代码模式。但是把它切换为Server Component后,无编辑权限的人,不会下载EditToolbar组件。(CSR模式只能做到下载但不显示)
修改+更新,只触发1次请求
CSR时代,我们通常先调用API去修改数据,才重新get新的数据。但是使用Server Component后,我们只需要1次请求,就可以完成这件事! Amazing!
React IO
React鼓励社区去开发维护这些IO库,可用于Server Components。但这不意味着重新造轮子,他们只是针对已有的node库做了一层很薄的封装,例如react-fs只封装了fs,react-fetch只封装了fetch。都不到一百行。封装成React IO库只是增加了缓存层。
关于慢请求
结合Suspense,以流的形式渲染,开发成本低,效果好!
会取代GraphQL吗
不会,他们可以协作。
优势
解决瀑布流问题
0打包体积
假设我们开发一款MD编辑器。服务端传递给前端MD格式的字符串。 我们需要在前端引入将MD解析为HTML字符串的库。这个库就有206k。 import marked from 'marked'; // 35.9K (11.2K gzipped) import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
function NoteWithMarkdown({text}) { const html = sanitizeHtml(marked(text)); return (/* render */); }
通过ServerComponent我们怎么解决这个问题呢? 只需要简单将NoteWithMarkdown标记为ServerComponent,将引入并解析MD这部分逻辑放在服务端执行。 ServerComponent并不会增加前端项目打包体积。这个例子中,一次性为我们减少了前端206K (63.3K gzipped)的打包体积以及解析MD的时间。
自动代码分割
通过使用React.lazy可以实现组件的动态import。之前,这需要我们在切换组件/路由时手动执行。在ServerComponent中,都是自动完成的。
在上图中,左侧列表是ServerComponent,当点击其中卡片时,组件对应数据会动态加载。
与相关技术的对比
SSR
Server Components 跟过去的 SSR 相比,你在拉取后不会丢失客户端的状态; SSR 首次访问时返回渲染完的页面,Server Components 输出的是一系列指令。 Server Components可以通过分块加载、减少打包体积等方式,进一步提升加载速度。 注:二者是不同的技术方案,解决的问题不同,但可以混用。SSR主要解决的问题是SEO和首屏渲染速度。
PHP等后端框架
在 PHP/ASP 时代,页面都是由服务器来渲染。服务器接到请求后,查询数据库然后把数据“塞”到页面里面,最后把生成好的 html 发送给客户端。当用户点击链接后,继续重复上面的步骤。这样用户体验不是很好,每个操作几乎都要刷新页面,服务器处理完之后再返回新的页面。
SPA
而 Angular/Vue/React 这种单页应用(SPA)则主要是客户端渲染。服务器接到请求后,把 index.html 以及 js/css/img 等发送给浏览器,浏览器负责渲染整个页面。后续用户操作和前面的 php/jquery 一样,通过 ajax 和后端交互。但是和 php 相比,第一次访问时只返回了什么内容都没有的 idnex.html 空页面,没法做 SEO。另一点就是页面需要等到 js/css 和接口都返回之后才能显示出来,首次访问会有白屏。
推荐阅读
- reactjs.org/blog/2020/1…
- github.com/reactjs/rfc…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!