最近在作項目的時候遇到了一個場景:一個項目有多個入口,不一樣的入口,路由、組件、資源等有重疊部分,也有各自不一樣的部分。因爲不一樣入口下的路由頁面有一些是重複的,所以我考慮使用 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:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~
另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~