vue+webpack4.0 生產環境手動一步一步配置 (持續更新中......)

vue-webpack

介紹 vue+webpack 手動搭建過程,熟悉 webpack 的配置,loader&插件的使用

介紹

目標:熟悉 webpack 的配置,loader&插件的使用

通常新建 vue 項目都使用 vue-cli 腳手架,其實對於 webpack 的配置是一知半解,當需要升級 webpack 或者優化項目配置時,就顯得很無力。通過手動搭建 webpack 可以對 webpack 有更深入的瞭解,當我們使用其他模塊管理器時(eg:rollup,gulp),也不會那麼生疏。

關於 dependencies 和 devDependencies

通過 NODE_ENV=developement 或 NODE_ENV=production 指定開發還是生產環境

devDependencies 是只會在開發環境下依賴的模塊,生產環境不會被打入包內。

而 dependencies 依賴的包不僅開發環境能使用,生產環境也能使用。其實這句話是重點,按照這個觀念很容易決定安裝模塊時是使用--save 還是--save-dev。

項目包版本

項目地址

    "dependencies": {
        "@xunlei/vue-lazy-component": "^1.1.3",
        "echarts": "^4.2.1",
        "element-resize-event": "^3.0.3",
        "element-ui": "^2.7.2",
        "file-loader": "^4.0.0",
        "html-loader": "^0.5.5",
        "html-webpack-plugin": "^3.2.0",
        "intersection-observer": "^0.7.0",
        "lodash": "^4.17.11",
        "url-loader": "^2.0.0",
        "vue": "^2.6.10",
        "webpack": "^4.34.0"
    }

遇到的配置錯誤

1.vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.

解決辦法:Vue-loader 在 15.*之後的版本都是 vue-loader 的使用都是需要伴生 VueLoaderPlugin 的

2.Entrypoint undefined = index.html 代碼不報錯但是頁面白版

解決辦法:webpcak.config.js 中

new HTMLPlugin({
  template: 'index.html'
}),

3.Child html-webpack-plugin for "index.html": Entrypoint undefined = index.html

解決辦法:webpcak.config.js 中

  resolve: {
    alias: {
      vue: 'vue/dist/vue.js'
    }
  },

這裏涉及到一個小知識點 編譯時&運行時

4.運行時 + 編譯器 vs. 只包含運行時

https://cn.vuejs.org/v2/guide...

因爲在 Vue.js 2.0 中,最終渲染都是通過 render 函數,如果寫 template 屬性,則需要編譯成 render 函數,那麼這個編譯過程會發生運行時,所以需要帶有編譯器的版本。很顯然,這個編譯過程對性能會有一定損耗,所以通常我們更推薦使用 Runtime-Only 的 Vue.js。

只有以下情況會用到 compiler: 1.有指定 template; 2.沒指定 template,也沒指定 render(這時候使用的就是被掛載元素的 outerHtml)。

所以,沒有使用到 compiler 的情況只有:沒有指定 template,但指定了 render。

有時會遇到這樣的錯誤:[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

以上提到,解決這個問題有兩種方式,但大多會選擇後者,也就是使用全功能的 vue(runtime+compiler),這個版本的 vue 文件相比僅包含 runtime 的版本體積要大,而且運行時的 compiler 轉換會消耗性能,compiler 過程其實可以放到構建過程中進行。總結就是,如果可以的話,儘量使用 runtime 版的 vue 文件。

5. It's no longer allowed to omit the '-loader' suffix when using loaders.

Webpack 新版本要求配置 module 中的 loader 不能縮寫,也就是

loader:"json-loader"

中的-loader 必須要寫。

(在網上找配置時需要注意)

搭建步驟

1.生成 package.json

npm init

2.安裝依賴

npm i webpack vue vue-loader
npm i css-loader vue-template-compiler

3.文件目錄

新建 app.vue
<template>
  <div id="test">{{text}}</div>
</template>

<script>
  export default {
    data() {
      return {
        text: 'abc'
      }
    }
  }
</script>
<style>
  #test {
    color: red;
  }
</style>
新建入口文件 index.js
import Vue from 'vue'
import App from './app.vue'

// Runtime Only
// new Vue({
//   render: h => h(App) //h就是vue中的createApp參數
// }).$mount('#app') //將app掛載到body下的div上

// 會用到 compiler 所以使用全功能的 vue
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})
新建 webpack.config.js 配置
const path = require('path') //nodeJs的基本包
module.exports = {
  //path.join(__dirname, 'src/index.js')中__dirname表示當前文件的路徑,path.join就是將當前文件的路徑跟'src/index.js'拼接起來,形成一個絕對路徑
  entry: path.join(__dirname, 'src/index.js'),
  //輸出文件,取名爲bundle.js,路徑爲dist文件夾
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader'
      }
    ]
  }
}
修改 package.json 文件
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js"
  }
