單頁應用首次進入項目會獲取一部分數據,以後將JS包分片,走到那塊再去加載那塊的JS。
這樣跨頁面重複的JS,CSS沒必要再去獲取,跨頁面就不會出現進度條。這樣減小了等待時間,提高了用戶體驗,省去了沒必要要的流量。
可是單頁應用也有一個顯著的問題:首次進入的時候,加載的資源太多,白屏時間太長。css
這裏介紹一些經常使用的webpack打包優化解決方案node
webpack有個插件,能夠查看項目一共打了多少包,每一個包的體積,每一個包裏面的包狀況。
首先下載插件react
npm intall webpack-bundle-analyzer --save-dev
同時在webpack.config.js配置jquery
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; webpackConfig.plugins.push(new BundleAnalyzerPlugin());
在package.json中添加命令webpack
「script」: { "analyz": "NODE_ENV=production npm_config_report=true npm run deploy:prod" }
個人webpack是1.X的 webpack 2.X的同窗請看git
而後命令行輸入github
npm run analyz
開始構建,根據項目大小不一樣,時間也不一樣。
過一會輸出結果以下:web
能夠清晰的看到項目中一共有多少個包,包的體積是多少,裏面加載了哪些東西,大小是多少。
這裏演示的是一個乾淨的demo,打的包不多,體積也都很小,在真正項目中可能截圖以下:npm
能夠看到五光十色的十分好看。
這裏面 app.js 和 vendor.js是項目的主文件,其中包含了少部分業務代碼和大部分公共依賴。
剩餘的 number.hash.chunk.js是業務分片的代碼,其中包含了大部分業務代碼和少部分公共依賴。
到這裏,咱們就是用工具完成對項目打包的展現。json
列出了項目中較大的包,剩下的事情就是想辦法如何減少這些包的體積(將一個大包拆成多個小包)。
項目中產生較大的包的緣由能夠從兩個方面去考慮:
對於這兩個問題,咱們能夠從兩個方面着手解決:
這裏面第二項涉及到改動業務代碼,具體的狀況就不一樣了,適合查看 如何優化JS代碼。
咱們來討論第一種方法,在不改動業務代碼的狀況下,如何減少公共依賴。
要知道這些依賴是咱們須要的,不可能排除不引入。
可是他們都是全局依賴的,萬年不變的,可使用瀏覽器本身的緩存來實現不重複加載。
具體作法就是:
將項目中須要的一些公共依賴包,而且不常變更的,單獨取出,再也不每次都打包編譯(如React,Redux等)。
而是經過使用script標籤形式cdn引入,這樣在有緩存的狀況下,這些資源均走緩存,沒必要加載。
具體作法:
這些依賴須要知足必定的條件:
常見的知足這類條件的包有:
另一些包文件如 Antd庫文件,整個包較大,可是每次用什麼取什麼的話,庫文件也會按需加載,沒必要單獨取出。
還有這類庫文件不建議單獨取出,由於裏面可能會有bug,須要更新。
以個人demo爲例:我須要抽離出的文件有 react,react-dom,react-router,redux,react-redux,history。
將這些文件放到cnd上,注意,這些文件要是壓縮版本,而且是用ES5編寫的,不然瀏覽器報錯。
<head> <title>React Starter Kit</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 體積較大的包 --> <script src="https://cdn.bootcss.com/react/15.0.0/react-with-addons.min.js"></script> <script src="https://cdn.bootcss.com/react/15.0.0/react-dom.min.js"></script> <script src="https://cdn.bootcss.com/react-router/3.0.0/ReactRouter.min.js"></script> <script src="https://cdn.bootcss.com/redux/3.6.0/redux.min.js"></script> <script src="https://cdn.bootcss.com/react-redux/5.0.1/react-redux.min.js"></script> <script src="https://cdn.bootcss.com/history/4.5.0/history.min.js"></script> </head>
資源已經引入,接下來須要配置webpack,使其打包的時候不在將這些資源打包。
const webpackConfig = { name: 'client', target: 'web', devtool: config.compiler_devtool, resolve: { root: paths.client(), extensions: ['', '.js', '.jsx', '.json'], }, externals: { 'react': 'React', 'react-dom': 'ReactDOM', 'react-router': 'ReactRouter', 'redux': 'Redux', 'history': 'History' }, module: {}, }
這裏externals告訴webpack那些資源從哪裏尋找。
該對象的鍵表示 require 或者 import 時候的字符串
值表示的當前環境下的變量,好比引入React以後,React被做爲全局對象,webpack就回去尋找React對象。
若是其中有一個找不到,打包就會失敗。
接下來配置vendor,使vendor也不打包該些JS
compiler_vendors : [ // 'react', // 'react-redux', // 'react-router', // 'redux', ],
接下來再次運行 npm run analyz
對比第一次的效果圖,很明顯app.js由原來的625kb減小到了78kb,
原來第二大的vendor.js如今已經很小了。
可是要注意的是,並非包越小越好,越小的包反而越耗費連接。
應該讓你的包裏面的業務代碼佔大多數。
後來被告知,最大的包的體積壓縮以後80k之內就能夠。
dll 全稱是:dynamic link library(動態連接庫)
dll方式也就是經過配置,告訴webpack指定庫在項目中的位置,從而直接引入,不將其打包在內。
上面介紹的方式是將包放到cdn上,build的時候不在引入對應的包;
dll方式就是指定包在項目中,build的時候不在打包對應的包,使用的時候引入。webpack
經過webpack.DllPlugin
與webpack.DllReferencePlugin
兩個內嵌插件實現此功能。
const webpack = require('webpack'); module.exports = { entry: { bundle: [ 'react', 'react-dom', //其餘庫 ], }, output: { path: './build', filename: '[name].js', library: '[name]_library' }, plugins: [ new webpack.DllPlugin({ path: './build/bundle.manifest.json', name: '[name]_library', }) ] };
webpack.DllPlugin選項:
運行:webpack --config webpack.dll.config.js
生成兩個文件,一個是打包好的bundlejs,另一個是bundle.mainifest.json,大體內容以下:
{ "name": "bundle_library", "content": { "./node_modules/react/react.js": 1, "./node_modules/react/lib/React.js": 2, "./node_modules/process/browser.js": 3, "./node_modules/object-assign/index.js": 4, "./node_modules/react/lib/ReactChildren.js": 5, "./node_modules/react/lib/PooledClass.js": 6, "./node_modules/react/lib/reactProdInvariant.js": 7, //其餘引用 }
const webpack = require('webpack'); var path = require('path'); module.exports = { entry: { main: './main.js', }, output: { path: path.join(__dirname, "build"), publicPath: './', filename: '[name].js' }, module: { loaders:[ { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}, { test: /\.jsx?$/, loaders: ['babel-loader?presets[]=es2015&presets[]=react'], include: path.join(__dirname, '.') } ] }, plugins: [ new webpack.DllReferencePlugin({ context: '.', manifest: require("./build/bundle.manifest.json"), }), ] };
webpack.DllReferencePlugin的選項中:
參考連接: