webpack.config.js
參考鏈接:2020年了,再不會(huì)webpack敲得代碼就不香了(近萬(wàn)字實(shí)戰(zhàn))
const path = require('path')
#每次執(zhí)行npm run build會(huì)發(fā)現(xiàn)dist文件夾里會(huì)殘留上次打包的文件,這里我們推薦一個(gè)plugin來(lái)幫我們?cè)诖虬敵銮扒蹇瘴募Aclean-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
#html-webpack-plugin---webpack打包生成的js文件會(huì)被自動(dòng)引入html文件中
#生成多個(gè)html-webpack-plugin實(shí)例來(lái)解決多入口文件開(kāi)發(fā)的問(wèn)題
const HtmlWebpackPlugin = require('html-webpack-plugin')
#vue-loader 用于解析.vue文件
#vue-template-compiler 用于編譯模板
const vueLoaderPlugin = require('vue-loader/lib/plugin')
#webpack4.0以后,官方推薦使用mini-css-extract-plugin插件來(lái)打包c(diǎn)ss文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
#process 對(duì)象是一個(gè) global (全局變量),提供有關(guān)信息,控制當(dāng)前 Node.js 進(jìn)程。作為一個(gè)對(duì)象,它對(duì)于 Node.js 應(yīng)用程序始終是可用的,故無(wú)需使用 require()。
#process是node全局屬性,直接用就行
#process.env屬性返回一個(gè)包含用戶環(huán)境信息的對(duì)象。#常用process.env.NODE_ENV來(lái)設(shè)置環(huán)境變量。在不同環(huán)境的機(jī)器上設(shè)置不同的 NODE_ENV,當(dāng)然這個(gè)字段也不一定。你也可以換成其他的NODE_ENV_NIZUISHUAI等等,反正是自定義的。參考鏈接:[Node環(huán)境變量 process.env 的那些事兒](https://segmentfault.com/a/1190000011683741)
#process.argv 屬性會(huì)返回一個(gè)數(shù)組,其中包含當(dāng) Node.js 進(jìn)程被啟動(dòng)時(shí)傳入的命令行參數(shù)。 第一個(gè)元素是 process.execPath。 如果需要訪問(wèn) argv[0] 的原始值,則參見(jiàn) process.argv0。 第二個(gè)元素是正被執(zhí)行的 JavaScript 文件的路徑。 其余的元素是任何額外的命令行參數(shù)。
#process.execPath 屬性返回啟動(dòng) Node.js 進(jìn)程的可執(zhí)行文件的絕對(duì)路徑名。比如'/usr/local/bin/node'
const devMode = process.argv.indexOf('--mode=production') === -1;#判斷當(dāng)前是否是dev環(huán)境
module.exports = {
#entry: ["@babel/polyfill",path.resolve(__dirname,'../src/index.js')], // 入口文件
entry:{
main:path.resolve(__dirname,'../src/main.js')
},
output:{
path:path.resolve(__dirname,'../dist'),#打包后的目錄
filename:'js/[name].[hash:8].js',#打包后的文件名稱
chunkFilename:'js/[name].[hash:8].js'
},
module:{
rules:[
{
#用babel轉(zhuǎn)義js文件 # npm i -D babel-loader @babel/preset-env @babel/core
test:/\.js$/,
use:{
#babel-loader只會(huì)將ES6/7/8語(yǔ)法轉(zhuǎn)換為ES5語(yǔ)法,但是對(duì)新api并不會(huì)轉(zhuǎn)換 例如(promise、Generator、Set、Maps、Proxy等)此時(shí)我們需要借助babel-polyfill來(lái)幫助我們轉(zhuǎn)換(用法可看entry配置示例)
loader:'babel-loader',#當(dāng)寫法為babel-loader?cacheDirectory=true時(shí),表示可以將Babel編譯過(guò)的文件緩存起來(lái),下次只需要編譯更改過(guò)的代碼文件即可,可以大幅度加快打包時(shí)間
options:{
presets:['@babel/preset-env',{ modules: false }]#因?yàn)锽abel的預(yù)案(preset)默認(rèn)會(huì)將任何模塊類型都轉(zhuǎn)譯成CommonJS類型,這樣會(huì)導(dǎo)致tree-shaking失效。修正這個(gè)問(wèn)題也很簡(jiǎn)單,在.babelrc文件或在webpack.config.js文件中設(shè)置modules: false就好了
}
},
exclude:/node_modules/#配置include exclude也可以減少webpack loader的搜索轉(zhuǎn)換時(shí)間。
},
{
test:/\.vue$/,
use:[{
loader:'vue-loader',
options:{
compilerOptions:{
preserveWhitespace:false
}
}
}]
},
{
test:/\.css$/,
use:[{
loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options:{
publicPath:"../dist/css/",
hmr:devMode
}
},'css-loader',{
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
}]#遵循從右向左解析原則
},
{
test:/\.less$/,
use:[{
loader:devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options:{
publicPath:"../dist/css/",
hmr:devMode
}
},'css-loader','less-loader',{
#為css添加瀏覽器前綴
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
}]
},
#打包 圖片、字體、媒體、等文件
#file-loader就是將文件在進(jìn)行一些處理后(主要是處理文件名和路徑、解析文件url),并將文件移動(dòng)到輸出的目錄中
#url-loader 一般與file-loader搭配使用,功能與 file-loader 類似,如果文件小于限制的大小。則會(huì)返回 base64 編碼,否則使用 file-loader 將文件移動(dòng)到輸出的目錄中
{
test:/\.(jep?g|png|gif)$/,
use:{
loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'img/[name].[hash:8].[ext]'
}
}
}
}
},
{
test:/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use:{
loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'media/[name].[hash:8].[ext]'
}
}
}
}
},
{
test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use:{
loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'media/[name].[hash:8].[ext]'
}
}
}
}
}
]
},
#關(guān)于resolve的具體用法,參考鏈接:[webpack學(xué)習(xí)筆記--配置resolve](https://www.cnblogs.com/joyco773/p/9049760.html)
resolve:{
#當(dāng)我們代碼中出現(xiàn) import 'vue'時(shí), webpack會(huì)采用向上遞歸搜索的方式去node_modules 目錄下找。為了減少搜索范圍我們可以直接告訴webpack去哪個(gè)路徑下查找。也就是別名(alias)的配置。
alias:{
'vue$':'vue/dist/vue.runtime.esm.js',
' @':path.resolve(__dirname,'../src')
},
#webpack會(huì)根據(jù)extensions定義的后綴查找文件(頻率較高的文件類型優(yōu)先寫在前面)
extensions:['*','.js','.json','.vue']
},
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html')#打包后生成的js文件會(huì)被自動(dòng)引入html文件中
}),
new vueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
})
]
}
webpack.dev.js(開(kāi)發(fā)環(huán)境配置文件)
mode可設(shè)置development production兩個(gè)參數(shù)
如果沒(méi)有設(shè)置,webpack4 會(huì)將 mode 的默認(rèn)值設(shè)置為 production。
production模式下會(huì)進(jìn)行tree shaking(去除無(wú)用代碼)和uglifyjs(代碼壓縮混淆)
const Webpack = require('webpack')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
module.exports = WebpackMerge(webpackConfig,{
mode:'development',
devtool:'cheap-module-eval-source-map',
#配置webpack-dev-server進(jìn)行熱更新 npm i -D webpack-dev-server
devServer:{
port:3000,
hot:true,
contentBase:'../dist'
},
plugins:[
new Webpack.HotModuleReplacementPlugin()
]
})
webpack.prod.js(生產(chǎn)環(huán)境配置文件)
#生產(chǎn)環(huán)境主要實(shí)現(xiàn)的是壓縮代碼、提取css文件、合理的sourceMap、分割代碼
#需要安裝以下模塊:
#npm i -D webpack-merge copy-webpack-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
const path = require('path')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')#合并配置
const CopyWebpackPlugin = require('copy-webpack-plugin')#拷貝靜態(tài)資源
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')#壓縮css
#webpack mode設(shè)置production的時(shí)候會(huì)自動(dòng)壓縮js代碼。原則上不需要引入uglifyjs-webpack-plugin進(jìn)行重復(fù)工作。但是optimize-css-assets-webpack-plugin壓縮css的同時(shí)會(huì)破壞原有的js壓縮,所以這里我們引入uglifyjs進(jìn)行壓縮
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')#壓縮js
module.exports = WebpackMerge(webpackConfig,{
mode:'production',
devtool:'cheap-module-source-map',
plugins:[
new CopyWebpackPlugin([{
from:path.resolve(__dirname,'../public'),
to:path.resolve(__dirname,'../dist')
}]),
],
optimization:{
minimizer:[
#或者使用webpack-parallel-uglify-plugin 增強(qiáng)代碼壓縮
new UglifyJsPlugin({//壓縮js
cache:true,
parallel:true,
sourceMap:true
}),
new OptimizeCssAssetsPlugin({})
],
splitChunks:{
chunks:'all',
cacheGroups:{
libs: {
name: "chunk-libs",
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: "initial" // 只打包初始時(shí)依賴的第三方
}
}
}
}
})
優(yōu)化webpack配置
1.優(yōu)化打包速度(構(gòu)建速度指的是我們每次修改代碼后熱更新的速度以及發(fā)布前打包文件的速度。)
- 合理的配置mode參數(shù)與devtool參數(shù)
- 縮小文件的搜索范圍(配置include exclude alias noParse extensions)
- 使用HappyPack開(kāi)啟多進(jìn)程Loader轉(zhuǎn)換
npm i -D happypack- 受限于Node是單線程運(yùn)行的,所以Webpack在打包的過(guò)程中也是單線程的,特別是在執(zhí)行Loader的時(shí)候,長(zhǎng)時(shí)間編譯的任務(wù)很多,這樣就會(huì)導(dǎo)致等待的情況
- HappyPack可以將Loader的同步執(zhí)行轉(zhuǎn)換為并行的,這樣就能充分利用系統(tǒng)資源來(lái)加快打包效率了
module:{ loaders:[{ test:/\.js$/, include:[resolve('src')], exclude:/node_modules/, loader:'happypack/loader?id=happybabel' }] }, plugins:[ new HappyPack({ id:'happybabel', loaders:['babel-loader?cacheDirectory'], threads:4#開(kāi)啟4個(gè)線程 }) ] - 使用webpack-parallel-uglify-plugin 增強(qiáng)代碼壓縮
npm i -D webpack-parallel-uglify-plugin - 抽離第三方模塊(使用webpack內(nèi)置的DllPlugin DllReferencePlugin進(jìn)行抽離)
#對(duì)于開(kāi)發(fā)項(xiàng)目中不經(jīng)常會(huì)變更的靜態(tài)依賴文件。類似于我們的elementUi、vue全家桶等等。因?yàn)楹苌贂?huì)變更,所以我們不希望這些依賴要被集成到每一次的構(gòu)建邏輯中去。
#這樣做的好處是每次更改我本地代碼的文件的時(shí)候,webpack只需要打包我項(xiàng)目本身的文件代碼,而不會(huì)再去編譯第三方庫(kù)。以后只要我們不升級(jí)第三方包的時(shí)候,那么webpack就不會(huì)對(duì)這些庫(kù)去打包,這樣可以快速的提高打包的速度。
- 配置緩存
#我們每次執(zhí)行構(gòu)建都會(huì)把所有的文件都重復(fù)編譯一遍,這樣的重復(fù)工作是否可以被緩存下來(lái)呢,答案是可以的,目前大部分 loader 都提供了cache 配置項(xiàng)。
#比如在 babel-loader 中,可以通過(guò)設(shè)置cacheDirectory 來(lái)開(kāi)啟緩存,babel-loader?cacheDirectory=true 就會(huì)將每次的編譯結(jié)果寫進(jìn)硬盤文件(默認(rèn)是在項(xiàng)目根目錄下的node_modules/.cache/babel-loader目錄內(nèi),當(dāng)然你也可以自定義)
#但如果 loader 不支持緩存呢?我們也有方法,我們可以通過(guò)cache-loader ,它所做的事情很簡(jiǎn)單,就是 babel-loader 開(kāi)啟 cache 后做的事情,將 loader
#的編譯結(jié)果寫入硬盤緩存。再次構(gòu)建會(huì)先比較一下,如果文件較之前的沒(méi)有發(fā)生變化則會(huì)直接使用緩存。使用方法如官方 demo 所示,在一些性能開(kāi)銷較大的 loader 之前添加此 loader即可。
- 如果你確定一個(gè)文件下沒(méi)有其他依賴,就可以使用module.noParse屬性讓W(xué)ebpack不掃描該文件,這種方式對(duì)于大型的類庫(kù)很有幫助
2.優(yōu)化打包文件體積 - 引入webpack-bundle-analyzer分析打包后的文件
- externals
- Tree-shaking(tree-shaking的主要作用是用來(lái)清除代碼中無(wú)用的部分。)
- scope Hoisting:它會(huì)分析出模塊間的依賴關(guān)系,盡可能的把打包出來(lái)的模塊合并到一個(gè)函數(shù)中去
module.exports={ optimization:{ concatenateModules:true } }
生產(chǎn)環(huán)境中刪除console.log
參考鏈接:vuecli3+webpack4優(yōu)化實(shí)踐(刪除console.log和配置dllPlugin)
#可以通過(guò)配置webpack4中的terser-webpack-plugin插件達(dá)成目的,compress參數(shù)配置如下:
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
},
},
}),
],
},
};
#在 vue.config.js 中的 configureWebpack 選項(xiàng)提供一個(gè)對(duì)象會(huì)被 webpack-merge 合并入最終的 webpack配置,因此vue-cli3構(gòu)建的項(xiàng)目中只需要修改terserOptions即可,vue.config.js配置如下:
module.exports = {
publicPath: '/',
outputDir: 'dist',
devServer: {
port: 8080,
https: false,
hotOnly: true,
disableHostCheck: true,
open: true,
},
productionSourceMap: false, // 生產(chǎn)打包時(shí)不輸出map文件,增加打包速度
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions.compress.warnings = false
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
config.optimization.minimizer[0].options.terserOptions.compress.drop_debugger = true
config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ['console.log']
}
}
}
#配置完成后使用 vue inspect --mode=production > output.js 命令審查項(xiàng)目的 webpack 配置
webpack魔術(shù)注釋:
參考鏈接:項(xiàng)目不知道如何做性能優(yōu)化?不妨試一下代碼分割
- 魔術(shù)注釋是由 Webpack 提供的,可以為代碼分割服務(wù)的一種技術(shù)。通過(guò)在 import 關(guān)鍵字后的括號(hào)中使用指定注釋,我們可以對(duì)代碼分割后的 chunk 有更多的控制權(quán)
import (
/* webpackChunkName: “my-chunk-name” */
/* webpackMode: lazy */#webpackMode 的默認(rèn)值為 lazy 它會(huì)使所有異步模塊都會(huì)被單獨(dú)抽離成單一的 chunk,若設(shè)置該值為 lazy-once,Webpack 就會(huì)將所有帶有標(biāo)記的異步加載模塊放在同一個(gè) chunk 中。
'./someModule'
)
#通過(guò)添加 webpackPrefetch 魔術(shù)注釋,Webpack 令我們可以使用與 <link rel=“prefetch”> 相同的特性。讓瀏覽器會(huì)在 Idle 狀態(tài)時(shí)預(yù)先幫我們加載所需的資源,善用這個(gè)技術(shù)可以使我們的應(yīng)用交互變得更加流暢。
import(
/* webpackPrefetch: true */
'./someModule'
)
使用vue-cli時(shí),Vue.config.js 配置選項(xiàng)
參考鏈接:vue.config.js 配置
module.exports = {
publicPath: './',#基本路徑
outputDir: 'dist', #構(gòu)建時(shí)的輸出目錄
assetsDir: 'static', #放置靜態(tài)資源的目錄
indexPath: 'index.html', #html的輸出路徑#指定生成的 index.html 的輸出路徑 (相對(duì)于 outputDir)。也可以是一個(gè)絕對(duì)路徑。
filenameHashing: true, #文件名哈希
pages: { #用于多頁(yè)配置,默認(rèn)是 undefined
index: {
entry: 'src/index/main.js', # page 的入口文件
template: 'public/index.html', # 模板文件
filename: 'index.html', # 在 dist/index.html 的輸出文件
title: 'Index Page', # 當(dāng)使用頁(yè)面 title 選項(xiàng)時(shí), # template 中的 title 標(biāo)簽需要是 <title><%= htmlWebpackPlugin.options.title %></title>
chunks: ['chunk-vendors', 'chunk-common', 'index'] # 在這個(gè)頁(yè)面中包含的塊,默認(rèn)情況下會(huì)包含 # 提取出來(lái)的通用 chunk 和 vendor chunk。
},
subpage: 'src/subpage/main.js' # 當(dāng)使用只有入口的字符串格式時(shí), # 模板文件默認(rèn)是 `public/subpage.html` # 如果不存在,就回退到 `public/index.html`。 # 輸出文件默認(rèn)是 `subpage.html`。
},
lintOnSave: true, #是否在保存的時(shí)候使用 `eslint-loader` 進(jìn)行檢查。
runtimeCompiler: false,#是否使用帶有瀏覽器內(nèi)編譯器的完整構(gòu)建版本
transpileDependencies: ['resize-detector'], #babel-loader 默認(rèn)會(huì)跳過(guò) node_modules 依賴。
productionSourceMap: true, # 是否為生產(chǎn)環(huán)境構(gòu)建生成 source map?
crossorigin: '', # 設(shè)置生成的 HTML 中 <link rel="stylesheet"> 和 <script> 標(biāo)簽的 crossorigin 屬性。
integrity: false, # 在生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 標(biāo)簽上啟用Subresource Integrity (SRI)
configureWebpack: () => {}, #(Object | Function)#如果這個(gè)值是一個(gè)對(duì)象,則會(huì)通過(guò) webpack-merge 合并到最終的配置中#如果這個(gè)值是一個(gè)函數(shù),則會(huì)接收被解析的配置作為參數(shù)。該函數(shù)及可以修改配置并不返回任何東西,也可以返回一個(gè)被克隆或合并過(guò)的配置版本。
chainWebpack: () => {}, #允許對(duì)內(nèi)部的 webpack 配置進(jìn)行更細(xì)粒度的修改。
# 配置 webpack-dev-server 行為。
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',
port: 8080,
https: false,
hotOnly: false,
# 查閱 https:#github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/cli-service.md#配置代理
proxy: {
'/api': {
target: 'http:#app.rmsdmedia.com',
changeOrigin: true,
secure: false,
pathRewrite: {
'^/api': ''
}
},
'/foo': {
target: '<other_url>'
}
},
before: app => {}
},
# CSS 相關(guān)選項(xiàng)
css: {
# 將組件內(nèi)的 CSS 提取到一個(gè)單獨(dú)的 CSS 文件 (只用在生產(chǎn)環(huán)境中)
# 也可以是一個(gè)傳遞給 `extract-text-webpack-plugin` 的選項(xiàng)對(duì)象
extract: true,
# 是否開(kāi)啟 CSS source map?
sourceMap: false,
# 為預(yù)處理器的 loader 傳遞自定義選項(xiàng)。比如傳遞給 # Css-loader 時(shí),使用 `{ Css: { ... } }`。
loaderOptions: {
css: {
# 這里的選項(xiàng)會(huì)傳遞給 css-loader
},
postcss: {
# 這里的選項(xiàng)會(huì)傳遞給 postcss-loader
}
},
# 為所有的 CSS 及其預(yù)處理文件開(kāi)啟 CSS Modules。 # 這個(gè)選項(xiàng)不會(huì)影響 `#.vue` 文件。
modules: false
},
# 在生產(chǎn)環(huán)境下為 Babel 和 TypeScript 使用 `thread-loader` # 在多核機(jī)器下會(huì)默認(rèn)開(kāi)啟。
parallel: require('os').cpus().length > 1,
# PWA 插件的選項(xiàng)。 # 查閱 https:#github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli-plugin-pwa/README.md
pwa: {},
# 三方插件的選項(xiàng)
pluginOptions: {
# ...
}
}
.browserslistrc文件配置或者是package.json中配置的browserslist對(duì)象
具體配置參考鏈接:browserslist 目標(biāo)瀏覽器配置表
> 1% #全球超過(guò)1%人使用的瀏覽器
last 2 versions#所有瀏覽器兼容到最后兩個(gè)版本根據(jù)CanIUse.com追蹤的版本
not dead