小程序自助开发工具_Angular开发实践之效劳端烘托

  • 栏目:行业动态 时间:2021-01-08 15:48 分享新闻到:
<返回列表

Angular开发实践之服务端渲染       这篇文章主要介绍了Angular开发实践之服务端渲染,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Angular Universal

Angular在服务端渲染方面提供一套前后端同构解决方案,它就是(统一平台),一项在服务端运行 Angular 应用的技术。

标准的 Angular 应用会执行在浏览器中,它会在 DOM 中渲染页面,以响应用户的操作。

而 Angular Universal 会在服务端通过一个被称为服务端渲染(server-side rendering - SSR)的过程生成静态的应用页面。

它可以生成这些页面,并在浏览器请求时直接用它们给出响应。 它也可以把页面预先生成为 HTML 文件,然后把它们作为静态文件供服务器使用。

工作原理

要制作一个 Universal 应用,就要安装 platform-server 包。 platform-server 包提供了服务端的 DOM 实现、XMLHttpRequest 和其它底层特性,但不再依赖浏览器。

你要使用 platform-server 模块而不是 platform-browser 模块来编译这个客户端应用,并且在一个 Web 服务器上运行这个 Universal 应用。

服务器(下面的示例中使用的是 Node Express 服务器)会把客户端对应用页面的请求传给 renderModuleFactory 函数。

<)、一个包含组件的 Angular 模块和一个用于决定该显示哪些组件的路由作为输入。

该路由从客户端的请求中传给服务器。 每次请求都会给出所请求路由的一个适当的视图。

renderModuleFactory 在模板中的 app 标记中渲染出哪个视图,并为客户端创建一个完成的 HTML 页面。

最后,服务器就会把渲染好的页面返回给客户端。

为什么要服务端渲染

三个主要原因:

帮助网络爬虫(SEO) 提升在手机和低功耗设备上的性能 迅速显示出第一个页面

帮助网络爬虫(SEO)

Google、Bing、百度、Facebook、Twitter 和其它搜索引擎或社交媒体网站都依赖网络爬虫去索引你的应用内容,并且让它的内容可以通过网络搜索到。

这些网络爬虫可能不会像人类那样导航到你的具有高度交互性的 Angular 应用,并为其建立索引。

Angular Universal 可以为你生成应用的静态版本,它易搜索、可链接,浏览时也不必借助 JavaScript。它也让站点可以被预览,因为每个 URL 返回的都是一个完全渲染好的页面。

启用网络爬虫通常被称为搜索引擎优化 (SEO)。

提升手机和低功耗设备上的性能

有些设备不支持 JavaScript 或 JavaScript 执行得很差,导致用户体验不可接受。 对于这些情况,你可能会需要该应用的服务端渲染、无 JavaScript 的版本。 虽然有一些限制,不过这个版本可能是那些完全没办法使用该应用的人的唯一选择。

快速显示首页

快速显示首页对于吸引用户是至关重要的。

如果页面加载超过了三秒中,那么 53% 的移动网站会被放弃。 你的应用需要启动的更快一点,以便在用户决定做别的事情之前吸引他们的注意力。

使用 Angular Universal,你可以为应用生成“着陆页”,它们看起来就和完整的应用一样。 这些着陆页是纯 HTML,并且即使 JavaScript 被禁用了也能显示。 这些页面不会处理浏览器事件,不过它们可以用 routerLink 在这个网站中导航。

在实践中,你可能要使用一个着陆页的静态版本来保持用户的注意力。 同时,你也会在幕后加载完整的 Angular 应用。 用户会认为着陆页几乎是立即出现的,而当完整的应用加载完之后,又可以获得完全的交互体验。

示例解析

下面将基于我在GitHub上的示例项目 来进行讲解。

这个项目与第一篇的示例项目一样,都是基于 Angular CLI进行开发构建的,因此它们的区别只在于服务端渲染所需的那些配置上。

安装工具

在开始之前,下列包是必须安装的(示例项目均已配置好,只需 npm install 即可):

@angular/platform-server - Universal 的服务端元件。 @nguniversal/module-map-ngfactory-loader - 用于处理服务端渲染环境下的惰性加载。 @nguniversal/express-engine - Universal 应用的 Express 引擎。 ts-loader - 用于对服务端应用进行转译。 express - Node Express 服务器

使用下列命令安装它们:

npm install --save @angular/platform-server @nguniversal/module-map-ngfactory-loader ts-loader @nguniversal/express-engine express

