angular服務端渲染

angular服務端渲染

此項目未使用ng-zorro

基本準備

  1. 建立一個angular項目,ng new 項目名
  2. 運行ng add @nguniversal/express-engine --clientProject 項目名
  3. 安裝一些服務端模塊compression(頁面壓縮),http-proxy-middleware(http代理),multer(文件上傳)

服務端準備

平時開發過程當中,咱們仍然使用以前的模式,自備本地server
  1. 新建文件夾server,該server主要用來存放服務端(相似之前的server.ts和fileUtils.ts)相關的文件
  2. 將server.ts文件移動到server文件夾中,修改webpack.server.config.js文件
entry: {
        server: './server/server.ts' // 這裏改爲這樣
    },
  1. 修改server.ts文件並增長fileUtils.ts文件
import 'zone.js/dist/zone-node';
//新增
import { config } from './config/config.js'
import * as express from 'express';
//新增
import { createProxyMiddleware } from 'http-proxy-middleware'
import { join } from 'path';
import { APP_BASE_HREF } from '@angular/common';
//新增
import fileUtils from './fileUtils';
//新增
import * as multer from 'multer'
//新增
import * as compression from 'compression'

const app = express();
//新增
app.use(compression())
// 修改
const PORT = config.port;
// 修改
const DIST_FOLDER = join(process.cwd(), 'dist/dist');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = require('./dist/server/main');

app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
        provideModuleMap(LAZY_MODULE_MAP)
    ]
}));

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
//新增
var proxyConfig = createProxyMiddleware({
    target: config.proxy.target,
    changeOrigin: true,
    pathRewrite: {
        '^/api': ''
    },
    onProxyReq: function onProxyReq(proxyReq, req, res) {

    }
});
//新增
app.use('/api', proxyConfig)
app.get('*.*', express.static(DIST_FOLDER, {
    maxAge: '1y'
}));
let uploadSingle = multer({
    dest: 'upload/'
});
//新增
app.post('/upload', uploadSingle.single('file'), function (req, res) {
    // 這裏是文件上傳代碼 具體能夠查看詳細文件
});

app.get('*', (req, res) => {
    res.render('index', { req });
});

app.listen(PORT, () => {
    console.log(`Node Express server listening on http://localhost:${PORT}`);
});
  1. server中的config也作了必定的修改,請仔細查看

服務端請求攔截

主要是針對須要渲染數據的頁面,須要服務器渲染的使用絕對路徑(這裏統一配置),詳細說明,官方已指出,地址 運行在服務端時,使用絕對URL發起請求,在瀏覽器中使用相對URL
  1. 新建universal-interceptor.ts文件
import { Injectable, Inject, Optional } from '@angular/core'
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'
import { Request } from 'express'

import { REQUEST } from '@nguniversal/express-engine/tokens'
import { config } from '../../server/config/config.js'

@Injectable()
export class UniversalInterceptor implements HttpInterceptor {
    constructor(@Optional() @Inject(REQUEST) protected request: Request) { }

    // 服務器渲染時 請求攔截,在這裏組裝爲絕對路徑
    // 這裏也能夠對路徑作處理,而後api.service能夠不作處理
    intercept(req: HttpRequest<any>, next: HttpHandler) {
        let serverReq: HttpRequest<any> = req;
        if (this.request) {
             // 這裏的config和server的config是用同一個的,獲取接口的origin部分
            let newUrl = config.proxy.target
            if (!req.url.startsWith('/')) {
                newUrl += '/'
            }
            newUrl += req.url;
            serverReq = req.clone({ url: newUrl })
        }
        return next.handle(serverReq)
    }
}
2. `app.server.module.ts`中引入`universal-interceptor`文件

瀏覽器端渲染數據

主要須要使用 TransferState, ServerTransferStateModule, BrowserTransferStateModule
  1. app.module.ts中引入BrowserTransferStateModule
  2. app.server.module.ts中引入ServerTransferStateModule
  3. 在須要服務端渲染的接口中作判斷
import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common'
import { HttpService } from './http.service'

@Injectable()
export class ApiService {
    //prefix = '/api/v1'
    prefix = '/api/mock/5e9575d592cee10f807aabe6/test'
    prefix_render = '/mock/5e9575d592cee10f807aabe6/test'
    constructor(private http: HttpService, @Inject(PLATFORM_ID) private platformId) { }

    public getIndexData(params) {
        // 在這裏判斷當前環境是不是瀏覽器端
        let url = isPlatformBrowser(this.platformId) ? `${this.prefix}/test` : `${this.prefix_render}/test`
        return this.http.get(url, params)
    }
    public upload(params) {
        let url = '/upload'
        return this.http.upload(url, params)
    }
}
  1. 修改須要渲染的頁面
async getData() {
    // 獲取存儲在TransferState中key爲INDEX_DATA的值
        const kfcList: any[] = this.state.get(INDEX_DATA, null as any); 
        let res;
        // 該判斷主要是爲了處理請求兩次的問題
        if (!kfcList) {
            res = await this.api.getIndexData({ page: this.page.pageNow, pageSize: this.page.pageSize })
            this.list = res['list']
            this.page.total = res['total']
            // 獲取到值後將值存儲到state中
            this.state.set(INDEX_DATA, res) 
        } else {
            // 假如已經請求過了,則將值取出來
            res = this.state.get(INDEX_DATA, { list: [], total: 1 })
            this.list = res.list
        }
        //  判斷當前是不是在瀏覽器環境下,若是是的話計算頁碼
        if (isPlatformBrowser(this.platformId)) {
            this.calcPage(res.total) // 計算頁碼
        }
    }
    // 這個請求主要是分頁請求,是瀏覽器請求,不適用上面那個方法,因此另外新定義了
    async getDataByPage(pageNow) {
        this.page.pageNow = pageNow
        let res = await this.api.getIndexData({ page: pageNow, pageSize: this.page.pageSize })
        this.list = res['list']
    }
相關文章
相關標籤/搜索