Angular开发实践之服务端渲染

Angular Universal

Angular在服务端渲染方面提供一套前后端同构解决方案,它就是Angular Universal(统一平台),一项在服务端运行 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 函数。

renderModuleFactory 函数接受一个模板 HTML 页面(通常是 index.html)、一个包含组件的 Angular 模块和一个用于决定该显示哪些组件的路由作为输入。

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

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

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

为什么要服务端渲染

三个主要原因:

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

帮助网络爬虫(SEO)

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

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

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

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

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

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

快速显示首页

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

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

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

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

示例解析

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

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

安装工具

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

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

使用下列命令安装它们:

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

项目配置

配置工作有:

  1. 创建服务端应用模块:src/app/app.server.module.ts
  2. 修改客户端应用模块:src/app/app.module.ts
  3. 创建服务端应用的引导程序文件:src/main.server.ts
  4. 修改客户端应用的引导程序文件:src/main.ts
  5. 创建 TypeScript 的服务端配置:src/tsconfig.server.json
  6. 修改 @angular/cli 的配置文件:.angular-cli.json
  7. 创建 Node Express 的服务程序:server.ts
  8. 创建服务端预渲染的程序:prerender.ts
  9. 创建 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 './app.component';

// 可以注册那些在 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 的差异在于:

module 属性必须是 commonjs,这样它才能被 require() 方法导入你的服务端应用。

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

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

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

apps 下添加:

{
  "platform": "server",
  "root": "src",
  "outDir": "dist/server",
  "assets": [
   "assets",
   "favicon.ico"
  ],
  "index": "index.html",
  "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');

// Our index.html we'll use as our template
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).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 @ https://github.com/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 http://localhost:${PORT}`);
});

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');

// Load the index.html file containing referances to your application bundle.
const index = readFileSync(join('browser', 'index.html'), '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);
  }

  // Writes rendered HTML to index.html, replacing the file if it already exists.
  previousRender = previousRender.then(_ => renderModuleFactory(AppServerModuleNgFactory, {
    document: index,
    url: route,
    extraProviders: [
      provideModuleMap(LAZY_MODULE_MAP)
    ]
  })).then(html => writeFileSync(join(fullPath, 'index.html'), html));
});

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

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

// Work around for https://github.com/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来为应用程序提供服务 http://localhost:4000

dist目录:

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

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

dist目录:

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

export const ROUTES = [
  '/',
  '/lazy'
];

因此,从dist目录可以看到,服务端预渲染会根据配置好的路由在 browser 生成对应的静态index.html。如 / 对应 /index.html/lazy 对应 /lazy/index.html

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

在前面的介绍中,我们在 app.server.module.ts 中导入了 ServerTransferStateModule,在 app.module.ts 中导入了 BrowserTransferStateModuleTransferHttpCacheModule

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

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

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

比如在 home.component.ts 中有如下代码:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.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(`http://restapi.amap.com/v3/place/text?keywords=${text}&city=${city}&offset=20&key=55f909211b9950837fba2c71d0488db9&extensions=all`));
  }
}

代码运行之后,

服务端请求并打印:

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

方法1:使用 TransferHttpCacheModule

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

方法2:使用 BrowserTransferStateModule

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

调整 home.component.ts 代码如下:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

const KFCLIST_KEY = makeStateKey('kfcList');

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.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(`http://restapi.amap.com/v3/place/text?keywords=${text}&city=${city}&offset=20&key=55f909211b9950837fba2c71d0488db9&extensions=all`));
  }
}
  1. 使用 const KFCLIST_KEY = makeStateKey('kfcList') 创建储存传输数据的 StateKey
  2. HomeComponent 的构造函数中注入 TransferState
  3. ngOnInit 中根据 this.state.get(KFCLIST_KEY, null as any) 判断数据是否存在(不管是服务端还是客户端),存在就不再请求,不存在则请求数据并通过 this.state.set(KFCLIST_KEY, data as any) 存储传输数据
  4. ngOnDestroy 中根据当前是否客户端来决定是否将存储的数据进行删除

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

(0)

