首页 >

使用Angular5实现服务端渲染实战

web前端|js教程使用Angular5实现服务端渲染实战
Angular5,服务端渲染
web前端-js教程
本篇文章主要介绍了详解Angular5 服务端渲染实战,现在分享给大家,也给大家做个参考。
php评论回复系统源码,sql格式化vscode,ubuntu主题存放,tomcat端口如何更改,怎么认识爬虫,php 循环 拼接字符串,长沙专业seo优化哪家好,php建立网站代码,报社网站模板lzw
本文基于上一篇 Angular5 的文章继续进行开发,上文中讲了搭建 Angular5 有道翻译的过程,以及遇到问题的解决方案。
酒店信息管理系统源码,vscode运行汇编,ubuntu21.10源,tomcat 内存过大,清除数据sqlite,html5 动画插件,手机端前端ui框架,python爬虫文字,php iis6,seo 网易博客,网站系统破解版,个人简介网页代码模板下载,中文网页买电脑模板,注册页面jq代码模板,crm客户关系管理系统视频下载,vb.net小程序lzw
使用Angular5实现服务端渲染实战
新视频打赏平台源码,ubuntu调不了亮度,tomcat项目名怎么看,java爬虫 js 生成,php 1到100的素数代码,宁波seo主管lzw
随后改了 UI,从 bootstrap4 改到 angular material,这里不详细讲,服务端渲染也与修改 UI 无关。

看过之前文章的人会发现,文章内容都偏向于服务端渲染,vue 的 nuxt,react 的 next。

在本次改版前也尝试去找类似 nuxt.js 与 next.js 的顶级封装库,可以大大节省时间,但是未果。

最后决定使用从 Angular2 开始就可用的前后端同构解决方案 Angular Universal (Universal (isomorphic) JavaScript support for Angular.)

在这里不详细介绍文档内容,本文也尽量使用通俗易懂的语言带入 Angular 的 SSR

前提

前面写的 udao 这个项目是完全遵从于 angular-cli 的,从搭建到打包,这也使得本文通用于所有 angular-cli 搭建的 angular5 项目。

搭建过程

首先安装服务端的依赖

yarn add @angular/platform-server expressyarn add -D ts-loader webpack-node-externals npm-run-all

这里需要注意的是 @angular/platform-server 的版本号最好根据当前 angular 版本进行安装,如: @angular/platform-server@5.1.0 ,避免与其它依赖有版本冲突。

创建文件: src/app/app.server.module.ts

import { NgModule } from '@angular/core'import { ServerModule } from '@angular/platform-server'import { AppModule } from './app.module'import { AppComponent } from './app.component'@NgModule({ imports: [  AppModule,  ServerModule ], bootstrap: [AppComponent],})export class AppServerModule { }

更新文件: src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser'import { NgModule } from '@angular/core'// ...import { AppComponent } from './app.component'// ...@NgModule({ declarations: [  AppComponent  // ... ], imports: [  BrowserModule.withServerTransition({ appId: 'udao' })  // ... ], providers: [], bootstrap: [AppComponent]})export class AppModule { }

我们需要一个主文件来导出服务端模块

创建文件: src/main.server.ts

export { AppServerModule } from './app/app.server.module'

现在来更新 @angular/cli 的配置文件 .angular-cli.json

