module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}
]
}
複製代碼
'babel-loader?cacheDirectory'javascript
You can also speed up babel-loader by as much as 2x by using the cacheDirectory option. This will cache transformations to the filesystem.css
[BABEL] Note: The code generator has deoptimised the styling of "/Users/xxx/Documents/xxx/webpack_test/test3/node_modules/lodash/lodash.js" as it exceeds the max of "500KB".html
加上exclude限制範圍就不會報錯了java
resolve: {
modules: [path.resolve('node_modules'), path.resolve('lib')]
}
複製代碼
Absolute and relative paths can both be used, but be aware that they will behave a bit differently.node
A relative path will be scanned similarly to how Node scans for node_modules, by looking through the current directory as well as it's ancestors (i.e. ./node_modules, ../node_modules, and on).react
With an absolute path, it will only search in the given directory.jquery
If you want to add a directory to search in that takes precedence over node_modules/:(便是有前後順序的)webpack
modules: [path.resolve(__dirname, "src"), "node_modules"]
複製代碼
Module not found: Error: Can't resolve 'ajax' in '/Users/xxx/Documents/xxx/webpack_test/test3/src'git
當你須要指定除node_modules以外的其它模塊目錄的時候能夠在數組中添加屬性es6
安裝的第三方模塊中都會有一個 package.json文件,用於描述這個模塊的屬性,其中有些字段用於描述入口文件在哪裏,resolve.mainFields 用於配置採用哪一個字段做爲入口文件的描述。
能夠存在多個字段描述入口文件的緣由是由於有些模塊能夠同時用在多個環境中,針對不一樣的運行環境須要使用不一樣的代碼。 以 isomorphic-fetch API 爲例,它是 Promise的一個實現,但可同時用於瀏覽器和 Node.js 環境。
爲了減小搜索步驟,在你明確第三方模塊的入口文件描述字段時,你能夠把它設置的儘可能少。 因爲大多數第三方模塊都採用 main字段去描述入口文件的位置,能夠這樣配置 Webpack:
module.exports = {
resolve: {
// 只採用 main 字段做爲入口文件描述字段,以減小搜索步驟
mainFields: ['main'],
},
};
複製代碼
alias: {
"bootstrap": "bootstrap/dist/css/bootstrap.css"
}
複製代碼
module: {
noParse: [/react\.min\.js/]
}
複製代碼
被忽略掉的文件裏不該該包含 import 、 require 、 define 等模塊化語句
dll 爲後綴的文件稱爲動態連接庫,在一個動態連接庫中能夠包含給其餘模塊調用的函數和數據
定義插件(DLLPlugin) ---> 引用插件(DllReferencePlugin)
本次例子用jquery舉例
webpack.jquery.config.js
module.exports = {
entry: ["jquery"],
output: {
filename: "vendor.js",
path: path.resolve(__dirname, "dist"),
libraryTarget: 'var',// 打包的方式,hou
library: "vendor_lib_vendor"// DLL的名字
},
plugins: [
new webpack.DllPlugin({
name: "vendor_lib_vendor",// 定義DLL
path: path.resolve(__dirname, "dist/vendor-manifest.json")
})
]
};
複製代碼
package.json 的scripts添加
"dll": "webpack --config webpack.jquery.config.js --mode development"
複製代碼
配置好上述的文件後,在終端運行 npm run dll
,時候會在dist目錄下生成兩個文件,分別是vendor.js
和 vendor-manifest.json
。vendor.js
包含的就是打包後的jquery
文件代碼,vendor-manifest.json
是用來作關聯的。DLL定義好了,接下來就是應用打包好的DLL了。
webpack.config.js 配置文件中引入DllPlugin插件打包好的動態鏈接庫
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require("./dist/vendor-manifest.json")
})
],
複製代碼
app.html 在app.html的底部添加
<script src="./vendor.js"></script>
複製代碼
libraryTarget
和library
當用 Webpack 去構建一個能夠被其餘模塊導入使用的庫時須要用到它們。
output.libraryTarget 是字符串的枚舉類型,支持如下配置。
編寫的庫將經過 var 被賦值給經過 library 指定名稱的變量。
假如配置了 output.library='LibraryName',則輸出和使用的代碼以下:
// Webpack 輸出的代碼
var LibraryName = lib_code; //其中 lib_code 代指導出庫的代碼內容,是有返回值的一個自執行函數。
// 使用庫的方法
LibraryName.doSomething();
複製代碼
編寫的庫將經過 CommonJS 規範導出。
假如配置了 output.library='LibraryName',則輸出和使用的代碼以下:
// Webpack 輸出的代碼
exports['LibraryName'] = lib_code;
// 使用庫的方法
require('library-name-in-npm')['LibraryName'].doSomething();
// 其中 library-name-in-npm 是指模塊發佈到 Npm 代碼倉庫時的名稱。
複製代碼
編寫的庫將經過 CommonJS2 規範導出,輸出和使用的代碼以下:
// Webpack 輸出的代碼
module.exports = lib_code;
// 使用庫的方法
require('library-name-in-npm').doSomething();
複製代碼
CommonJS2 和 CommonJS 規範很類似,差異在於 CommonJS 只能用 exports 導出,而 CommonJS2 在 CommonJS 的基礎上增長了 module.exports 的導出方式。 在 output.libraryTarget 爲 commonjs2 時,配置 output.library 將沒有意義。
編寫的庫將經過 this 被賦值給經過 library 指定的名稱,輸出和使用的代碼以下:
// Webpack 輸出的代碼
this['LibraryName'] = lib_code;
// 使用庫的方法
this.LibraryName.doSomething();
複製代碼
編寫的庫將經過 window 被賦值給經過 library 指定的名稱,即把庫掛載到 window 上,輸出和使用的代碼以下:
// Webpack 輸出的代碼
window['LibraryName'] = lib_code;
// 使用庫的方法
window.LibraryName.doSomething();
複製代碼
編寫的庫將經過 global 被賦值給經過 library 指定的名稱,即把庫掛載到 global 上,輸出和使用的代碼以下:
// Webpack 輸出的代碼
global['LibraryName'] = lib_code;
// 使用庫的方法
global.LibraryName.doSomething();
複製代碼
HappyPack就能讓Webpack把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。
install
因爲webpack 4.0 剛剛發佈,響應的插件尚未更新完,不過能夠在後面加一個@next
來安裝即將發佈的版本
npm i happypack@next -D
複製代碼
webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: 'happypack/loader?id=css',
//把對.js文件的處理轉交給id爲babel的HappyPack實例
//用惟一的標識符id來表明當前的HappyPack是用來處理一類特定文件
include: path.resolve('./src'),
exclude: /node_modules/
},
{
test: /\.js/,
use: 'happypack/loader?id=babel',
include: path.resolve('./src'),
exclude: /node_modules/
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html'
}),
new HappyPack({
id: 'babel',
loaders: ['babel-loader']// 和rules裏的配置相同
}),
new HappyPack({
id: 'css',
loaders: ['style-loader', 'css-loader']// 和rules裏的配置相同
}),
]
複製代碼
insatll
npm install webpack-parallel-uglify-plugin -D
複製代碼
webpackage.config.js
new ParallelUglifyPlugin({
workerCount: os.cpus().length - 1,//開啓幾個子進程去併發的執行壓縮。默認是當前運行電腦的 CPU 核數減去1
uglifyJS: {
output: {
beautify: false, //不須要格式化
comments: true, //不保留註釋
},
compress: {
warnings: false, // 在UglifyJs刪除沒有用到的代碼時不輸出警告
drop_console: true, // 刪除全部的 `console` 語句,能夠兼容ie瀏覽器
collapse_vars: true, // 內嵌定義了可是隻用到一次的變量
reduce_vars: true, // 提取出出現屢次可是沒有定義成變量去引用的靜態值
}
}
})
複製代碼
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1
}
複製代碼
watch
只有在開啓監聽模式時(watch爲true),watchOptions纔有意義
aggregateTimeout
監聽到變化發生後等300(ms)再去執行動做,防止文件更新太快致使編譯頻率過高
poll
經過不停的詢問文件是否改變來判斷文件是否發生變化,默認每秒詢問1000次
文件監聽流程
webpack定時獲取文件的更新時間,並跟上次保存的時間進行比對,不一致就表示發生了變化,poll就用來配置每秒問多少次。
當檢測文件再也不發生變化,會先緩存起來,等等待一段時間後以後再通知監聽者,這個等待時間經過aggregateTimeout配置。
webpack只會監聽entry依賴的文件 咱們須要儘量減小須要監聽的文件數量和檢查頻率,固然頻率的下降會致使靈敏度降低。
devServer: {
inline: true
},
複製代碼
webpack負責監聽文件變化,webpack-dev-server負責刷新瀏覽器。這些文件會被打包到chunk中,它們會代理客戶端向服務器發起WebSocket鏈接
webpack.config.js
devServer: {
hot:true//將hot設置爲true
},
// 須要的插件
plugins: [
new webpack.NamedModulesPlugin(),//顯示模塊的相對路徑
new webpack.HotModuleReplacementPlugin()// 啓動熱加載功能
]
複製代碼
code
if (module.hot) {
module.hot.accept('./hot.js', () => {
let hot = require('./hot');
document.getElementById('app2').innerHTML = hot + '1';
})
}
複製代碼
須要熱加載的模塊須要在初始化的時候引入到模塊中,不然不會觸發HMR。
在開發網頁的時候,通常都會有多套運行環境,例如,在開發過程當中方便開發調試的環境。發佈到線上給用戶使用的運行環境。
線上的環境和開發環境區別主要有如下不一樣:
package.json
cross-env
跨平臺設置環境變量(後面沒有&&)"scripts": {
"build-dev": "cross-env NODE_ENV=development webpack --mode development",
"build-prod": "cross-env NODE_ENV=production webpack --mode production"
}
複製代碼
webpack.config.js
webpack.base.config.js
合併,生產環境(或者開發環境)的優先級高於webpack.base.config.js
的配置。let merge = require('webpack-merge');
let base = require('./webpack.base.config');
let other = null;
if (process.env.NODE_ENV === 'development') {
other = require('./webpack.dev.config');
} else {
other = require('./webapack.prod.config');
}
module.exports = merge(base, other);
複製代碼
webpack.base.config.js
webpack.DefinePlugin
定義環境變量基本配置...
plugins: [
new webpack.DefinePlugin({
__isDevelopment__: JSON.stringify(process.env.NODE_ENV == 'development')
})
]
複製代碼
webpack.dev.config.js
output
舉例,若是開發和生產環境的參數不一樣,就會覆蓋webpack.base.config.js
裏面的配置const path = require('path');
module.exports = {
output: {
path: path.resolve('./dist'),
filename: "[name].dev.[hash:2].js"
}
};
複製代碼
webpack.prod.config.js
output
舉例)const path = require('path');
module.exports = {
output: {
path: path.resolve('./dist'),
filename: "[name].prod.[hash:8].js"
}
};
複製代碼
base.js
webpack.DefinePlugin
定義的變量(__isDevelopment__
),在入口文件和入口文件引用的其餘文件中均可以獲取到__isDevelopment__
的值let env = null;
if (__isDevelopment__) {
env = 'dev';
} else {
env = 'prod';
}
module.exports = env;
複製代碼
index.js
let env = require('./base.js');
if (__isDevelopment__) {
console.log('dev');
} else {
console.log('prod');
}
console.log('env', env);
/* prod env prod */
複製代碼
webpack.DefinePlugin
定義環境變量的值時用 JSON.stringify 包裹字符串的緣由是環境變量的值須要是一個由雙引號包裹的字符串,而 JSON.stringify('production')的值正好等於'"production"'
CDN 又叫內容分發網絡,經過把資源部署到世界各地,用戶在訪問時按照就近原則從離用戶最近的服務器獲取資源,從而加速資源的獲取速度。
tree Shaking 能夠用來剔除JavaScript中用不上的死代碼。
use: {
loader: 'babel-loader',
query: {
presets: [
[
"env", {
modules: false //含義是關閉 Babel 的模塊轉換功能,保留本來的 ES6 模塊化語法
}
],
"react"
]
}
},
複製代碼
須要注意的是它依賴靜態的ES6模塊化語法,例如經過import和export導入導出。也就是說若是項目代碼運行在不支持es6語法的環境上,Tree Shaking也就沒有意義了。
大網站有多個頁面,每一個頁面因爲採用相同技術棧和樣式代碼,會包含不少公共代碼,若是都包含進來會有問題
相同的資源被重複的加載,浪費用戶的流量和服務器的成本; 每一個頁面須要加載的資源太大,致使網頁首屏加載緩慢,影響用戶體驗。 若是能把公共代碼抽離成單獨文件進行加載能進行優化,能夠減小網絡傳輸流量,下降服務器成本
不一樣類型的文件,打包後的代碼塊也不一樣:
webpack.config.js
optimization: {
splitChunks: {
cacheGroups: {
commons: {// 頁面之間的公用代碼
chunks: 'initial',
minChunks: 2,
maxInitialRequests: 5, // The default limit is too small to showcase the effect
minSize: 0 // This is example is too small to create commons chunks
},
vendor: {// 基礎類庫
chunks: 'initial',
test: /node_modules/,
name: "vendor",
priority: 10,
enforce: true
}
}
}
},
複製代碼
./src/pageA.js
require('./utils/utility1.js');
require('./utils/utility2.js');
require('react');
複製代碼
./src/pageB.js
require('./utils/utility2.js');
require('./utils/utility3.js');
複製代碼
./src/pageC.js
require('./utils/utility2.js');
require('./utils/utility3.js');
複製代碼
utils/utility1.js
module.exports = 1;
複製代碼
utils/utility2.js
module.exports = 2;
複製代碼
utils/utility3.js
module.exports = 3;
複製代碼
打包後的結果
上述三種代碼的生成的結果,以下圖:
Scope Hoisting 可讓 Webpack 打包出來的代碼文件更小、運行的更快, 它又譯做 "做用域提高",是在 Webpack3 中新推出的功能。
package.json
"build": "webpack --display-optimization-bailout --mode development",
複製代碼
webpack.config.js
plugins: [
new ModuleConcatenationPlugin()
],
複製代碼
./h.js
export default 'scope hoist'
複製代碼
./index.js
import str from './h.js'
console.log(str);
複製代碼
必須使用ES6語法,不然不起做用(
--display-optimization-bailout
參數會提示)
代碼分離是 webpack 中最引人注目的特性之一。此特性可以把代碼分離到不一樣的 bundle 中,而後能夠按需加載或並行加載這些文件。 有三種經常使用的代碼分離方法:
入口起點和防止重複上面已經提到了,下面咱們重點講一下動態導入
用戶當前須要用什麼功能就只加載這個功能對應的代碼,也就是所謂的按需加載 在給單頁應用作按需加載優化時,通常採用如下原則:
import(module)
的語法document.getElementById('play').addEventListener('click',function(){
import('./vedio.js').then(function(video){
let name = video.getName();
console.log(name);
});
});
複製代碼