最近在作項目的時候遇到了一個場景:一個項目有多個入口,不一樣的入口,路由、組件、資源等有重疊部分,也有各自不一樣的部分。因爲不一樣入口下的路由頁面有一些是重複的,所以我考慮使用 Webpack 多入口配置來解決這個需求。javascript
再一次,在網上找的很多文章都不合個人需求,不少文章都是隻簡單介紹了生產環境下配置,沒有介紹開發環境下的配置,有的也沒有將多入口結合 vue-router
、vuex
、ElementUI
等進行配置,所以在下經過不斷探坑,而後將思路和配置過程記錄下來,留給本身做爲筆記,同時也分享給你們,但願能夠幫助到有一樣需求的同窗們~html
代碼倉庫:multi-entry-vue前端
示意圖以下:vue
首先咱們 vue init webpack multi-entry-vue
使用 vue-cli
建立一個 webpack 模版的項。文件結構以下:java
.
├── build
├── config
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue
│ └── main.js
├── static
├── README.md
├── index.html
├── package-lock.json
└── package.json
複製代碼
這裏順便介紹在不一樣系統下生成目錄樹的方法:node
- mac 系統命令行生成目錄樹的方法
tree -I node_modules --dirsfirst
,這個命令的意思是,不顯示node_modules
路徑的文件,而且以文件夾在前的排序方式生成目錄樹。若是報沒有找到 tree 命令的錯,安裝 tree 命令行brew install tree
便可。- windows 系統在目標目錄下使用
tree /f 1.txt
便可把當前目錄樹生成到一個新文件1.txt
中。
首先咱們簡單介紹一下 Webpack 的相關配置項,這些配置項根據使用的 Webpack 模版不一樣,通常存放在 webpack.config.js
或 webpack.base.conf.js
中:webpack
const path = require('path')
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'output-file.js',
publicPath: '/'
},
module: {}, // 文件的解析 loader 配置
plugins: [], // 插件,根據須要配置各類插件
devServer: {} // 配置 dev 服務功能
}
複製代碼
這個配置的意思是,進行 Webpack 後,會在命令的執行目錄下新建 dist
目錄(若是須要的話),並將打包 src
目錄下的 main.js
和它的依賴,生成 output-file.js
放在 dist
目錄中。git
下面稍微解釋一下相關配置項:github
app
是入口名稱,若是 output.filename
中有 [name]
的話,就會被替換成 app
。entry
選項的基礎目錄(絕對路徑),entry
入口起點會相對於此目錄查找,至關於公共目錄,下面全部的目錄都在這個公共目錄下面。dist
,那麼就會將輸出的文件放在當前目錄同級目錄的 dist
文件夾下,沒有這個文件夾就新建一個。 能夠配置爲 path.resolve(__dirname, './dist/${Date.now()}/')
(md 語法不方便改爲模板字符串,請自行修改)方便作持續集成。[name]
的意爲根據入口文件的名稱,打包成相同的名稱,有幾個入口,就能夠打包出幾個文件。 好比入口的 key
爲 app
,打包出來就是 app.js
,入口是 my-entry
,打包出來就是 my-entry.js
。靜態資源最終訪問路徑 = output.publicPath + 資源loader或插件等配置路徑
。 舉個例子,publicPath
配置爲 /dist/
,圖片的 url-loader
配置項爲 name: 'img/[name].[ext]'
,那麼最終打包出來文件中圖片的引用路徑爲 output.publicPath + 'img/[name].[ext]' = '/dist/img/[name].[ext]'
。本文因爲是入口和出口相關的配置,因此內容主要圍繞着 entry
、output
和一個重要的 webpack 插件 html-webpack-plugin,這個插件是跟打包出來的 HTML 文件密切相關,主要有下面幾個做用:web
link
、script
等;下面咱們從頭一步步配置一個多入口項目。
在 src
目錄下將 main.js
和 App.vue
兩個文件各複製一下,做爲不一樣入口,文件結構變爲:
.
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js # 主要配置目標
│ └── webpack.prod.conf.js # 主要配置目標
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue
│ ├── App2.vue # 新增的入口
│ ├── main.js
│ └── main2.js # 新增的入口
├── static
├── README.md
├── index.html
└── package.json
複製代碼
要想從不一樣入口,打包出不一樣 HTML,咱們能夠改變一下 entry
和 output
兩個配置,
// build/webpack.prod.conf.js
module.exports = {
entry: {
entry1: './src/main.js',
entry2: './src/main2.js'
},
output: {
filename: '[name].js',
publicPath: '/'
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html", // 要打包輸出哪一個文件,可使用相對路徑
filename: "index.html" // 打包輸出後該html文件的名稱
})
]
}
複製代碼
根據上面一小節咱們知道,webpack 配置裏的 output.filename
若是有 [name]
意爲根據入口文件的名稱,打包成對應名稱的 JS 文件,那麼如今咱們是能夠根據兩個入口打包出 entry.js
和 entry2.js
。
打包的結果以下:
當前代碼:Github - multi-entry-vue1
如上圖,此時咱們 npm run build
打包出一個引用了這兩個文件的 index.html
,那麼如何打包出不一樣 HTML 文件,分別應用不一樣入口 JS 文件呢,此時咱們須要藉助於 HtmlWebpackPlugin
這個插件。
HtmlWebpackPlugin
這個插件,new
一個,就打包一個 HTML 頁面,因此咱們在 plugins
配置裏 new
兩個,就能打包出兩個頁面來。
咱們把配置文件改爲下面這樣:
// build/webpack.prod.conf.js
module.exports = {
entry: {
entry: './src/main.js', // 打包輸出的chunk名爲entry
entry2: './src/main2.js' // 打包輸出的chunk名爲entry2
},
output: {
filename: '[name].js',
publicPath: '/'
},
plugins: [
new HtmlWebpackPlugin({
filename: 'entry.html', // 要打包輸出的文件名
template: 'index.html', // 打包輸出後該html文件的名稱
chunks: ['manifest', 'vendor', 'entry'] // 輸出的html文件引入的入口chunk
// 還有一些其餘配置好比minify、chunksSortMode和本文無關就省略,詳見github
}),
new HtmlWebpackPlugin({
filename: 'entry2.html',
template: 'index.html',
chunks: ['manifest', 'vendor', 'entry2']
})
]
}
複製代碼
上面一個配置要注意的是 chunks
,若是沒有配置,那麼生成的 HTML 會引入全部入口 JS 文件,在上面的例子就是,生成的兩個 HTML 文件都會引入 entry.js
和 entry2.js
,因此要使用 chunks
配置來指定生成的 HTML 文件應該引入哪一個 JS 文件。配置了 chunks
以後,才能達到不一樣的 HTML 只引入對應 chunks
的 JS 文件的目的。
你們能夠看到除了咱們打包生成的 chunk
文件 entry.js
和 entry2.js
以外,還有 manifest
和 vendor
這兩個,這裏稍微解釋一下這兩個 chunk
:
vendor
是指提取涉及 node_modules
中的公共模塊;manifest
是對 vendor
模塊作的緩存;打包完的結果以下:
文件結構:
如今打包出來的樣式正是咱們所須要的,此時咱們在 dist
目錄下啓動 live-server
(若是你沒安裝的話能夠先安裝 npm i -g live-server
),就能夠看到效果出來了:
當前代碼:Github - multi-entry-vue2
至此就實現了一個簡單的多入口項目的配置。
咱們在前文進行了多入口的配置,要想新建一個新的入口,就複製多個文件,再手動改一下對應配置。
可是若是不一樣的 HTML 文件下不一樣的 vue-router
、vuex
都放到 src
目錄下,多個入口的內容平鋪在一塊兒,項目目錄會變得凌亂不清晰,所以在下將多入口相關的文件放到一個單獨的文件夾中,之後若是有多入口的內容,就到這個文件夾中處理。
下面咱們進行文件結構的改造:
entries
文件夾,把不一樣入口的 router
、store
、main.js
都放這裏,每一個入口相關單獨放在一個文件夾;src
目錄下創建一個 common
文件夾,用來存放多入口共用的組件等;如今的目錄結構:
.
├── build # 沒有改動
├── config # 沒有改動
├── entries # 存放不一樣入口的文件
│ ├── entry1
│ │ ├── router # entry1 的 router
│ │ │ └── index.js
│ │ ├── store # entry1 的 store
│ │ │ └── index.js
│ │ ├── App.vue # entry1 的根組件
│ │ ├── index.html # entry1 的頁面模版
│ │ └── main.js # entry1 的入口
│ └── entry2
│ ├── router
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── common # 多入口通用組件
│ │ └── CommonTemplate.vue
│ └── components
│ ├── HelloWorld.vue
│ ├── test1.vue
│ └── test2.vue
├── static
├── README.md
├── index.html
├── package-lock.json
└── package.json
複製代碼
而後咱們在 build/utils
文件中加兩個函數,分別用來生成 webpack 的 entry
配置和 HtmlWebpackPlugin
插件配置,因爲要使用 node.js
來讀取文件夾結構,所以須要引入 fs
、glob
等模塊:
// build/utils
const fs = require('fs')
const glob = require('glob')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ENTRY_PATH = path.resolve(__dirname, '../entries')
// 多入口配置,這個函數從 entries 文件夾中讀取入口文件,裝配成webpack.entry配置
exports.entries = function() {
const entryFiles = glob.sync(ENTRY_PATH + '/*/*.js')
const map = {}
entryFiles.forEach(filePath => {
const filename = filePath.replace(/.*\/(\w+)\/\w+(\.html|\.js)$/, (rs, $1) => $1)
map[filename] = filePath
})
return map
}
// 多頁面輸出模版配置 HtmlWebpackPlugin,根據環境裝配html模版配置
exports.htmlPlugin = function() {
let entryHtml = glob.sync(ENTRY_PATH + '/*/*.html')
let arr = []
entryHtml.forEach(filePath => {
let filename = filePath.replace(/.*\/(\w+)\/\w+(\.html|\.js)$/, (rs, $1) => $1)
let conf = {
template: filePath,
filename: filename + '.html',
chunks: [filename],
inject: true
}
// production 生產模式下配置
if (process.env.NODE_ENV === 'production') {
conf = merge(conf, {
chunks: ['manifest', 'vendor'],
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'
})
}
arr.push(new HtmlWebpackPlugin(conf))
})
return arr
}
複製代碼
稍微解釋一下這兩個函數:
exports.entries
函數從 entries
文件夾中找到二級目錄下的 JS 文件做爲入口文件,而且將二級目錄的文件夾名做爲 key
,生成這樣一個對象:{"entry1": "/multi-entry-vue/entries/entry1/main.js"}
,多個入口狀況下會有更多鍵值對;
exports.htmlPlugin
函數和以前函數的原理相似,不過組裝的是 HtmlWebpackPlugin
插件的配置,生成這樣一個數組,能夠看到和咱們手動設置的配置基本同樣,只不過如今是根據文件夾結構來生成的:
// production 下
[
{
template: "/multi-entry-vue/entries/entry1/index.html",
chunks: ['manifest', 'vendor', 'entry1'],
filename: "entry1.html",
chunksSortMode: 'dependency'
},
{ ... } // 下一個入口的配置
]
複製代碼
有了這兩個根據 entries
文件夾的結構來自動生成 webpack 配置的函數,下面來改一下 webpack 相關的幾個配置文件:
// build/webpack.base.conf.js
module.exports = {
entry: utils.entries(), // 使用函數生成 entry 配置
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
}
}
複製代碼
// build/webpack.dev.conf.js
// const HtmlWebpackPlugin = require('html-webpack-plugin') // 不須要了
const devWebpackConfig = merge(baseWebpackConfig, {
devServer: {
historyApiFallback: {
rewrites: [ // 別忘了把 devserver 的默認路由改一下
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'entry1.html') },
],
}
},
plugins: [
// https://github.com/ampedandwired/html-webpack-plugin
// new HtmlWebpackPlugin({
// filename: 'index.html',
// template: 'index.html',
// inject: true
// }), // 註釋掉原來的 HtmlWebpackPlugin 配置,使用生成的配置
].concat(utils.htmlPlugin())
})
複製代碼
// build/webpack.prod.conf.js
// const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackConfig = merge(baseWebpackConfig, {
plugins: [
// new HtmlWebpackPlugin({
// ... 註釋掉,不須要了
// }),
].concat(utils.htmlPlugin())
})
複製代碼
如今咱們再 npm run build
,看看生成的目錄是什麼樣的:
此時咱們在 dist
目錄下啓動 live-server
看看是什麼效果:
當前代碼:Github - multi-entry-vue3
網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出~
參考:
PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~
另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~