如何解决从 Lambda 执行 SSR 时,如何缩短冷启动时间
我有一个用 CRA、ConnectedRouter、Redux 和 Saga 制作的 React.JS 应用程序的 SSR 服务器。我正在尝试使用以下代码在 AWS Lambda 下托管此服务器:
const serverlessExpress = require('@vendia/serverless-express');
const app = require('./index');
const binaryMimeTypes = [
'application/javascript',...
'text/xml'
];
exports.handler = serverlessExpress({
app,binaryMimeTypes
}).handler;
const md5File = require('md5-file');
const fs = require('fs');
const path = require('path');
// CSS styles will be imported on load and that complicates matters... ignore those bad boys!
const ignoreStyles = require('ignore-styles');
const register = ignoreStyles.default;
// We also want to ignore all image requests
// When running locally these will load from a standard import
// When running on the server,we want to load via their hashed version in the build folder
const extensions = ['.gif','.jpeg','.jpg','.png','.svg'];
// Override the default style ignorer,also modifying all image requests
register(ignoreStyles.DEFAULT_EXTENSIONS,(mod,filename) => {
if (!extensions.find(f => filename.endsWith(f))) {
// If we find a style
return ignoreStyles.noOp();
}
// for images that less than 10k,CRA will turn it into Base64 string,but here we have to do it again
const stats = fs.statSync(filename);
const fileSizeInBytes = stats.size / 1024;
if (fileSizeInBytes <= 10) {
mod.exports = `data:image/${mod.filename
.split('.')
.pop()};base64,${fs.readFileSync(mod.filename,{
encoding: 'base64'
})}`;
return ignoreStyles.noOp();
}
// If we find an image
const hash = md5File.sync(filename).slice(0,8);
const bn = path.basename(filename).replace(/(\.\w{3})$/,`.${hash}$1`);
mod.exports = `/static/media/${bn}`;
});
// Set up babel to do its thing... env for the latest toys,react-app for CRA
// Notice three plugins: the first two allow us to use import rather than require,the third is for code splitting
// polyfill is required for Babel 7,polyfill includes a custom regenerator runtime and core-js
require('@babel/polyfill');
require('@babel/register')({
ignore: [/\/(build|node_modules)\//],presets: ['@babel/preset-env','@babel/preset-react'],plugins: [
'@babel/plugin-Syntax-dynamic-import','@babel/plugin-proposal-class-properties','dynamic-import-node','react-loadable/babel'
]
});
// Now that the nonsense is over... load up the server entry point
const app = require('./server');
module.exports = app;
然后我的 server.js 中有常规的 express 服务器
// Express requirements
import bodyParser from 'body-parser';
import compression from 'compression';
import express from 'express';
import morgan from 'morgan';
import path from 'path';
import forceDomain from 'forcedomain';
import Loadable from 'react-loadable';
import cookieParser from 'cookie-parser';
// Our loader - this basically acts as the entry point for each page load
import loader from './loader';
// Create our express app using the port optionally specified
const main = () => {
const app = express();
const PORT = process.env.PORT || 3000;
// Compress,parse,log,and raid the cookie jar
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(morgan('dev'));
app.use(cookieParser());
// Set up homepage,static assets,and capture everything else
app.use(express.Router().get('/',loader));
const favicon = require('serve-favicon');
app.use(favicon(path.resolve(__dirname,'../build/icons/favicon.ico')));
app.use(express.static(path.resolve(__dirname,'../build')));
app.use(loader);
// We tell React Loadable to load all required assets and start listening - ROCK AND ROLL!
Loadable.preloadAll().then(() => {
app.listen(PORT,console.log(`App listening on port ${PORT}!`));
});
// Handle the bugs somehow
app.on('error',error => {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof PORT === 'string' ? 'Pipe ' + PORT : 'Port ' + PORT;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});
return app;
};
module.exports = main();
然后,我让加载程序执行:
// Express requirements
import path from 'path';
import fs from 'fs';
// React requirements
import React from 'react';
import { renderToString } from 'react-dom/server';
import Helmet from 'react-helmet';
import { Provider } from 'react-redux';
import { StaticRouter } from 'react-router-dom';
import { Frontload,frontloadServerRender } from 'react-frontload';
import Loadable from 'react-loadable';
// Our store,entrypoint,and manifest
import createStore from '../src/configureStore';
import App from '../src/containers/app';
import manifest from '../build/asset-manifest.json';
// Some optional Redux functions related to user authentication
//import { setCurrentUser,logoutUser } from '../src/modules/auth';
// LOADER
export default (req,res) => {
/*
A simple helper function to prepare the HTML markup. This loads:
- Page title
- SEO Meta tags
- Preloaded state (for Redux) depending on the current route
- Code-split script tags depending on the current route
*/
const injectHTML = (data,{ html,title,Meta,body,scripts,state }) => {
data = data.replace('<html>',`<html ${html}>`);
data = data.replace(/<title>.*?<\/title>/g,title);
data = data.replace('</head>',`${Meta}</head>`);
data = data.replace(
'<div id="root"></div>',`<div id="root">${body}</div><script>window.__PRELOADED_STATE__ = ${state}</script>${scripts.join(
''
)}`
);
return data;
};
// Load in our HTML file from our build
fs.readFile(
path.resolve(__dirname,'../build/index.html'),'utf8',(err,htmlData) => {
...
整个过程,包括转译,以及启动 express 服务器所需的时间需要相当长的时间。对于温暖的 lambda,我的延迟可能在 100 毫秒之间,一直到大约 3 秒。
是否有一些简单的改进可以应用到我的代码中?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。