在家的日子不能出去玩,不能出去吃,的確是很不開心的,不過也是真的增長了好多空閒時間css
與其在家呆着無聊,不如安安靜靜的學習一下html
疫情期間,無論怎樣,心態不能崩,武漢加油,中國加油vue
閒言少敘,如今咱們就開始一塊兒學習吧node
優化,就是加以改變或選擇使優良,在工做當中是讓提高效率的好辦法。react
固然,webpack優化千千萬,但我以爲這些就夠了jquery
首當其衝的,就是工做中那些用不到的樣式,多是因爲歷史遺留緣由已經忘記哪些是沒有使用的樣式了,一一排查太過耗時費力了webpack
因而,purgecss-webpack-plugin和glob它倆就登場了,它的做用就是解決上面提到的問題,讓咱們來看看如何使用吧web
插件千萬個,安裝第一步:npm
npm i purgecss-webpack-plugin glob -D
工欲善其事必先利其器,裝備好了,咱們也來看一眼實際的狀況,而後再進行有效的配置吧 json
那麼,不廢話,解決它,咱們開始配置吧
// webpack.config.js文件
const path = require('path');
// html模板
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 從js中抽離出css
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 去除無用的樣式
const glob = require('glob');
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve('dist')
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new HtmlWebpaclPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin(),
// 去除無用的樣式
new PurgecssWebpackPlugin({
paths: glob.sync('./src/**/*', {nodir: true})
})
]
};
複製代碼
配置完畢了,上面包含了一些基本的配置。你們能夠把重點放在註釋爲去除無用的樣式代碼部分便可了
下面咱們來簡單分析分析:
glob.sync('./src/**/*', {nodir: true}
// 同步查找src目錄下的任意文件夾下的任意文件
// 返回一個數組,如['真實路徑/src/css/style.css','真實路徑/src/index.js',...]
// {nodir: true}表示不包含文件夾,加快查找速度
複製代碼
new PurgecssWebpackPlugin({
// paths表示指定要去解析的文件名數組路徑
// Purgecss會去解析這些文件而後把無用的樣式移除
paths: glob.sync('./src/**/*', {nodir: true})
})
複製代碼
大功告成,進入下一環節!!!
在html文件中引入cdn文件,在webpack配置externals,這樣就不會打包引入的cdn的庫
// index.html文件
<body>
<div id="root"></div>
<!-- 引入jquery的cdn -->
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</body>
// webpack.config.js文件
module.exports = {
externals: {
'jquery': '$'
}
}
複製代碼
這樣寫完後,在js文件中咱們就能夠不用再導入jquery也能直接使用$操做符了
But,這只是個過渡而已,下面有請主角登場
因爲每次都須要在index.html模板中手動引入須要的cdn文件,而後還要在webpack裏配置,有點繁瑣了
So,html-webpack-externals-plugin這樣的插件就應運而生了
安裝步驟我就略過了,直接看代碼
// webpack.config.js文件
// 動態添加CDN
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
module.exports = {
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{ // 引入的模塊
module: 'jquery',
// cdn的地址
entry: 'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js',
// 掛載到了window上的名稱
// window.jQuery就能夠全局使用
global: 'jQuery'
},
{
module: 'vue',
entry: 'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
global: 'Vue'
}
]
})
]
};
複製代碼
這是一個webpack內置的優化能力,webpack很好很強大,哈哈
在生產環境下,Tree-shaking會進行自動刪除的操做
若是經過ES6的import引用的方式就會把沒有用到的代碼給刪除掉
那麼在打包的時候,就不會打包那些未引用的方法了
接下來,咱們看個栗子:
在src目錄下新建一個common.js,而後在裏面隨便寫點東東
// src/common.js文件
export const flatten = arr => {
console.log('my-flatten');
return arr.reduce((all, cur) => {
if (Array.isArray(cur) {
return [...all, ...flatten(cur)];
} else {
return [...all, cur];
}
}, []);
};
export const myBind = (fn, context) => {
console.log('my-bind');
let args = [].slice.call(arguments, 2);
return function() {
let newArgs = [].slice.call(arguments);
fn.apply(context, args.concat(newArgs));
}
};
複製代碼
import引用一下
// index.js文件
import { flatten } from './common';
let arr = [1,[3,[4, 5]], [2, [20]]];
console.log(flatten(arr));
複製代碼
console.log('my-bind')
的代碼的,這就說明完美的去掉了沒有引用到的代碼了
完美,完美,完美了
高興的太早了,是藥三分毒,有反作用的
如今咱們在src目錄再建立一個test.js文件,讓你們再感覺一下反作用
// src/test.js文件
export default test = () => {
console.log('test');
}
test();
複製代碼
此時,簡單修改一下index.js
// index.js文件
import { flatten } from './common';
import test from './test';
let arr = [1,[3,[4, 5]], [2, [20]]];
console.log(flatten(arr));
複製代碼
儘管引入了test可是並無使用,但是打包後的bundle.js文件卻變化了,請看大屏幕
腫麼辦?沒引用卻打包進去了,表慌,讓咱們來將文件標記爲無反作用的
須要配合package.json文件,在裏面添加一個sideEffects屬性,賦值爲false就把這些反作用給幹掉了不會再打包進去了,So Easy
// package.json文件
{
...省略
"sideEffects": false
}
複製代碼
很好理解,由於咱們在js中引入css文件是import './style.css'
這樣,因此就出現了反作用,引用卻沒使用的尷尬
也是由於sideEffects: false一股腦的全給標記過濾掉了
如今修改一下sideEffects的值就能夠,給它一個去除反作用的範圍
// package.json文件
{
...省略
"sideEffects": ["./src/**/*.css"]
}
複製代碼
過濾掉引入的css文件產生的反作用,這樣走丟的css文件就又找回來了
下面一氣呵成繼續說個內置的插件,很是實用,come on baby
不少時候咱們在開發時不管是用React仍是Vue,咱們都不但願這個開發的主力框架每次都被打包一遍,這樣也是費時費力的事情
因此,出現了DllPlugin這種插件,它純屬webpack內置的,放心大膽的用
做用:
import React from 'react'
這樣的引用,它就會先去所謂的緩存文件裏找,找到了就直接用,也不用再進行對react打包了說多了,都是淚,看代碼更實在,重寫index.js
// index.js文件
import React from 'react';
import { render } from 'react-dom';
import './style.css';
render(<React.Fragment>
<h1 className="title">聽媽媽的話-周杰倫</h1>
<button className="btn">顯示歌詞</button>
</React.Fragment>, window.root);
複製代碼
把index.js重寫後咱們再來npm run build打包一下看看
因此,接下來咱們來看看DllPlugin會幫咱們怎麼處理吧
在根目錄下建立一個webpack.dll.js文件,用來打包出dll文件
// webpack.dll.js文件
const path = require('path');
// 引入webpack
const webpack = require('webpack');
module.exports = {
entry: ['react', 'react-dom'],
output: {
filename: 'react.dll.js',
path: path.resolve('dll'),
library: 'react' // 打包後被引用的變量名
},
plugins: [
// 動態連接庫
new webpack.DllPlugin({
name: 'react',
path: path.resolve('dll', 'manifest.json')
})
]
};
複製代碼
代碼寫完了,npm run dll
,以後會出現一個dll的文件夾,裏面會包含你打包出來的文件,以下圖
回到咱們的主戰場webpack.config.js中
// webpack.config.js文件
const path = require('path');
// 引入webpack
const webpack = require('webpack');
module.exports = {
plugins: [
// 引用對應的動態連接庫的manifest.json文件
// 這樣之後再引入react的時候就會優先在json文件裏去尋找
new webpack.DllReferencePlugin({
manifest: path.resolve('dll', 'manifest.json')
})
]
};
複製代碼
寫到這裏還不算完,還須要在src目錄下的index.html模板中引入一下
<script src="../dll/react.dll.js"></script>
插一句:之因此,會新建一個dll目錄,由於在npm run dev開發環境編譯的時候,dist目錄的內容都在內存中了,是找不到react.dll.js文件的
好了,如今讓咱們看看效果吧,npm run dev啓動一下
每次在index.html中手動引入畢竟不是長久之計,那麼接下來就再看一個好東東
經過add-asset-html-webpack-plugin
插件就能夠完成這樣的需求,來看代碼
// webpack.config.js文件
const webpack = require('webpack');
// 添加資源到html文件
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
plugins: [
// 引用打包好的react,不會打包到bundle裏
new webpack.DllReferencePlugin({
manifest: path.resolve('dll', 'manifest.json')
}),
// 直接將打包好的react.dll.js添加到html模板
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve('dll', 'react.dll.js')
})
]
};
複製代碼
經過上面的改造後,就動態的把react.dll.js文件添加到html文件中了。
咱們以前在index.html模板裏手動引入js的那一行就能夠刪除掉了
說到懶加載必然是一種很好的優化網頁或應用的方式,那麼在webpack中也是經過ES6的import()語法來引入的。
雖然這個import()語法目前還處在草案的第三階段,不過並不影響你們對它的一致好評
用過Vue-Router的同窗都知道,你們寫的路由經過component: () => import()
的方式,也是能夠進行懶加載的
So,好飯不怕晚,遲早會成爲正式一員的。下面仍是用實際栗子演示一下吧
// index.js文件
import './style.css';
import React from 'react';
import { render } from 'react-dom';
// 寫個輔助的類
class Music extends React.Component {
constructor() {
super();
this.state = { lrc: [] };
}
showLrc() {
// 經過ES6的import()方法實現了懶加載功能,其實是利用了jsonp去動態導入了
import('./lrc').then(data => {
let lrc = data.default.split('\n').filter(item => item !== '');
this.setState({ lrc });
});
}
render() {
return (
<div>
<button className="btn" onClick={() => this.showLrc()}>顯示歌詞</button>
<div className="lrc-box">
{this.state.lrc && this.state.lrc.map((item, index) => (
<p className="lrc" key={index}>{item}</p>
))}
</div>
</div>
)
}
}
render(<React.Fragment>
<h1 className="title">聽媽媽的話-周杰倫</h1>
<Music></Music>
</React.Fragment>, window.root);
複製代碼
npm run dev
讓咱們看看是否是有了懶加載的效果
這個文件就是咱們經過懶加載導入的lrc.js文件,因而可知,懶加載功能驗證成功,撒花
接下來再來個老生常談的優化,請繼續往下看,不要停
開發的時候,常常會有不一樣的模塊引用了同一個第三方包。
這裏舉個栗子,好比有兩個js文件,一個是index.js另外一個是lrc.js,它們都引用了著名的實用工具庫lodash,代碼以下下下
// index.js文件
import _ from 'lodash';
console.log(_.xor([2, 1], [2, 3]));
// lrc.js文件
import _ from 'lodash';
console.log(_.flatten([1,[3, 4, 5, [2]]]));
複製代碼
看完以上代碼就明白了,他們的公共部分就是都引了lodash,這樣會分別打包到他們所在的文件中去,這樣打包的js文件就會很大了
因此,必須得把lodash提取出來,廢話很少說,看招
// webpack.config.js文件
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'initial',
minSize: 0,
minChunks: 2,
test: /node_modules/,
priority: 1
}
}
}
}
};
複製代碼
webpack4中自帶了抽取公共代碼的方法,經過optimization裏的splitChunks來作到
固然了,項目中不少js文件不只僅會引用第三方模塊來開發,咱們也會使用寫好的公共模塊,那麼是否是也能夠提取出來呢?
// index.js文件
import { flatten } from './common';
console.log('index',flatten([1,[33, 4, 5, [34]]]));
// lrc.js文件
import {flatten} from './common';
console.log(flatten([1,[33, 4, 5, [34]]]));
複製代碼
上面在兩個js文件中都引入了common.js中寫好的flatten方法,既然它們都引用到了common.js,因此想固然的也能夠把它抽取出來的
那麼參照提取第三方代碼的實現來寫一下吧
// webpack.config.js文件
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
utils: {
chunks: 'initial',
minSize: 0,
minChunks: 2
}
}
}
}
};
複製代碼
以上就是抽取公共代碼部分了,到此爲止了
熱更新對於開發來講能夠說是很是高效的,並且webpack如今也自帶插件支持熱更新了
經過devServer來啓動熱更新
☆:devServer的使用須要在項目中提早安好webpack-dev-server
那麼咱們先來看看配置部分:
// webpack.config.js文件
// 引入webpack
const webpack = require('webpack');
module.exports = {
devServer: {
hot: true, // 啓動熱更新
port: 8080,
contentBase: './dist'
},
plugins: [
// webpack支持熱更新插件
new webpack.HotModuleReplacementPlugin(),
// 打印更新了的文件路徑
new webpack.NamedModulesPlugin()
]
};
複製代碼
熱更新只適合在開發環境下來搞,因此配置好後,再執行npm run dev
如今讓咱們回到index.js文件裏去,讓咱們感覺一下
// index.js文件
// 熱更新
// 在src目錄下新建立了audio.js文件
// 須要導入這個文件,否則熱更新失效
import audio from './audio';
// 檢查是否支持熱更新
if (module.hot) {
// 接收熱更新的模塊
module.hot.accept('./audio.js', (path) => {
console.log(path);
console.log('audio文件更新了');
});
}
複製代碼
這樣就完成了熱更新操做,接收了audio.js文件,因此在audio.js文件內部若是進行修改保存後,會在控制檯裏展現對應的更新信息,以下圖
好了,不囉嗦了,下面仍是乾貨滿滿的,Enjoy it
嚴格意義上來說,經過webpack來作跨域請求實際上也不能算在優化裏。不過在開發中,這種跨域的狀況仍是不少的,瞭解了也不吃虧,一塊兒看看吧
仍是經過老朋友devServer來實現的,上代碼
// webpack.config.js文件
module.exports = {
devServer: {
proxy: {
// 能夠這樣寫
// '/api': 'http://localhost:3000',
// 也能夠這樣寫,多配置
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': ''
}
}
}
}
};
複製代碼
經過devServer提供的proxy屬性,能夠完成咱們想要的跨域請求,下面看看參數都是幹什麼的
// webpack.config.js文件
module.exports = {
devServer: {
// 利用node來寫
before(app) {
// 至關於直接寫了後端的接口,哈哈
app.get('/api/info', (req, res) => {
res.json({
nickname: '我滴個大榴蓮啊',
level: 8,
src: 'https://music.163.com/song/media/outer/url?id=1382794914.mp3'
});
});
}
}
};
複製代碼
固然,以上兩種方式你們知道便可了,第一種也是最廣泛的實現方式,媽媽不再用擔憂個人跨域問題了
做用: 忽略打包第三方模塊指定的目錄
爲何要忽略呢? 經過下面的栗子來看一下
相信不少人應該或多或少的都聽過moment這個時間庫,不知道也不要緊,我來演示一波
先安裝moment: npm i moment -S
// index.js文件
// 導入moment
import moment from 'moment';
// 設置中文
moment.locale('zh-cn');
let time = moment().endOf('day').fromNow();
window.root.innerHTML += time;
複製代碼
這是神馬緣由呢,實際上是由於moment被導入的時候,附贈了整個locale語言包,這種買一贈一的行爲就不用提如今代碼世界了,吃不消了
// webpack.config.js文件
const webpack = require('webpack');
module.exports = {
plugins: [
// 忽略moment目錄下的locale文件夾
new webpack.IgnorePlugin(/\.\/locale/, /moment/)
]
};
複製代碼
配置改寫後,再回到index.js中單獨導入中文語言包就行了
// index.js文件
// 利用IgnorePlugin把只須要的語言包導入使用就能夠了,省去了一會兒打包整個語言包
import moment from 'moment';
// 單獨導入中文語言包
import 'moment/locale/zh-cn';
let time = moment().endOf('day').fromNow();
window.root.innerHTML += time;
複製代碼
再從新npm run build
打包後,體積瞬間減小了278k啊啊啊啊,下圖顯示
noParse的做用是不去解析你所使用的第三方庫中的依賴庫
廢話很少說,直接上代碼
// webpack.config.js文件
module.exports = {
module: {
// 不去解析jquery或lodash中的依賴庫
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader'
}
}
]
}
}
複製代碼
在工做中,忽略大型的庫能夠提升構建性能,能夠從構建時間上看出來速度的提高,如上面代碼中提到的jquery和lodash
從這個英文就能看出來,它就是配置模塊如何解析用的,配置太多也不必一一介紹了,仍是直接說重點寫出經常使用的配置吧
// webpack.config.js文件
const { resolve } = require('path');
module.exports = {
resolve: {
modules: [resolve('node_modules')],
alias: {
Utils: resolve(__dirname, 'src/utils/'),
'@': resolve(__dirname, 'src')
},
extensions: ['.js', '.css', '.json']
}
}
複製代碼
此刻,咱們往src目錄下建立一個utils文件夾,而後新建一個parse-url.js文件
// src/utils/parse-url.js文件
// 就簡單導出一下
export default '我是解析url的方法';
// src/index.js文件
import parseUrl from 'Utils/parse-url';
console.log(parseUrl); // 打印:我是解析url的方法
複製代碼
上面代碼爲咱們展現了alias別名的效果,真的是頗有用很方便的,哈哈
兩者使用一個便可了,想必這個優化的點你們並不陌生的,看眼代碼吧
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader'
},
exculde: /node_modules/, // 二選一
include: path.resolve('src') // 二選一
}
]
}
}
複製代碼
webpack在Node環境下運行因此也是單線程操做,一件一件的去處理事情。這樣很不nice,本着如今cpu都那麼威猛的狀況下,運用多核運算徹底是小兒科的
因而乎,就有了happypack的用武之地了,它的做用就是能夠實現多進程打包操做
下面咱們再來看下是如何配置的
配置前先來安裝一下npm i happypack -D
// webpack.config.js文件
const Happypack = require('happypack');
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'Happypack/loader?id=js'
},
{
test: /\.css$/,
use: 'Happypack/loader?id=css'
}
]
},
plugins: [
new Happypack({
id: 'js',
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react'
]
}
}
]
}),
new Happypack({
id: 'css',
use: ['style-loader', 'css-loader']
})
]
}
複製代碼
差很少了,你們真的辛苦了,哈哈,堅持看下來的小夥伴,真是太給力了
優化的內容有多種多樣,也不會所有都包含,上面提到的也都是比較常見的一些優化方式,各取所需、各取所用就行了
仍是那句話,疫情總會過去的,沒什麼好怕的,一塊兒努力吧!!!
感謝你們,886