webpack多入口文件頁面打包配置

大多數狀況下,咱們使用 webpack來打包單頁應用程序,這個時候只須要配置一個入口,一個模板文件,但也不滿是如此,有時候也會碰到多頁面的項目,並且以個人經驗來看,這種狀況出現的頻率還不低,例如項目比較大,沒法進行全局的把握,或者項目須要屢次的更新迭代等,都適合作成多頁面程序,這就涉及到了 webpack的多頁面文件的打包配置問題。css


手動配置

單頁應用程序和多頁應用程序的 webpack配置文件其實絕大部分都仍是相同的,只不過多頁的配置須要在單頁配置的基礎上顧及到多個頁面罷了,loaderoutputplugins這些基本都不須要改動,須要改動的通常都是入口文件 entry,若是你用到了 抽離css樣式的插件 extract-text-webpack-plugin、自動模板插件 html-webpack-plugin的話,那麼還須要對這兩個插件進行額外的改寫,大多數狀況下,咱們也都只須要改動這三個地方,因此本文就只簡單說下這三個位置,若是在實際的項目中還有其餘的地方須要改動,參照這三個位置便可。html

示例的文件目錄以下:node

1

entry

單頁應用程序的入口配置通常以下所示:webpack

entry: resolve(__dirname, "src/home/index.js")
複製代碼

這個配置就是指定 webpack/src/home/index.js這個文件開始進入,進行一系列的打包編譯過程。git

若是是多頁應用程序,則須要多個入口文件,例如:github

entry: {
  home: resolve(__dirname, "src/home/index.js"),
  about: resolve(__dirname, "src/about/index.js")
}
複製代碼

這樣,整個項目就有了兩個入口 homeaboutweb

extract-text-webpack-plugin

extract-text-webpack-plugin 插件主要是爲了抽離css樣式,防止將樣式打包在 js中引發頁面樣式加載錯亂或者 js腳本體積過大等狀況,單頁程序中,通常這樣使用此插件:json

plugins: [
	new ExtractTextPlugin('style.[contenthash].css')
]
複製代碼

而到了多頁程序,由於存在多個入口文件以及對應的多個頁面,每一個頁面都有本身的 css樣式,因此須要爲每一個頁面各自配置一下:windows

plugins: [
	new ExtractTextPlugin('home/[name].[contenthash].css'),
	new ExtractTextPlugin('about/[name].[contenthash].css')
]
複製代碼

除此以外還須要注意一點,每一個頁面也只須要本身的 css樣式,理論上把別的頁面 css樣式文件也打包到本身的頁面中固然也是能夠的,但顯然是不合理的,這隻會增長冗餘代碼,還可能會致使不可預測的樣式覆蓋等問題,因此須要對下面這種 loader配置進行修改:sass

{
    test: /\.css$/,
    loader: 'style!css!autoprefixer'
},
{
    test: /\.scss$/,
    loaders: [
      'style',
      'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
      'sass',
      'autoprefixer'
    ]
},
複製代碼

上面的配置會把全部編譯出來的 css文件打包到同一個文件中,咱們要作的就是把這些 css分離開,每一個頁面都有各自單獨的 css樣式文件:

// 爲每一個頁面定義一個 ExtractTextPlugin
const homeExtractCss = new ExtractTextPlugin('home/[name].[contenthash].css')
const aboutExtractCss = new ExtractTextPlugin('about/[name].[contenthash].css')
// ...
module: {
    rules: [
      // 每一個頁面的 ExtractTextPlugin 只處理這個頁面的樣式文件
	  {
        test: /src(\\|\/)home(\\|\/)css(\\|\/).*\.(css|scss)$/,
        use: homePageExtractCss.extract({
          fallback: 'style-loader',
          use: ['css-loader', 'postcss-loader', 'sass-loader']
        })
      },
      {
        test: /src(\\|\/)about(\\|\/)css(\\|\/).*\.(css|scss)$/,
        use: salePersonalCenterExtractCss.extract({
          fallback: 'style-loader',
          use: ['css-loader', 'postcss-loader', 'sass-loader']
        })
      }
	]
}
// ...
// 每一個頁面都有各自的 ExtractTextPlugin,因此須要都聲明一遍
plugins: [
    homeExtractCss,
    aboutExtractCss
]
複製代碼

html-webpack-plugin

html-webpack-plugin插件的使用,在單頁應用程序和多頁應用程序中的 webpack配置沒什麼區別