项目配置

配置工作有:

创建服务端应用模块:src/app/app.server.module.ts 修改客户端应用模块:src/app/app.module.ts 创建服务端应用的引导程序文件:src/main.server.ts 修改客户端应用的引导程序文件:src/main.ts 创建 TypeScript 的服务端配置:src/tsconfig.server.json 修改 @angular/cli 的配置文件:.angular-cli.json 创建 Node Express 的服务程序:server.ts 创建服务端预渲染的程序:prerender.ts 创建 Webpack 的服务端配置:webpack.server.config.js

1、创建服务端应用模块:src/app/app.server.module.ts

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppBrowserModule } from './app.module';
import { AppComponent } from './ponent';
// 可以注册那些在 Universal 环境下运行应用时特有的服务提供商
@NgModule({
 imports: [
 AppBrowserModule, // 客户端应用的 AppModule
 ServerModule, // 服务端的 Angular 模块
 ModuleMapLoaderModule, // 用于实现服务端的路由的惰性加载
 ServerTransferStateModule, // 在服务端导入,用于实现将状态从服务器传输到客户端
 bootstrap: [AppComponent],
export class AppServerModule {
}

服务端应用模块(习惯上叫作 AppServerModule)是一个 Angular 模块,它包装了应用的根模块 AppModule,以便 Universal 可以在你的应用和服务器之间进行协调。 AppServerModule 还会告诉 Angular 再把你的应用以 Universal 方式运行时,该如何引导它。

2、修改客户端应用模块:src/app/app.module.ts

@NgModule({
 imports: [
 AppRoutingModule,
 BrowserModule.withServerTransition({appId: 'my-app'}),
 TransferHttpCacheModule, // 用于实现服务器到客户端的请求传输缓存,防止客户端重复请求服务端已完成的请求
 BrowserTransferStateModule, // 在客户端导入,用于实现将状态从服务器传输到客户端
 HttpClientModule
 declarations: [
 AppComponent,
 HomeComponent
 providers: [],
 bootstrap: [AppComponent]
export class AppBrowserModule {
 constructor(@Inject(PLATFORM_ID) private platformId: Object,
 @Inject(APP_ID) private appId: string) {
 // 判断运行环境为客户端还是服务端
 const platform = isPlatformBrowser(platformId) 'in the browser' : 'on the server';
 console.log(`Running ${platform} with appId=${appId}`);
}

将 NgModule 的元数据中 BrowserModule 的导入改成 BrowserModule.withServerTransition({appId: 'my-app'}),Angular 会把 appId 值(它可以是任何字符串)添加到服务端渲染页面的样式名中,以便它们在客户端应用启动时可以被找到并移除。

此时,我们可以通过依赖注入(@Inject(PLATFORM_ID) 及 @Inject(APP_ID))取得关于当前平台和 appId 的运行时信息:

constructor(@Inject(PLATFORM_ID) private platformId: Object,
 @Inject(APP_ID) private appId: string) {
 // 判断运行环境为客户端还是服务端
 const platform = isPlatformBrowser(platformId) 'in the browser' : 'on the server';
 console.log(`Running ${platform} with appId=${appId}`);
}

3、创建服务端应用的引导程序文件:src/main.server.ts

该文件导出服务端模块:

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

4、修改客户端应用的引导程序文件:src/main.ts

监听 DOMContentLoaded 事件,在发生 DOMContentLoaded 事件时运行我们的代码,以使 TransferState 正常工作

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppBrowserModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
 enableProdMode();
// 在 DOMContentLoaded 时运行我们的代码,以使 TransferState 正常工作
document.addEventListener('DOMContentLoaded', () = {
 platformBrowserDynamic().bootstrapModule(AppBrowserModule);
});

5、创建 TypeScript 的服务端配置:src/tsconfig.server.json

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

与 tsconfig.app.json 的差异在于:

monjs,这样它才能被 require() 方法导入你的服务端应用。

angularCompilerOptions 部分有一些面向 AOT 编译器的选项:

entryModule - 服务端应用的根模块,其格式为 path/to/file#ClassName。

6、修改 @angular/cli 的配置文件:.angular-cli.json

在 ap凡科抠图 下添加:

 "platform": "server",
 "root": "src",
 "outDir": "dist/server",
 "assets": [
 "assets",
 "favicon.ico"
 "",
 "main": "main.server.ts",
 "test": "test.ts",
 "tsconfig": "tsconfig.server.json",
 "testTsconfig": "tsconfig.spec.json",
 "prefix": "",
 "styles": [
 "styles.scss"
 "scripts": [],
 "environmentSource": "environments/environment.ts",
 "environments": {
 "dev": "environments/environment.ts",
 "prod": "environments/environment.prod.ts"
}

7、创建 Node Express 的服务程序:server.ts

import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '@angular/core';
import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');
//  we'll use as our template
')).toString();
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main.bundle');
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
// Our Universal express-engine (found @ angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
 bootstrap: AppServerModuleNgFactory,
 providers: [
 provideModuleMap(LAZY_MODULE_MAP)
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
/* - Example Express Rest API endpoints -
 app.get('/api/**', (req, res) = { });
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), {
 maxAge: '1y'
// ALl regular routes use the Universal engine
app.get('*', (req, res) = {
 res.render('index', {req});
// Start up the Node server
app.listen(PORT, () = {
 console.log(`Node Express server listening on pre>

8、创建服务端预渲染的程序:prerender.ts

// Load zone.js for the server.
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { enableProdMode } from '@angular/core';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
import { renderModuleFactory } from '@angular/platform-server';
import { ROUTES } from './static.paths';
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main.bundle');
const BROWSER_FOLDER = join(process.cwd(), 'browser');
//  file containing referances to your application bundle.
'), 'utf8');
let previousRender = Promise.resolve();
// Iterate each route path
ROUTES.forEach(route = {
 const fullPath = join(BROWSER_FOLDER, route);
 // Make sure the directory structure is there
 if (!existsSync(fullPath)) {
 mkdirSync(fullPath);
 // , replacing the file if it already exists.
 previousRender = previousRender.then(_ = renderModuleFactory(AppServerModuleNgFactory, {
 document: index,
 url: route,
 extraProviders: [
 provideModuleMap(LAZY_MODULE_MAP)
 })).'), html));
});

9、创建 Webpack 的服务端配置:webpack.server.config.js

Universal 应用不需要任何额外的 Webpack 配置,Angular CLI 会帮我们处理它们。但是由于本例子的 Node Express 的服务程序是 TypeScript 应用(server.ts及prerender.ts),所以要使用 Webpack 来转译它。这里不讨论 Webpack 的配置,需要了解的移步

// Work around for angular/angular-cli/issues/7200
const path = require('path');
const webpack = require('webpack');
module.exports = {
 entry: {
 server: './server.ts', // This is our Express server for Dynamic universal
 prerender: './prerender.ts' // This is an example of Static prerendering (generative)
 target: 'node',
 resolve: {extensions: ['.ts', '.js']},
 externals: [/(node_modules|main\..*\.js)/,], // Make sure we include all node_modules etc
 output: {
 path: path.join(__dirname, 'dist'), // Puts the output at the root of the dist folder
 filename: '[name].js'
 module: {
 rules: [
 {test: /\.ts$/, loader: 'ts-loader'}
 plugins: [
 new webpack.ContextReplacementPlugin(
 /(.+) angular(\\|\/)core(.+) /, // fixes WARNING Critical dependency: the request of a dependency is an expression
 path.join(__dirname, 'src'), // location of your src
 {} // a map of your routes
 new webpack.ContextReplacementPlugin(
 /(.+) express(\\|\/)(.+) /, // fixes WARNING Critical dependency: the request of a dependency is an expression
 path.join(__dirname, 'src'),
};

测试配置

通过上面的配置,我们就制作完成一个可在服务端渲染的 Angular Universal 应用。

在 package.json 的 scripts 区配置 build 和 serve 有关的命令:

 "scripts": {
 "ng": "ng",
 "start": "ng serve -o",
 "ssr": "npm run build:ssr npm run serve:ssr",
 "prerender": "npm run build:prerender npm run serve:prerender",
 "build": "ng build",
 "build:client-and-server-bundles": "ng build --prod ng build --prod --app 1 --output-hashing=false",
 "build:prerender": "npm run build:client-and-server-bundles npm run webpack:server npm run generate:prerender",
 "build:ssr": "npm run build:client-and-server-bundles npm run webpack:server",
 "generate:prerender": "cd dist node prerender",
 "webpack:server": "webpack --config webpack.server.config.js --progress --colors",
 "serve:prerender": "cd dist/browser http-server",
 "serve:ssr": "node dist/server"
}

开发只需运行 npm run start执行 npm run ssr 编译应用程序,并启动一个Node Express来为应用程序提供服务 p>

dist目录:

执行npm run prerender - 编译应用程序并预渲染应用程序文件,启动一个演示http服务器,以便您可以查看它 p>

注意: 要将静态网站部署到静态托管平台,您必须部署dist/browser文件夹, 而不是dist文件夹

dist目录:

根据项目实际的路由信息并在根目录的 static.paths.ts 中配置,提供给 prerender.ts 解析使用。

export const ROUTES = [
 '/',
 '/lazy'
];
<,/lazy 对应 /。

服务器到客户端的状态传输

在前面的介绍中,我们在 app.server.module.ts 中导入了 ServerTransferStateModule,在 app.module.ts 中导入了 BrowserTransferStateModule 和 TransferHttpCacheModule。

这三个模块都与服务器到客户端的状态传输有关:

ServerTransferStateModule:在服务端导入,用于实现将状态从服务器传输到客户端 BrowserTransferStateModule:在客户端导入,用于实现将状态从服务器传输到客户端 TransferHttpCacheModule:用于实现服务器到客户端的请求传输缓存,防止客户端重复请求服务端已完成的请求

使用这几个模块,可以解决 http请求在服务端和客户端分别请求一次 的问题。

ponent.ts 中有如下代码:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from 'mon/http';
import { Observable } from 'rxjs/Observable';
@Component({
 selector: 'app-home',
 ',
 styleUrls: ['./ponent.scss']
export class HomeComponent implements OnInit, OnDestroy {
 constructor(public http: HttpClient) {
 ngOnInit() {
 this.poiSearch(this.keyword, '北京市').subscribe((data: any) = {
 console.log(data);
 ngOnDestroy() {
 poiSearch(text: string, city : string): Observable any {
 return this.http.get(encodeURI(`v3/place/text keywords=${text} city=${city} offset=20 key=55f90fba2c71d0488db9 extensions=all`));
}

代码运行之后,

服务端请求并打印:

客户端再一次请求并打印:

方法1:使用 TransferHttpCacheModule

使用 TransferHttpCacheModule 很简单,代码不需要改动。在 app.module.ts 中导入之后,Angular自动会将服务端请求缓存到客户端,换句话说就是服务端请求到数据会自动传输到客户端,客户端接收到数据之后就不会再发送请求了。

方法2:使用 BrowserTransferStateModule

该方法稍微复杂一些,需要改动一些代码。

ponent.ts 代码如下:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { HttpClient } from 'mon/http';
import { Observable } from 'rxjs/Observable';
const KFCLIST_KEY = makeStateKey('kfcList');
@Component({
 selector: 'app-home',
 ',
 styleUrls: ['./ponent.scss']
export class HomeComponent implements OnInit, OnDestroy {
 constructor(public http: HttpClient,
 private state: TransferState) {
 ngOnInit() {
 // 采用一个标记来区分服务端是否已经拿到了数据,如果没拿到数据就在客户端请求,如果已经拿到数据就不发请求
 const kfcList:any[] = this.state.get(KFCLIST_KEY, null as any);
 if (!this.kfcList) {
 this.poiSearch(this.keyword, '北京市').subscribe((data: any) = {
 console.log(data);
 this.state.set(KFCLIST_KEY, data as any); // 存储数据
 ngOnDestroy() {
 if (typeof window === 'object') {
 this.state.set(KFCLIST_KEY, null as any); // 删除数据
 poiSearch(text: string, city : string): Observable any {
 return this.http.get(encodeURI(`v3/place/text keywords=${text} city=${city} offset=20 key=55f90fba2c71d0488db9 extensions=all`));
}

使用 const KFCLIST_KEY = makeStateKey('kfcList') 创建储存传输数据的 StateKey 在 HomeComponent 的构造函数中注入 TransferState 在 ngOnInit 中根据 this.state.get(KFCLIST_KEY, null as any) 判断数据是否存在(不管是服务端还是客户端),存在就不再请求,不存在则请求数据并通过 this.state.set(KFCLIST_KEY, data as any) 存储传输数据 在 ngOnDestroy 中根据当前是否客户端来决定是否将存储的数据进行删除

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持凡科。


分享新闻到:

更多阅读

小程序自助开发工具_Angular开发实践之效

行业动态 2021-01-08
Angular开发设计实践活动之服务端3D渲染 本文关键详细介绍了Angular开发设计实践活动之服...
查看全文

B2C电子商务网站建-怎样给模版网站增加颜

行业动态 2021-01-08
很多人认为,模板建站,没有灵魂,缺少创意,失去引人注意的资本。小编觉得,这种看法过...
查看全文

微信小程序开发价格_JS在if中的强制类型

行业动态 2021-01-08
JS在if中的强制性种类变换方法 大家都知道,JS在许多状况下能开展强制性种类变换,在...
查看全文
返回全部新闻


区域站点: 南丰县如何建立微信小程序   南宫市自己搭建服务器   囊谦县建立网站   南和县自助建站免费建站平台   南华县如何建立微信小程序   南江县自己搭建服务器   南京市建立网站   南靖县自助建站免费建站平台   南康市如何建立微信小程序   南乐县自己搭建服务器   南陵县建立网站   南宁市自助建站免费建站平台   南平市如何建立微信小程序   南皮县自己搭建服务器   南市区建立网站   南通市自助建站免费建站平台   南投县如何建立微信小程序   南雄市自己搭建服务器   南溪县建立网站   南阳市自助建站免费建站平台   南漳县如何建立微信小程序   南召县自己搭建服务器   南郑县建立网站   那坡县自助建站免费建站平台   那曲县如何建立微信小程序   纳雍县自己搭建服务器   讷河市建立网站   内黄县自助建站免费建站平台   内江市如何建立微信小程序   内丘县自己搭建服务器   内乡县建立网站   嫩江市自助建站免费建站平台   聂荣县如何建立微信小程序   尼玛县自己搭建服务器   尼木县建立网站   宁安市自助建站免费建站平台   宁波市如何建立微信小程序   宁城县自己搭建服务器   宁德市建立网站   宁都县自助建站免费建站平台   宁国市如何建立微信小程序   宁海县自己搭建服务器   宁化县建立网站   宁晋县自助建站免费建站平台   宁陵县如何建立微信小程序   宁明县自己搭建服务器   宁南县建立网站   宁强县自助建站免费建站平台   宁陕县如何建立微信小程序   宁武县自己搭建服务器   宁乡市建立网站   宁阳县自助建站免费建站平台   宁远县如何建立微信小程序   农安县自己搭建服务器   磐安县建立网站   盘锦市自助建站免费建站平台   盘山县如何建立微信小程序   磐石市自己搭建服务器   盘州市建立网站   蓬安县自助建站免费建站平台   澎湖县如何建立微信小程序   蓬莱市自己搭建服务器   彭山县建立网站   蓬溪县自助建站免费建站平台   彭阳县如何建立微信小程序   彭泽县自己搭建服务器   彭州市建立网站   偏关县自助建站免费建站平台   平安县如何建立微信小程序   平昌县自己搭建服务器   平定县建立网站   屏东县自助建站免费建站平台   平度市如何建立微信小程序   平果县自己搭建服务器   平和县建立网站   平湖市自助建站免费建站平台   平江县如何建立微信小程序   平乐县自己搭建服务器   平凉市建立网站   平利县自助建站免费建站平台   平罗县如何建立微信小程序   平陆县自己搭建服务器   屏南县建立网站   平泉市自助建站免费建站平台   屏山县如何建立微信小程序   平顺县自己搭建服务器   平塘县建立网站   平潭县自助建站免费建站平台   平武县如何建立微信小程序   萍乡市自己搭建服务器   平乡县建立网站   平阳县自助建站免费建站平台   平遥县如何建立微信小程序   平阴县自己搭建服务器   平邑县建立网站   平远县自助建站免费建站平台   平舆县如何建立微信小程序   皮山县自己搭建服务器   普安县建立网站   浦北县自助建站免费建站平台   浦城县如何建立微信小程序   普洱市自己搭建服务器   普格县建立网站   浦江县自助建站免费建站平台   普兰县如何建立微信小程序   普宁市自己搭建服务器   莆田市建立网站   迁安市自助建站免费建站平台   乾安县如何建立微信小程序   潜江市自己搭建服务器   潜山市建立网站  

友情链接: 自助免费建站 快速建站 360免费建站永久免 自助建站

Copyright © 2002-2020 建立网站_自助建站免费建站平台_如何建立微信小程序_自己搭建服务器_网站建设空间 版权所有 (网站地图) 备案号:粤ICP备10235580号