{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "project": {  "name": "udao" }, "apps": [  {   "root": "src",   "outDir": "dist/browser",   "assets": [    "assets",    "favicon.ico"   ]   // ...  },  {   "platform": "server",   "root": "src",   "outDir": "dist/server",   "assets": [],   "index": "index.html",   "main": "main.server.ts",   "test": "test.ts",   "tsconfig": "tsconfig.server.json",   "testTsconfig": "tsconfig.spec.json",   "prefix": "app",   "scripts": [],   "environmentSource": "environments/environment.ts",   "environments": {    "dev": "environments/environment.ts",    "prod": "environments/environment.prod.ts"   }  } ] // ...}

上面的 // … 代表省略掉,但是 json 没有注释一说,看着怪怪的….

当然 .angular-cli.json 的配置不是固定的,根据需求自行修改

我们需要为服务端创建 tsconfig 配置文件: src/tsconfig.server.json

{ "extends": "../tsconfig.json", "compilerOptions": {  "outDir": "../out-tsc/app",  "baseUrl": "./",  "module": "commonjs",  "types": [] }, "exclude": [  "test.ts",  "**/*.spec.ts",  "server.ts" ], "angularCompilerOptions": {  "entryModule": "app/app.server.module#AppServerModule" }}

然后更新: src/tsconfig.app.json

{ "extends": "../tsconfig.json", "compilerOptions": {  "outDir": "../out-tsc/app",  "baseUrl": "./",  "module": "es2015",  "types": [] }, "exclude": [  "test.ts",  "**/*.spec.ts",  "server.ts" ]}

现在可以执行以下命令,看配置是否有效

ng build -prod --build-optimizer --app 0ng build --aot --app 1

运行结果应该下图例

使用Angular5实现服务端渲染实战

然后就是创建 Express.js 服务, 创建文件: src/server.ts

import 'reflect-metadata'import 'zone.js/dist/zone-node'import { renderModuleFactory } from '@angular/platform-server'import { enableProdMode } from '@angular/core'import * as express from 'express'import { join } from 'path'import { readFileSync } from 'fs'enableProdMode();const PORT = process.env.PORT || 4200const DIST_FOLDER = join(process.cwd(), 'dist')const app = express()const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString()const { AppServerModuleNgFactory } = require('main.server')app.engine('html', (_, options, callback) => { const opts = { document: template, url: options.req.url } renderModuleFactory(AppServerModuleNgFactory, opts)  .then(html => callback(null, html))});app.set('view engine', 'html')app.set('views', 'src')app.get('*.*', express.static(join(DIST_FOLDER, 'browser')))app.get('*', (req, res) => { res.render('index', { req })})app.listen(PORT, () => { console.log(`listening on http://localhost:${PORT}!`)})

理所当然需要一个 webpack 配置文件来打包 server.ts 文件: webpack.config.js

const path = require('path');var nodeExternals = require('webpack-node-externals');module.exports = { entry: {  server: './src/server.ts' }, resolve: {  extensions: ['.ts', '.js'],  alias: {   'main.server': path.join(__dirname, 'dist', 'server', 'main.bundle.js')  } }, target: 'node', externals: [nodeExternals()], output: {  path: path.join(__dirname, 'dist'),  filename: '[name].js' }, module: {  rules: [   { test: /\.ts$/, loader: 'ts-loader' }  ] }}

为了打包方便最好在 package.json 里面加几行脚本,如下:

"scripts": { "ng": "ng", "start": "ng serve", "build": "run-s build:client build:aot build:server", "build:client": "ng build -prod --build-optimizer --app 0", "build:aot": "ng build --aot --app 1", "build:server": "webpack -p", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e"}

现在尝试运行 npm run build ,将会看到如下输出:

使用Angular5实现服务端渲染实战

node 运行刚刚打包好的 node dist/server.js 文件

打开 http://localhost:4200/ 会正常显示项目主页面

使用Angular5实现服务端渲染实战

从上面的开发者工具可以看出 html 文档是服务端渲染直出的,接下来尝试请求数据试一下。

注意:本项目显式(菜单可点击)的几个路由初始化都没有请求数据,但是单词解释的详情页是会在 ngOnInit() 方法里获取数据,例如: http://localhost:4200/detail/add 直接打开时会发生奇怪的现象,请求在服务端和客户端分别发送一次,正常的服务端渲染项目首屏初始化数据的请求在服务端执行,在客户端不会二次请求!

发现问题后,就来踩平这个坑

试想如果采用一个标记来区分服务端是否已经拿到了数据,如果没拿到数据就在客户端请求,如果已经拿到数据就不发请求

当然 Angular 早有一手准备,那就是 Angular Modules for Transfer State

那么如何真实运用呢?见下文

请求填坑

在服务端入口和客户端入口分别引入 TransferStateModule

import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';// ...@NgModule({ imports: [  // ...  ServerModule,  ServerTransferStateModule ] // ...})export class AppServerModule { }import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';// ...@NgModule({ declarations: [  AppComponent  // ... ], imports: [  BrowserModule.withServerTransition({ appId: 'udao' }),  BrowserTransferStateModule  // ... ] // ...})export class AppModule { }

以本项目为例在 detail.component.ts 里面,修改如下

import { Component, OnInit } from '@angular/core'import { HttpClient } from '@angular/common/http'import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'import { TransferState, makeStateKey } from '@angular/platform-browser'const DETAIL_KEY = makeStateKey('detail')// ...export class DetailComponent implements OnInit { details: any // some variable constructor(  private http: HttpClient,  private state: TransferState,  private route: ActivatedRoute,  private router: Router ) {} transData (res) {  // translate res data } ngOnInit () {  this.details = this.state.get(DETAIL_KEY, null as any)  if (!this.details) {   this.route.params.subscribe((params) => {    this.loading = true    const apiURL = `https://dict.youdao.com/jsonapi?q=${params['word']}`    this.http.get(`/?url=${encodeURIComponent(apiURL)}`)    .subscribe(res => {     this.transData(res)     this.state.set(DETAIL_KEY, res as any)     this.loading = false    })   })  } else {   this.transData(this.details)  } }}

代码够简单清晰,和上面描述的原理一致

现在我们只需要对 main.ts 文件进行小小的调整,以便在 DOMContentLoaded 时运行我们的代码,以使 TransferState 正常工作:

import { enableProdMode } from '@angular/core'import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'import { AppModule } from './app/app.module'import { environment } from './environments/environment'if (environment.production) { enableProdMode()}document.addEventListener('DOMContentLoaded', () => { platformBrowserDynamic().bootstrapModule(AppModule)  .catch(err => console.log(err))})

到这里运行 npm run build && node dist/server.js 然后刷新 http://localhost:4200/detail/add 到控制台查看 network 如下:

使用Angular5实现服务端渲染实战

发现 XHR 分类里面没有发起任何请求,只有 service-worker 的 cache 命中。

到这里坑都踩完了,项目运行正常,没发现其它 bug。

总结

2018 第一篇,目的就是探索所有流行框架服务端渲染的实现,开辟了 angular 这个最后没尝试的框架。

当然 Orange 还是前端小学生一枚,只知道实现,原理说的不是很清楚,源码看的不是很明白,如有纰漏还望指教。

最后 Github 地址和之前文章一样:https://github.com/OrangeXC/udao

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详解如何实现vuex(详细教学)

通过vue.js实现微信支付

在Vue2.0中实现用户权限控制


使用Angular5实现服务端渲染实战
  • Angular5中提取公共组件之radio list的实例代码_AngularJS
  • Angular5中提取公共组件之radio list的实例代码_AngularJS | Angular5中提取公共组件之radio list的实例代码_AngularJS ...

    使用Angular5实现服务端渲染实战
  • Angular如何制作animations动画三?这里有angularjs制作animations动画的详情
  • Angular如何制作animations动画三?这里有angularjs制作animations动画的详情 | Angular如何制作animations动画三?这里有angularjs制作animations动画的详情 ...

    使用Angular5实现服务端渲染实战
  • 在Angular5中如何调用第三方js插件(详细教程)
  • 在Angular5中如何调用第三方js插件(详细教程) | 在Angular5中如何调用第三方js插件(详细教程) ...