摘要:或者的,都會(huì)對(duì)其進(jìn)行分析。舒適的開發(fā)體驗(yàn),有助于提高我們的開發(fā)效率,優(yōu)化開發(fā)體驗(yàn)也至關(guān)重要組件熱刷新熱刷新自從推出熱刷新后,前端開發(fā)者在開環(huán)境下體驗(yàn)大幅提高。實(shí)現(xiàn)熱調(diào)試后,調(diào)試流程大幅縮短,和普通非直出模式調(diào)試體驗(yàn)保持一致。
webpack,打包所有的資源不知道不覺,webpack已經(jīng)偷偷更新到4.34版本了,本人決定,這是今年最后一篇寫webpack的文章,除非它更新到版本5,本人今年剩下的時(shí)間都會(huì)放在Golang和二進(jìn)制數(shù)據(jù)操作以及后端的生態(tài)上 在看本文前,假設(shè)你對(duì)webpack有一定了解,如果不了解,可以看看我之前的手寫React和Vue腳手架的文章
手寫優(yōu)化版React腳手架
手寫Vue的腳手架
前端性能優(yōu)化不完全手冊(cè)
跨平臺(tái)webpack配置
都是百星star的優(yōu)質(zhì)文章
在此對(duì)webpack的性能優(yōu)化進(jìn)行幾點(diǎn)聲明:
在部分極度復(fù)雜的環(huán)境下,需要雙package.json文件,即實(shí)行三次打包
在代碼分割時(shí),低于18K的文件沒必要多帶帶打包成一個(gè)chunk,http請(qǐng)求次數(shù)過(guò)多反而影響性能
prerender和PWA互斥,這個(gè)問(wèn)題暫時(shí)沒有解決
babel緩存編譯緩存的是索引,即hash值,非常吃內(nèi)存,每次開發(fā)完記得清理內(nèi)存
babel-polyfill按需加載在某些非常復(fù)雜的場(chǎng)景下比較適合
prefetch,preload對(duì)首屏優(yōu)化提升是明顯
代碼分割不管什么技術(shù)棧,一定要做,不然就是垃圾項(xiàng)目
多線程編譯對(duì)構(gòu)建速度提升也很明顯
代碼分割配合PWA+預(yù)渲染+preload是首屏優(yōu)化的巔峰,但是pwa無(wú)法緩存預(yù)渲染的html文件
本文的webpack主要針對(duì)React技術(shù)棧,實(shí)現(xiàn)功能如下:開發(fā)模式熱更新
識(shí)別JSX文件
識(shí)別class組件
代碼混淆壓縮,防止反編譯代碼,加密代碼
配置alias別名,簡(jiǎn)化import的長(zhǎng)字段
同構(gòu)直出,SSR的熱調(diào)試(基于Node做中間件)
實(shí)現(xiàn)javaScript的tree shaking 搖樹優(yōu)化 刪除掉無(wú)用代碼
實(shí)現(xiàn)CSS的tree shaking
識(shí)別 async / await 和 箭頭函數(shù)
react-hot-loader記錄react頁(yè)面留存狀態(tài)state
PWA功能,熱刷新,安裝后立即接管瀏覽器 離線后仍讓可以訪問(wèn)網(wǎng)站 還可以在手機(jī)上添加網(wǎng)站到桌面使用
preload 預(yù)加載資源 prefetch按需請(qǐng)求資源
CSS模塊化,不怕命名沖突
小圖片的base64處理
文件后綴省掉jsx js json等
實(shí)現(xiàn)React懶加載,按需加載 , 代碼分割 并且支持服務(wù)端渲染
支持less sass stylus等預(yù)處理
code spliting 優(yōu)化首屏加載時(shí)間 不讓一個(gè)文件體積過(guò)大
加入dns-prefetch和preload預(yù)請(qǐng)求必要的資源,加快首屏渲染(京東策略)
加入prerender,極大加快首屏渲染速度
提取公共代碼,打包成一個(gè)chunk
每個(gè)chunk有對(duì)應(yīng)的chunkhash,每個(gè)文件有對(duì)應(yīng)的contenthash,方便瀏覽器區(qū)別緩存
圖片壓縮
CSS壓縮
增加CSS前綴 兼容各種瀏覽器
對(duì)于各種不同文件打包輸出指定文件夾下
緩存babel的編譯結(jié)果,加快編譯速度
每個(gè)入口文件,對(duì)應(yīng)一個(gè)chunk,打包出來(lái)后對(duì)應(yīng)一個(gè)文件 也是code spliting
刪除HTML文件的注釋等無(wú)用內(nèi)容
每次編譯刪除舊的打包代碼
將CSS文件多帶帶抽取出來(lái)
讓babel不僅緩存編譯結(jié)果,還在第一次編譯后開啟多線程編譯,極大加快構(gòu)建速度
等等....
本質(zhì)上,webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時(shí),它會(huì)遞歸地構(gòu)建一個(gè)依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個(gè)模塊,然后將所有這些模塊打包成一個(gè)或多個(gè) bundle webpack打包原理識(shí)別入口文件
通過(guò)逐層識(shí)別模塊依賴。(Commonjs、amd或者es6的import,webpack都會(huì)對(duì)其進(jìn)行分析。來(lái)獲取代碼的依賴)
webpack做的就是分析代碼。轉(zhuǎn)換代碼,編譯代碼,輸出代碼
最終形成打包后的代碼
這些都是webpack的一些基礎(chǔ)知識(shí),對(duì)于理解webpack的工作機(jī)制很有幫助。
舒適的開發(fā)體驗(yàn),有助于提高我們的開發(fā)效率,優(yōu)化開發(fā)體驗(yàn)也至關(guān)重要組件熱刷新、CSS熱刷新
自從webpack推出熱刷新后,前端開發(fā)者在開環(huán)境下體驗(yàn)大幅提高。
沒有熱刷新能力,我們修改一個(gè)組件后
加入熱刷新后
主要看一下React技術(shù)棧,如何在構(gòu)建中接入熱刷新無(wú)論什么技術(shù)棧,都需要在dev模式下加上 webpack.HotModuleReplacementPlugin插件
devServer: { contentBase: "../build", open: true, port: 5000, hot: true },
注:也可以使用react-hot-loader來(lái)實(shí)現(xiàn),具體參考官方文檔
在開發(fā)模式下也要代碼分割,加快打開頁(yè)面速度optimization: { runtimeChunk: true, splitChunks: { chunks: "all", minSize: 10000, // 提高緩存利用率,這需要在http2/spdy maxSize: 0,//沒有限制 minChunks: 3,// 共享最少的chunk數(shù),使用次數(shù)超過(guò)這個(gè)值才會(huì)被提取 maxAsyncRequests: 5,//最多的異步chunk數(shù) maxInitialRequests: 5,// 最多的同步chunks數(shù) automaticNameDelimiter: "~",// 多頁(yè)面共用chunk命名分隔符 name: true, cacheGroups: {// 聲明的公共chunk vendor: { // 過(guò)濾需要打入的模塊 test: module => { if (module.resource) { const include = [/[/]node_modules[/]/].every(reg => { return reg.test(module.resource); }); const exclude = [/[/]node_modules[/](react|redux|antd)/].some(reg => { return reg.test(module.resource); }); return include && !exclude; } return false; }, name: "vendor", priority: 50,// 確定模塊打入的優(yōu)先級(jí) reuseExistingChunk: true,// 使用復(fù)用已經(jīng)存在的模塊 }, react: { test({ resource }) { return /[/]node_modules[/](react|redux)/.test(resource); }, name: "react", priority: 20, reuseExistingChunk: true, }, antd: { test: /[/]node_modules[/]antd/, name: "antd", priority: 15, reuseExistingChunk: true, }, }, } }簡(jiǎn)要解釋上面這段配置
將node_modules共用部分打入vendor.js bundle中;
將react全家桶打入react.js bundle中;
如果項(xiàng)目依賴了antd,那么將antd打入多帶帶的bundle中;(其實(shí)不用這樣,可以看我下面的babel配置,性能更高)
最后剩下的業(yè)務(wù)模塊超過(guò)3次引用的公共模塊,將自動(dòng)提取公共塊
注意 上面的配置只是為了給大家看,其實(shí)這樣配置代碼分割,性能更高
optimization: { runtimeChunk: true, splitChunks: { chunks: "all", } }react-hot-loader記錄react頁(yè)面留存狀態(tài)state
yarn add react-hot-loader
// 在入口文件里這樣寫 import React from "react"; import ReactDOM from "react-dom"; import { AppContainer } from "react-hot-loader";-------------------1、首先引入AppContainre import { BrowserRouter } from "react-router-dom"; import Router from "./router"; /*初始化*/ renderWithHotReload(Router);-------------------2、初始化 /*熱更新*/ if (module.hot) {-------------------3、熱更新操作 module.hot.accept("./router/index.js", () => { const Router = require("./router/index.js").default; renderWithHotReload(Router); }); } function renderWithHotReload(Router) {-------------------4、定義渲染函數(shù) ReactDOM.render(, document.getElementById("app") ); }
然后你再刷新試試React的按需加載,附帶代碼分割功能 ,每個(gè)按需加載的組件打包后都會(huì)被多帶帶分割成一個(gè)文件
import React from "react" import loadable from "react-loadable" import Loading from "../loading" const LoadableComponent = loadable({ loader: () => import("../Test/index.jsx"), loading: Loading, }); class Assets extends React.Component { render() { return (* 加入html-loader識(shí)別html文件) } } export default Assets這即將按需加載
{ test: /.(html)$/, loader: "html-loader" }配置別名
resolve: { modules: [ path.resolve(__dirname, "src"), path.resolve(__dirname,"node_modules"), ], alias: { components: path.resolve(__dirname, "/src/components"), }, }加入eslint-loader
{ enforce:"pre", test:/.js$/, exclude:/node_modules/, include:resolve(__dirname,"/src/js"), loader:"eslint-loader" }resolve解析配置,為了為了給所有文件后綴省掉 js jsx json,加入配置
resolve: { extensions: [".js", ".json", ".jsx"] }加入HTML文件壓縮,自動(dòng)將入門的js文件注入html中,優(yōu)化HTML文件
new HtmlWebpackPlugin({ template: "./public/index.html", minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, } }),SSR同構(gòu)直出熱調(diào)試
, 采用 webpack watch+nodemon 結(jié)合的模式實(shí)現(xiàn)對(duì)SSR熱調(diào)試的支持。node 服務(wù)需要的html/js通過(guò)webpack插件動(dòng)態(tài)輸出,當(dāng)nodemon檢測(cè)到變化后將自動(dòng)重啟,html文件中的靜態(tài)資源全部替換為dev模式下的資源,并保持socket連接自動(dòng)更新頁(yè)面。
實(shí)現(xiàn)熱調(diào)試后,調(diào)試流程大幅縮短,和普通非直出模式調(diào)試體驗(yàn)保持一致。下面是SSR熱調(diào)試的流程圖:
加入 babel-loader 還有 解析JSX ES6語(yǔ)法的 babel preset@babel/preset-react解析 jsx語(yǔ)法
@babel/preset-env解析es6語(yǔ)法
@babel/plugin-syntax-dynamic-import解析react-loadable的import按需加載,附帶code spliting功能
["import", { libraryName: "antd-mobile", style: true }], Antd-mobile的按需加載
{ loader: "babel-loader", options: { //jsx語(yǔ)法 presets: ["@babel/preset-react", //tree shaking 按需加載babel-polifill ["@babel/preset-env", { "modules": false, "useBuiltIns": "false", "corejs": 2 }]], plugins: [ //支持import 懶加載 "@babel/plugin-syntax-dynamic-import", //andt-mobile按需加載 true是less,如果不用less style的值可以寫"css" ["import", { libraryName: "antd-mobile", style: true }], //識(shí)別class組件 ["@babel/plugin-proposal-class-properties", { "loose": true }], ], cacheDirectory: true }, }
特別提示,如果電腦性能不高,不建議開啟babel緩存索引,非常吃內(nèi)存,記得每次開發(fā)完了清理內(nèi)存加入thread-loader,在babel首次編譯后開啟多線程
const os = require("os") { loader: "thread-loader", options: { workers: os.cpus().length } }加入多帶帶抽取CSS文件的loader和插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin") { test: /.(less)$/, use: [ MiniCssExtractPlugin.loader, { loader: "css-loader", options: { modules: true, localIdentName: "[local]--[hash:base64:5]" } }, {loader:"postcss-loader"}, { loader: "less-loader" } ] } new MiniCssExtractPlugin({ filename:"[name].[contenthash:8].css" }),CSS的tree shaking
const PurifyCSS = require("purifycss-webpack") const glob = require("glob-all") plugins:[ // 清除無(wú)用 css new PurifyCSS({ paths: glob.sync([ // 要做 CSS Tree Shaking 的路徑文件 path.resolve(__dirname, "./src/*.html"), // 請(qǐng)注意,我們同樣需要對(duì) html 文件進(jìn)行 tree shaking path.resolve(__dirname, "./src/*.js") ]) }) ]對(duì)小圖片進(jìn)行base64處理,減少http請(qǐng)求數(shù)量,并對(duì)輸出的文件統(tǒng)一打包處理
{ test: /.(jpg|jpeg|bmp|svg|png|webp|gif)$/, loader: "url-loader", options: { limit: 8 * 1024, name: "[name].[hash:8].[ext]", } }, { exclude: /.(js|json|less|css|jsx)$/, loader: "file-loader", options: { outputPath: "media/", name: "[name].[hash].[ext]" } } ] }] },加入多帶帶抽取CSS文件的loader和插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin") { test: /.(less)$/, use: [ MiniCssExtractPlugin.loader, { loader: "css-loader", options: { modules: true, localIdentName: "[local]--[hash:base64:5]" } }, {loader:"postcss-loader"}, { loader: "less-loader" } ] } new MiniCssExtractPlugin({ filename:"[name].[contenthash:8].css" }),加入壓縮css的插件
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin") new OptimizeCssAssetsWebpackPlugin({ cssProcessPluginOptions:{ preset:["default",{discardComments: {removeAll:true} }] } }),加入每次打包輸出文件清空上次打包文件的插件
const CleanWebpackPlugin = require("clean-webpack-plugin") new CleanWebpackPlugin()加入圖片壓縮
{ test: /.(jpg|jpeg|bmp|svg|png|webp|gif)$/, use:[ {loader: "url-loader", options: { limit: 8 * 1024, name: "[name].[hash:8].[ext]", outputPath:"/img" }}, { loader: "img-loader", options: { plugins: [ require("imagemin-gifsicle")({ interlaced: false }), require("imagemin-mozjpeg")({ progressive: true, arithmetic: false }), require("imagemin-pngquant")({ floyd: 0.5, speed: 2 }), require("imagemin-svgo")({ plugins: [ { removeTitle: true }, { convertPathData: false } ] }) ] } } ] }加入代碼混淆,反編譯
var JavaScriptObfuscator = require("webpack-obfuscator"); // ... // webpack plugins array plugins: [ new JavaScriptObfuscator ({ rotateUnicodeArray: true }, ["excluded_bundle_name.js"]) ],加入 PWA的插件 , WorkboxPlugin
pwa這個(gè)技術(shù)其實(shí)要想真正用好,還是需要下點(diǎn)功夫,它有它的生命周期,以及它在瀏覽器中熱更新帶來(lái)的副作用等,需要認(rèn)真研究。可以參考百度的lavas框架發(fā)展歷史~
const WorkboxPlugin = require("workbox-webpack-plugin") new WorkboxPlugin.GenerateSW({ clientsClaim: true, //讓瀏覽器立即servece worker被接管 skipWaiting: true, // 更新sw文件后,立即插隊(duì)到最前面 importWorkboxFrom: "local", include: [/.js$/, /.css$/, /.html$/,/.jpg/,/.jpeg/,/.svg/,/.webp/,/.png/], }),加入預(yù)加載preload
new PreloadWebpackPlugin({ rel: "preload", as(entry) { if (/.css$/.test(entry)) return "style"; if (/.woff$/.test(entry)) return "font"; if (/.png$/.test(entry)) return "image"; return "script"; }, include: "allChunks" //include: ["app"] }),加入預(yù)渲染
const PrerenderSPAPlugin = require("prerender-spa-plugin") new PrerenderSPAPlugin({ routes: ["/","/home","/shop"], staticDir: resolve(__dirname, "../dist"), }),
我這套webpack配置,無(wú)論多復(fù)雜的環(huán)境,都是可以搞定的
webpack真的非常非常重要,如果用不好,就永遠(yuǎn)是個(gè)初級(jí)前端
只要webpack不更新到5,以后就不出webpack的文章了
webpack4大結(jié)局,謝謝
以后會(huì)出一些偏向跨平臺(tái)技術(shù),原生javascript,TS,Golang等內(nèi)容的文章
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/54021.html
摘要:或者的,都會(huì)對(duì)其進(jìn)行分析。舒適的開發(fā)體驗(yàn),有助于提高我們的開發(fā)效率,優(yōu)化開發(fā)體驗(yàn)也至關(guān)重要組件熱刷新熱刷新自從推出熱刷新后,前端開發(fā)者在開環(huán)境下體驗(yàn)大幅提高。實(shí)現(xiàn)熱調(diào)試后,調(diào)試流程大幅縮短,和普通非直出模式調(diào)試體驗(yàn)保持一致。 showImg(https://segmentfault.com/img/bVbtOR3?w=1177&h=635); webpack,打包所有的資源 不知道不...
摘要:或者的,都會(huì)對(duì)其進(jìn)行分析。舒適的開發(fā)體驗(yàn),有助于提高我們的開發(fā)效率,優(yōu)化開發(fā)體驗(yàn)也至關(guān)重要組件熱刷新熱刷新自從推出熱刷新后,前端開發(fā)者在開環(huán)境下體驗(yàn)大幅提高。實(shí)現(xiàn)熱調(diào)試后,調(diào)試流程大幅縮短,和普通非直出模式調(diào)試體驗(yàn)保持一致。 showImg(https://segmentfault.com/img/bVbtOR3?w=1177&h=635); webpack,打包所有的資源 不知道不...
摘要:根據(jù)依賴關(guān)系,按照配置文件把模塊函數(shù)分組打包成若干個(gè)。會(huì)隨著自身的的修改,而發(fā)生變化。只需要在命令行運(yùn)行時(shí)帶上參數(shù)就搞定一些插件的廢除和替換廢棄了頂替者用屬性變化壓縮優(yōu)化代碼分割,下面詳解還有一些新的插件,。 1. 前端工程化項(xiàng)目打包歷史 前端工程化之前的時(shí)代略過(guò) 1. 半自動(dòng)執(zhí)行腳本來(lái)壓縮合并文件 自從xmlhttprequest被挖掘出來(lái),網(wǎng)頁(yè)能夠和服務(wù)端通訊,js能做的事越來(lái)越多...
摘要:根據(jù)依賴關(guān)系,按照配置文件把模塊函數(shù)分組打包成若干個(gè)。會(huì)隨著自身的的修改,而發(fā)生變化。只需要在命令行運(yùn)行時(shí)帶上參數(shù)就搞定一些插件的廢除和替換廢棄了頂替者用屬性變化壓縮優(yōu)化代碼分割,下面詳解還有一些新的插件,。 1. 前端工程化項(xiàng)目打包歷史 前端工程化之前的時(shí)代略過(guò) 1. 半自動(dòng)執(zhí)行腳本來(lái)壓縮合并文件 自從xmlhttprequest被挖掘出來(lái),網(wǎng)頁(yè)能夠和服務(wù)端通訊,js能做的事越來(lái)越多...
閱讀 2782·2021-10-11 11:08
閱讀 1498·2021-09-30 09:48
閱讀 1059·2021-09-22 15:29
閱讀 1044·2019-08-30 15:54
閱讀 986·2019-08-29 15:19
閱讀 537·2019-08-29 13:12
閱讀 3172·2019-08-26 13:53
閱讀 970·2019-08-26 13:28