webpack 配置項目加載各種靜態資源及 css 預處理器

其中 index.js 入口文件如下:

import './assets/css/global.css'

其中 webpack.config.js 如下:

  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.html$/i,
        loader: 'html-loader'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
      },
      {
        test: /\.(gif|jpg|jpeg|png|svg)$/,
        use: [
          {
            loader: 'url-loader', //能夠將圖片轉成base64代碼直接寫在js裏面,依賴file-loader,所以在安裝的時候不僅要裝url-loader還要裝file-loader
            options: {
              limit: 1024, //如果文件大小小於1024字節,就會轉義成base64,否則仍然是圖片
              name: '[name]-aaa.[ext]' //輸出文件的名字,name就是原先圖片的名字,-aaa是自己家的字段
            }
          }
        ]
      }
    ]
  },
webpack-dev-server 的配置和使用
  • 先在 package.json 中的 script 中加一個命令"dev":“webpack-dev-server --config webpack.config.js”
"scripts": {
  "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
  "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
}

cross-env 能跨平臺地設置及使用環境變量

npm 安裝方式

npm i --save-dev cross-env

在 npm 腳本(多是 package.json)裏這麼配置

"scripts": {
  "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
}

運行 npm run build,這樣 NODE_ENV 便設置成功,無需擔心跨平臺問題

  • 修改 webpack 設置,來專門適應我們的 webpack-dev-server 的開發模式
  • 添加 webpack 的編譯目標 target 爲 web
  • 添加變量 isDev,用於讀取是否爲 development 環境。
  • 需要一個 html,去容納我們的 js 文件,不然沒有 html,我們的項目是不能在瀏覽器中顯示的html-webpack-plugin 詳細用法
  • devServer 中,還有其他的配置
    1.historyFallback 對於非定義的路由的處理
    2.open: true,//啓動的時候,自動打開瀏覽器
    3.hot: true,//熱加載,不需要刷新頁面就能加載出來
  • 當使用熱加載時還需要添加插件
    1.new webpack.HotModuleReplacementPlugin()
    2.new webpack.NoEmitOnErrorsPlugin()//減少我們不需要的信息的展示
  • source-map 的配置
    config.devtool = '#cheap-module-eval-source-map';

所以,整個 webpack 的代碼爲:

const path = require('path')
const HTMLPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const webpack = require('webpack')
const isDev = process.env.NODE_ENV === 'development'

const config = {
  target: 'web',
  entry: path.join(__dirname, 'src/index.js'),
  output: {
    filename: 'dist/bundle.js',
    path: path.join(__dirname, 'dist')
  },
  resolve: {
    alias: {
      vue$: 'vue/dist/vue.esm.js' // 用 webpack 1 時需用 'vue/dist/vue.common.js'
    }
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.html$/i,
        loader: 'html-loader'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
      },
      {
        test: /\.(gif|jpg|jpeg|png|svg)$/,
        use: [
          {
            loader: 'url-loader', //能夠將圖片轉成base64代碼直接寫在js裏面,依賴file-loader,所以在安裝的時候不僅要裝url-loader還要裝file-loader
            options: {
              limit: 1024, //如果文件大小小於1024字節,就會轉義成base64,否則仍然是圖片
              name: '[name]-aaa.[ext]' //輸出文件的名字,name就是原先圖片的名字,-aaa是自己家的字段
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: isDev ? '"development"' : '"production"'
      }
    }), //一般vue、react等框架都要用到這個插件。
    //在這裏定義了,在我們的js代碼中是可以引用到的。
    //現在,veu/react這類框架會根據環境去區分打包,打包後的dist在開發環境中是比較大的,因爲有很多類似錯誤的信息們可以幫助我們開發人員開發,而生產環境是比較小的,沒有繁多的錯誤信息,我們也不希望錯誤信息給用戶看,所以就沒必要把錯誤信息打包進去了
    //爲什麼單引號裏面還要雙引號?因爲如果沒有的話,調用的時候,就成了process.env.NODE_ENV = development,這時候development就成了一個變量,所以需要寫上雙引號
    new HTMLPlugin({
      template: 'index.html'
    }),
    new VueLoaderPlugin()
  ]
}