相关推荐

  • 详解Angular5 服务端渲染实战

    本文基于上一篇 Angular5 的文章继续进行开发,上文中讲了搭建 Angular5 有道翻译的过程,以及遇到问题的解决方案. 随后改了 UI,从 bootstrap4 改到 angular material,这里不详细讲,服务端渲染也与修改 UI 无关. 看过之前文章的人会发现,文章内容都偏向于服务端渲染,vue 的 nuxt,react 的 next. 在本次改版前也尝试去找类似 nuxt.js 与 next.js 的顶级封装库,可以大大节省时间,但是未果. 最后决定使用从 Angular

  • 详解基于Angular4+ server render(服务端渲染)开发教程

    目标: 1.更好的 SEO,方便搜索爬虫抓取页面内容 2.更快的内容到达时间(time-to-content) 影响: 1.用户:比原来更快的看到渲染的页面,提升用户体验 2.开发人员:某些代码可能需要特殊处理,才能在服务器渲染应用程序中运行(window,document, navigator等) 安装: 1.nodejs 建议6+ 2.angular建议4.1+ 理论实现: 尽管这是一张来自vue官网服务器渲染的一张示意图,但是原理上和angular都是一样的,只是实现的代码不一致. SSR

  • Angular开发实践之服务端渲染

    Angular Universal Angular在服务端渲染方面提供一套前后端同构解决方案,它就是Angular Universal(统一平台),一项在服务端运行 Angular 应用的技术. 标准的 Angular 应用会执行在浏览器中,它会在 DOM 中渲染页面,以响应用户的操作. 而 Angular Universal 会在服务端通过一个被称为服务端渲染(server-side rendering - SSR)的过程生成静态的应用页面. 它可以生成这些页面,并在浏览器请求时直接用它们给出

  • 详解基于 Nuxt 的 Vue.js 服务端渲染实践

    Vue.js 是目前最火热的前端框架之一,而 Nuxt.js 是针对 Vue.js 推出的服务端渲染框架,通过高度定制化的配置以及简洁的 API,开发者可以快速进行服务端渲染项目的开发,本文将对 Nuxt.js 框架做一个简要介绍. 服务端渲染 服务端渲染(Server Side Render)并不是一个新的概念,在单页应用(SPA)还没有流行起来的时候,页面就是通过服务端渲染好,并传递给浏览器的.当用户需要访问新的页面时,需要再次请求服务器,返回新的页面. 为了优化体验,开发者们开始选择采用

  • Vue服务端渲染实践之Web应用首屏耗时最优化方案

    随着各大前端框架的诞生和演变,SPA开始流行,单页面应用的优势在于可以不重新加载整个页面的情况下,通过ajax和服务器通信,实现整个Web应用拒不更新,带来了极致的用户体验.然而,对于需要SEO.追求极致的首屏性能的应用,前端渲染的SPA是糟糕的.好在Vue 2.0后是支持服务端渲染的,零零散散花费了两三周事件,通过改造现有项目,基本完成了在现有项目中实践了Vue服务端渲染. 关于Vue服务端渲染的原理.搭建,官方文档已经讲的比较详细了,因此,本文不是抄袭文档,而是文档的补充.特别是对于如何与现

  • React服务端渲染原理解析与实践

    关于服务端渲染也就是我们说的SSR大多数人都听过这个概念,很多同学或许在公司中已经做过服务端渲染的项目了,主流的单页面应用比如说Vue或者React开发的项目采用的一般都是客户端渲染的模式也就是我们说的CSR. 但是这种模式会带来明显的两个问题,第一个就是TTFP时间比较长,TTFP指的就是首屏展示时间,同时不具备SEO排名的条件,搜索引擎上排名不是很好.所以我们可以借助一些工具来进行改良我们的项目,将单页面应用编程服务器端渲染项目,这样就可以解决掉这些问题了. 目前主流的服务器端渲染框架也就是

  • 详解Nuxt.js Vue服务端渲染摸索

    本文采用nuxt进行服务端渲染https://zh.nuxtjs.org/ Nuxt.js 十分简单易用.一个简单的项目只需将 nuxt 添加为依赖组件即可. Vue因其简单易懂的API.高效的数据绑定和灵活的组件系统,受到很多前端开发人员的青睐.国内很多公司都在使用vue进行项目开发,我们正在使用的简书,便是基于Vue来构建的. 我们知道,SPA前端渲染存在两大痛点:(1)SEO.搜索引擎爬虫难以抓取客户端渲染的页面meta信息和其他SEO相关信息,使网站无法在搜索引擎中被用户搜索到.(2)用

  • 详解Vue基于 Nuxt.js 实现服务端渲染(SSR)

    直接使用 Vue 构建前端单页面应用,页面源码时只有简单的几行 html,这并不利于网站的 SEO,这时候就需要服务端渲染 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了一个 React 的服务端渲染应用框架 Next.js 几小时后,一个基于 Vue.js 的服务端渲染应用框架应运而生,与 Next.js 异曲同工,这就是Nuxt.js 一.快速模板 在已经安装了 vue-cli 的前提下,可以快速创建一个 nuxt 的项目模板 vue init nuxt-commun

  • 详解React 服务端渲染方案完美的解决方案

    最近在开发一个服务端渲染工具,通过一篇小文大致介绍下服务端渲染,和服务端渲染的方式方法.在此文后面有两中服务端渲染方式的构思,根据你对服务端渲染的利弊权衡,你会选择哪一种服务端渲染方式呢? 什么是服务器端渲染 使用 React 构建客户端应用程序,默认情况下,可以在浏览器中输出 React 组件,进行生成 DOM 和操作 DOM.React 也可以在服务端通过 Node.js 转换成 HTML,直接在浏览器端"呈现"处理好的 HTML 字符串,这个过程可以被认为 "同构&qu

  • 详解React+Koa实现服务端渲染(SSR)

    React是目前前端社区最流行的UI库之一,它的基于组件化的开发方式极大地提升了前端开发体验,React通过拆分一个大的应用至一个个小的组件,来使得我们的代码更加的可被重用,以及获得更好的可维护性,等等还有其他很多的优点... 通过React, 我们通常会开发一个单页应用(SPA),单页应用在浏览器端会比传统的网页有更好的用户体验,浏览器一般会拿到一个body为空的html,然后加载script指定的js, 当所有js加载完毕后,开始执行js, 最后再渲染到dom中, 在这个过程中,一般用户只能

随机推荐