webpack
,打包全部的資源
webpack
已經偷偷更新到4.34
版本了,本人決定,這是今年最後一篇寫webpack
的文章,除非它更新到版本5,本人今年剩下的時間都會放在Golang
和二進制數據操做以及後端的生態上webpack
有必定了解,若是不瞭解,能夠看看我以前的手寫React
和Vue
腳手架的文章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
等less sass stylus
等預處理code spliting
優化首屏加載時間 不讓一個文件體積過大dns-prefetch
和preload
預請求必要的資源,加快首屏渲染(京東策略)prerender
,極大加快首屏渲染速度chunk
chunk
有對應的chunkhash
,每一個文件有對應的contenthash
,方便瀏覽器區別緩存CSS
壓縮CSS
前綴 兼容各類瀏覽器babel
的編譯結果,加快編譯速度chunk
,打包出來後對應一個文件 也是code spliting
HTML
文件的註釋等無用內容CSS
文件單獨抽取出來webpack
是一個現代 JavaScript
應用程序的靜態模塊打包器(module bundler
)。當 webpack
處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph
),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle
webpack
打包原理Commonjs、amd
或者es6的import,webpack
都會對其進行分析。來獲取代碼的依賴)webpack
作的就是分析代碼。轉換代碼,編譯代碼,輸出代碼webpack
的一些基礎知識,對於理解webpack
的工做機制頗有幫助。
React
技術棧,如何在構建中接入熱刷新dev
模式下加上 webpack.HotModuleReplacementPlugin
插件devServer: { contentBase: '../build', open: true, port: 5000, hot: true },
注:也可使用react-hot-loader來實現,具體參考官方文檔javascript
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, }, }, } }
vendor.js bundle
中;react.js bundle
中;antd
,那麼將antd
打入單獨的bundle
中;(其實不用這樣,能夠看我下面的babel
配置,性能更高)注意 上面的配置只是爲了給你們看,其實這樣配置代碼分割,性能更高
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";-------------------一、首先引入AppContainre import { BrowserRouter } from "react-router-dom"; import Router from "./router"; /*初始化*/ renderWithHotReload(Router);-------------------二、初始化 /*熱更新*/ if (module.hot) {-------------------三、熱更新操做 module.hot.accept("./router/index.js", () => { const Router = require("./router/index.js").default; renderWithHotReload(Router); }); } function renderWithHotReload(Router) {-------------------四、定義渲染函數 ReactDOM.render( <AppContainer> <BrowserRouter> <Router /> </BrowserRouter> </AppContainer>, 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 ( <div> <div>這即將按需加載</div> <LoadableComponent /> </div> ) } } export default Assets
html-loader
識別html
文件{ 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
鏈接自動更新頁面。
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
等內容的文章