基於 Webpack4 搭建 Vue 開發環境

Russ_Zhong發表於2018-10-14

自從工作之後,就已經很久沒有寫過部落格了。時間被分割得比較碎,積累了一段時間的學習成果,才寫下了這篇部落格。

之前有寫過 Webpack4 的文章,但是都比較偏入門,唯一的一篇實戰篇 —— 基於Webpack搭建React開發環境,也是比較簡單的,沒有涉及到 CSS 抽取,第三方庫打包等功能,這篇文章相對而言比較深入。但由於作者水平有限,難免存在謬誤之處,歡迎大家指正。

還有沒入門的童鞋可以參考我之前的文章:

一、初始化專案

在命令列中敲入如下命令:

mkdir Webpack-Vue && cd Webpack-Vue && npm init -y
複製程式碼

然後你就可以在你的當前路徑下看到一個叫 Webpack-Vue 的資料夾,裡面有一個包含預設資訊的 package.json 檔案,開啟並修改這個檔案的一些內容。

然後我們在專案資料夾中建立以下幾個資料夾:

  • dist
  • src、src/components
  • build

Linux 下可以輸入一下命令進行快速建立:

mkdir src src/components dist build -p
複製程式碼

其中,dist 用於存放 Webpack 打包後的專案檔案、src 用於存放你的原始碼檔案、build 用於存放 Webpack 打包相關的配置檔案。

在 src 下,建立入口檔案 index.js

Linux 下建立的命令:

touch ./src/index.js
複製程式碼

在根目錄下建立 index.html 檔案,內容如下:

<!DOCTYPE html>
<html>
  <head>
    <title>Webpack Vue Demo</title>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
複製程式碼

這將用於作為我們應用的模板,打包的 js 檔案會在 Webpack 外掛的處理下插入到這個檔案中。

其他配置性檔案根據你自己的喜好來新增了,比如 .gitignore 檔案等。

二、安裝 Webpack

要使用 Webpack,第一步當然是先安裝。使用以下命令進行安裝:

npm i webpack webpack-cli -D
複製程式碼

然後你就可以看到你的專案資料夾中多了一個 node_modules 資料夾,然後 package.json 檔案中多了一個 devDependencies 屬性。裡面包含了安裝的依賴名稱和依賴版本,現在暫時還只有 webpackwebpack-cli

三、配置最基本的 Webpack

這一節我們將著手配置一個具有最基本打包功能的專案,從 src/index.js 開始對專案進行打包。

為了專案結構更加科學合理,我們把所有的 Webpack 配置相關的檔案都存放在了 build 目錄中。

進入 build 資料夾,然後建立以下幾個檔案:

  • webpack.base.conf.js
  • webpack.dev.conf.js
  • webpack.prod.conf.js
  • build.js

在 Linux 中,可以敲入如下命令快速建立:

cd build/ && touch webpack.base.conf.js webpack.dev.conf.js webpack.prod.conf.js build.js
複製程式碼

其中,webpack.base.conf.js 是最基礎的打包配置,是開發環境和生產環境都要用到的配置。webpack.dev.conf.js 就是在開發環境要使用的配置。webpack.prod.conf.js 就是在生產環境要使用的配置了。build.js 是通過 Node 介面進行打包的指令碼。

接下來我們在對應的檔案中寫入最基本的配置資訊。

(1) webpack.base.conf.js

先寫最基本的配置資訊:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: {
    bundle: path.resolve(__dirname, '../src/index.js')
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].[hash].js'
  },
  module: {
    rules: [
      
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../index.html')
    })
  ]
};
複製程式碼

(2) webpack.dev.conf.js

同樣寫入最基本的配置資訊:

const merge = require('webpack-merge');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../dist'),
    open: true
  }
});
複製程式碼

(3) webpack.prod.conf.js

繼續寫入最基礎的配置:

const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
  mode: 'production',
  devtool: 'source-map',
  module: {
    rules: []
  },
  plugins: [
    new CleanWebpackPlugin(['dist/'], {
      root: path.resolve(__dirname, '../'),
      verbose: true,
      dry: false
    })
  ]
});
複製程式碼

注意到我們上面引用了兩個新的依賴,需要先進行安裝才能使用:

cnpm i webpack-merge clean-webpack-plugin webpack-dev-server html-webpack-plugin -D
複製程式碼

(4) build.js

