创建React App V2-多个入口点

时间:2019-03-22 22:36:27

标签: reactjs webpack create-react-app

我正在尝试构建一个具有2个入口点的React应用程序,一个用于应用程序,一个用于管理面板。

我从Create React App V2开始,并遵循gitHub问题线程https://github.com/facebook/create-react-app/issues/1084和本教程http://imshuai.com/create-react-app-multiple-entry-points/

我正在尝试移植有关从CRA V1添加多个入口点的说明以在V2中工作,但我认为我缺少一些东西。

先弹出CRA,这些是我已更改/添加到path.js的路径:

module.exports = {
    appBuild: resolveApp('build/app'),
    appPublic: resolveApp('public/app'),
    appHtml: resolveApp('public/app/index.html'),
    appIndexJs: resolveModule(resolveApp, 'src/app'),
    appSrc: resolveApp('src'),
    adminIndexJs: resolveModule(resolveApp, 'src/admin'),
    adminSrc: resolveApp('src'),
    adminPublic: resolveApp('public/admin'),
    adminHtml: resolveApp('public/admin/index.html'),
};

我已将以下入口点添加到webpack:

    entry: {
        app: [
            isEnvDevelopment &&
                require.resolve('react-dev-utils/webpackHotDevClient'),
            paths.appIndexJs,
        ].filter(Boolean),
        admin: [
            isEnvDevelopment &&
                require.resolve('react-dev-utils/webpackHotDevClient'),
            paths.adminIndexJs,
        ].filter(Boolean)
    },
    output: {
      path: isEnvProduction ? paths.appBuild : undefined,
      pathinfo: isEnvDevelopment,
      filename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].js'
        : isEnvDevelopment && 'static/js/bundle.js',
      chunkFilename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].chunk.js'
        : isEnvDevelopment && 'static/js/[name].chunk.js',
      publicPath: publicPath,
      devtoolModuleFilenameTemplate: isEnvProduction
        ? info =>
            path
              .relative(paths.appSrc, info.absoluteResourcePath)
              .replace(/\\/g, '/')
        : isEnvDevelopment &&
          (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
    },

我已经像这样修改了HtmlWebpackPlugin:

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

并修改了webpack开发服务器:

historyApiFallback: {
  disableDotRule: true,
  rewrites: [
    { from: /^\/admin.html/, to: '/build/admin/index.html' },
  ]
},

我的文件结构如下:

.
+-- _src
|   +-- app.js
|   +-- admin.js
|   +-- _app
|       +-- App.js
|   +-- _admin
|       +-- App.js
|   +-- _shared
|       +-- serviceWorker.js
+-- _public
|   +-- _app
|       +-- index.html
|       +-- manifest.json
|   +-- _admin
|       +-- index.html
|       +-- manifest.json

我希望我的构建文件夹包含一个应用程序文件夹和一个管理文件夹,其中包含2个单独的SPA。

当我运行yarn start时,它不会引发任何错误并说Compiled successfully!,但是它仅部分编译了该应用程序,而不是管理应用程序,也没有编译或添加任何js。

yarn build确实抛出了一个错误,并且编译了一半的应用程序,没有管理应用程序。这是错误:

yarn run v1.12.3
$ node scripts/build.js
Creating an optimized production build...
Failed to compile.

EISDIR: illegal operation on a directory, open 
'foo/bar/public/app'


error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

在构建文件夹中,它在退出之前已经创建了很多内容:

.
+-- _static
|   +-- _css
|   +-- _js
|   +-- _media
|       +-- logo.5d5d9eef.svg
+-- precache-manifest.a9c066d088142837bfe429bd3779ebfa.js
+-- service-worker.js
+-- asset-manifest.json
+-- manifest.json

有人知道我要正确完成这项工作所缺少的吗?

3 个答案:

答案 0 :(得分:3)