if (isDev) {
  config.devtool = '#cheap-module-eval-source-map' //幫助我們在頁面上調試我們的代碼的,並且有很多種source-map的映射方式,不同映射方式有不同的優缺點,這裏寫的只是其中一種,這個值,可以讓你在瀏覽器看到源碼
  config.devServer = {
    port: 8088,
    host: 'localhost', //可以通過localhost,127.0.0.1,本機的內網IP進行訪問(IP的話,就可以在別人的電腦上訪問)
    overlay: {
      error: true //如果編譯有錯誤,就直接顯示在網頁上
    },
    open: true,
    hot: true //熱加載,不需要刷新頁面就能加載出來
  }
  config.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin() //減少我們不需要的信息的展示
  )
}
module.exports = config

webpack 指定 mode

這部分厲害了,看完文檔就知道爲什麼使用 vue-cli 腳手架 vue init webpack [項目名]生成的 webpack 配置文件包含

;-build | -webpack.base.conf.js | -webpack.base.dev.js | -webpack.base.prod.js

配置

development(開發環境) 和 production(生產環境) 這兩個環境下的構建目標存在着巨大差異。

  • 開發環境中:強大的 source map 和一個有着 live reloading(實時重新加載) 或 hot module replacement(熱模塊替換) 能力的 localhost server
  • 生產環境:關注點在於壓縮 bundle、更輕量的 source map、資源優化等,通過這些優化方式改善加載時間。

我們先從安裝 webpack-merge 開始,並將已經成型的那些代碼進行分離:

npm install --save-dev webpack-merge
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js

具體使用方式查看文檔webpack 指定 mode

生產環境性能優化

1.html-webpack-plugin

該插件將爲你生成一個 HTML5 文件, 其中包括使用 script 標籤的 body 中的所有 webpack 包。

插件配置項地址

new HTMLPlugin({
      template: 'index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        useShortDoctype: true
      }
    }),

針對生成的 html 移除註釋、刪除空行、html 壓縮等操作

2.mini-css-extract-plugin

將 CSS 提取爲獨立的文件的插件

MiniCssExtractPlugin

查看文件中 webpack.config.js 配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // 類似 webpackOptions.output裏面的配置 可以忽略
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
  ],
  module: {
    rules: [
            {
        test: /\.(sa|sc|c)ss$/,
        use: [
          isDev ? 'sass-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  }
}

測試 css 被單獨提取,文件大小爲 main.css 264K

階段進行壓縮

webpack5 可能會內置 CSS 壓縮器,webpack4 需要自己使用壓縮器,可以使用 optimize-css-assets-webpack-plugin 插件。 設置 optimization.minimizer 覆蓋 webpack 默認提供的,確保也指定一個 JS 壓縮器

  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourcMap: true
      }),
      new OptimizeCSSAssetsPlugin({}),
    ],
  }

測試 css 被單獨提取,文件大小爲 main.css 219K 比壓縮前減少 45K

3.uglifyjs-webpack-plugin

遇到的錯誤

ERROR in bundle.js from UglifyJs
Unexpected token: punc «(» src/app.vue:21,0

解決方法: 配置 babel

未使用前 bundle.js 1937KB 使用後壓縮到 752KB

4.CompressionPlugin

當我們的項目越來越龐大是時候 會發現 即使做了 code split 代碼壓縮 動態加載 等等一系列優化之後 頁面的響應速度依舊很慢

這個時候時候可以使用 compression-webpack-plugin 這個插件

new CompressionPlugin({
  "filename": "[path].gz[query]",
  "test": new RegExp(
    "\\.(js|css)$" //壓縮 js 與 css
  ),
  "threshold": 500, // 當文件超過限制大小 使用gzip
  "minRatio": 0.8,
  "algorithm": "gzip"
})

該插件的作用是 在超過限定的文件大小的 時候會生成一個跟文件同名的 gz 包,這個時候我們需要在改下 nginx 的配置 啓用 gzip 壓縮並 開啓 gzip_static

vendors~main.772d21ef3139973e4cab.bundle.js.gz 可以將原來 819KB 壓縮到 220KB 壓縮力度還是非常大的

參考(特別感謝)

  1. 你真的理解 devDependencies 和 dependencies 區別嗎?
  2. webpack 中文文檔
  3. vue 編譯時運行時
  4. 瞭解 vue 裏的 Runtime Only 和 Runtime+Compiler
  5. It’s no longer allowed to omit the ‘-loader’ suffix
  6. vue 項目從 0 搭建(webpack 手動搭建)
  7. webpack4 mini-css-extract-plugin
  8. MiniCssExtractPlugin 需要添加 babel
  9. webpack-bundle-analyzer 打包文件分析工具
  10. SplitChunksPlugin
  11. Webpack SplitChunksPlugin 的三種模式
  12. 基於 vue2.x 的 webpack4 配置(生產環境~)
  13. vue 進行 gzip 壓縮和服務器如何開啓 gzip)
  14. web 應用性能優化之 nginx + compression-webpack-plugin
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章