前言

使用過 vue 進行項目開發的同學,一定知道或者使用過 vue-cli 腳手架,他能夠很好的搭建項目結構和工程,讓我們能夠把足夠的精力放在業務開發上。也正是因為這樣,很多時候我們會因為項目工期短等原因來不及或則不會刻意去了解項目工程配置,我們今天不去介紹腳手架的使用,我們去了解下腳手架為我們創建好的打包工程是怎麼做的。

項目結構

├── build --------------------------------- webpack相關配置文件
│ ├── build.js --------------------------webpack打包配置文件
│ ├── check-versions.js ------------------------------ 檢查npm,nodejs版本
│ ├── logo.png ---------------------------------- 項目 logo
│ ├── utils.js --------------------------------------- 配置資源路徑,配置css載入器
│ ├── vue-loader.conf.js ----------------------------- 配置css載入器等
│ ├── webpack.base.conf.js --------------------------- webpack基本配置
│ ├── webpack.dev.conf.js ---------------------------- 用於開發的webpack設置
│ ├── webpack.prod.conf.js --------------------------- 用於打包的webpack設置
├── config ---------------------------------- 配置文件
├── index.js ------------------------------ 開發和生產環境配置文件
├── node_modules ---------------------------- 存放依賴的目錄
├── src ------------------------------------- 源碼
│ ├── assets ------------------------------ 靜態文件
│ ├── components -------------------------- 組件
│ ├── main.js ----------------------------- 主js
│ ├── App.vue ----------------------------- 項目入口組件
│ ├── router ------------------------------ 路由
├── package.json ---------------------------- node配置文件
├── .babelrc--------------------------------- babel配置文件
├── .editorconfig---------------------------- 編輯器配置
├── .gitignore------------------------------- 配置git可忽略的文件

webpack配置劃重點

在看項目配置文件之前,我們先了解下 webpack 幾個常用的工具和插件,如果你已經十分熟悉,你可以跳過這一小節,直接去看,配置文件解析

1. path模塊

path 是 node.js 中的一個模塊,用於處理目錄的對象,提高開發效

常用方法:
path.join(): 用於連接路徑。該方法的主要用途在於,會正確使用當前系統的路徑分隔符,Unix 系統是 」/「,Windows系統是 」「
path.resolve() 用於將相對路徑轉為絕對路徑

常使用的文件路徑
__dirname: 總是返回被執行的 js 所在文件夾的絕對路徑
__filename: 總是返回被執行的 js 的絕對路徑
process.cwd(): 總是返回運行 node 命令時所在的文件夾的絕對路徑

2.process

process對象是Node的一個全局對象,提供當前Node進程的信息。

process 對象提供一系列屬性,用於返回系統信息
process.argv:返回當前進程的命令行參數數組。
process.env:返回一個對象,成員為當前Shell的環境變數,比如process.env.HOME
process.pid:當前進程的進程號

3.Source map

簡單說,Source map就是一個信息文件,裡面儲存著位置信息。也就是說,轉換後的代碼的每一個位置,所對應的轉換前的位置。有了它,出錯的時候,debug 工具將直接顯示原始代碼,而不是轉換後的代碼。這無疑給開發者帶來了很大方便。webpack 的 devtool里有 7種 SourceMap 模式

模式解釋eval每個 module 會封裝到 eval 里包裹起來執行,並且會在末尾追加註釋 //@ sourceURLsource-map生成一個 SourceMap 文件.hidden-source-map和 source-map 一樣,但不會在 bundle 末尾追加註釋.inline-source-map生成一個 DataUrl 形式的 SourceMap 文件.eval-source-map每個 module 會通過 eval() 來執行,並且生成一個 DataUrl 形式的 SourceMap .cheap-source-map生成一個沒有列信息(column-mappings)的 SourceMaps 文件,不包含 loader 的 sourcemap(譬如 babel 的 sourcemap)cheap-module-source-map生成一個沒有列信息(column-mappings)的 SourceMaps 文件,同時 loader 的 sourcemap 也被簡化為只包含對應行的。

4. webpack-merge

開發環境(development)和生產環境(production)的構建目標差異很大。在開發環境中,我們需要具有強大的、具有實時重新載入(live reloading)或熱模塊替換(hot module replacement)能力的 source map 和 localhost server。而在生產環境中,我們的目標則轉向於關注更小的 bundle,更輕量的 source map,以及更優化的資源,以改善載入時間。由於要遵循邏輯分離,我們通常建議為每個環境編寫彼此獨立的 webpack 配置。通用的配置部分,我們抽象出一個公共文件,通過 webpack-merge 工具的「通用」配置,我們不必在環境特定的配置中重複代碼。

