【譯文】四種使用webpack提高Vue應用的方式

做者:孫輝,美團金融前端團隊成員。15年畢業加入美團,相信技術,更相信技術只是大千世界裏知識的一種,我的博客: sunyuhui.comhtml

譯者注:本篇文章所提到的幾個措施你們可能都曾經在項目裏用過,可是就如做者所言:你只是在用,並不知道爲何用,本文最大的價值在於提供了系統的優化方案並解釋了緣由,原文:4 Ways To Boost Your Vue.js App With Webpack前端

webpack是開發Vue單頁應用必不可少的工具,它能管理複雜的構建步驟,而且優化你的應用大小和性能, 使你的開發工做流更加簡單。vue

在這篇文章中,我將解釋使用webpack提高你的Vue應用的4種方式,包括:node

  1. 單文件組件
  2. 優化Vue構建過程
  3. 瀏覽器緩存管理
  4. 代碼分離

關於vue-cli

若是你在使用 vue-cli 提供的模板來構建你的應用,那麼webpack的相關配置已經提供好了,這些配置已經通過很好地優化,我也給不了你其餘的優化建議了。webpack

不過由於配置是開箱即用的,因此你頗有可能並不知道這些配置真正在作什麼,因爲 vue-cli 裏提供了我將要討論的優化措施,因此,你能夠把這篇文章當作模板裏webpack配置的概述。web

1.單文件組件

Vue的特色之一就是使用了基於HTML的模板組件,這帶來了一個本質問題:要麼HTML標記使用醜陋的JavaScript字符串,要麼將模板內容和組件定義寫在不一樣的文件中。這帶來了一些困難。vue-router

Vue提供了一種叫作Single File Components(SFCs)的方式來解決這個問題,將模板、組件定義、CSS寫在一個 .vue 文件裏。vue-cli

MyComponent.vue瀏覽器

<template>
  <div id="my-component">...</div>
</template>
<script>
  export default {...}
</script>
<style>
  #my-component {...}
</style>複製代碼

Vue-loader 使得 SFCs成爲可能,這個 webpack loader 將SFCs分隔成不一樣語言塊,而後輸出到合適的loader,例如 script塊 輸出到babel-loader, 模板塊輸出到Vue本身的vue-template-loader,該loader可以將模板轉換成JavaScript的render函數。緩存

vue-loader的最終輸出是一個將要包含在 Webpack bundle 中的JavaScript模塊。

典型的vue-loader配置以下所示:

module: {
  rules: [
    {
      test: /\.vue$/,
      loader: 'vue-loader',
      options: {
        loaders: {
          // Override the default loaders
        }
      }
    },
  ]
}複製代碼

2.優化Vue構建

運行時構建

若是在你的應用中僅僅使用 render函數,不須要HTML模板,那你不須要使用Vue的模板編譯器,經過去掉模板編譯器,可讓你在Webpack構建過程當中,減小bundle的體積。

記住單文件組件在開發模式中使用了預編譯

Vue的運行時構建版本包含了Vue的全部特色,除了模板編譯器,被稱爲vue.runtime.js,體積比全功能版本小了20KB,因此這值得你嘗試一下。

默認狀況下使用的是運行時構建版本,因此當你使用 import vue from 'vue' 來引用Vue的時候,你獲得的是運行時構建版本,不過你能經過 alias 配置項來改變。

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js' // Use the full build
  }
}複製代碼

譯者注:在Vue模塊中包含8個文件,各文件的區別能夠參考:官方文檔,在須要使用完整版時使用了運行時版本會報warn:

You are using the runtime-only build of Vue where the template option is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

production環境中去掉 warn 和 error 信息

另一個減小Vue構建體積的方式是在 production環境中去掉全部 error 和 warn信息,這些沒必要要的代碼致使你打包後的體積膨脹,並且增長了運行時耗時,你最好避免這些消耗。

若是你調試Vue的源代碼,你會發現這些提示信息都是經過環境變量 process.env.NODE_ENV 的值來判斷的,例如:

if (process.env.NODE_ENV !== 'production') {
  warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm);
}複製代碼

若是 process.env.NODE_ENV 的值設置成 production ,那麼提示信息在構架過程當中就會自動被剔出去。

能夠經過使用 DefinePlugin 去設置 process.env.NODE_ENV 的值,同時使用 UglifyJsPlugin 去減少代碼體積而且去掉不須要的代碼塊。

if (process.env.NODE_ENV === 'production') {
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin()
  ])
}複製代碼

譯者注:實際項目中使用的更多的方式是 production環境 和 其餘環境 使用不一樣的webpack配置文件.

3.瀏覽器緩存管理

瀏覽器可以緩存你的站點文件,只有在你本地沒有副本時或者副本已通過期時纔會從新下載。

若是你全部的代碼都在一個文件裏,那一個微小的改動也會致使整個文件的下載,理想狀況下,你想要你的用戶儘量少的下載文件,因此將頻繁改動的代碼和不怎麼改動的代碼分開是很是明智的。

Vendor file

Common Chunks插件能把你的Vendor代碼(例如Vue.js這些不會常常改動的依賴包)和應用代碼(每次開發過程當中都會改動的代碼)分離開。

