摘要:或者的,都會對其進行分析。舒適的開發體驗,有助于提高我們的開發效率,優化開發體驗也至關重要組件熱刷新熱刷新自從推出熱刷新后,前端開發者在開環境下體驗大幅提高。實現熱調試后,調試流程大幅縮短,和普通非直出模式調試體驗保持一致。
webpack,打包所有的資源不知道不覺,webpack已經偷偷更新到4.34版本了,本人決定,這是今年最后一篇寫webpack的文章,除非它更新到版本5,本人今年剩下的時間都會放在Golang和二進制數據操作以及后端的生態上 在看本文前,假設你對webpack有一定了解,如果不了解,可以看看我之前的手寫React和Vue腳手架的文章
手寫優化版React腳手架
手寫Vue的腳手架
前端性能優化不完全手冊
跨平臺webpack配置
都是百星star的優質文章
在此對webpack的性能優化進行幾點聲明:
在部分極度復雜的環境下,需要雙package.json文件,即實行三次打包
在代碼分割時,低于18K的文件沒必要多帶帶打包成一個chunk,http請求次數過多反而影響性能
prerender和PWA互斥,這個問題暫時沒有解決
babel緩存編譯緩存的是索引,即hash值,非常吃內存,每次開發完記得清理內存
babel-polyfill按需加載在某些非常復雜的場景下比較適合
prefetch,preload對首屏優化提升是明顯
代碼分割不管什么技術棧,一定要做,不然就是垃圾項目
多線程編譯對構建速度提升也很明顯
代碼分割配合PWA+預渲染+preload是首屏優化的巔峰,但是pwa無法緩存預渲染的html文件
本文的webpack主要針對React技術棧,實現功能如下:開發模式熱更新
識別JSX文件
識別class組件
代碼混淆壓縮,防止反編譯代碼,加密代碼
配置alias別名,簡化import的長字段
同構直出,SSR的熱調試(基于Node做中間件)
實現javaScript的tree shaking 搖樹優化 刪除掉無用代碼
實現CSS的tree shaking
識別 async / await 和 箭頭函數
react-hot-loader記錄react頁面留存狀態state
PWA功能,熱刷新,安裝后立即接管瀏覽器 離線后仍讓可以訪問網站 還可以在手機上添加網站到桌面使用
preload 預加載資源 prefetch按需請求資源
CSS模塊化,不怕命名沖突
小圖片的base64處理
文件后綴省掉jsx js json等
實現React懶加載,按需加載 , 代碼分割 并且支持服務端渲染
支持less sass stylus等預處理
code spliting 優化首屏加載時間 不讓一個文件體積過大
加入dns-prefetch和preload預請求必要的資源,加快首屏渲染(京東策略)
加入prerender,極大加快首屏渲染速度
提取公共代碼,打包成一個chunk
每個chunk有對應的chunkhash,每個文件有對應的contenthash,方便瀏覽器區別緩存
圖片壓縮
CSS壓縮
增加CSS前綴 兼容各種瀏覽器
對于各種不同文件打包輸出指定文件夾下
緩存babel的編譯結果,加快編譯速度
每個入口文件,對應一個chunk,打包出來后對應一個文件 也是code spliting
刪除HTML文件的注釋等無用內容
每次編譯刪除舊的打包代碼
將CSS文件多帶帶抽取出來
讓babel不僅緩存編譯結果,還在第一次編譯后開啟多線程編譯,極大加快構建速度
等等....
本質上,webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle webpack打包原理識別入口文件
通過逐層識別模塊依賴。(Commonjs、amd或者es6的import,webpack都會對其進行分析。來獲取代碼的依賴)
webpack做的就是分析代碼。轉換代碼,編譯代碼,輸出代碼
最終形成打包后的代碼
這些都是webpack的一些基礎知識,對于理解webpack的工作機制很有幫助。
舒適的開發體驗,有助于提高我們的開發效率,優化開發體驗也至關重要組件熱刷新、CSS熱刷新
自從webpack推出熱刷新后,前端開發者在開環境下體驗大幅提高。
沒有熱刷新能力,我們修改一個組件后
加入熱刷新后
主要看一下React技術棧,如何在構建中接入熱刷新無論什么技術棧,都需要在dev模式下加上 webpack.HotModuleReplacementPlugin插件
devServer: { contentBase: "../build", open: true, port: 5000, hot: true },
注:也可以使用react-hot-loader來實現,具體參考官方文檔
在開發模式下也要代碼分割,加快打開頁面速度optimization: { runtimeChunk: true, splitChunks: { chunks: "all", minSize: 10000, // 提高緩存利用率,這需要在http2/spdy maxSize: 0,//沒有限制 minChunks: 3,// 共享最少的chunk數,使用次數超過這個值才會被提取 maxAsyncRequests: 5,//最多的異步chunk數 maxInitialRequests: 5,// 最多的同步chunks數 automaticNameDelimiter: "~",// 多頁面共用chunk命名分隔符 name: true, cacheGroups: {// 聲明的公共chunk vendor: { // 過濾需要打入的模塊 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,// 確定模塊打入的優先級 reuseExistingChunk: true,// 使用復用已經存在的模塊 }, 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, }, }, } }簡要解釋上面這段配置
將node_modules共用部分打入vendor.js bundle中;
將react全家桶打入react.js bundle中;
如果項目依賴了antd,那么將antd打入多帶帶的bundle中;(其實不用這樣,可以看我下面的babel配置,性能更高)
最后剩下的業務模塊超過3次引用的公共模塊,將自動提取公共塊
注意 上面的配置只是為了給大家看,其實這樣配置代碼分割,性能更高
optimization: { runtimeChunk: true, splitChunks: { chunks: "all", } }react-hot-loader記錄react頁面留存狀態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、定義渲染函數 ReactDOM.render(, document.getElementById("app") ); }
然后你再刷新試試React的按需加載,附帶代碼分割功能 ,每個按需加載的組件打包后都會被多帶帶分割成一個文件
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識別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文件壓縮,自動將入門的js文件注入html中,優化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同構直出熱調試
, 采用 webpack watch+nodemon 結合的模式實現對SSR熱調試的支持。node 服務需要的html/js通過webpack插件動態輸出,當nodemon檢測到變化后將自動重啟,html文件中的靜態資源全部替換為dev模式下的資源,并保持socket連接自動更新頁面。
實現熱調試后,調試流程大幅縮短,和普通非直出模式調試體驗保持一致。下面是SSR熱調試的流程圖:
加入 babel-loader 還有 解析JSX ES6語法的 babel preset@babel/preset-react解析 jsx語法
@babel/preset-env解析es6語法
@babel/plugin-syntax-dynamic-import解析react-loadable的import按需加載,附帶code spliting功能
["import", { libraryName: "antd-mobile", style: true }], Antd-mobile的按需加載
{ loader: "babel-loader", options: { //jsx語法 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 }], //識別class組件 ["@babel/plugin-proposal-class-properties", { "loose": true }], ], cacheDirectory: true }, }
特別提示,如果電腦性能不高,不建議開啟babel緩存索引,非常吃內存,記得每次開發完了清理內存加入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:[ // 清除無用 css new PurifyCSS({ paths: glob.sync([ // 要做 CSS Tree Shaking 的路徑文件 path.resolve(__dirname, "./src/*.html"), // 請注意,我們同樣需要對 html 文件進行 tree shaking path.resolve(__dirname, "./src/*.js") ]) }) ]對小圖片進行base64處理,減少http請求數量,并對輸出的文件統一打包處理
{ 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這個技術其實要想真正用好,還是需要下點功夫,它有它的生命周期,以及它在瀏覽器中熱更新帶來的副作用等,需要認真研究。可以參考百度的lavas框架發展歷史~
const WorkboxPlugin = require("workbox-webpack-plugin") new WorkboxPlugin.GenerateSW({ clientsClaim: true, //讓瀏覽器立即servece worker被接管 skipWaiting: true, // 更新sw文件后,立即插隊到最前面 importWorkboxFrom: "local", include: [/.js$/, /.css$/, /.html$/,/.jpg/,/.jpeg/,/.svg/,/.webp/,/.png/], }),加入預加載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"] }),加入預渲染
const PrerenderSPAPlugin = require("prerender-spa-plugin") new PrerenderSPAPlugin({ routes: ["/","/home","/shop"], staticDir: resolve(__dirname, "../dist"), }),
我這套webpack配置,無論多復雜的環境,都是可以搞定的
webpack真的非常非常重要,如果用不好,就永遠是個初級前端
只要webpack不更新到5,以后就不出webpack的文章了
webpack4大結局,謝謝
以后會出一些偏向跨平臺技術,原生javascript,TS,Golang等內容的文章
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/54021.html
摘要:或者的,都會對其進行分析。舒適的開發體驗,有助于提高我們的開發效率,優化開發體驗也至關重要組件熱刷新熱刷新自從推出熱刷新后,前端開發者在開環境下體驗大幅提高。實現熱調試后,調試流程大幅縮短,和普通非直出模式調試體驗保持一致。 showImg(https://segmentfault.com/img/bVbtOR3?w=1177&h=635); webpack,打包所有的資源 不知道不...
摘要:或者的,都會對其進行分析。舒適的開發體驗,有助于提高我們的開發效率,優化開發體驗也至關重要組件熱刷新熱刷新自從推出熱刷新后,前端開發者在開環境下體驗大幅提高。實現熱調試后,調試流程大幅縮短,和普通非直出模式調試體驗保持一致。 showImg(https://segmentfault.com/img/bVbtOR3?w=1177&h=635); webpack,打包所有的資源 不知道不...
摘要:根據依賴關系,按照配置文件把模塊函數分組打包成若干個。會隨著自身的的修改,而發生變化。只需要在命令行運行時帶上參數就搞定一些插件的廢除和替換廢棄了頂替者用屬性變化壓縮優化代碼分割,下面詳解還有一些新的插件,。 1. 前端工程化項目打包歷史 前端工程化之前的時代略過 1. 半自動執行腳本來壓縮合并文件 自從xmlhttprequest被挖掘出來,網頁能夠和服務端通訊,js能做的事越來越多...
摘要:根據依賴關系,按照配置文件把模塊函數分組打包成若干個。會隨著自身的的修改,而發生變化。只需要在命令行運行時帶上參數就搞定一些插件的廢除和替換廢棄了頂替者用屬性變化壓縮優化代碼分割,下面詳解還有一些新的插件,。 1. 前端工程化項目打包歷史 前端工程化之前的時代略過 1. 半自動執行腳本來壓縮合并文件 自從xmlhttprequest被挖掘出來,網頁能夠和服務端通訊,js能做的事越來越多...
閱讀 2771·2021-10-11 11:08
閱讀 1489·2021-09-30 09:48
閱讀 1049·2021-09-22 15:29
閱讀 1037·2019-08-30 15:54
閱讀 976·2019-08-29 15:19
閱讀 527·2019-08-29 13:12
閱讀 3161·2019-08-26 13:53
閱讀 957·2019-08-26 13:28