5. ExtractTextWebpackPlugin

ExtractTextWebpackPlugin 插件通常用來做樣式文件的分離,被分離的文件不會被內嵌到 JS bundle 中,而會被放到一個單獨的文件中,在樣式文件比較大的時候,能夠提前樣式的載入,配置示例如下

const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}

它會將所有的入口 chunk(entry chunks)中引用的 *.css,移動到獨立分離的 CSS 文件。因此,你的樣式將不再內嵌到 JS bundle 中,而是會放到一個單獨的 CSS 文件(即 styles.css)當中。 如果你的樣式文件大小較大,這會做更快提前載入,因為 CSS bundle 會跟 JS bundle 並行載入。

6.html-webpack-plugin

如果你有多個 webpack 入口點, 他們都會在生成的HTML文件中的 script 標籤內。如果你有任何 CSS assets 在 webpack 的輸出中(例如, 利用ExtractTextPlugin提取CSS), 那麼這些將被包含在HTML head中的標籤內。通常在開發中,我們為了避免 CDN 和瀏覽器的緩存通常會個輸出文件 bundle.js 加上一個hash 值例如 [hash].bundle.js,使用 html-webpack-plugin 能夠在創建新的 html 文件的時候將我們把帶有哈希值的 bundle.js 引用到 html 文件.

7.optimize-css-assets-webpack-plugin

用來優化從腳本里提煉出來的 css ,配置示例如下

var OptimizeCssAssetsPlugin = require(optimize-css-assets-webpack-plugin);
module.exports = {
module: {
rules: [
{
test: /.css$/,
loader: ExtractTextPlugin.extract(style-loader, css-loader)
}
]
},
plugins: [
new ExtractTextPlugin(styles.css),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /.optimize.css$/g,
cssProcessor: require(cssnano),
cssProcessorOptions: { discardComments: { removeAll: true } },
canPrint: true
})
]
};

8.CopyWebpackPlugin

CopyWebpackPlugin從插件名稱上我們不難看出他的作用,通常用來拷貝資源,對項目文件進行歸類整合

9.friendly-errors-webpack-plugin

friendly-errors-webpack-plugin能夠更好在終端看到webapck運行的警告和錯誤,提高開發體驗

10.UglifyjsWebpackPlugin

UglifyjsWebpackPlugin用來壓縮 js 代碼

11.開發中 Server(DevServer)

webpack 項目服務,我們通常會在開發階段用來配置項目的熱刷新,服務壓縮,項目代理等,常用的幾個配置參數介紹如下

const config = require(../config)

// config 文件里做了用戶自定的服務參數配置

devServer: {
clientLogLevel: warning, // 在開發攻擊的控制台中顯示信息,便於開發調試,你可以將參數配置成 "none" 來進行關閉
historyApiFallback: { // 當使用 HTML5 History API 時,任意的 404 響應都可能需要被替代為 index.html
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, index.html) },
],
},
hot: true, //啟用項目的熱刷新,即模塊熱替換特性
contentBase: false, // 告訴伺服器從哪裡提供內容。只有在你想要提供靜態文件時才需要。這裡禁用,因為配置了 CopyWebpackPlugin 的使用
compress: true,
host: HOST || config.dev.host, //指定使用一個域名。默認是 localhost
port: PORT || config.dev.port, //指定要監聽請求的埠號:
open: config.dev.autoOpenBrowser, //open 參數配置,如果配置成 true ,項目啟動後會自動打開瀏覽器
overlay: config.dev.errorOverlay //當有錯誤或則警告的時候在頁面上顯示一個全屏的遮罩提示
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath, //此路徑下的打包文件可在瀏覽器中訪問
proxy: config.dev.proxyTable, //代理API的請求
quiet: true, //啟用 quiet 後,除了初始啟動信息之外的任何內容都不會被列印到控制台,特別是使用了 FriendlyErrorsPlugin 插件的時候
watchOptions: { //與監視文件相關的控制選項。是否使用輪詢
poll: config.dev.poll,
}
},

配置文件解析

通過了解了上面的配置,我們應該對 webpack 的常用插件和工具有了一定了解,我們來看下 vue-cli 腳手架給我們生成的配置情況

config.js

use strict

const path = require(path) // 引用項目的 path 模塊

