欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Angular5 服務(wù)端渲染實戰(zhàn)

 更新時間:2018年01月04日 09:35:16   作者:orangexc  
本篇文章主要介紹了詳解Angular5 服務(wù)端渲染實戰(zhàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

本文基于上一篇 Angular5 的文章繼續(xù)進行開發(fā),上文中講了搭建 Angular5 有道翻譯的過程,以及遇到問題的解決方案。

 

隨后改了 UI,從 bootstrap4 改到 angular material,這里不詳細講,服務(wù)端渲染也與修改 UI 無關(guān)。

看過之前文章的人會發(fā)現(xiàn),文章內(nèi)容都偏向于服務(wù)端渲染,vue 的 nuxt,react 的 next。

在本次改版前也嘗試去找類似 nuxt.js 與 next.js 的頂級封裝庫,可以大大節(jié)省時間,但是未果。

最后決定使用從 Angular2 開始就可用的前后端同構(gòu)解決方案 Angular Universal (Universal (isomorphic) JavaScript support for Angular.)

在這里不詳細介紹文檔內(nèi)容,本文也盡量使用通俗易懂的語言帶入 Angular 的 SSR

前提

前面寫的 udao 這個項目是完全遵從于 angular-cli 的,從搭建到打包,這也使得本文通用于所有 angular-cli 搭建的 angular5 項目。

搭建過程

首先安裝服務(wù)端的依賴

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

這里需要注意的是 @angular/platform-server 的版本號最好根據(jù)當前 angular 版本進行安裝,如: @angular/platform-server@5.1.0 ,避免與其它依賴有版本沖突。

創(chuàng)建文件: 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 { }

我們需要一個主文件來導(dǎo)出服務(wù)端模塊

創(chuàng)建文件: src/main.server.ts

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

現(xiàn)在來更新 @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 的配置不是固定的,根據(jù)需求自行修改

我們需要為服務(wù)端創(chuàng)建 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"
 ]
}

現(xiàn)在可以執(zhí)行以下命令,看配置是否有效

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

運行結(jié)果應(yīng)該如下圖所示

 

然后就是創(chuàng)建 Express.js 服務(wù), 創(chuàng)建文件: 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 || 4200
const 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"
}

現(xiàn)在嘗試運行 npm run build ,將會看到如下輸出:

 

node 運行剛剛打包好的 node dist/server.js 文件

打開 http://localhost:4200/ 會正常顯示項目主頁面

 

從上面的開發(fā)者工具可以看出 html 文檔是服務(wù)端渲染直出的,接下來嘗試請求數(shù)據(jù)試一下。

注意:本項目顯式(菜單可點擊)的幾個路由初始化都沒有請求數(shù)據(jù),但是單詞解釋的詳情頁是會在 ngOnInit() 方法里獲取數(shù)據(jù),例如: http://localhost:4200/detail/add 直接打開時會發(fā)生奇怪的現(xiàn)象,請求在服務(wù)端和客戶端分別發(fā)送一次,正常的服務(wù)端渲染項目首屏初始化數(shù)據(jù)的請求在服務(wù)端執(zhí)行,在客戶端不會二次請求!

發(fā)現(xiàn)問題后,就來踩平這個坑

試想如果采用一個標記來區(qū)分服務(wù)端是否已經(jīng)拿到了數(shù)據(jù),如果沒拿到數(shù)據(jù)就在客戶端請求,如果已經(jīng)拿到數(shù)據(jù)就不發(fā)請求

當然 Angular 早有一手準備,那就是 Angular Modules for Transfer State

那么如何真實運用呢?見下文

請求填坑

在服務(wù)端入口和客戶端入口分別引入 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)
  }
 }
}

代碼夠簡單清晰,和上面描述的原理一致

現(xiàn)在我們只需要對 main.ts 文件進行小小的調(diào)整,以便在 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 如下:

 

發(fā)現(xiàn) XHR 分類里面沒有發(fā)起任何請求,只有 service-worker 的 cache 命中。

到這里坑都踩完了,項目運行正常,沒發(fā)現(xiàn)其它 bug。

總結(jié)

2018 第一篇,目的就是探索所有流行框架服務(wù)端渲染的實現(xiàn),開辟了 angular 這個最后沒嘗試的框架。

當然 Orange 還是前端小學生一枚,只知道實現(xiàn),原理說的不是很清楚,源碼看的不是很明白,如有紕漏還望指教。

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • AngularJS 如何在控制臺進行錯誤調(diào)試

    AngularJS 如何在控制臺進行錯誤調(diào)試

    本文主要介紹AngularJS 如何在控制臺進行錯誤調(diào)試,還不錯,分享給大家,希望給大家做一個參考。
    2016-06-06
  • 如何在Angular.JS中接收并下載PDF

    如何在Angular.JS中接收并下載PDF

    最近這兩天公司正在做一個PDF協(xié)議下載的功能,目前的解決方案可以分為完全前端生成和后端生成兩種方式。前端生成PDF有jsPDF 和pdfmake 兩種方式,下面這篇文章就給大家分享下如何在Angular.JS中接收并下載PDF的方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-11-11
  • Angular處理未可知異常錯誤的方法詳解

    Angular處理未可知異常錯誤的方法詳解

    這篇文章主要給大家介紹了關(guān)于Angular如何處理未可知異常錯誤的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • 使用Angular Cli如何創(chuàng)建Angular私有庫詳解

    使用Angular Cli如何創(chuàng)建Angular私有庫詳解

    這篇文章主要給大家介紹了關(guān)于使用Angular Cli如何創(chuàng)建Angular私有庫的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-01-01
  • 三種AngularJS中獲取數(shù)據(jù)源的方式

    三種AngularJS中獲取數(shù)據(jù)源的方式

    這篇文章主要介紹了三種AngularJS中獲取數(shù)據(jù)源的方式,需要的朋友可以參考下
    2016-02-02
  • AngularJS入門教程之數(shù)據(jù)綁定用法示例

    AngularJS入門教程之數(shù)據(jù)綁定用法示例

    這篇文章主要介紹了AngularJS之數(shù)據(jù)綁定用法,結(jié)合實例形式分析了AngularJS基于內(nèi)置指令ng-model實現(xiàn)數(shù)據(jù)綁定的操作技巧,需要的朋友可以參考下
    2016-11-11
  • Angular 封裝并發(fā)布組件的方法示例

    Angular 封裝并發(fā)布組件的方法示例

    本篇文章主要介紹了Angular 封裝并發(fā)布組件的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Angular 4.x中表單Reactive Forms詳解

    Angular 4.x中表單Reactive Forms詳解

    這篇文章主要介紹了Angular 4.x中表單Reactive Forms的相關(guān)資料,文中通過示例代碼介紹的非常詳細,相信對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。
    2017-04-04
  • 利用angular.copy取消變量的雙向綁定與解析

    利用angular.copy取消變量的雙向綁定與解析

    眾所周知AngularJS的雙向綁定在表單應(yīng)用中強大又方便,但是偶爾會遇到需要解除對象變量的雙向綁定。Angular提供的angular.copy的方法可以實現(xiàn)解除雙向綁定。所以這篇文章就來給大家詳細的介紹下angular.copy,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-11-11
  • AngularJS入門心得之directive和controller通信過程

    AngularJS入門心得之directive和controller通信過程

    Angular JS (Angular.JS) 是一組用來開發(fā)Web頁面的框架、模板以及數(shù)據(jù)綁定和豐富UI組件,接下來通過本文給大家介紹AngularJS入門心得之directive和controller通信過程,對angularjs相關(guān)知識感興趣的朋友一起學習吧
    2016-01-01

最新評論