React-Webpack多页

781 阅读4分钟

react- scripts 中的一系列技术栈配置都 在使用create-react-app构建项目时并未开放,需要使用以下命令弹射到应用的顶层以供个性化,不过特别注意,整个过程是不可逆的 ,eject后就可以看见熟悉的webpack配置目录config。

npm run eject

webpack 是什么?归根到底,webpack 就是一个.js 配置文件,你的架构好或坏 都体现在webpack.config.js这个配置里。

webpack配置中最重要也是必选的两项是入口(Entry)和出口(Output)。入口的作用是告诉webpack从哪里开始寻找依赖,并且编译,出口则用来配置编译后的文件储存位置和文件名。

通过观察发现 entry 处 只配置了一个入口 paths.appIndexJs ,点进去可以看见指向index.js。

entry: [
  // Include an alternative client for WebpackDevServer. A client's job is to
  // connect to WebpackDevServer by a socket and get notified about changes.
  // When you save a file, the client will either apply hot updates (in case
  // of CSS changes), or refresh the page (in case of JS changes). When you
  // make a syntax error, this client will display a syntax error overlay.
  // Note: instead of the default WebpackDevServer client, we use a custom one
  // to bring better experience for Create React App users. You can replace
  // the line below with these two lines if you prefer the stock client:
  // require.resolve('webpack-dev-server/client') + '?/',
  // require.resolve('webpack/hot/dev-server'),
  isEnvDevelopment &&
    require.resolve('react-dev-utils/webpackHotDevClient'),
  // Finally, this is your app's code:
  paths.appIndexJs,
  // We include the app code last so that if there is a runtime error during
  // initialization, it doesn't blow up the WebpackDevServer client, and
  // changing JS code would still trigger a refresh.
].filter(Boolean),
webpack官方文档有介绍 入口起点(entry points)的多种书写形式如下:
entry: './path/to/my/entry/file.js'  //单个入口(简写)语法
entry: {
    main: './path/to/my/entry/file.js'  //对象语法
}
 entry: {  //多页面应用程序
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
 }

这里可以看出将 单个入口 改写为 对象语法 的多页面应用程序就可以达到多入口的目的。

工程化 可以用到 globby 的帮助 搜寻 src 目录下的多页面应用:

// 引入 globby 模块
const globby = require('globby');
// 入口文件路径
const entriesPath = globby.sync([resolveApp('src') + '/*/index.tsx']);

通过遍历 globby 获得的目录数组 获得 多页面 路径入口文件:

function getEntries(){
  const entries = {};
  const files = paths.entriesPath;
  files.forEach(filePath => {
    let tmpArr = filePath.split('/');
    let name = tmpArr[tmpArr.length - 2];
    entries[name] = [
      filePath,
    ];
    isEnvDevelopment && entries[name].push(require.resolve('react-dev-utils/webpackHotDevClient')); 
    //这份代码是由react官方的create-react-app提供的热构建插件
  });
  return entries;
}
入口 boundle 如何插入对应的 html 中?

我们通常需要这个插件HtmlWebpackPlugin自动处理,目前代码是这样:

new HtmlWebpackPlugin(
  Object.assign(
    {},
    {
      inject: true,
      template: paths.appHtml,
    },
    isEnvProduction
      ? {
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }
      : undefined
  )
),

配置 HtmlWebpackPlugin 插件, 指定入口生成对应的 html 文件,有多少个页面就需要 new 多少个 HtmllWebpackPlugin webpack配置多入口后,只是编译出多个入口的JS,同时入口的HTML文件由HtmlWebpackPlugin生成,也需做配置。 chunks,指明哪些 webpack入口的JS会被注入到这个HTML页面。如果不配置,则将所有entry的JS文件都注入HTML。 filename,指明生成的HTML路径,如果不配置就是build/index.html,需要 配置新的filename,避免与第一个入口的index.html相互覆盖。

我们已经 通过 globby 的帮助拿到多入口对象 entries,这里可以遍历处理:

const htmlPlugin = Object.keys(entries).map(item => {
  return  new HtmlWebpackPlugin(
      Object.assign(
          {},
          {
            inject: true,
            template: paths.appHtml,
            filename: item + '.html',
            chunks: [item]
          },
          isEnvProduction
              ? {
                minify: {
                  removeComments: true,
                  collapseWhitespace: true,
                  removeRedundantAttributes: true,
                  useShortDoctype: true,
                  removeEmptyAttributes: true,
                  removeStyleLinkTypeAttributes: true,
                  keepClosingSlash: true,
                  minifyJS: true,
                  minifyCSS: true,
                  minifyURLs: true,
                },
              }
              : undefined
      )
  );
});

将 处理完成的 htmlPlugin 放入对应的 plugins,这里可以使用扩展运算符( spread ):

plugins: [
  ...htmlPlugin,
  
      以下代码省略... 

如此能正常的打包出正确的 多页面应用了,但是开发环境会受到静态资源名字没有Hash 而出现白屏,需要将 出口(Output)开发环境静态资源加上 Hash值:

output: {
  // The build folder.
  path: isEnvProduction ? paths.appBuild : undefined,
  // Add /* filename */ comments to generated require()s in the output.
  pathinfo: isEnvDevelopment,
  // There will be one main bundle, and one file per asynchronous chunk.
  // In development, it does not produce real files.
  filename: isEnvProduction
    ? 'static/js/[name].[contenthash:8].js'
    : isEnvDevelopment && 'static/js/[name].bundle.js',
  // TODO: remove this when upgrading to webpack 5
  futureEmitAssets: true,
  // There are also additional JS chunk files if you use code splitting.
  chunkFilename: isEnvProduction
    ? 'static/js/[name].[contenthash:8].chunk.js'
    : isEnvDevelopment && 'static/js/[name].chunk.js',
    
    以下代码省略... 

欢迎光临我的博客交流学习