一个帮助你构建预渲染页面的工具,你可以利用此工具来为你的页面生成骨架屏。
按照下面的步骤来帮助你生成骨架屏。
npm install prerender-cli -g
在你的项目根路径下,创建一份名为prerender.config.js
的配置。
// prerender.config.js
const path = require('path');
module.exports = {
server: {
// 打包后文件的路径
staticDir: path.resolve(__dirname, './testDist')
},
routes: [
{
// 需要预渲染的页面路径
path: '/page1/index.html',
},
],
// 产物中需要做cdn映射的地址
cdnMaps: [
{
regExp: /other.domain.com/g,
targetPath: '/page1',
},
{
regExp: /other.domain2.com/g,
targetPath: '/page1',
},
],
// 构建时注入到windows上的配置
injectConfig: {
propertyName: 'isPreRender',
value: true,
},
};
执行命令
build-prerender
or
build-prerender -c yourConfig.js
export interface IServerConfig {
port?: number; // 静态服务器端口号,默认为8888
staticDir: string; // 编译后文件的根路径
}
export interface IRoute {
// 页面的路径
path: string;
// 预渲染html输出的路径,不传则覆盖原来的html
outputPath?: string;
// 等待指定元素出现
// document.querySelector
captureAfterElementExists?: string | string[];
// 等待指定事件捕获
// 你需要在代码里手动去触发这个事件 document.dispatchEvent(new Event('yourEventName'))
captureAfterDocumentEvent?: string;
// 等待指定时间
captureAfterTime?: number;
}
export interface IPreRenderConfig {
routes: IRoute[];
// cdn地址的映射
// 比方说把 //other.domain.com/js/chunk-vendors.f8844798.js 映射到 /page1/js/chunk-vendors.f8844798.js
/**
* {
* regExp: /other.domain.com/g,
* targetPath: '/page1',
* },
**/
cdnMaps?:{
regExp: RegExp;
targetPath: string;
}[];
server: IServerConfig;
// 这个配置会往window上注入
/**
* 在预渲染时候会往window上注入变量,window.isPreRender = true;
*{
* propertyName: 'isPreRender',
* value: true,
*}
**/
injectConfig?: {
propertyName: string;
value:any;
};
// 需要模拟什么设备打开页面
// https://github.com/puppeteer/puppeteer/blob/main/src/common/DeviceDescriptors.ts
deviceName?: string;
plugins?: PreRenderCliPlugin[];
}
就是提前生成好静态的 HTML,用户访问的时候直接就能将页面呈现出来。这点有点类似于 SSR,但是 SSR 需要维护一个 node 或者 php 的 SSR 服务器来生成页面,通常对于我们的项目来说将项目改造成 SSR 也需要不小的开发量,并且需要前端去维护 SSR 的服务器,这点带来的收益和付出的成本往往不成正比。
而预渲染,则是通过 puppeteer 控制无头浏览器打开页面,然后将浏览器生成好的 html 注入到入口 html 中,当用户访问入口 html 的时候,看到的就是我们预渲染生成的页面了。
这个插件能够帮助你代理转发你的接口
// prerender.config.js
const PreRenderCliHttpProxyPlugin = require('prerender-cli-http-proxy');
module.exports = {
plugins: [
new PreRenderCliHttpProxyPlugin([
{
path: '/api',
options: {
target: 'https://www.api.server.com',
},
},
{
path: '/api2',
options: {
target: 'https://www.api2.server.com',
},
},
]),
],
};
此插件依赖了express-mock-api-middleware
// /mock/userInfo.js
module.exports = {
'GET /api/user/info': {
id: 101,
userName: 'bob',
email: '[email protected]',
firstName: 'Bob',
lastName: 'Bushee',
},
'POST /api/user/login': (req, res) => {
const { userName, password } = req.body;
if (userName === 'bob' && password === 'password') {
res.send({
success: true,
});
} else {
res.send({
success: false,
});
}
},
'GET /api/product/:id': (req, res) => {
const { id } = req.params;
res.sendFile(path.join(__dirname, `products/${id}.json`));
}
};
// prerender.config.js
const PrerenderCliMock = require('prerender-cli-mock');
const path = require('path');
module.exports = {
plugins: [
new PrerenderCliMock({
path: path.resolve(__dirname, './mock'),
}),
]
};
export interface ICompiler {
hooks: any;
}
export abstract class PreRenderCliPlugin<T> {
protected config: T;
public abstract apply(compiler: ICompiler): void;
}
启动静态服务前触发
compiler.hooks.beforeStartStaticServer.tap('Plugin Name', (app: Express) => {})
加载页面前执行
compiler.hooks.beforeLoadPage.tapAsync('Plugin Name', async (page: Page) => {});
预渲染捕获完成后触发
compiler.hooks.afterCapture.tapAsync('Plugin Name', async (page: Page) => {});