2026/4/5 15:11:28
网站建设
项目流程
为什么有的网站只有版权没有备案,设计制作一个保温杯教学反思,wordpress怎么开发,wordpress点文字弹出层在聊 Angular 的 SSR 之前#xff0c;有必要把视角稍微拉远一点#xff1a;浏览器到底在做什么#xff0c;以及 SSR 在浏览器渲染链路里究竟改变了哪几件关键事情。
浏览器拿到一个 HTML 文档后#xff0c;会经历一条相当固定的路径#xff1a;解析 HTML 构建 DOM 树有必要把视角稍微拉远一点浏览器到底在做什么以及SSR在浏览器渲染链路里究竟改变了哪几件关键事情。浏览器拿到一个HTML文档后会经历一条相当固定的路径解析HTML构建DOM树解析CSS构建CSSOM把两者合成渲染树计算布局绘制再合成到屏幕。CSR纯客户端渲染的问题不在于浏览器不会渲染而在于浏览器最开始拿到的HTML往往只有一个空壳容器真正的内容要等JavaScript下载、解析、执行完毕再由框架把DOM生成出来。结果就是用户看到首屏内容的时间被推迟搜索引擎抓取与社交分享的meta信息也更容易出现缺失或不稳定。Angular的SSR解决的核心矛盾很直接把Angular的首屏DOM生成这件事从浏览器挪到服务器端去做让浏览器一开始就拿到带内容的HTML随后再把交互能力补回来这个补回交互的过程就是Hydration水合。官方对Hydration的描述非常清晰它要复用服务器端已经渲染出来的DOM结构、恢复应用状态、把服务器端获取的数据转交给客户端避免重复请求等。(Angular)下面按你关心的点来拆解Angular用了哪些技术实现SSR这些技术在架构里分别扮演什么角色以及在真实项目里如何组合它们。Angular SSR在今天的定位Hybrid Rendering而不是单一SSR从Angular的官方文档体系看SSR现在被放进了更大的概念Hybrid Rendering混合渲染里同一个应用里你可以对不同路由选择SSR、SSG预渲染或CSR以便在SEO、首屏性能、个性化和服务器成本之间做更细颗粒度的权衡。官方给出的三种RenderMode也很明确Server按请求服务器渲染、Client浏览器端渲染、Prerender构建期生成静态HTML。(Angular)更重要的是现在启用这套能力的路径已经非常产品化新项目可以用ng new --ssr已有项目用ng add angular/ssr这在官方Hybrid Rendering指南里就是推荐做法。(Angular)技术栈总览Angular SSR由哪几层组成把Angular SSR拆成工程可落地的组件通常会落到七层构建与产物层Angular的新构建系统负责把同一套源码编译成浏览器产物与服务器产物并把SSR、Prerender的工作流整合进CLI。服务器运行时层最常见是Node.js也可以是非Node.js的运行时只要能提供类Fetch的Request/Response语义。服务器端渲染引擎层把Angular应用启动起来并渲染为字符串HTML典型代表是renderApplication与CommonEngine。路由与渲染模式编排层对不同路由选择SSR/SSG/CSR并控制构建期或请求期的渲染策略。客户端恢复层Hydration负责把服务器端HTML变成可交互应用包含事件回放、增量水合等能力。数据传递与缓存层把服务器端请求结果带到客户端避免Hydration后二次HTTP请求核心是HttpClient传输缓存与TransferState思路。跨平台兼容层解决window/document等浏览器专属对象缺失的问题包含平台判断、延后执行钩子、必要时的DOM模拟如domino。接下来逐层展开。构建与产物层新构建系统esbuildVite把SSR变成一等公民从Angular 17起Angular的新构建系统逐步稳定并成为主流路径。官方对这套系统的描述里有几句非常关键它使用ESM输出格式、引入esbuild与Vite等现代工具并且集成了SSR与Prerendering能力。(Angular)这意味着两件事SSR不再是额外拼装的脚手架而是编译链路原生支持的目标之一。应用在构建后会形成面向浏览器与面向服务器的两份运行产物CLI可以直接用这些产物执行SSR或SSG。在团队协作里这个变化的价值非常现实以前SSR常常意味着你要维护一套特殊的webpack配置、单独的服务器编译流程、以及跟主工程不同步的构建参数现在大多数项目能把这些复杂度交还给Angular CLI。服务器端渲染引擎层renderApplication与CommonEngine是两块核心积木renderApplication最底层的SSR能力从 API 语义上看renderApplication做的事情非常直白引导启动一个Angular应用实例并把它渲染成字符串HTML。(Angular)你可以把它理解成Angular在服务器端的bootstrapApplication serialize DOM的组合。它解决的是SSR的最核心问题在没有真实浏览器的环境里如何把组件树跑一遍、生成首屏DOM并把结果序列化成HTML。CommonEngine面向Node.js应用的通用渲染引擎在真实项目里你更常见到的是CommonEngine。官方 API 把它定义为A common engine to use to server render an application并提供render方法返回渲染好的HTML。(Angular)CommonEngine的优势在于它把很多工程细节封装好了文档模板路径、目标url、静态资源publicPath、平台级providers注入等适合跟Express或其它Node框架整合。服务器运行时层Node.js默认方案以及非Node.js的angular/ssrNode.jsExpress最常见的落地组合官方SSR指南里给了一个非常典型的server.ts架构用Express托管静态资源对普通路由调用CommonEngine.render把生成的HTML返回给浏览器。文档还点出一个容易被忽视的细节从Angular 17开始ng serve不再依赖server.ts开发服务器会直接使用main.server.ts执行服务器端渲染。(v19.angular.dev)这件事对开发体验的影响很大你不需要每次都把Express服务器跑起来才看得到SSR的效果CLI会把SSR融进常规的开发工作流。一个更贴近现代Angular风格ESM、更少脚手架依赖的server.ts通常会长这样示例代码用单引号避免引入英文双引号import{APP_BASE_HREF}fromangular/common;import{CommonEngine}fromangular/ssr/node;importexpressfromexpress;import{dirname,join,resolve}fromnode:path;import{fileURLToPath}fromnode:url;importbootstrapfrom./src/main.server;exportfunctionapp():express.Express{constserverexpress();constserverDistFolderdirname(fileURLToPath(import.meta.url));constbrowserDistFolderresolve(serverDistFolder,../browser);constindexHtmljoin(serverDistFolder,index.server.html);constenginenewCommonEngine();server.set(view engine,html);server.set(views,browserDistFolder);server.get(*.*,express.static(browserDistFolder,{maxAge:1y}));server.get(*,(req,res,next){const{protocol,originalUrl,headers}req;engine.render({bootstrap,documentFilePath:indexHtml,url:${protocol}://${headers.host}${originalUrl},publicPath:browserDistFolder,providers:[{provide:APP_BASE_HREF,useValue:req.baseUrl}],}).then(htmlres.send(html)).catch(next);});returnserver;}你会发现它本质上是三段式静态资源交给Express动态路由走CommonEngine.render通过providers把请求上下文如APP_BASE_HREF注入到Angular的依赖注入系统里。这段整体结构与官方示例一致。(v19.angular.dev)非Node.jsangular/ssr用Web API的Request/Response语义做适配当你把SSR部署到一些更偏Edge的运行环境比如支持Fetch标准的Serverless平台时Node.js的req/res并不是天然存在的。官方Hybrid Rendering指南明确提到angular/ssr提供了在非Node.js平台做服务器端渲染的关键 API并基于标准Web API的Request与Response对象来集成。(Angular)这个设计背后的思路很像浏览器内核的演进路线尽量围绕标准化的Web Platform API做抽象层减少对某一种服务器实现的绑定。对架构师来说这等于给部署形态留下了更大的弹性空间。顺带一提从npm的版本信息可以看到angular/ssr在 2025 年底仍在快速迭代截至 2026 年 1 月最新版已到21.x。(npm)路由与渲染模式编排层把SSR、SSG、CSR做成可配置策略Hybrid Rendering最有工程价值的一点是把渲染策略提升为路由级别的配置而不是全站一刀切。官方文档用RenderMode来描述不同路由的渲染模式Server、Client、Prerender。(Angular)把它翻译成真实世界的产品逻辑大概是这样营销落地页、活动页内容相对稳定追求极致首屏与缓存命中适合PrerenderSSG。商品详情、文章详情内容变化频繁但又强依赖SEO适合Server请求期SSR。登录后控制台、内部管理界面SEO不重要更在意交互与开发效率ClientCSR很合理。官方还提到一个配置点默认情况下Angular会对整个应用执行Prerender并生成 server 文件若你要生成完全静态站点可以设置outputMode为static。(Angular)这类能力一旦落到团队开发会显著减少争论不是讨论SSR要不要上而是讨论某一组路由到底用SSR还是SSG边界清晰很多。客户端恢复层Hydration、事件回放与增量水合如果把SSR比作把菜提前端上桌那么Hydration就是把桌上的菜变成可以吃的状态绑定事件、恢复状态、让组件树重新接管DOM。provideClientHydration水合的入口与默认能力集合Angular的provideClientHydration是启用水合的核心 API。官方说明它默认启用一组推荐特性包含DOM的协调式水合reconciling以及服务器端运行时的HttpClient响应缓存并传递给客户端避免重复请求。(Angular)这两点非常关键协调式水合意味着客户端不会粗暴地丢掉服务器端DOM重建而是尽量复用已有结构减少首屏闪烁与重排。HttpClient传输缓存意味着你在服务器端SSR时请求过的数据客户端水合阶段可以直接复用减少瀑布流请求带来的LCP波动。事件回放withEventReplay解决水合窗口期的交互丢失现实体验里一个常见问题是页面已经有HTML了用户看到按钮就会点但此时水合尚未完成事件监听器还没挂上。Angular的withEventReplay就是为这个窗口期设计的在水合完成前捕获用户事件比如click等水合完成后再回放执行。(Angular)这在电商类站点特别实用用户往往在首屏刚出来就点筛选、点加入购物车事件回放能显著减少SSR页面给人的假可点体验。增量水合withIncrementalHydration把水合从一次性变成按需当应用很大、组件树很深时全量水合会带来明显的主线程压力。Angular提供了Incremental Hydration让水合按一定策略分批进行。官方文档还提到增量水合依赖并会自动启用事件回放如果你已经启用withEventReplay开启增量水合后可以移除前者。(Angular)把这点放到浏览器内核视角它的意义在于减少一次性JS执行与事件绑定造成的长任务让交互恢复更平滑避免把首屏可交互时间推迟太多。数据传递与缓存层Http Transfer Cache与可控的缓存策略SSR的另一个高频痛点是服务器端渲染时请求了一遍数据客户端启动后又请求一遍既浪费带宽也影响性能。Angular官方给出的路径更偏框架级方案通过水合体系自带的HttpClient缓存把服务器端响应带到客户端。(Angular)你可以用withHttpTransferCacheOptions来控制缓存策略比如包含哪些请求头、是否缓存POST、通过filter决定哪些请求进入缓存。(Angular)一个实战化的配置示例仍然避免英文双引号import{provideClientHydration,withHttpTransferCacheOptions}fromangular/platform-browser;exportconstappConfig{providers:[provideClientHydration(withHttpTransferCacheOptions({includeHeaders:[X-Trace-Id],includePostRequests:false,filter:reqreq.methodGETreq.url.includes(/api/),}),),],};真实项目里这样的过滤策略通常会配合接口分层/api/public/*适合缓存并传递/api/user/*含鉴权或个性化信息谨慎传递甚至直接禁用/api/checkout/*强一致性与安全优先一般不做传递缓存。跨平台兼容层浏览器不是服务器服务器也不该假装成浏览器Angular官方在SSR文档里专门强调了一个事实在服务器上不能使用window、document、navigator、location这类浏览器全局对象也不能依赖某些HTMLElement属性。(Angular)这条规则看似简单但它对应的坑非常真实你在组件构造函数里读了window.innerWidth本地CSR完全正常一上SSR就直接ReferenceError。平台判断isPlatformBrowser是最基础的开关官方提供的isPlatformBrowser用来判断当前平台是否为浏览器。(Angular)一种工程上更可维护的写法是把平台判断封装到服务里组件只依赖服务而不是到处散落if。这样做的好处是将来如果你切到Zoneless或者引入Edge SSR改动面更小。import{Injectable,inject}fromangular/core;import{PLATFORM_ID}fromangular/core;import{isPlatformBrowser}fromangular/common;Injectable({providedIn:root})exportclassPlatformService{privatereadonlyplatformIdinject(PLATFORM_ID);getisBrowser():boolean{returnisPlatformBrowser(this.platformId);}}延后到浏览器阶段执行afterNextRender等钩子更贴合SSR语义很多逻辑并不是非要写平台判断而是它本质上就应该发生在浏览器首帧渲染之后比如初始化图表、读取真实布局尺寸、绑定第三方DOM插件。Angular提供了afterNextRender这类 API并明确指出它只会在浏览器平台运行。(Angular)这类 API 的思路很像浏览器渲染管线里的post paint任务把必须依赖真实DOM的操作推迟到正确的时间点执行而不是在SSR阶段硬凑一个window出来。DOM模拟层当你必须在服务器上满足某些DOM依赖时domino是典型工具有些第三方库写得很强势加载时就直接访问document或window根本不给你在业务代码里做平台判断的机会。这个时候工程上常见的补丁方案就是DOM模拟库比如domino。社区教程对domino的定位很明确在Angular SSR场景下如果你需要访问DOMAPI可以使用domino来提供querySelector等能力。(danywalls.com)但这里要强调一个架构层面的判断domino更像是兼容性垫片而不是理想解。原因很简单模拟DOM永远不可能完整覆盖真实浏览器的布局与渲染行为它只能让代码不崩不能保证视觉与交互逻辑一致。实践里更推荐的路线通常是能替换库就替换成SSR friendly的版本不能替换就做平台隔离与懒加载真的没办法再上domino并把它控制在尽量小的范围内。Angular Universal在今天的角色从核心方案降级为兼容路径很多团队仍然在用Angular Universal的Express引擎。nguniversal/express-engine在npm上的描述很直接这是一个Express Engine用于在服务器上运行Angular应用以实现SSR。(npm)把它放到今天的语境里可以把Angular SSR的演进理解为早期Angular Universal是主力方案ng add nguniversal/express-engine负责把SSR脚手架搭起来。现在SSR能力更多被angular/ssr与新构建系统吸收Universal在不少项目里承担的是迁移期兼容与历史包袱承接。这对架构决策的影响是新项目更倾向直接走angular/ssr与官方的Hybrid Rendering流程存量项目则根据成本选择渐进迁移。真实世界案例一个电商站点如何用Angular SSR把性能与成本压到合理区间假设你在做一个跨境电商站点业务目标有三条商品详情页要有稳定的SEO与社交分享卡片首屏要快LCP要稳服务器成本不能炸不能所有页面都请求期SSR。一套常见而有效的组合是/、/campaign/*、/about用PrerenderSSG。这些页面变化不频繁适合直接上CDN命中率极高。(Angular)/product/:id、/category/:id用Server请求期SSR。需要对不同商品动态生成内容同时对搜索引擎友好。(Angular)/account/*、/admin/*用ClientCSR。对SEO没要求优先交互体验与开发效率。(Angular)客户端水合层面开启provideClientHydration基础水合与HttpClient传输缓存。(Angular)withEventReplay或直接上withIncrementalHydration避免用户在首屏点了按钮却没反应。(Angular)withHttpTransferCacheOptions只缓存商品与类目接口过滤掉用户态接口。(Angular)跨平台兼容层面所有依赖window的逻辑都放在浏览器阶段执行要么用isPlatformBrowser保护要么用afterNextRender延迟。(Angular)你会发现这套方案的本质是一种工程化的分层渲染把最贵的请求期SSR留给真正需要的页面把能静态化的页面尽量静态化把交互恢复做得足够平滑避免SSR变成新的体验问题。什么时候SSR反而不该用一条务实的判断线再强调一次SSR不等于无脑更好它会带来服务器运行成本、构建复杂度、缓存策略复杂度、以及第三方库兼容成本。哪怕是很早期的Angular SSR文章也反复提醒过只有在确实需要SSR优势时才值得引入这份复杂度。(ANGULARarchitects)一个比较务实的判断线是强SEO、强分享卡片、首屏必须稳SSR或SSG几乎必选。页面高度个性化且强实时倾向请求期SSR但要做缓存分层与降级策略。登录后系统、内部工具多数情况下CSR足够SSR的收益很难覆盖成本。小结回答Angular用了哪些技术实现SSR把全文收束成清单你问的Angular用了哪些技术实现SSR可以概括为这些关键点框架级服务器渲染能力angular/platform-server提供renderApplication把应用渲染成字符串HTML。(Angular)面向服务器的渲染引擎封装CommonEngine作为通用渲染引擎负责把url、模板、静态资源路径、依赖注入等工程要素串起来。(Angular)运行时与适配层默认Node.jsExpress并通过angular/ssr支持基于Web API Request/Response的非Node.js平台集成。(v19.angular.dev)构建系统支撑新构建系统引入esbuild与Vite并原生集成SSR与Prerender工作流。(Angular)混合渲染编排用RenderMode在路由级别选择SSR/SSG/CSR并通过outputMode等配置控制生成形态。(Angular)客户端恢复与体验增强provideClientHydration提供水合能力配合HttpClient传输缓存withEventReplay解决水合窗口期事件丢失withIncrementalHydration把水合拆成增量过程。(Angular)数据传递与缓存控制withHttpTransferCacheOptions与HttpTransferCacheOptions提供可配置缓存策略减少重复请求。(Angular)跨平台兼容与必要的DOM模拟官方明确禁止在服务器使用window等对象需要用平台判断与延迟执行在极端情况下可用domino做DOMAPI 垫片。(Angular)历史兼容路径Angular Universal的nguniversal/express-engine仍在大量存量项目中使用更多承担迁移与兼容角色。(npm)如果你愿意我也可以按一个你熟悉的项目类型比如企业后台、内容站、跨境电商、B2B门户给出一份更落地的SSR架构蓝图路由渲染模式划分、缓存分层、数据预取策略、以及如何在不牺牲开发体验的前提下把SSR纳入日常迭代。