OverRainbow

Migrate Razzle from 3 to 4

☕️ 1 min read

你可以从本文了解到

背景

由于我们的业务需要为 razzle 编写 plugin,而 4 版本的架构理念让一切都 plugin,显然对此提供了更好的支持。

那么我们一起来看看升级的时候会遇到什么坑吧。

过程和遇到问题

我们演示的项目是我之前建的脚手架 demo

首先打开upgrade-guide,开始升级!

更新依赖

执行以下

yarn add --dev \
  razzle \
  razzle-dev-utils \
  babel-preset-razzle

会将相关依赖更新到最新

// package.json
{
    "devDependencies": {
        "babel-preset-razzle": "^4.2.13",
        "razzle": "^4.2.13",
        "razzle-dev-utils": "^4.2.13"
    }
}

接着安装 peerDependencies

yarn add --dev \
  webpack-dev-server@3.11.0 \
  mini-css-extract-plugin@0.9.0 \
  postcss@8.2.4

以及选择一个 webpack 版本及其插件,这里选择 4 吧。

yarn add --dev webpack@4.46.0 html-webpack-plugin@4.5.2

然后准备 start 我们的 server 看能不能跑,嗯跑起来了,没有任何问题。

如果你的 app 不用读取打包产物 chunks,那么升级到这里就结束了。

但是,如果你的 app 用了After.js,那么可能编译会报错,提示大概是 webpack 找不到 module 'undefined’。

Cannot find module 'undefined'
    at webpackEmptyContext (/Users/apple/@Projects/oss/cra-ts-ssr-zero/src sync:2:1)
    at Module../src/server.tsx (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/src/server.tsx:19:1)
    at __webpack_require__ (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/webpack/bootstrap:748:1)
    at fn (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/webpack/bootstrap:59:1)
    at Module../src/index.ts (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/src/index.ts:5:1)
    at __webpack_require__ (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/webpack/bootstrap:748:1)
    at fn (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/webpack/bootstrap:59:1)
    at Object.0 (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/server.js:1622:1)
    at __webpack_require__ (/Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/webpack/bootstrap:748:1)
    at /Users/apple/@Projects/oss/cra-ts-ssr-zero/build/webpack:/webpack/bootstrap:815:1
sswp> !!! Script exited with code 1

这个问题跟RAZZLE_CHUNKS_MANIFEST这个环境变量相关。我们来看After的一个demo。

// ./src/server.js
import React from 'react';
import express from 'express';
import {render} from '@jaredpalmer/after';
import {renderToString} from 'react-dom/server';
import {ApolloProvider, getDataFromTree} from 'react-apollo';
import routes from './routes';
import createApolloClient from './createApolloClient';
import Document from './Document';
const chunks = require(process.env.RAZZLE_CHUNKS_MANIFEST); 
const server = express();
server
    .disable('x-powered-by')
    .use(express.static(process.env.RAZZLE_PUBLIC_DIR))
    .get('/*', async (req, res) => {
        const client = createApolloClient({ssrMode: true});

        const customRenderer = (node) => {
            const App = <ApolloProvider client={client}>{node}</ApolloProvider>;
            return getDataFromTree(App).then(() => {
                const initialApolloState = client.extract();
                const html = renderToString(App);
                return {html, initialApolloState};
            });
        };

        try {
            const html = await render({
                req,
                res,
                routes,
                chunks,                customRenderer,
                document: Document
            });
            res.send(html);
        } catch (error) {
            console.error(error);
            res.json({message: error.message, stack: error.stack});
        }
    });

export default server;

原因是process.env.RAZZLE_CHUNKS_MANIFEST这个环境变量没有被注入,require了个undefined,于是出现了上面的报错。

这算是Razzle从3到4的一个breaking change吧。

我们可以去看V3

是从crateConfig调用了来自pathsbuild/chunks.json文件

那怎么办呢?这个点在razzle官网的plugin的doc上并没有说明。导致用After的我当时一脸懵逼。并不知道这个东西独立成插件了。

但是我们去V4看代码,发现仓库里确实有这么一个razzle-plugin-manifest这个插件,它就是做这个事情的。

 webpackConfig.plugins.push(
      new webpackObject.DefinePlugin({
        'process.env.RAZZLE_CHUNKS_MANIFEST': JSON.stringify(pluginOptions.fileName),
      })
    );

解决问题

可以在After的官网examples里面看到这个例子basic

它使用的唯一的razzle插件就是manifest。

那么解决这个问题的办法就是装上这个插件。

安装

yarn add --dev razzle-plugin-manifest

应用插件

// razzle.config.js
module.exports = {
  plugins: ['manifest'],
};

然后就可以跑了

总结

升级的时候出现一些问题,需要细心定位,步步为营。