create-react-app 修改成多入口編譯

create-react-app 修改成多入口編譯

  • date: 2018.07.31

需求和出發點

咱們會有較多的小的單頁應用,主要是一些簡單的頁面和活動之類。這些頁面相互之間沒有交集,可是會有一些能夠共用的代碼,資源、接口、組件啥的。html

對此,咱們想到了兩種解決方案:react

  • react-router 路由方案;
  • 同一個項目的多入口編譯;

針對咱們的業務需求,其實 react-router 方案會有兩個小問題:webpack

  • 單個活動的修改,其實須要編譯整個項目;
  • 如果不作編譯優化,整個項目的包會比較大,但其實不必,固然這個能夠經過 react-router 的按需加載來解決;

權衡之下,咱們仍是選擇了第二個方案——改造項目成爲多入口編譯。git

文件結構設計

改進後,整個項目的結構大致以下:github

- 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 模板和入口文件。下面,咱們看下是如何修改編譯過程的。web

修改入口和出口

編譯須要指定編譯的入口和輸出的位置,在 create-react-app 原本生成的 code 中,只有單入口和單出口,可是其實 webpack 是支持多入口、多出口的。npm

入口修改

create-react-app 命令生成的 config 文件夾中,有個 paths.js 文件,這裏面 export 了比較經常使用的路徑。在這裏,我對 src/site 文件夾內的文件夾進行了遍歷,生成爲對象。具體代碼以下:json

// 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 屬性,將其修改成:api

// 動態生成 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 以使靜態資源區分不一樣項目:react-router

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/facebookincubator/create-react-app/issues/387.
      disableDotRule: true,
      // 指明哪些路徑映射到哪一個html
      rewrites: rewrites,
    },
    ...
  };
};

OK,到這裏,整個改造就完成了。

相關文章
相關標籤/搜索