這個指令碼用於構建生產環境,開發環境基於 webpack-dev-server 搭建,不寫指令碼。

接下來,寫入我們的打包指令碼,通過 Node 呼叫 Webpack 進行打包。

const webpack = require('webpack');
const config = require('./webpack.prod.conf');

webpack(config, (err, stats) => {
  if (err || stats.hasErrors()) {
    // 在這裡處理錯誤
    console.error(err);
    return;
  }
  // 處理完成
  console.log(stats.toString({
    chunks: false,  // 使構建過程更靜默無輸出
    colors: true    // 在控制檯展示顏色
  }));
});
複製程式碼

這樣做的好處是可以利用 Node 做一些其他的事情,另外當 Webpack 配置檔案不在專案資料夾根部時方便呼叫。

(5) npm scripts

配置 npm scripts 能夠使我們更方便的使用打包命令。

package.json 檔案的 scripts 屬性中,寫入如下兩條:

"build": "node build/build.js",
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
複製程式碼

基本的配置寫完了,我們測試一下打包效果,在 src/index.js 中寫入如下程式碼:

console.log('index.js!');
複製程式碼

然後在命令列中輸入:

npm run dev
複製程式碼

在自動開啟的網頁中,我開啟控制檯,我們可以看到輸出了一句“index.js”,符合預期。

然後輸入構建命令進行構建:

npm run build
複製程式碼

截圖如下:

img

這就表示打包成功了,但是我們還只完成了最基本的打包功能,Vue 還不能使用,接下來我們將這個專案變得更加強大。

四、引入一些基礎的 Loader

為了方便開發,我們需要引入一些 Loader,以簡化我們的寫法以及使我們的程式碼相容更多的環境。

這一部分可以根據 Webpack 的文件來寫,因為都是一些基本的東西,配置起來也不難。

(1)babel-loader

為了使我們的 JavaScript 程式碼相容更多環境,我們需要使用 babel-loader。

配置方法:

首先安裝 babel-loaderbabel-preset-envbabel-core。需要注意的是,如果你的 babel-loader 是 7.x 版本的話,你的 babel-core 必須是 6.x 版本;如果你的 babel-loader 是 8.x 版本的話,你的 babel-core 必須是 7.x 版本。如果不這樣的話,Webpack 會報錯。

安裝命令如下:

npm i babel-loader@7 babel-core babel-preset-env -D
複製程式碼

然後在 webpack.base.conf.jsmodule.rules 中新增如下物件:

{
  test: /\.js$/,
  use: 'babel-loader',
  exclude: /node_modules/
}
複製程式碼

我們還需要新增一個配置檔案(.babelrc)在根目錄下:

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }]
  ]
}
複製程式碼

這就是 babel-preset-env 的作用,幫助我們配置 babel。我們只需要告訴它我們要相容的情況(目標執行環境),它就會自動把程式碼轉換為相容對應環境的程式碼。

以上程式碼表示我們要求程式碼相容最新兩個版本的瀏覽器,不用相容 IE 8,另外市場份額超過 1% 的瀏覽器也必須支援。

只需要告訴 babel-preset-env 你想要相容的環境,它就會自動轉換,是不是很爽?再也不用配置那麼多了。

接下來我們試一試,把 src/index.js 中的程式碼改寫為:

const x = 'index.js';

const y = (x) => {
  console.log(x);
}

y(x);
複製程式碼

然後使用 npm run build 進行打包,打包之後的程式碼中:

var x = 'index.js';

var y = function y(x) {
  console.log(x);
};

y(x);
複製程式碼

說明我們的程式碼已經被成功地轉換了。

為了做一個對比,未配置 babel 時,轉換結果如下:

const x = 'index.js';

const y = (x) => {
  console.log(x);
}

y(x);
複製程式碼

(2)file-loader

這個用於將字型檔案、圖片檔案進行模組化。

首先安裝 file-loader

npm i file-loader -D
複製程式碼

然後在 webpack.base.conf.js 中新增如下配置到 module.rules

{
  test: /\.(png|svg|jpg|gif)$/,
  use: [
    'file-loader'
  ]
},
{
  test: /\.(woff|woff2|eot|ttf|otf)$/,
  use: [
    'file-loader'
  ]
}
複製程式碼

當然可以簡化配置資訊,把兩個 test 正則合併到一處。

接下來我們配置 vue-loader

(3) vue-loader

為了使用 Vue 單檔案元件,我們需要對 .vue 檔案進行處理,使用 vue-loader