你能配置插件檢查一個依賴是否來自於node_modules,若是是,那就打包到vendor.js文件。

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module) {
    return module.context && module.context.indexOf('node_modules') !== -1;
  }
})複製代碼

這麼作以後,在構建後的輸出文件中,將有兩個獨立的文件,可以分別被瀏覽器緩存。

<script src="vendor.js" charset="utf-8"></script>
<script src="app.js" charset="utf-8"></script>複製代碼

指紋

當構建後的文件改動了,咱們該怎麼丟棄緩存呢?

默認狀況下,只有當一個緩存文件過時,或者用戶手動清除緩存,瀏覽器纔會從新從服務器請求文件,若是服務器提示文件已經改動,那文件纔會從新被下載(若是返回304則不會)。

爲了不沒必要要的請求,咱們能夠在每次文件內容改動時,改變文件的名字,從而強制瀏覽器從新下載,經過在文件名稱後面添加一個"指紋":hash,咱們能夠很是容易達到這個目的。

cache control
cache control

Common Chunks插件生成"chunkhash",能隨着文件的改動而更新,Webpack能在輸出時,將這個hash值添加到文件名稱末尾。

output: {
  filename: '[name].[chunkhash].js'
},複製代碼

這樣作的話,你會發現輸出的文件名稱相似於app.3b80b7c17398c31e4705.js

譯者注:chunkhash並不須要Common Chunks生成,而是webpack自動生成的,並且據官方文檔Common Chunks是沒法生成chunkhash的,做者在這裏這麼寫讓我摸不着頭腦, 另外,還有一種添加hash值的方式是使用

output: {
  filename: '[name].[hash].js'
},複製代碼

其中的區別主要在於chunkhash是基於內容生成的hash值,而hash值是基於模塊標識(webpack打包時每一個模塊都有一個惟一標識),hash值的主要問題在於任何一個文件更新以後都會更新hash值,致使那些內容沒有更新的文件的文件名也更新了,須要從新下載。具體區別可參考:webpack配置:緩存

自動插入構建文件

固然了,增長了hash值以後,你就必需要在index文件裏更新你的引用,不然瀏覽器是不會知道的。

<script src="app.3b80b7c17398c31e4705.js"></script>複製代碼

手動去作這件事將是一件很是沉悶無聊的事情,可使用HTML Webpack Plugin來作這件事。這個插件能在構建過程當中自動在你的HTML文件裏插入對構建文件的引用。

先來把構建文件中的引用去掉

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>test-6</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files should go here, but will be auto injected -->
  </body>
</html>複製代碼

而後在Webpack配置裏增長HTML Webpack Plugin的配置。

new HtmlWebpackPlugin({
  filename: 'index.html'
  template: 'index.html',
  inject: true,
  chunksSortMode: 'dependency'
})複製代碼

如今帶有hash值的構建文件將會自動增長到index文件,同時,你的index.html文件也會被包含在輸出文件裏,因此你可能須要將這一點告訴服務器。

4.代碼分離

默認狀況下,Webpack將把你的全部應用代碼輸出到一個大的構建文件中,可是若是你有多個頁面,分隔代碼將每一個獨立的頁面輸出到不一樣的文件中,只有在須要的時候纔去加載。

Webpack有個功能 "code splitting",能夠用來作這件事,在Vue.js中須要使用async components,配合Vue Router使用更簡單。

Async componets

相比以一個定義對象做爲第二個參數,Async components第二個參數是一個Promise函數,該函數將一個對象resolve,例如:

Vue.component('async-component', function (resolve, reject) {
  setTimeout(() => {
    resolve({
      // Component definition including props, methods etc.
    });
  }, 1000)
})複製代碼

Vue只有在這個組件須要被渲染時,纔會調用這個函數,同時也會爲之後的預渲染緩存返回結果。

若是咱們將每一個頁面都當作一個組件,而且將定義對象放在服務器端,那在代碼分離的路上,咱們已經走了一半了。

require

爲了從服務器上加載你的異步組件代碼。使用Webpack的require語法,這將通知Webpack將async-component打包到一個分隔的bundle,更妙的是,Webpack將使用AJAX處理bundle的加載,因此你的代碼能夠像下面這樣簡單:

Vue.component('async-component', function (resolve) {
  require(['./AsyncComponent.vue'], resolve)
});複製代碼

懶加載

在Vue.js應用中,vue-router是一個典型模塊,你能夠用它來將SPA轉換成多頁應用,懶加載是利用Vue和Webpack實現代碼分離的官方推薦方式。

const HomePage = resolve => require(['./HomePage.vue'], resolve);

const rounter = new VueRouter({
  routes: [
    {
      path: '/',
      name: 'HomePage',
      component: HomePage
    }
  ]
})複製代碼

最後,團隊爲了招聘方便,整了個公衆號,主要是一些招聘信息,團隊信息,全部的技術文章在公衆號裏也能夠看到,對了,若是你想去美團其餘團隊,咱們也能夠幫你內推哦 ~

二維碼
二維碼

done

相關文章
相關標籤/搜索