module.exports = { dev: {

// 路徑配置
assetsSubDirectory: static,
assetsPublicPath: /,
proxyTable: {},

// 各種開發服務配置
host: localhost, // 開發環境域名 可以被 node 全局變數process.env.HOST 重寫
port: 8080, //配置開發服務埠,可以被 node 全局變數 process.env.PORT 重寫, 需要使用未被佔用的埠
autoOpenBrowser: false, //服務啟動是否自動代開瀏覽器
errorOverlay: true, //是否在發生錯誤的時候,在頁面整屏增加一個錯誤遮罩
notifyOnErrors: true, //是否通知錯誤 ,在我們的項目配置中和 friendly-errors-webpack-plugin 結合使用
poll: false, // 服務監聽是否輪詢操作

// 配飾是否使用 Eslint Loader 進行語法檢測
// 如果使用,在開發構建階段,會對你的代碼會進行檢測
// 檢測出來的警告和錯誤會白展示在開發工具的控制台

useEslint: true, //進行語法檢測

// 配置是否將 eslint 語法檢測的警告和錯誤展示在頁面整屏的遮罩上

showEslintErrorsInOverlay: false, // 語法檢測的警告和錯誤不展示在遮罩上

/**
* Source Maps
*/

// https://webpack.js.org/configuration/devtool/#development
// 在上面的介紹中,我們知道 source map 是用來將我們構建後被轉化的代碼對應構建前的代碼,便於 debug
// cheap-module-eval-source-map 和我們介紹的 cheap-module-source-map 很類似,但是 SourceMap 會被作為數據添加到包中
devtool: cheap-module-eval-source-map,

// 如果你的開發工具不能進行 vue-files 的 debug ,可以將以下設置設置成 false

cacheBusting: true,

cssSourceMap: true

},

build: { // index.html 文件模板 index: path.resolve(__dirname, ../dist/index.html),

// 打包路徑配置
assetsRoot: path.resolve(__dirname, ../dist),
assetsSubDirectory: static,
assetsPublicPath: /,

/**
* Source Maps
*/

//生產環境 source map 配置

productionSourceMap: true,
devtool: #source-map,

// 因為很多的主流服務都會 通過 gzip 壓縮過你的所有靜態資源,我們的配置默認不開啟 gzip
// 如果要設置成開啟,請先確保已經安裝好 compression-webpack-plugin 插件
productionGzip: false,
productionGzipExtensions: [js, css],

// 啟動 build 命令的時候,額外添加一個參數,打包後會自動生成一個分析報告文件,例如 npm run build --report ,可以通過配置 true ,false 來關閉
bundleAnalyzerReport: process.env.npm_config_report

} }

check-versions.js

這個文件主要是用來檢測當前環境中的node和npm版本和我們需要的是否一致的。

use strict
const chalk = require(chalk) // 改變命令行中的字體顏色,大致這樣用chalk.blue(Hello world)
const semver = require(semver) //是用來對特定的版本號做判斷的

const packageConfig = require(../package.json) // 項目 npm 配置文件,獲取依賴及版本信息,requrie返回的就是json對象
const shell = require(shelljs) //用來執行Unix系統命令,調用系統命令更加方便

//把cmd這個參數傳遞的值轉化成前後沒有空格的字元串,也就是版本號
function exec (cmd) {
return require(child_process).execSync(cmd).toString().trim()
}

const versionRequirements = [
{
name: node,
currentVersion: semver.clean(process.version), // 提取進程版本信息轉化成規定格式,也就是 =v1.2.3 -> 1.2.3 這種功能
versionRequirement: packageConfig.engines.node // package.json 的 node 的版本信息
}
]

if (shell.which(npm)) {
versionRequirements.push({
name: npm,
currentVersion: exec(npm --version), //當前的版本信息
versionRequirement: packageConfig.engines.npm //package.json 的 node 的版本信息
})
}

module.exports = function () {
const warnings = []

for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]

// 如果當前版本號不符合 package.json 要求的版本號,紅色表示當前版本信息,綠色表示要求的版本信息,添加到 warnings 待輸出
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + : +
chalk.red(mod.currentVersion) + should be +
chalk.green(mod.versionRequirement)
)
}
}

//輸出版本號不相符的提示 warnings
if (warnings.length) {
console.log()
console.log(chalk.yellow(To use this template, you must update following to modules:))
console.log()

for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log( + warning)
}

console.log()
process.exit(1)
}
}

build.js

use strict

//打包前判斷當先開發環境的 node 和 npm 版本和 package.json 要求的時候一樣
require(./check-versions)()

