官网地址:https://angular.io/guide/universal
I have mainly used angular universal for SEO purposes. In that, the server will render enough information on the page so that when Google crawls the page, you can have the server load some asynchronous data (eg. a description of a product from a databse) before serving the page so the crawler sees the information.
使用服务器端渲染,可以提前让服务器加载一些异步数据,比如从数据库里读取产品的描述信息,便于爬虫读取。
Asynchronously loaded html via javascript is often missed by crawlers.
网络爬虫一般不会通过执行 JavaScript 的方式去异步加载 HTML.
so you need the server to run a bit of javascript before serving the page to the client sometimes.
因此通常情况下,我们需要服务器先执行一些 JavaScript 片段,才能将生成的页面返回给客户端。
SSR can generate a static version of a page and serve it. That doesn’t mean it will be fully functional.
SSR 可以生成一个应用的静态版本,但并不意味着该版本完全可用。
Serve, as needed, a static version of the page with most of the DOM elements preloaded, instead of waiting for angular to bootstrap and do this asynchronously via Javascript.
应用页面的静态版本,大多数 DOM 元素都已经提前加载。这种情况下,不需要等待 Angular 完成 bootstrap 后,再执行 JavaScript 完成页面异步加载的效果。
To support offline mode, it has to be combined with pwa techniques, such as caching from the serviceWorker, getting data from local/session storage/ indexedDB, etc.
为了支持离线模式,SSR 需要和 PWA 技术配合起来使用,比如基于 Service Worker 的缓存,从 local / session 存储介质里读取数据,等等。
Universal web servers
A Universal web server responds to application page requests with static HTML rendered by the Universal template engine.
Universal web 服务器,使用 Universal 模板引擎,给应用程序的页面请求,回复一个静态版本的渲染完毕的 HTML 页面。
The server receives and responds to HTTP requests from clients (usually browsers), and serves static assets such as scripts, CSS, and images.
服务器接收到从客户端(通常情况下都是浏览器)发起的 HTTP 请求,并且以静态资源,比如 scripts,CSS 和图片的方式来响应。
It may respond to data requests, either directly or as a proxy to a separate data server.
它也能响应数据请求,或者直接响应,或者作为代理,将该数据请求转发给另外的数据服务器。
The sample web server for this guide is based on the popular Express framework.
Any web server technology can serve a Universal app as long as it can call Universal's renderModule() function. The principles and decision points discussed here apply to any web server technology.
任何 web 服务器都能够架设 Universal 应用,只要其能够调用 Universal 的 renderModule() 方法。
Universal applications use the Angular platform-server package (as opposed to platform-browser)
Universal 应用使用 Angular platform-server 包
which provides server implementations of the DOM, XMLHttpRequest, and other low-level features that don't rely on a browser.
这个包提供了服务器端的 DOM 实现,XMLHttpRequest,以及其他不依赖于浏览器的底层特性。
The server (Node.js Express in this guide's example) passes client requests for application pages to the NgUniversal ngExpressEngine.
服务器(在这个学习笔记里,服务器由 nodejs Express 实现) 将客户端对于应用页面的请求,转发给 NgUniversal Express engine.
Under the hood, this calls Universal's renderModule() function, while providing caching and other helpful utilities.
其底层实现原理是,调用 Universal renderModule 函数,提供缓存和其他功能。
The renderModule() function takes as inputs a template HTML page (usually index.html), an Angular module containing components, and a route that determines which components to display. The route comes from the client's request to the server.
renderModule 的输入:
- HTML 模板页面,通常为 index.html
- 一个包含 Components 的 Angular module
- 一个 route,决定哪些 Component 应该显示
Each request results in the appropriate view for the requested route. The renderModule() function renders the view within the
tag of the template, creating a finished HTML page for the client.
每个请求最终会导致其对应的路由视图被渲染在模板的 app 标签里。
Because a Universal app doesn't execute in the browser, some of the browser APIs and capabilities may be missing on the server.
Universal 应用并不会执行在浏览器环境里,因此部分浏览器 API 和功能,很可能在服务器端不可用。
For example, server-side applications can't reference browser-only global objects such as window, document, navigator, or location.
例如,服务器端应用无法使用只有在浏览器端可用的 window,document,navigator,location 等全局对象。
Angular provides some injectable abstractions over these objects, such as Location or DOCUMENT; it may substitute adequately for these APIs.
针对这些对象,Angular 提供了可注入的抽象,比如 Location 或者 DOCUMENT.
If Angular doesn't provide it, it's possible to write new abstractions that delegate to the browser APIs while in the browser and to an alternative implementation while on the server (aka shimming).
当然我们也能自行在应用程序里编写这些抽象逻辑,即所谓的 shimming.
Similarly, without mouse or keyboard events, a server-side app can't rely on a user clicking a button to show a component.
相应的,因为缺少鼠标或者键盘事件,服务器端应用无法基于用户点击按钮的方式来触发 Component 的显示。
The app must determine what to render based solely on the incoming client request. This is a good argument for making the app routable.
应用必须能够纯粹基于客户端发起的请求来判断出什么内容需要渲染。这是让应用支持路由的一个极佳的理由。
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
The ngExpressEngine() function is a wrapper around Universal's renderModule() function which turns a client's requests into server-rendered HTML pages.
ngExpressEngine 函数是 Universal renderModule 函数的一个 wrapper,将客户端请求转换成服务器端渲染好的 HTML 页面。
接收的输入参数:
bootstrap: The root NgModule or NgModule factory to use for bootstraping the app when rendering on the server.
根 NgModule 或者 NgModule 工厂,用于 bootstrap 在服务器端渲染的应用。
For the example app, it is AppServerModule.
It's the bridge between the Universal server-side renderer and the Angular application.
AppServerModule 是 Universal 服务器端渲染器和 Angular 应用之间的桥梁。
SSR 需要对下列三种不同类型的请求分别进行处理:
- Data request: request URL that begins /api. 数据请求,url 里常包含诸如 api 类型的片段。
- App navigation: request URL with no file extension.路由请求,不包含文件扩展名。
- Static asset: all other requests.静态资源请求。
A Node.js Express server is a pipeline of middleware that filters and processes requests one after the other.
nodejs Express 服务器实际是一种中间件传播途径,以一个接一个的方式,过滤和处理请求。
下列的代码,将所有不包含 file extension 的请求,都当作是导航请求进行处理:
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in the /dist folder and only honor requests for files from the /dist folder.
一个最佳实践是,为了确保客户端只能下载其权限允许范围内的文件,将所有该类型的文件都放到 /dist 文件夹里。
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
In a server-side rendered app, HTTP URLs must be absolute (for example, https://my-server.com/api/heroes).
服务器端渲染,HTTP URL 必须是绝对路径。
This means that the URLs must be somehow converted to absolute when running on the server and be left relative when running in the browser.
这意味着,当运行在服务器端时,url 必须被转换成绝对路径,而运行在浏览器端时,可以仍然保留成相对路径。
If you are using one of the @nguniversal/*-engine packages (such as @nguniversal/express-engine), this is taken care for you automatically. You don't need to do anything to make relative URLs work on the server.
@nguniversal/express-engine 会帮助我们自动完成绝对路径转相对路径的操作。
更多Jerry的原创文章,尽在:"汪子熙":