tree shakingcss
tree shaking是一個術語、一般用於打包時移除js中未引用的代碼(dead-code),它依賴於ES6模塊系統中的import 和 export 的***靜態結構特性***html
開發時引入一個模塊時,若是隻引用其中一個功能,上線打包時只會把用到的功能打包進bundle中,其餘沒有用到的功能都不會打包進來,能夠實現最簡單的基本優化vue
export const add = (a, b) => a + b
export const minus = (a, b) => a- b
複製代碼
// tree shaking 分析
// 如果此時使用 require 引入,無論 math 中的方法是否使用,都會被打包
const math = require('./utils/math')
// 如果使用 import 引入, 只會打包使用了 math 的方法
import { add } from './utils/math'
console.log('index 頁面',math.add(1,2));
console.log('index 頁面',add(1,2));
複製代碼
scope hoistingnode
Scope hositing 做用:是將模塊之間的關係進行結果推測,可讓webpack文件打包出來的代碼文件更小、運行的更快jquery
scope hositing實現原理:分析出模塊之間的依賴關係,儘量的把打散的模塊合併到一個函數中,可是前提是不能形成代碼冗餘, 所以只有哪些被引用了一次的模塊可能被合併webpack
因爲scope hositing 須要分析出模塊之間的依賴關係,所以源碼必須使用ES6模塊化語句,否則就不能生效,緣由和 tree shaking同樣web
在 main.js 中定義幾個變量並輸出vue-router
const a = 1
const b = 2
const c = 3
// webpack 在這裏會進行 預執行,將結果推斷後打包放在這裏
console.log(a + b + c)
console.log(a, b, c)
複製代碼
打包以後代碼變成npm
console.log(6),console.log(1,2,3)
複製代碼
由於三個變量只是在這個地方定義而且使用,並無在其餘位置使用,webpack會直接以具體的數值進行打包,節省了三個變量的定義json
代碼壓縮
全部代碼使用UglifyJsPlugin進行壓縮、混淆
Mini-css-extract-plugin 是用於將 CSS 提取爲獨立的文件的插件,對每一個包含css的js文件都會建立一個css文件,支持按需加載css和sourceMap
只能用於webpack4中,優點
使用
安裝
npm i -D mini-css-extract-plugin
複製代碼
引用
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
複製代碼
建立插件對象,配置抽離的css文件名,支持placeholder語法
new MiniCssExtractPlugin({
filename:'[name].css' // [name] 就是 placeholder 語法
})
複製代碼
將原來配置的全部 style-loader 替換爲 MiniCssExtractPlugin.loader
{
test:/\.css$/,
use:[MiniCssExtractPlugin.loader, 'css-loader']
},
{
test:/\.less$/,
use:[MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
},
{
test:/\.scss$/,
use:[MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
},
複製代碼
使用 postcss,須要使用 postcss-loader 和 autoprefixer
安裝
npm i -D postcss-loader autoprefixer
複製代碼
修改配置文件,將 postcss-loader 放置在 css-loader 右邊
{
test:/\.css$/,
use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader',]
},
{
test:/\.less$/,
use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
},
{
test:/\.scss$/,
use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader']
},
複製代碼
項目根目錄下添加 postcss 的配置文件: postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({
browsers: [
// 加這個後能夠出現額外的兼容性前綴
"> 0.01%"
]
})
]
}
複製代碼
須要使用 optimize-css-assets-webpack-plugin 插件來完成css壓縮
可是因爲配置css壓縮時會覆蓋掉webpack默認的優化設置,致使JS代碼沒法壓縮,因此還須要把JS代碼壓縮插件倒入進來 terser-webpack-plugin
安裝
npm i -D terser-webpack-plugin optimize-css-assets-webpack-plugin
複製代碼
引用
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
複製代碼
配置
optimization:{
minimizer: [
new TerserPlugin({}),
new OptimizeCssAssetsPlugin({})
]
}
複製代碼
webpack4默認採用的JS壓縮插件是 uglifyjs-webpack-plugin,在 mini-css-extract-plugin上一個版本中還推薦使用該插件,可是新的版本卻建議使用 terser-webpack-plugin
code splitting 是webpack打包時用到的重要的優化特性之1、此特性可以把代碼分離到不一樣的bundle中,而後能夠按需加載或者並行加載這些文件,代碼分離能夠用於獲取更小的bundle,以及控制資源加載優先級,若是可以合理的使用可以極大影響加載時間
在webpack配置文件中配置多個入口
entry:{
main: './src/main.js',
other: './src/other.js'
},
output:{
path: path.join(__dirname, '..', './dist'),
filename: '[name].js',
publickPath: '/'
}
複製代碼
在main.js 和 other.js 中都共同引入一個模塊, 並使用其功能
Main.js
import $ from 'jquery'
$(() => {
$('<div></div>').html('main').appendTo('body')
})
複製代碼
other.js
import $ from 'jquery'
$(() => {
$('<div></div>').html('other').appendTo('body')
})
複製代碼
打包文件,能夠看到 main 和 other 打包的文件中都加載的了 jquery
webpack4 以上使用的插件爲 SplitChunksPlugin,webpack4 以前的使用的 CommonChunkPlugin已經被移除,最新版本的webpack中只須要在配置文件中的optimization節點下添加一個splitChunks屬性便可進行相關配置
修改配置文件
optimization
splitChunks:{
chunks: "all"
}
}
複製代碼
打包查看文件
打包以後會將各自的入口文件進行打包,額外會再生產一份js文件,此文件中就是各個chunk中所引用的公共部分
splitChunksPlugin 配置參數
SplitChunksPlugin 的配置只須要在 optimization 節點下的 splitChunks 進行修改便可,若是沒有任何修改,則會使用默認設置
默認的 SplitChunksPlugin 配置適用於絕大多數用戶
webpack 會基於以下默認原則自動分割代碼
SplitChunksPlugin 默認配置
module.exports = {
optimization: {
splitChunks: {
chunks: 'async', // 只對異步加載的模塊進行拆分,import('jquery').then()就是典型的異步加載,可選項還有 all | initial
minSize: 30000, // 模塊最少大於 30kb 纔會拆分
maxSize: 0, // 爲0時模塊大小無上限,只要大於 30kb 都會拆分。如果非0,超過了maxSize的值,會進一步拆分
minChunks: 1, // 模塊最少引用一次纔會拆分
maxAsyncRequests: 5, // 異步加載時同時發送的請求數量最大不能超過5,超過5的部分不拆分
maxInitialRequests: 3, // 頁面初始化時,同時發送的請求數量最大不能超過3,超過3的不跟不拆分
automaticNameDelimiter: '~', // 默認的鏈接符
name: true, // 拆分的chunk名,設置爲true表示根據模塊名和CacheGroup的key來自動生成,使用上面的鏈接符鏈接
cacheGroups: { // 緩存組配置,上面配置讀取完成後進行拆分,若是須要把多個模塊拆分到一個文件,就須要緩存,因此命名爲緩存組
vendors: { // 自定義緩存組名
test: /[\\/]node_modules[\\/]/, // 檢查 node_modules 目錄,只要模塊在該目錄下就使用上面配置拆分到這個組
priority: -10, // 權重爲-10,決定了那個組優先匹配,假如node_modules下面有個模塊要拆分,同時知足vendors和default組,此時就會分到 priority 值比較大的組,由於 -10 > -20 因此分到 vendors 組
filename:'vendoes.js'
},
default: { // 默認緩存組名
minChunks: 2, // 最少引用兩次纔會被拆分
priority: -20, // 權重 -20
reuseExistingChunk: true // 若是主入口中引入了兩個模塊,其中一個正好也引用了後一個,就會直接複用,無需引用兩次
}
}
}
}
};
複製代碼
webpack4默認是容許import語法動態導入的,可是須要babel的插件支持,最新版babel的插件包爲:@babel/plugin-syntax-dynamic-import,須要注意動態導入最大的好處就是實現了懶加載,用到那個模塊纔會加載那個模塊,能夠提升SPA應用程序的首屏加載速度,三大框架的路由懶加載原理同樣
安裝
npm i -D @babel/plugin-syntax-dynamic-import
複製代碼
修改 .babelrc ,添加 @babel/plugin-syntax-dynamic-import 插件
{
"presets": ["@babel/env"],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-syntax-dynamic-import"
]
}
複製代碼
將jq模塊動態導入
function getDivDom(){
// import('jquery') 返回的是一個 promise,如果低版本須要注意
return import('jquery').then(({default: $}) => {
return $('<div></div>').html('動態導入')
})
}
複製代碼
給某個按鈕添加點擊事件,點擊後調用getDivDom函數建立元素並添加到頁面
window.onload = () => {
document.getElementById('btn').addEventListener('click',() => {
getDivDom().then(item => {
item.appendTo('body')
})
})
}
複製代碼
在引入一些第三方模塊時,如jq等,咱們知道其內部確定不會依賴其餘模塊,由於咱們用到的只是一個單獨的js或者css文件,因此此時若是webpack再去解析他們的內部依賴關係,實際上是很是浪費時間的,就須要阻止webpack浪費精力去解析這些明知道沒有依賴的庫,能夠在webpack的配置文件的module節點下加上noParse,並配置正則來肯定不須要解析依賴關係的模塊
module:{
noParse: /jquery|bootstrap/ // jquery|bootstrap 之間不能加空格變成 jquery | bootstrap, 會無效
}
複製代碼
在引入一些第三方模塊時,例如momentJS、dayJS,其內部會作i18n處理,因此會包含不少語言包,而語言包打包時會比較佔用空間,若是項目只須要用到中文或者少數語言,能夠忽略掉全部的語言包,而後按需引入語言包,從而使得構建效率更高,打包生成的文件更小
以moment爲例
import moment from 'moment'
moment.locale('zh-CN') // 設置爲中文
console.log(moment().subtract(6, 'days').calendar())
複製代碼
首先要找到moment依賴的語言包時什麼,經過查看moment的源碼來分析
function loadLocale(name) {
var oldLocale = null;
// TODO: Find a better way to register and load all the locales in Node
if (!locales[name] && (typeof module !== 'undefined') &&
module && module.exports) {
try {
oldLocale = globalLocale._abbr;
var aliasedRequire = require;
aliasedRequire('./locale/' + name);
getSetGlobalLocale(oldLocale);
} catch (e) {}
}
return locales[name];
}
複製代碼
經過 aliasedRequire('./locale/' + name) 能夠知道momentJS的多語言目錄是locale,全部的語言JS文件都在這個目錄中
使用IgnorePlugin插件忽略其依賴
將momentJS的多語言目錄locale忽略
new webpack.IgnorePlugin(/\.\/locale/, /moment/)
複製代碼
須要使用某些依賴時自行手動引入
忽略其依賴以後,moment.locale('zh-CN')就會失效,由於其所依賴的語言包全都被忽略了,須要手動將其引入
import moment from 'moment'
import 'moment/locale/zh-cn' // 須要手動引入方可生效
moment.locale('zh-CN')
console.log(moment().subtract(6, 'days').calendar())
複製代碼
在引入一些第三方模塊時,例如Vue、React等,這些框架的文件通常都是不會修改的,而每次打包都須要去解析他們,也會影響打包速度,就算是作了拆分,也只是提升了上線後的用戶訪問速度,並不會提升構建速度,因此若是須要提升構建速度,應該使用動態連接庫的方式,相似windows的dll文件
藉助DLLPlugin插件實現將這些框架做爲一個個的動態連接庫,只構建一次,之後的每次構建都只會生成本身的業務代碼,能夠很好的提升構建效率
豬喲思想在於,講一些不作修改的依賴文件,提早打包,這樣咱們開發代碼發佈的時候就不須要再對這些代碼進行打包,從而節省了打包時間,主要使用兩個插件: DLLPlugin和DLLReferencePlugin
須要注意的是,如果使用的DLLPlugin,CleanWebpackPlugin插件會存在衝突,須要移除CleanWebpackPlugin插件
DLLPlugin
使用一個單獨webpack配置建立一個dll文件,而且它還建立一個manifest.json,DLLReferencePlugin使用該json文件來作映射依賴性,這個文件會告訴webpack哪些文件已經提取打包好了
DLLReferencePlugin
該插件主要用於主webpack配置,它引用的dll須要預先構建的依賴該系
// 此配置文件 是打包VUE全家桶的
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry:{
vue: [
'vue/dist/vue',
'vue-router'
]
},
output:{
path: path.resolve(__dirname, '../dist'),
filename: '[name]_dll.js',
library: '[name]_dll' // 最終會在全局暴露出一個[name]_dll的對象
},
plugins:[
new webpack.DllPlugin({
name: '[name]_dll',
path: path.resolve(__dirname, '../dist/manifest.json'),
})
]
}
複製代碼
webpack.vue.js 只是用來打包生成 [name]_dd.js 文件和 manifest.json文件的,是不須要參與到業務代碼打包的,由於只會在每一次修改了須要生成dll文件的時間纔會執行一次,不然不須要參與到打包
webpack.base.js
中進行插件的配置使用DllReferencePlugin指定manifest文件的位置便可
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dist/manifest.json'),
})
複製代碼
安裝add-asset-html-webpack-plugin
npm i -D add-asset-html-webpack-plugin
複製代碼
配置插件自動添加script標籤到HTML中,須要注意的是,必須在HtmlWebpackPlugin後面引入,由於HtmlWebpackPlugin是生產一個html文件,AddAssetHtmlWebpackPlugin是在已有的html中注入一個script,不然會被覆蓋
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dist/vue_dll.js')
})
複製代碼