我知道这是一个延迟的答案,但是为了以后的搜索,步骤是:

  1. 弹出(yarn eject
  2. 编辑path.js并在appHtml条目下添加第二个入口点html文件
appAdminHtml: resolveApp('public/admin.html'),
  1. 更新webpack.config.js内的条目以在每个入口点包含一个条目。
entry: {
  index: [
    isEnvDevelopment &&
    require.resolve('react-dev-utils/webpackHotDevClient'),
    paths.appIndexJs,
  ].filter(Boolean),
  admin: [
    isEnvDevelopment &&
    require.resolve('react-dev-utils/webpackHotDevClient'),
    paths.appSrc + '/admin/index.js',
  ].filter(Boolean)
},
  1. 将生成的输出JS文件更改为条目的名称(在webpack.config.js内部)
output: {
  path: isEnvProduction ? paths.appBuild : undefined,
  pathinfo: isEnvDevelopment,
  // This is the important entry
  filename: isEnvProduction
    ? 'static/js/[name].[contenthash:8].js'
    : isEnvDevelopment && 'static/js/[name].bundle.js',
  futureEmitAssets: true,
  chunkFilename: isEnvProduction
    ? 'static/js/[name].[contenthash:8].chunk.js'
    : isEnvDevelopment && 'static/js/[name].chunk.js',
  publicPath: publicPath,
  devtoolModuleFilenameTemplate: isEnvProduction
    ? info =>
        path
          .relative(paths.appSrc, info.absoluteResourcePath)
          .replace(/\\/g, '/')
    : isEnvDevelopment &&
      (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
  jsonpFunction: `webpackJsonp${appPackageJson.name}`,
  globalObject: 'this',
},
  1. 更新插件以使用注入的js脚本(也在webpack.config.js内)生成第二个文件。
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
  Object.assign(
    {},
    {
      inject: true,
      chunks: ['index'],
      template: paths.appHtml,
      filename: 'index.html'
    },
    isEnvProduction
      ? {
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }
      : undefined
  )
),
// Generates an `admin.html` file with the <script> injected.
new HtmlWebpackPlugin(
  Object.assign(
    {},
    {
      inject: true,
      chunks: ['admin'],
      template: paths.appAdminHtml,
      filename: 'admin.html',
    },
    isEnvProduction
      ? {
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }
      : undefined
  )
),
  1. 更新ManifestPlugin configuration to include the new entry point (also inside webpack.config.js`):
new ManifestPlugin({
  fileName: 'asset-manifest.json',
  publicPath: publicPath,
  generate: (seed, files, entrypoints) => {
    const manifestFiles = files.reduce((manifest, file) => {
      manifest[file.name] = file.path;
      return manifest;
    }, seed);
    let entrypointFiles = [];
    for (let [entryFile, fileName] of Object.entries(entrypoints)) {
      let notMapFiles = fileName.filter(fileName => !fileName.endsWith('.map'));
      entrypointFiles = entrypointFiles.concat(notMapFiles);
    };

    return {
      files: manifestFiles,
      entrypoints: entrypointFiles,
    };
  },
}),
  1. 更新您的服务器(开发人员和生产人员)以重写路径。
    • 对于开发服务器,您需要更新webpackDevServer.config.js
historyApiFallback: {
  disableDotRule: true,
  verbose: true,
  rewrites: [
    { from: /^\/admin/, to: '/admin.html' },
  ]
},

由于Prod服务器的设置可能大不相同,因此我让您弄清楚。

This post详细介绍了所有内容。

答案 1 :(得分:0)

答案 2 :(得分:0)

我意识到将filename中的HTMLWebpackPlugin设置为appPublicadminPublic是不正确的,应该为app/index.html admin/index.html

但是我想要在build文件夹中的2个单独的文件夹中,一个用于应用程序,另一个用于管理应用程序,使用此方法需要更多的复杂性,因为webpack中没有可用于设置目标位置的输入变量路径。例如,我需要能够做类似[entry]/static/js/[name].[contenthash:8].chunk.js的事情。我认为一种方法是使用Webpack MultiCompiler。

但是,我没有这样做,而是在package.json中将入口点作为环境变量传递了,像这样添加了REACT_APP_ENTRY=

  "scripts": {
    "start-app": "REACT_APP_ENTRY=app node scripts/start.js",
    "build-app": "REACT_APP_ENTRY=app node scripts/build.js",
    "start-admin": "REACT_APP_ENTRY=admin node scripts/start.js",
    "build-admin": "REACT_APP_ENTRY=admin node scripts/build.js",
    "test": "node scripts/test.js"
  },

在start.js中,我在顶部添加了const isApp = process.env.REACT_APP_ENTRY === 'app';

'use strict';

process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';

const isApp = process.env.REACT_APP_ENTRY === 'app';

并更新了设置端口的位置,这样我可以同时运行两个开发服务器而不会发生冲突:

const DEFAULT_PORT = parseInt(process.env.PORT, 10) || (isApp ? 3000 : 3001);
const HOST = process.env.HOST || '0.0.0.0';

然后在path.js的顶部添加const isApp = process.env.REACT_APP_ENTRY === 'app';

const envPublicUrl = process.env.PUBLIC_URL;
const isApp = process.env.REACT_APP_ENTRY === 'app';

最后根据env变量集更新路径:

module.exports = {
  dotenv: resolveApp('.env'),
  appPath: resolveApp('.'),
  appBuild: isApp ? resolveApp('build/app') : resolveApp('build/admin'),
  appPublic: isApp ? resolveApp('public/app') : resolveApp('public/admin'),
  appHtml: isApp ? resolveApp('public/app/index.html') : resolveApp('public/admin/index.html'),
  appIndexJs: isApp ? resolveModule(resolveApp, 'src/app') : resolveModule(resolveApp, 'src/admin'),
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  appTsConfig: resolveApp('tsconfig.json'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveModule(resolveApp, 'src/setupTests'),
  proxySetup: resolveApp('src/setupProxy.js'),
  appNodeModules: resolveApp('node_modules'),
  publicUrl: getPublicUrl(resolveApp('package.json')),
  servedPath: getServedPath(resolveApp('package.json')),
};

我认为这种方法以及简单得多的方法对于此用例而言是优越的,因为它允许灵活地仅编译应用程序或仅编译管理员,而不是在只更改一个时强制您编译两者。我可以同时在不同端口上运行单独的应用,同时运行yarn start-appyarn start-admin