首先安裝 vue-loadercss-loadervue-style-loadervue-template-compiler,後者也是必不可少的,少了會報錯。

命令:

npm i vue-loader css-loader vue-style-loader vue-template-compiler -D
複製程式碼

然後我們配置 webpack.base.conf.js,寫入以下程式碼到該檔案的 module.rules 屬性當中:

{
  test: /\.vue$/,
  loader: 'vue-loader'
},
{
  test: /\.css$/,
  use: ['vue-style-loader', 'css-loader']
}
複製程式碼

只有這一處配置是不行的,根據 vue-loader 官網的說明,我們還需要配置一個外掛,然後還需要配置 resolve.alias 別名,不然 Webpack 沒法找到 Vue 模組。

配置外掛,首先在檔案頭部引入:

const VueLoaderPlugin = require('vue-loader/lib/plugin');
複製程式碼

然後在 plugins 陣列中新增這個外掛物件:

new VueLoaderPlugin(),
複製程式碼

隨後我們還要配置別名,將 resolve.alias 配置為如下物件:

{
  'vue$': 'vue/dist/vue.esm.js',
  '@': path.resolve(__dirname, '../src'),
}
複製程式碼

這可以使得 Webpack 很方便的找到 Vue,我們在 JavaScript 檔案中引入依賴的時候,也可以方便地使用 @ 來代替 src,省去了寫檔案路徑的麻煩。

我們順便新增一個 resolve.extensions 屬性,方便我們引入依賴或者檔案的時候可以省略字尾:

extensions: ['*', '.js', '.json', '.vue'],
複製程式碼

extensions 屬性是一個陣列。這樣配置之後,我們在 JavaScript 檔案中 import JavaScript 檔案、json 檔案和 Vue 單檔案元件都可以省略字尾。

以上幾步都很重要,最好不要省略。

接下來我們驗證一下 Vue 單檔案元件是否可用。

安裝 Vue:

npm i vue --save
複製程式碼

然後修改 index.js 檔案內容如下:

import Vue from 'vue';
import App from './App';

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
});
複製程式碼

然後在同級目錄下建立一個 App.vue 檔案,內容如下:

<template>
  <h1>Hello World!</h1>
</template>

<script>
  export default {
    name: 'App'
  }
</script>

<style>
  html, body {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
    font-size: 16px;
  }
</style>
複製程式碼

執行命令 npm run dev 就可以看到一個大大的一級標題 —— Hello World 啦!

到這裡,我們的專案已經可以使用 Vue 單檔案元件進行開發了,但是還沒有完,我們還有一些任務要做。

五、優化 CSS 程式碼

這裡我們使用 postcss 的 autoprefixer 外掛為我們的 css 程式碼自動新增字首以適應不同的瀏覽器。

首先安裝依賴:

npm i postcss-loader autoprefixer -D
複製程式碼

然後修改 module.rules 中的 css 配置項,修改之後如下:

{
  test: /\.css$/,
  use: ['vue-style-loader', 'css-loader', 'postcss-loader']
}
複製程式碼

然後在我們專案的根目錄下新增配置檔案 postcss.config.js,內容如下:

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}
複製程式碼

代表我們將要使用 autoprefixer 外掛。

之後我們就可以愉快地寫程式碼了,可以自己驗證一下是否自動新增了字首,這裡不再贅述。

六、開啟熱更新

Webpack 4 開啟熱更新相對容易,具體步驟如下:

修改 webpack.dev.conf.js,在 devServer 屬性中設定 hot 的值為 true,這就代表開啟了熱更新。但是隻這樣做還不夠,需要我們新增一個外掛,繼續修改 webpack.dev.conf.js

設定其 plugins 屬性如下:

const webpack = require('webpack');
// 在檔案頭部引入 webpack 依賴
[
  new webpack.HotModuleReplacementPlugin()
]
複製程式碼

這就開啟了 css 熱更新(因為 vue-style-loader 封裝了 style-loader,熱更新開箱即用),但是 JavaScript 熱更新還不能用,每次修改程式碼我們都會重新整理瀏覽器,所以我們需要繼續配置。

為了使得 JavaScript 模組也能進行 HMR,我們需要在我們的 入口檔案(index.js) 的底部新增如下程式碼:

if (module.hot) {
  module.hot.accept();
}
複製程式碼

接下來就可以進行 HMR 了。

