Loaders
1、文件Loader
url-loader
像 file loader
一樣工作,可以返回 data URL
{
test: /\.(jpg|png|gif|jpeg)$/,
loader: 'url-loader',
options:{
name: 'img/[name]-[hash:8].[ext]',
outputPath: 'img',
esModule: false
}
}
file-loader
將文件發送到輸出文件夾,並返回相對URL
{
exclude: /\.(html|js|jpg|css|less)$/,
loader: 'file-loader',
options: {
name: '[hash:8].[ext]',
outputPath: 'media'
}
}
2、轉換編譯(Transpiling)
Name | Description |
---|---|
script-loader |
在全局上下文中執行一次 JavaScript 文件(如在 script 標簽),不需要解析 |
babel-loader |
加載 ES2015+ 代碼,然后使用 Babel 轉譯為 ES5 |
ts-loader |
或 awesome-typescript-loader 像 JavaScript 一樣加載 TypeScript 2.0+ |
coffee-loader |
JavaScript一樣加載 CoffeeScript` |
3、模板(Templating)
Name | Description |
---|---|
html-loader |
導出 HTML 為字符串,需要引用靜態資源 |
pug-loader |
加載 Pug 模板並返回一個函數 |
jade-loader |
加載 Jade 模板並返回一個函數 |
markdown-loader |
將 Markdown 轉譯為 HTML |
4、樣式
style-loader
將模塊的導出作為樣式添加到DOM
中css-loader
解析CSS
文件后,使用import
加載,並且返回CSS
代碼less-loader
加載和轉譯LESS
文件sass-loader
加載和轉譯SASS/SCSS
文件postcss-loader
使用PostCSS
加載和轉譯
yarn add postcss-loader postcss-preset-env
stylus-loader
加載和轉譯Stylus
文件
5、清理和測試(Linting)
eslint-loader
使用 ESLint語法檢查
設置代碼檢查的規則:
在package.json里面添加:
eslintConfig:{
extends: 'airbns'
}
添加依賴文件
yarn add eslint-config-airbnb-base eslint-plugin-import eslint -D
webpack.config.js 文件中
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
fix: true //自動格式化代碼
}
}
eslint-loader
使用 ESLint兼容性檢查
1、 preset-env 只能檢查簡單的語法。
2、@babel/polyfull 能解決兼用性問題。但是全部引入體積太大了。
3、 core-js第三方按需加載的包
添加依賴文件
npm install babel-loader @babel/core @bebel/preset-env -D
webpack.config.js文件中
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
@babel/preset-env,
{
useBuiltIns: 'usage',//按需加載
corejs: {
versions: 3//指定版本
},
targets: {
chrome: 60 //指定瀏覽器的版本
}
}
]
}
}
Plugins
1、HtmlWebpackPlugin
簡單創建 HTML 文件
plugins: [
new HtmlWebpackPlugin({
title: '',
filename: '',
template: './src/index.html',
publicPath: '',
favicon: '',
neta: {}
})
]
壓縮HTML文件
plugins:[
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
]
2、MinCssExtractPlugin
單獨提取css文件
plugins: [
new MinCssExtractPlugin({
filename: 'css/style.css',
chunkFilename: ''
})
]
配合loader
,postcss-loader
做瀏覽器的兼容性。添加了postcss-loader
后,要在package.json
里面配置browserslist:{development: {}, production: {}}
對象。
{
test: /\.css$/,
use: [
//style-loader
MinCssExtractPlugin.laoder,
css-loader,
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => require('postcss-preset-env')()
}
}
]
}
3、OptimizeCssAssetsWebpackPlugin 壓縮css
plugins:[
new OptimizeCssAssetsWebpackPlugin()
]
一些小的優化點
resolve.extensions
:用來表明文件后綴列表,默認查找順序是 ['.js', '.json'],如果你的導入文件沒有添加后綴就會按照這個順序查找文件。我們應該盡可能減少后綴列表長度,然后將出現頻率高的后綴排在前面。
resolve.alias
:可以通過別名的方式來映射一個路徑,能讓 Webpack 更快找到路徑。
module.exports ={
// ...省略其他配置
resolve: {
extensions: [".js",".jsx",".json",".css"],
alias:{
"jquery":jquery
}
}
};
提高 Webpack 打包速度
1、優化Loader搜索范圍(include,exclude
)
合理的使用exclude或者include的配置,來盡量減少loader被頻繁執行的頻率。當loader執行頻率降低時,也會提升webpack的打包速度。比如:
對於Loader
來說,影響打包效率首當其沖必屬 Babel 了。因為 Babel 會將代碼轉為字符串生成 AST,然后對 AST 繼續進行轉變最后再生成新的代碼,項目越大,轉換代碼越多,效率就越低。當然了,我們是有辦法優化的。
首先我們可以優化 Loader 的文件搜索范圍,在使用loader時,我們可以指定哪些文件不通過loader處理,或者指定哪些文件通過loader處理。
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
use: ['babel-loader'],
// 只處理src文件夾下面的文件
include: path.resolve('src'),
// 不處理node_modules下面的文件
exclude: /node_modules/
}
]
}
}
2、cache-loader緩存loader處理結果
緩存分為 babel緩存和文件資源緩存
Babel 緩存 cacheDirectory: true
{
cacheDirectory: true
}
文件資源的緩存
1.
hash
每次webpack構建時都會生成一個唯一的hash值 問題: 應為js和css同時使用一個hash值,如果重新打包就會導致緩存失效。
2.chunkhash
根據chunk生產的hash值,如果打包來源於同一個chunk,那么同一個文件的js和css的hash值就時一樣的。
3.contenthash
根據文件的內容生成hash值,不同文件的hash值是不一樣的。
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
use: [
'cache-loader',
...loaders
],
}
]
}
}
3、使用多線程處理打包
module: {
rules: [
{
test: /\.js$/,
// 把對 .js 文件的處理轉交給 id 為 babel 的 HappyPack 實例
use: ['happypack/loader?id=babel'],
exclude: path.resolve(__dirname, 'node_modules'),
},
{
test: /\.css$/,
// 把對 .css 文件的處理轉交給 id 為 css 的 HappyPack 實例
use: ['happypack/loader?id=css']
}
]
},
plugins: [
new HappyPack({
id: 'js', //ID是標識符的意思,ID用來代理當前的happypack是用來處理一類特定的文件的
threads: 4, //你要開啟多少個子進程去處理這一類型的文件
loaders: [ 'babel-loader' ]
}),
new HappyPack({
id: 'css',
threads: 2,
loaders: [ 'style-loader', 'css-loader' ]
})
]
4、DllPlugin&DllReferencePlugin 成動態鏈接庫
DllPlugin
可以將特定的類庫提前打包成動態鏈接庫,在一個動態鏈接庫中可以包含給其他模塊調用的函數和數據,把基礎模塊獨立出來打包到單獨的動態連接庫里,當需要導入的模塊在動態連接庫里的時候,模塊不用再次被打包,而是去動態連接庫里獲取。這種方式可以極大的減少打包類庫的次數,只有當類庫更新版本才有需要重新打包,並且也實現了將公共代碼抽離成單獨文件的優化方案。
這里我們可以先將react、react-dom
單獨打包成動態鏈接庫,首先新建一個新的webpack
配置文件:webpack.dll.js
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
// 想統一打包的類庫
entry:['react','react-dom'],
output:{
filename: '[name].dll.js', //輸出的動態鏈接庫的文件名稱,[name] 代表當前動態鏈接庫的名稱
path:path.resolve(__dirname,'dll'), // 輸出的文件都放到 dll 目錄下
library: '_dll_[name]',//存放動態鏈接庫的全局變量名稱,例如對應 react 來說就是 _dll_react
},
plugins:[
new DllPlugin({
// 動態鏈接庫的全局變量名稱,需要和 output.library 中保持一致
// 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: '_dll_[name]',
// 描述動態鏈接庫的 manifest.json 文件輸出時的文件名稱
path: path.join(__dirname, 'dll', '[name].manifest.json')
})
]
}
然后我們需要執行這個配置文件生成依賴文件:
webpack --config webpack.dll.js --mode development
接下來我們需要使用 DllReferencePlugin 將依賴文件引入項目中
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = {
// ...省略其他配置
plugins: [
new DllReferencePlugin({
// manifest 就是之前打包出來的 json 文件
manifest:path.join(__dirname, 'dll', 'react.manifest.json')
})
]
}
5、noParse 可以用於配置那些模塊文件的內容不需要進行解析(即無依賴)
module.noParse
屬性,可以用於配置那些模塊文件的內容不需要進行解析(即無依賴) 的第三方大型類庫(例如jquery
,lodash
)等,使用該屬性讓 Webpack
不掃描該文件,以提高整體的構建速度。
module.exports = {
module: {
noParse: /jquery|lodash/, // 正則表達式
// 或者使用函數
noParse(content) {
return /jquery|lodash/.test(content)
}
}
}
6、IgnorePlugin IgnorePlugin
用於忽略某些特定的模塊
IgnorePlugin用於忽略某些特定的模塊,讓webpack 不把這些指定的模塊打包進去。
module.exports = {
// ...省略其他配置
plugins: [
new webpack.IgnorePlugin(/^\.\/locale/,/moment$/)
]
}
webpack.IgnorePlugin()參數中第一個參數是匹配引入模塊路徑的正則表達式,第二個參數是匹配模塊的對應上下文,即所在目錄名。
7、打包文件分析工具
webpack-bundle-analyzer
插件的功能是可以生成代碼分析報告,幫助提升代碼質量和網站性能。
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports={
plugins: [
new BundleAnalyzerPlugin({
generateStatsFile: true, // 是否生成stats.json文件
})
// 默認配置的具體配置項
// new BundleAnalyzerPlugin({
// analyzerMode: 'server',
// analyzerHost: '127.0.0.1',
// analyzerPort: '8888',
// reportFilename: 'report.html',
// defaultSizes: 'parsed',
// openAnalyzer: true,
// generateStatsFile: false,
// statsFilename: 'stats.json',
// statsOptions: null,
// excludeAssets: null,
// logLevel: info
// })
]
}
使用方式:
"generateAnalyzFile": "webpack --profile --json > stats.json", // 生成分析文件
"analyz": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" // 啟動展示打包報告的http服務器
8、費時分析
speed-measure-webpack-plugin
,打包速度測量插件。這個插件可以測量webpack構建速度,可以測量打包過程中每一步所消耗的時間,然后讓我們可以有針對的去優化代碼。
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin();
// 用smw.wrap()包裹webpack的所有配置項
module.exports =smw.wrap({
module: {},
plugins: []
});
減少 Webpack 打包后的文件體積
1、對圖片進行壓縮和優化
image-webpack-loader這個loder可以幫助我們對打包后的圖片進行壓縮和優化,例如降低圖片分辨率,壓縮圖片體積等。
module.exports ={
// ...省略其他配置
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
}
]
}
]
}
};
2、刪除無用的CSS樣式
有時候一些時間久遠的項目,可能會存在一些CSS樣式被迭代廢棄,需要將其剔除掉,此時就可以使用purgecss-webpack-plugin插件,該插件可以去除未使用的CSS,一般與 glob、glob-all 配合使用。
注意:此插件必須和CSS代碼抽離插件mini-css-extract-plugin配合使用。
例如我們有樣式文件style.css:
body{
background: red
}
.class1{
background: red
}
這里的.class1顯然是無用的,我們可以搜索src目錄下的文件,刪除無用的樣式。
const glob = require('glob');
const PurgecssPlugin = require('purgecss-webpack-plugin');
module.exports ={
// ...
plugins: [
// 需要配合mini-css-extract-plugin插件
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`,
{nodir: true}), // 不匹配目錄,只匹配文件
})
}),
]
}
3、以CDN方式加載資源
我們知道,一般常用的類庫都會發布在CDN上,因此,我們可以在項目中以CDN的方式加載資源,這樣我們就不用對資源進行打包,可以大大減少打包后的文件體積。
以CDN方式加載資源需要使用到add-asset-html-cdn-webpack-plugin插件。我們以CDN方式加載jquery為例:
const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')
module.exports ={
// ...
plugins: [
new AddAssetHtmlCdnPlugin(true,{
'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
})
],
//在配置文件中標注jquery是外部的,這樣打包時就不會將jquery進行打包了
externals:{
'jquery':'$'
}
}
4、開啟Tree Shaking (去除無用的代碼,減少代碼的體積。前提條件: 1.模塊必須是ES6模塊,2.必須是production環境)
Tree-shaking,搖晃樹。顧名思義就是當我們搖晃樹的時候,樹上干枯的沒用的葉子就會掉下來。類比到我們的代碼中就是將沒用的代碼搖晃下來,從而實現刪除代碼中未被引用的代碼。
這個功能在webpack4中,當我們將mode設置為production時,會自動進行tree-shaking。
在package.json 種配置
sideEffects: false
所有的代碼都可以被tree-shaking 問題是: 可能吧css文件和@babel/polyfill
等副作用全部干掉。要小心使用。
解決方法{sideEffects: ['*.css']}
5、按需加載&動態加載
必大家在開發單頁面應用項目的時候,項目中都會存在十幾甚至更多的路由頁面。如果我們將這些頁面全部打包進一個文件的話,雖然將多個請求合並了,但是同樣也加載了很多並不需要的代碼,耗費了更長的時間。那么為了首頁能更快地呈現給用戶,我們肯定是希望首頁能加載的文件體積越小越好,這時候我們就可以使用按需加載,將每個路由頁面單獨打包為一個文件。在給單頁應用做按需加載優化時,一般采用以下原則:
- 對網站功能進行划分,每一類一個chunk
- 對於首次打開頁面需要的功能直接加載,盡快展示給用戶,某些依賴大量代碼的功能點可以按需加載
- 被分割出去的代碼需要一個按需加載的時機
動態加載目前並沒有原生支持,需要babel的插件:plugin-syntax-dynamic-import
。安裝此插件並且在.babelrc中配置:
{
// 添加
"plugins": ["transform-vue-jsx", "transform-runtime"],
}
列如下面的實例:index.js
let btn = document.createElement('button');
btn.innerHTML = '點擊加載視頻';
btn.addEventListener('click',()=>{
import(/* webpackChunkName: "video" */'./video').then(res=>{
console.log(res.default);
});
});
document.body.appendChild(btn);
webpack.config.js
module.exports = {
// ...
output:{
chunkFilename:'[name].min.js'
}
}
6、這樣打包后的結果最終的文件就是 video.min.js,並且剛啟動項目時不會加載該文件,只有當用戶點擊了按鈕時才會動態加載該文件。
代碼分割 code-slpit
1、根據入口文件構建分割代碼。
- 單入口打包,構建的就是一個文件,。
module.exports = {
enrty: './src/index.js',
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'dist')
}
}
2.多入口打包就會產生多個代碼塊
module.exports = {
enrty: {
index: './src/index.js',
home: './src/home.js'
},
output: {
filename: 'js/[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
}
}
2、用webapck自帶的 optimization
1.來吧node_modules 單獨打包到一個chunk輸出 2.自動分析多入口文件種有沒有公共的文件,如果有會打包成一個單獨的chunk 。
optimization: {
splitChunks: {
chunks: 'all'
}
}
3、用js代碼讓文件單獨打包成一個chunk 默認webapck會按文件的id打包生成文件名稱,可以用 /webpackChunkName: 'abc'/ 修改生成的文件名。
import(/*webpackChunkName: 'abc'*/'xxx.js').then(() => {
console.log('success')
}).catch(err){
console.log('error')
}