process.env.NODE_ENV = production

const ora = require(ora) // 在用戶打包的時候能夠讓用戶知道正在進行,一個載入中的樣式,轉啊轉
const rm = require(rimraf) //這個模塊是用來清除之前的打的包,因為在vue-cli中每次打包會生成不同的hash
const path = require(path) //node 路徑模塊,便於我們操作文件路徑
const chalk = require(chalk) //帶顏色的輸出模塊,能在控制台中輸出不同的樣色
const webpack = require(webpack) //webpack 不解釋
const config = require(../config) // 項目中的配置文件,??上面已經進行了配置介紹
const webpackConfig = require(./webpack.prod.conf) // 生產環境的配置文件

const spinner = ora(building for production...)// 實例一個打包載入中實例
spinner.start() //開始轉圈,營造一個正在打包的場景

// 刪除上一次打包的文件,刪除成功,開始按照生產環境配置進行打包
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err

//開始打包,打包結束停止 spinner 轉圈,有報錯則在控制台輸出
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err

// node 環境里的輸出配置,process.stdout.write 你可以理解成 js 里的 console
process.stdout.write(stats.toString({
colors: true, //讓打包的時候有顏色。
modules: false, //去掉內置模塊信息
children: false, // 去掉子模塊,如果你使用了 ts-loader,設置成 true 會在打包構建階段展示錯誤信息
chunks: false, // 增加包信息(設置為 false 能允許較少的冗長輸出)
chunkModules: false //去除包里內置模塊的信息
}) +

)

//打包出錯在控制台輸出 Build failed with errors ,退出打包程序
if (stats.hasErrors()) {
console.log(chalk.red( Build failed with errors.
))
process.exit(1)
}

//打包成功則輸出 Build complete 結束打包
console.log(chalk.cyan( Build complete.
))
console.log(chalk.yellow(
Tip: built files are meant to be served over an HTTP server.
+
Opening index.html over file:// won work.

))
})
})

webpack.base.conf.js

use strict
const path = require(path) // node 路徑模塊
const utils = require(./utils) //node 內部常用的工具類,其中包括:格式化字元串、對象的序列化、實現對象繼承等常用方法
const config = require(../config) //??上面我們介紹的,項目配置文件
const vueLoaderConfig = require(./vue-loader.conf) //?? 上面我們介紹的 vue 載入器配置文件

//返回當前配置文件位置是 build ,該方法放回 build/../dir 的相對路基
function resolve (dir) {
return path.join(__dirname, .., dir)
}