七、第三方庫單獨打包

每次我們對專案進行打包時,我們都會把引用的第三方依賴給打包一遍,比如 Vue、Vue-Router、React 等等。但是這些庫的程式碼基本都是不會變動的,我們沒必要每次打包都構建一次,所以我們最好將這些第三方庫提取出來單獨打包,這樣有利於減少打包時間。

官方外掛是 DllPlugin,但是這個外掛配置比較繁瑣。網上有人推薦一個比較好用的外掛 —— autodll-webpack-plugin,確實很好用。

下面是它的配置方法:

首先安裝:

npm i autodll-webpack-plugin -D
複製程式碼

然後在 webpack.base.conf.js 中引入:

const AutoDllPlugin = require('autodll-webpack-plugin');
複製程式碼

然後在 plugins 屬性中新增這個外掛:

new AutoDllPlugin({
  inject: true, // will inject the DLL bundle to index.html
  debug: true,
  filename: '[name]_[hash].js',
  path: './dll',
  entry: {
    vendor: ['vue', 'vue-router', 'vuex']
  }
})
複製程式碼

inject 為 true,外掛會自動把打包出來的第三方庫檔案插入到 HTML。filename 是打包後檔案的名稱。path 是打包後的路徑。entry 是入口,vendor 是你指定的名稱,陣列內容就是要打包的第三方庫的名稱,不要寫全路徑,Webpack 會自動去 node_modules 中找到的。

每次打包,這個外掛都會檢查註冊在 entry 中的第三方庫是否發生了變化,如果沒有變化,外掛就會使用快取中的打包檔案,減少了打包的時間,這時 Hash 也不會變化。

八、提取共同程式碼:

使用 splitChucksPlugin 外掛,這是 Webpack 自帶的,不用安裝第三方依賴。

使用方法:

webpack.base.conf.js 的 plugins 屬性中新增如下外掛物件;

new webpack.optimize.SplitChunksPlugin()
複製程式碼

這代表你將使用預設的提取配置來提取你的公共程式碼,如果你不想使用預設配置,請給外掛建構函式傳入配置物件.

具體怎麼配置,請參考冷星大神的部落格 —— webpack4——SplitChunksPlugin使用指南,裡面關於配置項的作用介紹得很清楚很詳細。

九、使用 stylus 前處理器

我個人比較喜歡 stylus,因為寫起來比較無拘無束,類似 Python,沒那麼多條條框框,而且用起來也不是很複雜。

引入方法:

首先下載 stylus 和 stylus-loader 依賴:

npm i stylus stylus-loader -D
複製程式碼

然後在配置項 module.rules 中新增一個處理 stylus 檔案的配置物件。

配置資訊如下:

{
  test: /\.styl(us)$/,
  use: ['vue-style-loader', 'css-loader', 'stylus-loader', 'postcss-loader']
}
複製程式碼

接下來只要你在 Vue 單檔案元件的 style 標籤加上 lang='stylus',你就可以使用 stylus 來寫 CSS 了。

十、抽取 CSS 到單檔案

這個功能的配置方法在 Vue Loader 官網交代得很清楚了。

使用的是 mini-css-extract-plugin 外掛,首先安裝:

npm i mini-css-extract-plugin -D
複製程式碼

然後在配置檔案頭部引入:

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
複製程式碼

然後當你要抽取 CSS 的時候(比如生產環境打包),你就把原來配置檔案中的所有 vue-style-loader 替換為 MiniCssExtractPlugin.loader,其他的什麼 css-loaderstylus-loader 等等都不要動。

最後,修改 plugins 選項,插入如下外掛:

new MiniCssExtractPlugin({
  filename: "[name].css",
  chunkFilename: "[id].css"
})
複製程式碼

打包之後,你會發現所有的 CSS 程式碼都被抽取到了一個單獨的 CSS 檔案當中。

收穫

  • 配置要謹慎,一個錯誤就可能導致莫名其妙的報錯。
  • 學習 Webpack 要多實踐,光理論學起來比較抽象。
  • 多查 Webpack 文件,其實很多問題裡面都交代清楚了,不要急於到網上找答案。
  • 現成的腳手架是最好的範例,比如 Vue-Cli,create-react-app。多看看它們是怎麼配置的,可以學到一些東西。

示例程式碼放在我的 GitHub 倉庫,需要的同學可以自取。

如有錯誤,敬請指出!

相關文章