往期回顧:
從0搭建本身的webpack開發環境(一)javascript
前四篇文章咱們已經掌握了webpack
各類常見的配置,這一片文章咱們來看看如何實現webpack
中的優化。java
咱們先來編寫最基本的webpack配置
,而後依次實現其中的各類優化。node
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = mode => { return { mode: mode, entry: "./src/main.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "dist") }, module: { rules: [ { test: /\.(png|jpg|gif)$/, use: "file-loader" }, { test: /\.js$/, use: "babel-loader" // .babelrc已經配置支持react }, { test: /\.css$/, use: [ mode !== "development" ? MiniCssExtractPlugin.loader : "style-loader", "css-loader" ] } ] }, plugins: [ new PurgecssPlugin({ paths: glob.sync(`${path.join(__dirname, "src")}/**/*`, { nodir: true }) // 不匹配目錄,只匹配文件 }), mode !== "development" && new MiniCssExtractPlugin({ filename: "css/[name].css" }), new HtmlWebpackPlugin({ template: "./src/template.html", filename: "index.html" }) ].filter(Boolean) }; };
.babelrc
配置文件react
{ "presets": [ "@babel/preset-env", "@babel/preset-react" ] }
先來看看編寫的代碼jquery
import './style.css' import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render(<div>hello</div>,document.getElementById('root'));
body{ background: red } .class1{ background: red }
這裏的.class1
顯然是無用的,咱們能夠搜索src
目錄下的文件,刪除無用的樣式
const glob = require('glob'); const PurgecssPlugin = require('purgecss-webpack-plugin'); // 這裏須要配合mini-css-extract-plugin插件 mode !== "development" && new PurgecssPlugin({ paths: glob.sync(`${path.join(__dirname, "src")}/**/*`, { nodir: true }) // 不匹配目錄,只匹配文件 }),
將打包後的圖片
進行優化webpack
npm install image-webpack-loader --save-dev
在file-loader
以前使用壓縮圖片插件es6
loader: "image-webpack-loader", options: { mozjpeg: { progressive: true, quality: 65 }, // optipng.enabled: false will disable optipng optipng: { enabled: false, }, pngquant: { quality: [0.90, 0.95], speed: 4 }, gifsicle: { interlaced: false, }, // the webp option will enable WEBP webp: { quality: 75 } }
能夠發現圖片大小是否有了明顯的變化
咱們但願經過cdn的方式
引入資源
const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin') new AddAssetHtmlCdnPlugin(true,{ 'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js' })
可是在代碼中還但願引入jquery
來得到提示
import $ from 'jquery' console.log('$',$)
可是打包時依然會將jquery
進行打包
externals:{ 'jquery':'$' }
在配置文件中標註jquery
是外部的,這樣打包時就不會將jquery進行打包了
顧名思義就是將沒用的內容搖晃掉,來看下代碼:
main.js
import { minus } from "./calc"; console.log(minus(1,1));
calc.js
import {test} from './test'; export const sum = (a, b) => { return a + b + 'sum'; }; export const minus = (a, b) => { return a - b + 'minus'; };
test.js
export const test = ()=>{ console.log('hello') } console.log(test());
觀察上述代碼會發現咱們主要使用minus
方法,test.js
代碼實際上是有反作用的!
默認mode:production
時,會自動tree-shaking
,可是打包後'hello'
依然會被打印出來,這時候咱們須要配置規避反作用!
在package.json
中配置
"sideEffects":false,
若是這樣設置,默認就不會導入css
文件啦,由於咱們引入css也是經過import './style.css'
的;
這裏重點就來了,tree-shaking
主要針對es6模塊,咱們可使用require
語法導入css,可是這樣用起來又有點格格不入,因此咱們配置css
文件不起反作用。
"sideEffects":[ "**/*.css" ]
在開發環境下默認tree-shaking
不會生效,能夠配置標識提示。
optimization:{ usedExports:true }
做用域提高,能夠減小代碼體積,節約內存
let a = 1; let b = 2; let c = 3; let d = a+b+c; export default d; // 引入d import d from './d'; console.log(d);
最終打包後的結果會變成
console.log(6)
每次構建時第三方模塊
都須要從新構建,這個性能消耗比較大,咱們能夠先把第三方庫打包成動態連接庫,之後構建時只須要查找構建好的庫就行了,這樣能夠大大節約構建時間。
import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render(<h1>hello</h1>,document.getElementById('root'))
這裏咱們能夠先將react
、react-dom
單獨進行打包
單獨打包建立webpack.dll.js
const path = require('path'); const DllPlugin = require('webpack/lib/DllPlugin'); module.exports = { entry:['react','react-dom'], mode:'production', output:{ filename:'react.dll.js', path:path.resolve(__dirname,'dll'), library:'react' }, plugins:[ new DllPlugin({ name:'react', path:path.resolve(__dirname,'dll/manifest.json') }) ] }
執行"webpack --config webpack.dll.js
命令,能夠看到dll目錄下建立了兩個文件分別是manifest.json
和react.dll.js
關係是這個醬紫的,到時候咱們會經過manifest.json
來找到react.dll.js
文件中的模塊進行加載。
在咱們的項目中能夠引用剛纔打包好的動態連接庫
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); // 構建時會引用動態連接庫的內容 new DllReferencePlugin({ manifest:path.resolve(__dirname,'dll/manifest.json') }), // 須要手動引入react.dll.js new AddAssetHtmlWebpackPlugin( { filepath: path.resolve(__dirname,'dll/react.dll.js') } )
使用DllPlugin
能夠大幅度提升構建速度
實現點擊後動態加載
文件
let btn = document.createElement('button'); btn.innerHTML = '點擊加載視頻'; btn.addEventListener('click',()=>{ import('./video').then(res=>{ console.log(res.default); }); }); document.body.appendChild(btn);
給動態引入的文件增長名字
output:{ chunkFilename:'[name].min.js' } import(/* webpackChunkName: "video" */ './video').then(res=>{ console.log(res.default); })
這樣打包後的結果最終的文件就是
video.min.js
安裝webpack-bundle-analyzer
插件
npm install --save-dev webpack-bundle-analyzer
安裝後使用插件
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer'); mode !== "development" && new BundleAnalyzerPlugin()
默認就會展示當前應用的分析圖表
咱們再來看下SplitChunks
這個配置,他能夠在編譯時抽離第三方模塊、公共模塊。
先將項目配置成多入口文件
entry:{ a:'./src/a.js', b:'./src/b.js' }
咱們讓a,b兩個模塊同時引用jquery
,這裏別忘了去掉以前的externals
配置
配置SplitChunks
插件
默認配置在此,咱們一個個描述下含義
splitChunks: { chunks: 'async', // 分割異步模塊 minSize: 30000, // 分割的文件最小大小 maxSize: 0, minChunks: 1, // 引用次數 maxAsyncRequests: 5, // 最大異步請求數 maxInitialRequests: 3, // 最大初始化請求數 automaticNameDelimiter: '~', // 抽離的命名分隔符 automaticNameMaxLength: 30, // 名字最大長度 name: true, cacheGroups: { // 緩存組 vendors: { // 先抽離第三方 test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, // 優先級 reuseExistingChunk: true } } }
咱們將async
改成initial
咱們在爲每一個文件動態導入lodash
庫,而且改爲async
import('lodash')
爲每一個入口引入
c.js
,而且改造配置文件
splitChunks: { chunks: 'all', name: true, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minSize:1, // 不是第三方模塊,被引入兩次也會被抽離 minChunks: 2, priority: -20, } } }
這樣再反過來看
chunks
的參數是否是就瞭然於胸了呢?
模塊熱替換(HMR - Hot Module Replacement)是webpack
提供的最有用的功能之一。它容許在運行時替換、添加、刪除各類模塊,而無需進行徹底刷新從新加載整個頁面。
啓用熱更新,默認樣式能夠支持熱更新,若是不支持熱更新則能夠採用強制刷新。
devServer:{ hot:true } new webpack.NamedModulesPlugin(),
讓js
支持熱更新
import sum from './sum'; console.log(sum(1,2)); if(module.hot){ // 若是支持熱更新 module.hot.accept(); // 當入口文件變化後從新執行當前入口文件 }
配置忽略 import
和require
語法
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
能夠計算每一步執行的運行速度
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin'); const smw = new SpeedMeasureWebpackPlugin(); module.exports =smw.wrap({ ... });
module.noParse
,對相似jq這類的依賴庫,內部不會引用其餘庫,那麼咱們在打包的時候就沒有必要再去解析,這樣可以增長打包速率。
noParse:/jquery/
resolve: { extensions: [".js",".jsx",".json",".css"], alias:{}, modules:['node_modules'] },
在使用loader
時,能夠指定哪些文件不經過loader
,或者指定哪些文件必須經過loader
。
{ test: /\.js$/, use: "babel-loader", // include:path.resolve(__dirname,'src'), exclude:/node_modules/ },
多線程打包
,咱們能夠將不一樣的邏輯交給不一樣的線程來處理。
npm install --save-dev happypack
使用插件
const HappyPack = require('happypack'); rules:[ { test: /\.js$/, use: 'happypack/loader?id=jsx' }, { test: /\.less$/, use: 'happypack/loader?id=styles' }, ] new HappyPack({ id: 'jsx', threads: 4, loaders: [ 'babel-loader' ] }), new HappyPack({ id: 'styles', threads: 2, loaders: [ 'style-loader', 'css-loader', 'less-loader' ] })
至此,咱們五篇連載《從0搭建本身的webpack開發環境》先要告一段落了,但願你們能夠經過這些文章對webpack能有全新的認識,對工做能更有幫助。
有什麼想要了解的前端知識,歡迎關注留言!謝謝你們的支持!