// eslint 語法檢測配置
const createLintingRule = () => ({
test: /.(js|vue)$/,
loader: eslint-loader,
enforce: pre,
include: [resolve(src), resolve(test)],
options: {
formatter: require(eslint-friendly-formatter),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})

// webpack 通用配置內容
module.exports = {
context: path.resolve(__dirname, ../), // 上下文,基礎目錄,用於從配置中解析入口起點和 loader
entry: {
app: ./src/main.js //起點或是應用程序的起點入口。從這個起點開始,應用程序啟動執行。如果傳遞一個數組,那麼數組的每一項都會執行。
},
output: {
path: config.build.assetsRoot, //輸出 bundle 的路徑
filename: [name].js, //輸出 bundle 的名稱
publicPath: process.env.NODE_ENV === production // 指定資源文件引用的目錄,例如圖片
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: [.js, .vue, .json], //配置模塊如何解析,
alias: { // 創建應用的別名,
vue$: vue/dist/vue.esm.js,
@: resolve(src),
}
},

module: { rules: [ //判斷配置中是否要是用 eslint 語法檢測,如果使用,就將 createLintingRule 配置對象返回 ...(config.dev.useEslint ? [createLintingRule()] : []),

//??是一些比較常用的載入器,及配置,不做詳細介紹了
{
test: /.vue$/,
loader: vue-loader,
options: vueLoaderConfig
},
{
test: /.js$/,
loader: babel-loader,
include: [resolve(src), resolve(test), resolve(node_modules/webpack-dev-server/client)]
},
{
test: /.(css | scss)$/,
loader: style-loader!css-loader!!sass-loader
},
{
test: /.(png|jpe?g|gif|svg)(?.*)?$/,
loader: url-loader,
options: {
limit: 10000,
name: utils.assetsPath(img/[name].[hash:7].[ext])
}
},
{
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,
loader: url-loader,
options: {
limit: 10000,
name: utils.assetsPath(media/[name].[hash:7].[ext])
}
},
{
test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
loader: url-loader,
options: {
limit: 10000,
name: utils.assetsPath(fonts/[name].[hash:7].[ext])
}
}
]

}, node: {

//防止因為 vue 資源本身就自帶的 無用的 node 注入,瀏覽器兼容處理
setImmediate: false,
dgram: empty,
fs: empty,
net: empty,
tls: empty,
child_process: empty

} }

webpack.dev.conf.js

use strict
const utils = require(./utils) //node 工具模塊
const webpack = require(webpack) //webpack 不解釋
const config = require(../config)//??提到的配置文件
const merge = require(webpack-merge) // merge 工具,用來合併生產和開發環境通用的基礎 webpack 配置
const path = require(path) //node 的路徑模塊
const baseWebpackConfig = require(./webpack.base.conf) //生產和開發環境通用的基礎 webpack 配置
const CopyWebpackPlugin = require(copy-webpack-plugin) //拷貝插件
const HtmlWebpackPlugin = require(html-webpack-plugin) //動態生成 html 插件
const FriendlyErrorsPlugin = require(friendly-errors-webpack-plugin) //友好的錯誤輸出插件
const portfinder = require(portfinder) //能夠獲取一個可用的隨機埠號

const HOST = process.env.HOST //node 全局環境變數的主機
const PORT = process.env.PORT && Number(process.env.PORT) //node 全局環境變數的埠

//合併基礎配置載入器的配置部分
const devWebpackConfig = merge(baseWebpackConfig, {

module: {
// 為 .vue 文件意外的獨立樣式文件配置載入器
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},

// cheap-module-eval-source-map 在開發環境中很快
devtool: config.dev.devtool,

// 開發服務配置,?? 已經細講過,順便回顧一下
devServer: {
clientLogLevel: warning, // 在開發攻擊的控制台中顯示信息,便於開發調試,你可以將參數配置成 "none" 來進行關閉
historyApiFallback: { // 當使用 HTML5 History API 時,任意的 404 響應都可能需要被替代為 index.html
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, index.html) },
],
},
hot: true, //啟用項目的熱刷新,即模塊熱替換特性
contentBase: false, // 告訴伺服器從哪裡提供內容。只有在你想要提供靜態文件時才需要。這裡禁用,因為配置了 CopyWebpackPlugin 的使用
compress: true,
host: HOST || config.dev.host, //指定使用一個域名。默認是 localhost
port: PORT || config.dev.port, //指定要監聽請求的埠號:
open: config.dev.autoOpenBrowser, //open 參數配置,如果配置成 true ,項目啟動後會自動打開瀏覽器
overlay: config.dev.errorOverlay //當有錯誤或則警告的時候在頁面上顯示一個全屏的遮罩提示
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath, //此路徑下的打包文件可在瀏覽器中訪問
proxy: config.dev.proxyTable, //代理API的請求
quiet: true, //啟用 quiet 後,除了初始啟動信息之外的任何內容都不會被列印到控制台,特別是使用了 FriendlyErrorsPlugin 插件的時候
watchOptions: { //與監視文件相關的控制選項。是否使用輪詢
poll: config.dev.poll,
}
},

plugins: [
// DefinePlugin 允許創建一個在編譯時可以配置的全局常量。這可能會對開發模式和發布模式的構建允許不同的行為非常有用
new webpack.DefinePlugin({
process.env: require(../config/dev.env)
}),
new webpack.HotModuleReplacementPlugin(), //啟用熱替換模塊(Hot Module Replacement),也被稱為 HMR
new webpack.NamedModulesPlugin(), // 當開啟 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境
new webpack.NoEmitOnErrorsPlugin(), 在編譯出現錯誤時,使用 NoEmitOnErrorsPlugin 來跳過輸出階段

//HtmlWebpackPlugin簡化了HTML文件的創建,以便為你的webpack包提供服務。這對於在文件名中包含每次會隨著編譯而發生變化哈希的 webpack bundle 尤其有用
new HtmlWebpackPlugin({
filename: index.html,
template: index.html,
inject: true
}),

// 拷貝自定義的靜態資源文件
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, ../static),
to: config.dev.assetsSubDirectory,
ignore: [.*]
}
])
]
})

// 實例一個非同步對象,執行 devWebpackConfig 配置編譯
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port //設置基礎埠
portfinder.getPort((err, port) => {獲取埠,輸出構建新
if (err) {
reject(err)
} else {
// 如果進行 e2e 測試,需要發布新埠
process.env.PORT = port

// 更新 devServer 的埠
devWebpackConfig.devServer.port = port

devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))

//執行打包配置文件
resolve(devWebpackConfig)
}
})
})

