视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
create-react-app 修改为多入口编译的方法
2020-11-27 22:10:43 责编:小采
文档


需求和出发点

我们会有较多的小的单页应用,主要是一些简单的页面和活动之类。这些页面相互之间没有交集,但是会有一些可以共用的代码,资源、接口、组件啥的。

对此,我们想到了两种解决方案:

  • react-router 路由方案;
  • 同一个项目的多入口编译;
  • 针对我们的业务需求,其实 react-router 方案会有两个小问题:

  • 单个活动的修改,其实需要编译整个项目;
  • 若是不做编译优化,整个项目的包会比较大,但其实没必要,当然这个可以通过 react-router 的按需加载来解决;
  • 权衡之下,我们还是选择了第二个方案——改造项目成为多入口编译。

    文件结构设计

    改进后,整个项目的结构大体如下:

    - project
     - build
     - config
     - public
     - scripts
     - src
     - api
     - component
     - site
     - site1
     - index.html
     - index.js
     - ...
     - site2
     - index.html
     - index.js
     - ...
     - package.json
    

    site 文件夹下的所有文件夹都是一个的项目,项目通用的代码、资源被抽离到更外层的文件夹内,如 api、component 等,文件夹内都会有自己的 index.html 和 index.js,这会作为该项目的 html 模板和入口文件。下面,我们看下是如何修改编译过程的。

    修改入口和出口

    编译需要指定编译的入口和输出的位置,在 create-react-app 本来生成的 code 中,只有单入口和单出口,但是其实 webpack 是支持多入口、多出口的。

    入口修改

    create-react-app 命令生成的 config 文件夹中,有个 paths.js 文件,这里面 export 了比较常用的路径。在这里,我对 src/site 文件夹内的文件夹进行了遍历,生成为对象。具体代码如下:

    // all site paths
    function allSitePath(source) {
     const { lstatSync, readdirSync } = fs
     const { join } = path
     const result = {}
     const isDirectory = source => lstatSync(source).isDirectory()
     readdirSync(source).map(name => {
     let path = join(resolveApp(source), name)
     if (isDirectory(path)) result[name] = path
     })
     return result
    }
    
    module.exports = {
     ...
     allSites: allSitePath('src/site'),
    }
    
    

    在 webpack.config.dev.js / webpack.config.prod.js 中找到 module.exports 的 entry 属性,将其修改为:

    // 动态生成 entry
    const entry = {}
    Object.keys(paths.allSites).forEach(item => {
     entry[item] = [
     require.resolve('./polyfills'),
     require.resolve('react-dev-utils/webpackHotDevClient'),
     require.resolve('react-error-overlay'),
     paths.allSites[item]
     ]
    })
    
    module.exports = {
     ...
     entry: entry,
     ...
    }
    
    

    出口修改

    出口的修改分为两部分,一部分是 module.exports 的 output,添加 name 以使静态资源区分不同项目:

    module.exports = {
     ...
     output: {
     path: paths.appBuild,
     pathinfo: true,
     filename: 'static/js/[name].bundle.js',
     chunkFilename: 'static/js/[name].chunk.js',
     publicPath: publicPath,
     devtoolModuleFilenameTemplate: info =>
     path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
     },
     ...
    } 
    

    另一部分是 plugin 的修改,webpack 中,每个 HTML 文件的输出,其实是一个 HtmlWebpackPlugin,我们需要添加多个 HtmlWebpackPlugin,以求生成多个 HTML:

    // 动态生成 plugins
    const plugins = []
    Object.keys(paths.allSites).forEach(item => {
     plugins.push(new HtmlWebpackPlugin({
     inject: true,
     chunks: [item],
     template: `${paths.allSites[item]}/index.html`,
     filename: `${item}/index.html`,
     }))
    })
    
    module.exports = {
     ...
     plugins: [
     ...
     ].concat(plugins), 
     ...
    }
    
    

    修改 webpack Dev Server 配置

    上述配置做完后,理论就可以打包出多入口的版本;但使用npm start启动后,发现无论输入/index.html还是/admin.html,好像都是和原来/index.html显示一样的内容。甚至输入显然不存在的/xxxx.html,也显示为/index.html的内容。

    这里,我们还需要修改 /config/webpackDevServer.config.js,做一些额外配置。

    const rewrites = []
    Object.keys(paths.allSites).forEach(item => {
     rewrites.push({
     from: new RegExp(`^\\/${item}/`, 'i'),
     to: `/${item}/index.html`,
     })
    })
    
    ...
    
    module.exports = function(proxy, allowedHost) {
     return {
     ...
     historyApiFallback: {
     // Paths with dots should still use the history fallback.
     // See https://github.com/incubator/create-react-app/issues/387.
     disableDotRule: true,
     // 指明哪些路径映射到哪个html
     rewrites: rewrites,
     },
     ...
     };
    };
    
    

    OK,到这里,整个改造就完成了。

    下载本文
    显示全文
    专题