與其說是優化 Vue,不如說主要是在 webpack 打包的配置中作些文章,使得 Vue 編譯後的文件儘量的小。如下介紹本身在項目中進行優化的過程,其中的內容也許並不適合於每一個項目,但總體思路是差很少的。
要想進行優化,首先咱們得清楚問題所在。即:是哪些代碼/依賴包致使最後的編譯文件過大?javascript
這裏,咱們須要使用 webpack-bundle-analyzer
工具。修改 package.json
文件,添加:php
"analyze": "NODE_ENV=production npm_config_report=true npm run build"
而後執行:css
npm run analyze
便會在瀏覽器中打開一個頁面,展現編譯後的文件大小及各部份內容大小。如下是項目在優化前的分析結果:html
從圖中能夠看出,最後編譯出的 vendor.js
文件達到了 5MB,其中主要來自 echarts
。此外,因爲 element-ui
在使用時已經注意到按需加載組件,因此可優化的部分很少;而 lodash
因爲沒有按需加載,因此成爲須要優化的另外一個核心部分。vue
這裏主要是對 lodash
進行優化。當咱們在使用 lodash
時,若是使用:java
import _ from 'lodash' _.get(obj, 'key', 'default_value')
這種方式的話,則在編譯時會默認將 lodash
的所有內容進行編譯打包。node
webpack 的介紹中確實提到了按需加載,但這個概念可能會出現理解上的誤差,下面咱們舉例說明:webpack
// 方法一:會致使加載所有的 lodash 庫 import _ from 'lodash' _.get() // 方法二:只會加載其中的 get 方法 import get from 'lodash/get' get()
即在不添加其餘插件和配置的狀況下,webpack 還作不到如此智能。nginx
想要實如今使用方法一的狀況下,也能按照咱們使用過的方法真正「按需加載」,則須要使用插件並添加配置:web
首先執行:
npm i --save-dev babel-plugin-lodash babel-cli babel-preset-es2015
而後修改 .babelrc
:
{ "plugins": ["lodash"], "presets": ["es2015"] }
以後修改 webpack.prod.conf.js
:
module: { loaders: [{ 'loader': 'babel-loader', 'test': /\.js$/, 'exclude': /node_modules/, 'query': { 'plugins': ['lodash'], 'presets': ['es2015'] } }] }
這以後即可以實現按需加載 lodash
了。從新進行分析,會發現 lodash 部分的大小已經能夠忽略不計了。
對於其餘的,如 Element-UI
之類的第三方庫,若是咱們只使用到了爲數很少的組件,建議查找相應的按需加載插件和配置方式,這樣能夠極大的減小該部分編譯的大小。
當咱們配合 Vue-Router 構建單頁應用時,大量的組件會致使首屏加載緩慢,如官方文檔所言:
當打包構建應用時,Javascript 包會變得很是大,影響頁面加載。若是咱們能把不一樣路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候才加載對應組件,這樣就更加高效了。
只需將原有的:
import Test from '../pages/test' export default new Router({ routes: [ { path: '/test', name: 'test', component: Test } ] });
改成:
const Test = () => import('../pages/test') export default new Router({ routes: [ { path: '/test', name: 'test', component: Test } ] });
注意首行的不一樣。
在實際開發中,可能存在這樣的場景:
在某個組件/文件中須要使用 moment 第三方庫來進行時間處理,但其餘組件根本用不到。
若是咱們這樣引入 moment:
import moment from 'moment' export default { data () { }, mounted () { } }
則該庫會合並在 vendor.js
中,形成首屏加載緩慢。
爲了解決這個問題,咱們能夠改爲如下引入方式:
export default { name: '', beforeCreate () { import('moment').then(module => { this.moment = module; }); }, data () { return { moment: null } } }
這種方式可使得 moment
庫只在該組件使用處引入。注意,這種方式須要考慮「moment
調用時機與 moment
使用的前後問題」。
注:若是該組件是頁面級別的組件,則使用「路由懶加載」中的方法就能夠了。
如上所示,echarts 模塊佔了很大的部分,因爲沒有找到 echarts 按需加載的插件,這裏咱們經過外部引用的方式來減小編譯的大小。
首先,咱們修改 index.html
,從 CDN 中引入 echarts 文件:
<script src="https://cdn.bootcss.com/echarts/3.7.0/echarts.min.js"></script>
注意,若是須要地圖組件,也須要一併引入。
這以後咱們須要刪除全部 import echarts from 'echarts'
的代碼,即再也不經過這種方式引入 echarts。
但問題來了,若是這麼作的話,webpack 在打包的時候會發現 echarts 變量不存在而中止編譯。解決辦法是,咱們須要在 webpack 配置中告知編譯器,對於 echarts 變量使用了引入外部資源的方式。須要修改 webpack.base.config.js
:
module.exports = { externals: { "echarts": "echarts" }, }
這以後咱們即可以直接使用 echarts
這一變量而不會致使編譯錯誤了。
當咱們將全部採用以前方式引入 echarts 的代碼刪除或註釋以後,再次進行分析,會發現編譯大小少了不少。
通過以上兩步,本來 5M 的編譯文件變爲了 1.67 M。
這以後,咱們還能夠根據分析結果,針對性地進行優化。如更換時間庫爲更輕量級的 spacetime
等。
使用 gzip 能夠進一步壓縮文件,使得服務器傳遞給瀏覽器的文件是經由壓縮以後的,待瀏覽器收到以後再解壓縮。要使用這一方式,須要服務器端的支持,這裏以 Nginx 爲例。
在 nginx.conf
中,添加以下配置:
gzip on; gzip_min_length 1k; gzip_buffers 4 16k; #gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/javascript; gzip_vary off;
以後刷新頁面( 注意禁用緩存 ),觀察 js、css 等資源文件的請求中是否包含 Content-Encoding: gzip
,若是存在,則代表 gzip 已成功。
注意,在 gzip_types
中規定了哪些請求類型會使用 gzip 進行壓縮。對於沒有使用 gzip 的資源文件,可將其 Content-type
類型加入 gzip_types
之中。
注意,在以上對打包過程的優化中,受影響的主要是 vendor.js
文件中第三方庫的部分( gzip 方法會影響所有資源文件 )。
若是咱們想繼續進行優化,就須要考慮服務器端渲染了。
Vue 的做用機制實際是使用 js 向 html 中掛載組件,若是咱們可以將這一過程放在服務器端進行,就能夠再也不向瀏覽器傳輸一部分驅動文件,從而進一步減小瀏覽器所需的文件大小。不過這一過程須要服務器的額外支持,有興趣的同窗能夠參考:實例 PK ( Vue服務端渲染 VS Vue 瀏覽器端渲染 )。