Node.js+React项目:URL含amp=1时服务端渲染AMP页面咨询
Hey there! Let's break down how to handle server-side AMP detection and rendering for your project. Here's a step-by-step solution tailored to your existing Koa setup:
一、服务端检测AMP查询参数
In your Koa context, you can directly grab the AMP query parameter via this.query.amp. We'll add a check at the start of your rendering logic to decide whether to serve the regular page or the AMP version.
二、核心思路:服务端渲染AMP页面
AMP has strict rules: no custom JS (except official AMP scripts), mandatory AMP-specific tags (like <amp-img> instead of <img>), and a required page boilerplate. So we need to:
- Build a set of AMP-compliant React components/routes (replace non-compliant parts, e.g., swap regular images for
amp-img, remove client-side interactive JS) - Render the corresponding AMP component tree on the server when the
amp=1parameter is present, along with AMP's required page structure
三、完整服务端代码修改
1. Main Rendering Logic Adjustment
Update your existing render code to include the AMP check:
// Import your AMP-specific route component (assume you've created this) const AmpRouterContext = require('./path/to/amp-router-context').default; async function renderPage(ctx, next) { const isAmp = ctx.query.amp === '1'; const state = { /* Your existing state data */ }; let body; if (isAmp) { // Render AMP-compliant component tree body = renderToString( <Provider store={store}> <AmpRouterContext {...props} /> </Provider> ); } else { // Render regular page as before body = renderToString( <Provider store={store}> <RouterContext {...props} /> </Provider> ); } // Pick the right template based on AMP status const template = isAmp ? 'amp-index' : 'index'; await ctx.render(template, { state: encodeURIComponent(JSON.stringify(state)), body }); }
2. Error Handler Adjustment
We need AMP-friendly error pages too—update the error middleware:
app.use(isProduction ? function * error(next) { try { yield next; if (this.response.status === 404 && !this.response.body) { this.throw(404); } } catch (err) { const STATUS_CODES = require('http').STATUS_CODES; const seo = require('app/modules/seo').initialState; this.status = err.status = err.status || 500; if (err instanceof URIError) { // Redirect to AMP search page if it's an AMP request const redirectUrl = this.query.amp === '1' ? '/search?amp=1' : '/search'; this.redirect(redirectUrl); return; } err.message = STATUS_CODES[this.status]; this.app.emit('error', err, this); // Select AMP or regular error template const errorTemplate = this.query.amp === '1' ? 'amp-error' : 'error'; yield this.render(errorTemplate, { assets, err, seo: {...seo, long_title: `${err.status} – ${seo.long_title}`} }); } } : error());
3. AMP Template Example (amp-index.html)
Make sure your AMP template follows all AMP rules:
<!doctype html> <html amp lang="en"> <head> <meta charset="utf-8"> <title><%= seo.long_title %></title> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <script async src="https://cdn.ampproject.org/v0.js"></script> <!-- Import AMP components you need, e.g., amp-img --> <script async custom-element="amp-img" src="https://cdn.ampproject.org/v0/amp-img-0.1.js"></script> <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style> <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> <!-- Your AMP-compatible custom styles --> <style amp-custom> /* Add AMP-safe styles here */ </style> </head> <body> <%= body %> </body> </html>
四、配套Webpack Configuration
AMP blocks custom client-side JS, so we'll set up a separate build for AMP-related code and disable client-side hydration (since server-rendered AMP pages don't need it):
1. Multi-Entry Setup
Add an AMP-specific entry in webpack.config.js:
const path = require('path'); module.exports = { entry: { main: './src/client/index.js', // Regular page entry amp: './src/client/amp-index.js' // AMP entry (only for server-side rendering) }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, optimization: { splitChunks: { chunks: 'all' } }, // Other existing config (loaders, plugins, etc.) };
2. AMP Client Entry (amp-index.js)
Since AMP doesn't need client-side rendering, this entry can be minimal:
// Only export components for server-side use—no client-side execution needed import AmpApp from './components/AmpApp'; export default AmpApp;
3. Server-Side Webpack Config (For Isomorphic Projects)
Ensure your server build handles AMP components correctly in webpack.server.js:
const path = require('path'); module.exports = { target: 'node', entry: './src/server/index.js', output: { filename: 'server.bundle.js', path: path.resolve(__dirname, 'dist'), libraryTarget: 'commonjs2' }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' }, { test: /\.css$/, use: ['css-loader/locals'] // No style injection needed for server rendering } ] } };
Final Notes
- Double-check your AMP components against the AMP Specification—ensure all images have width/height, use AMP-specific tags, and avoid inline/custom JS.
- You can remove the frontend
document.writelogic once server-side AMP rendering is working, since the server will output full AMP markup directly. - Use the official AMP Validator to test your pages for compliance.
内容的提问来源于stack exchange,提问作者A.Burdonskaya