webpack.prod.conf.js

use strict
const path = require(path) // node 路徑模塊
const utils = require(./utils) //小工具函數
const webpack = require(webpack) // webpack 不解釋
const config = require(../config)//??提到的配置文件
const merge = require(webpack-merge) // merge 工具,用來合併生產和開發環境通用的基礎 webpack 配置
const baseWebpackConfig = require(./webpack.base.conf)//產和開發環境通用的基礎 webpack 配置
const CopyWebpackPlugin = require(copy-webpack-plugin) //拷貝插件
const HtmlWebpackPlugin = require(html-webpack-plugin) //動態生成 html 插件
const ExtractTextPlugin = require(extract-text-webpack-plugin)//用來做文件分離的插件
const OptimizeCSSPlugin = require(optimize-css-assets-webpack-plugin)//優化提煉出來的css
const UglifyJsPlugin = require(uglifyjs-webpack-plugin)// 壓縮 js 文件插件

//生產環境配置
const env = require(../config/prod.env)

//合併基礎配置載入器的配置部分
const webpackConfig = merge(baseWebpackConfig, {
//為獨立分離出來的樣式配置載入器和source,map
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
//配置線上的 source map 便於排查問題
devtool: config.build.productionSourceMap ? config.build.devtool : false,
//配置輸出,路徑,文件名
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath(js/[name].[chunkhash].js),
chunkFilename: utils.assetsPath(js/[id].[chunkhash].js)
},
plugins: [
// DefinePlugin 允許創建一個在編譯時可以配置的全局常量。這可能會對開發模式和發布模式的構建允許不同的行為非常有用
new webpack.DefinePlugin({
process.env: env
}),

// 使用 UglifyJsPlugin 插件對 js 進行壓縮
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
//配置插件的source map
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// 提取 css 到單獨的文件,分離文件非同步載入,提高載入速度
new ExtractTextPlugin({
filename: utils.assetsPath(css/[name].[contenthash].css),

//如果把 allChunks 參數設置陳 false ,就不會把css 從代碼塊中分離出來
//代碼塊載入的時候 css 會被 styles-loader 動態的載入
allChunks: true,
}),

//使用這個插件,從不同的組件中複製脫離出來,進行 css 壓縮
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),

//自動生成 html 文件,通常 index.html 文件都會帶一個哈希值來清除緩存
new HtmlWebpackPlugin({
filename: config.build.index,
template: index.html,
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},

chunksSortMode: dependency
}),
//該插件會根據模塊的相對路徑生成一個四位數的hash作為模塊id, 渲染模塊沒有變化的時候,id 不會變。
new webpack.HashedModuleIdsPlugin(),

// 提升或者預編譯所有模塊到一個閉包中,提升你的代碼在瀏覽器中的執行速度。
new webpack.optimize.ModuleConcatenationPlugin(),

// 分離渲染的js 到獨立的文件中
new webpack.optimize.CommonsChunkPlugin({
name: vendor,
minChunks (module) {
//被引用到的包會從 node_modules 中提取出來
return (
module.resource &&
/.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, ../node_modules)
) === 0
)
}
}),

new webpack.optimize.CommonsChunkPlugin({
name: manifest,
minChunks: Infinity
}),

new webpack.optimize.CommonsChunkPlugin({
name: app,
async: vendor-async,
children: true,
minChunks: 3
}),

// 拷貝自定義的靜態資源文件
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, ../static),
to: config.build.assetsSubDirectory,
ignore: [.*]
}
])
]
})

//判斷如果配置了生產環境壓縮,是則使用插件進行壓縮
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require(compression-webpack-plugin)

webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: [path].gz[query],
algorithm: gzip,
test: new RegExp(
\.( +
config.build.productionGzipExtensions.join(|) +
)$
),
threshold: 10240,
minRatio: 0.8
})
)
}

//是否要生成代碼打包分析報告
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require(webpack-bundle-analyzer).BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

擴展

??這篇文章詳細的介紹了腳手架項目的 webpack 配置,但是只是 webpack 的一部分,還有很多內容值得我們去探究,如果你還感興趣,可以閱讀下面這些文章。也歡迎隨時與我進行交流,微信號:646321933

webpack指南

webpack文檔

webpack配置

自己動手實現一個腳手架

webpack 官方插件集合介紹文檔

《 使用vue-cli腳手架創建新項目》


推薦閱讀:
相关文章