new HtmlWebpackPlugin({
   filename: 'home/home.html',
   template: 'src/home/html/index.html',
   inject: true,
   minify: {
       removeComments: true,
       collapseWhitespace: true
   }
 })
 new HtmlWebpackPlugin({
   filename: 'about/about.html',
   template: 'src/about/html/index.html',
   inject: true,
   minify: {
       removeComments: true,
       collapseWhitespace: true
   }
 })
複製代碼

有幾個頁面,就對每一個頁面進行上述配置便可。


自動配置

上述的配置代碼已經能夠知足多頁面開發需求了,可是有一點彷佛有些遺憾,那就是每增長一個頁面,就須要更新一遍 entryextract-text-webpack-pluginHtmlWebpackPlugin的配置,雖然只是幾行代碼的問題,並且基本上都是複製粘貼沒什麼難度,但畢竟代碼再少也須要過問,而且須要改的地方比較多,倉促之下可能還會遺漏,要是能一勞永逸,寫一遍代碼,不管之後增刪頁面都不須要過問就行了。

稍微觀察下這個目錄就能夠發現,這個目錄結構實際上是頗有規律的:

1

每一個頁面都是 src/目錄下的一個文件夾,這個文件夾中有兩個子目錄,分別存放這個頁面的模板 html,樣式文件 css,還有一個入口文件 index.js

既然有規則,那麼確定是能夠進行程序編碼的,若是按照這種規則,每一個頁面都是 ./src下的一個目錄,目錄名即爲頁面名,而且這個目錄中的結構也都是相同的,那麼能夠經過一個通用方法來獲取全部的頁面名稱(例如 homeabout),這個通用方法的一個示例以下:

function getEntry () {
  let globPath = 'src/**/html/*.html'
  // (\/|\\\\) 這種寫法是爲了兼容 windows和 mac系統目錄路徑的不一樣寫法
  let pathDir = 'src(\/|\\\\)(.*?)(\/|\\\\)html'
  let files = glob.sync(globPath)
  let dirname, entries = []
  for (let i = 0; i < files.length; i++) {
    dirname = path.dirname(files[i])
    entries.push(dirname.replace(new RegExp('^' + pathDir), '$2'))
  }
  return entries
}
複製代碼

藉助 glob這個庫,遍歷 .src/目錄下具備這種規律 src/**/html/*.html的子目錄,經過正則匹配出這個子目錄的名稱

獲取到了全部的頁面名稱,下面就好辦了。

entry

// entry: resolve(__dirname, "src/home/index.js")
// 改成
entry: addEntry()
//...
function addEntry () {
  let entryObj = {}
  getEntry().forEach(item => {
    entryObj[item] = resolve(__dirname, 'src', item, 'index.js')
  })
  return entryObj
}
複製代碼

extract-text-webpack-plugin

// plugins: [
	// new ExtractTextPlugin('home/[name].[contenthash].css'),
	// new ExtractTextPlugin('about/[name].[contenthash].css')
//]
// 改成
const pageExtractCssArray = []
getEntry().forEach(item => {
  pageExtractCssArray.push(new ExtractTextPlugin(item + '/[name].[contenthash].css'))
})
// ...
plugins: [...pageExtractCssArray]
複製代碼

module.rules樣式相關的兩個loaders刪掉,改成動態添加:

getEntry().forEach((item, i) => {
  webpackconfig.module.rules.push({
    test: new RegExp('src' + '(\\\\|\/)' + item + '(\\\\|\/)' + 'css' + '(\\\\|\/)' + '.*\.(css|scss)$'),
    use: pageExtractCssArray[i].extract({
      fallback: 'style-loader',
      use: ['css-loader', 'postcss-loader', 'sass-loader']
    })
  })
})
// ...
module.exports = webpackconfig
複製代碼

html-webpack-plugin

plugins中無需手動初始化 html-webpack-plugin,改成動態添加:

getEntry().forEach(pathname => {
  let conf = {
    filename: path.join(pathname, pathname) + '.html',
    template: path.join(__dirname, 'src', pathname, 'html', 'index.html')
  }
  webpackconfig.plugins.push(new HtmlWebpackPlugin(conf))
})
// ...
module.exports = webpackconfig
複製代碼

完成了上述修改後,之後不管是在項目中添加頁面仍是刪除頁面,都無需再對 webpack配置進行手動修改了,雖然開始時開起來彷佛這種動態的自動配置代碼比較多,並且稍微複雜一點,可是從長期來看,絕對是一勞永逸的好作法。

另外,若是你的項目目錄結構和我示例的目錄結構不同,那麼就須要你根據本身的目錄結構對代碼進行少量的修改,但總體解決問題的方法是不變的,一個易於維護的項目,目錄結構都該是有律可循的。

相關文章
相關標籤/搜索