Laravel框架中縮小Vue應用的體積

Harlan2016發表於2018-01-25

這兩天寫一個很小的Laravel+Vue的前後端分離應用,前端的程式碼檔案並不多,但webpack打包生成的app.js居然有3M,生成的production版也有1.4M,放到自己的伺服器上,那速度完全無法忍受。於是探究起如何縮小應用體積。

定位導致應用體積大的原因

這裡使用webpack-bundle-analyzer,該外掛可以將內容束顯示為很直觀的樹狀圖,讓你明白構建包中真正引入的內容。藉助這個外掛可以瞭解應用有哪些模組組成,找到不合時宜的存在,然後進行優化。

  1. 安裝:npm install --save-dev webpack-bundle-analyzer
  2. 修改webpack.mix.js,注意Laravel自帶的webpack-mix的配置方式與平常的webpack配置略有不同
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    
    mix.webpackConfig({
      plugins: [
        new BundleAnalyzerPlugin(),
      ],
    }).js('resources/assets/js/app.js', 'public/js')
      .sass('resources/assets/sass/app.scss', 'public/css');
    複製程式碼

修改完之後像平常一樣使用npm run devnpm run production打包應用,打包完成後,會自動開啟http://127.0.0.1:8888來展示結果。

分析結果

解決辦法

從上面的這張圖可以分析出導致應用體積變大的原因就是在於引用很多第三方庫,這些庫會被webpack一起打包到應用內,所以減小應用體積的最有效方法就是將這些庫分離出去,別將它們與我們自己寫的應用程式碼打包到一起。大部分有名的第三方庫在公共CDN都有存放,如bootcss, unpkg等,速度飛快,可以直接引用。

webpack的選項有一個externals,可以指定哪些包不參與打包,即忽略掉相應import XXX from package,而程式碼中依舊可以通過CMD、AMD或window/global全域性的方式訪問。

mix.webpackConfig({
  plugins: [
    new BundleAnalyzerPlugin(),
  ],
  externals: {
    'element-ui': 'Element',
    'axios': 'axios',
    'vue': 'Vue',
    'vuex': 'Vuex',
    'vue-router': 'VueRouter',
    'vue-chartjs': 'VueChartJs',
    'lodash': '_',
  }
}).js('resources/assets/js/app.js', 'public/js')
 .sass('resources/assets/sass/app.scss', 'public/css')
 .styles(['node_modules/element-ui/lib/theme-chalk/index.css'], 'public/css/element-ui.css');
複製程式碼

在blade檔案中加入外鏈

<head>
    ...
    <script src="//cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    <script src="//cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
    <script src="//cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
    <script src="//cdn.bootcss.com/element-ui/2.0.11/index.js"></script>
    <script src="//cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>
    <script src="//cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
    <script src="//unpkg.com/vue-chartjs@2.8.7/dist/vue-chartjs.full.min.js"></script>
    <link href="https://cdn.bootcss.com/element-ui/2.0.11/theme-chalk/index.css" rel="stylesheet">
    ...
</head>
複製程式碼

然後重新打包應用,最後生成的應用只有幾十K了,這大小已經不成問題了

進一步優化

這裡將第三方庫完全改為外鏈,在生成環境內是沒問題,但我們在開發時,並不希望每次都是從網路獲取第三方庫,網路載入始終不會有本地載入來的快,本地載入並不需要考慮大小問題。

我們可以通過使用環境變數來控制要不要使用外鏈。

在.env中新增一個變數MIX_EXTERNAL=true (注意:如果要在App應用裡使用這個變數,這裡必須是以 MIX_ 開頭,參考 Mix 環境變數

Laravel自帶的webpack-mix預設不載入.env檔案,但已經帶有dotenv包,載入即可。

最後的webpack.mix.js檔案如下

let mix = require('laravel-mix');
require('dotenv').config();

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

let externals = undefined;
if (process.env.MIX_EXTERNAL === 'true') {
  externals = {
    'element-ui': 'Element',
    'axios': 'axios',
    'vue': 'Vue',
    'vuex': 'Vuex',
    'vue-router': 'VueRouter',
    'vue-chartjs': 'VueChartJs',
    'lodash': '_',
  };
}

mix.webpackConfig({
  plugins: [
    // new BundleAnalyzerPlugin(),
  ],
  externals,
})
  .js('resources/assets/js/app.js', 'public/js')
  .sass('resources/assets/sass/app.scss', 'public/css')
  .styles(['node_modules/element-ui/lib/theme-chalk/index.css'], 'public/css/element-ui.css');
複製程式碼

blade檔案:

@if (env('MIX_EXTERNAL'))
    <script src="//cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    <script src="//cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
    <script src="//cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
    <script src="//cdn.bootcss.com/element-ui/2.0.11/index.js"></script>
    <script src="//cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>
    <script src="//cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
    <script src="//unpkg.com/vue-chartjs@2.8.7/dist/vue-chartjs.full.min.js"></script>
    <link href="https://cdn.bootcss.com/element-ui/2.0.11/theme-chalk/index.css" rel="stylesheet">
@else
    <link rel="stylesheet" href="css/element-ui.css">
@endif
複製程式碼

應用內有些程式碼需要針對外鏈做區別處理,如ElementUI,如果使用import方式引入,需要加入Vue.use(ElementUI),但如果使用外鏈時,這句會出錯,所以可以加入判斷

if (process.env.MIX_EXTERNAL !== 'true') { // **注意是字串**
  Vue.use(Element);
}
複製程式碼

在app內使用process.env.MIX_XXXXX環境變數時,這些變數實際在打包過程中已經被替換為變數值(就像C裡面的巨集定義一樣),不會在執行求值,也不存在process物件,上面的if語句在打包後變成if (false) Vue.use(Element)

至此優化完畢,只需在.env裡修改一下變數,即可在兩種打包方式間切換,可以愉快地繼續開發了。

參考:

Webpack 打包優化之體積篇

相關文章