1. 程式人生 > >webpack 4.x 從零開始初始化一個vue專案

webpack 4.x 從零開始初始化一個vue專案

建立目錄

專案名稱: vue-init

  • app
    • css
      • reset.sass
    • js
      • home
        • index.vue
      • router
        • index.js
      • main.js
      • App.vue
    • views
      • index.html

安裝webpack

npm i -D webpack

建立配置檔案

webpack.config.js
基礎配置

  • entry 入口
  • module 模組
  • plugins 外掛
  • output 輸出

進階配置

  • resolve
  • devtool
  • devServer
  • ...

基礎配置

先寫好基本機構

module.exports = {
    enter: {},
    module: {},
    plugins: [],
    output: {}
}

配置入口檔案,以main.js作為打包入口檔案

    enter: {
        app: './app/js/main.js'
    }

配置module,裡面主要配置使用的各種loader

   module: {
        rules: [
            {
                test: /\.html$/,
                use: [
                    {
                        loader: 'html-loader'
                    }
                ]
            },
            {
                test: /\.vue$/,
                use: [
                    {
                        loader: 'vue-loader'
                    }
                ]
            },
            {
                test: /\.scss$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            module: true
                        }
                    },
                    { loader: 'sass-loader' },
                ]
            },
        ]
    },
  • test 的值為正冊表示式,配對檔案字尾,表示什麼檔案對應的loader
  • sass 需要使用多個loader,解析順序是從右向左
  • options: { module: true } 開啟css module

稍後再配置plugins,先配置output

//在webpack.config.js頂部引入path
const path = require('path');
    output: {
        filename: '[name].min.js',
        path: path.resolve(_dirname, 'dist')
    }
}
  • filename表示打包後輸出的檔名
  • [name] 對應 enter.app的值
  • path 打包輸出的路徑
  • path.resolve() webpack的執行環境是node,這裡的path是node裡的一個物件,用於處理檔案路徑和目錄路徑

配置好了 我們開始安裝loaders

npm i -D html-loader vue-loader style-loader css-loader sass-loader

如果有loader安裝不成功請再單個安裝它,或者換用cnpm

基礎配置程式碼

到這一步我們的基礎配置已經做好,程式碼如下:

module.exports = {
    enter: {
        app: './app/js/main.js'
    },
    module: {
        rules: [
            {
                test: /\.html$/,
                use: [
                    {
                        loader: 'html-loader'
                    }
                ]
            },
            {
                test: /\.vue$/,
                use: [
                    {
                        loader: 'vue-loader'
                    }
                ]
            },
            {
                test: /\.scss$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            module: true
                        }
                    },
                    { loader: 'sass-loader' },
                ]
            },
        ]
    },
    plugins: [],
    output: {
        filename: '[name].min.js',
        path: path.resolve(_dirname, 'dist')
    }
}

進階配置

使用devServer

  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000
  }
  • contentBase 告訴伺服器從哪個目錄中提供內容。
  • compress 壓縮
  • port 啟動埠號

配置好了 我們開始安裝它

npm i -D webpack-dev-server

測試

新增一些程式碼以供測試

home/index.vue

<template>
    <div id="home">
        <h1>首頁</h1>
        <p>123123<p>
    </div>
</template>

<script>
export default {}
</script>

<style lang="scss" scoped>
    .home {
        color: red;
        font-size: 80px;
        p {
            color: blue
        }
    }
</style>

router/index.js

import Vue from "vue"
import Router from "vue-router"
import Home from "../home/index.vue"

Vue.use(Router);

export default new Router({
    routes: [{
        path: '/',
        name: 'home',
        component: Home
    }]
})

App.vue

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
    name: 'app'
};
</script>

<style lang="scss" scoped>
</style>

main.js

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

Vue.config.productionTip = false;

new Vue({
    router,
    render: h => h(App)
}).$mount("#app")

我們還需要安裝 vue 和vue router

npm i vue vue-router

讓devServer跑起來

還需要安裝兩個依賴

npm i -D html-webpack-plugin clean-webpack-plugin

webpack.config.js頂部加入如下程式碼

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 
    // 注意這裡的寫法, 這樣寫 const CleanWebpackPlugin 會報錯
  • html-webpack-plugin

    官網文件解釋:HtmlWebpackPlugin簡化了HTML檔案的建立,以便為你的webpack包提供服務。這對於在檔名中包含每次會隨著編譯而發生變化雜湊的 webpack bundle 尤其有用。 你可以讓外掛為你生成一個HTML檔案,使用lodash模板提供你自己的模板,或使用你自己的loader。另外你可以在github檢視這個專案的詳細配置。

  • clean-webpack-plugin 在每次構建前清理 /dist 資料夾,這樣只會生成用到的檔案。

配置plugins

    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './views/index.html'
        })
    ],

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

package.json 加入 "start": "webpack-dev-server --open"

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open"
  },

跑完發現一大堆報錯

  • You may need an additional loader to handle the result of these loaders. Vue-loader在15.*之後的版本都是 vue-loader的使用都是需要伴生 VueLoaderPlugin的
npm i vue-loader-plugin -S

webpack.config.js

const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  // ...
  plugins: [
    new VueLoaderPlugin()
  ]
}
  • Cannot find module 'node-sass'
    安裝就完了,最後我們npm start 專案成功執行

使用px2rem

npm install -D px2rem-loader
module: {
        rules: [
            {
                test: /\.html$/,
                use: 'html-loader'
            },
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.scss$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    {
                        loader: 'px2rem-loader',
                        options: {
                            remUnit: 75,
                            remPrecision: 6
                        }
                    },
                    'sass-loader'
                ]
            },
        ]
    },

這部分為什麼這麼配置,參考了Vue官方文件 -> 單檔案組建 -> 針對高階使用者 -> VueLoader
原來的webpack3.x需要在vue-loader 下配置css 和 sass 並配置 px2rem。

使用css module

// ...
{
    test: /\.scss$/,
    use: [
        'vue-style-loader',
+        {
+            loader: 'css-loader',
+             options: {
+                 modules: true,
+                 localIdentName: '[local]_[hash:base64:8]'
+             }
+        },
        {
            loader: 'px2rem-loader',
            options: {
                remUnit: 75,
                remPrecision: 6
            }
        },
        'sass-loader'
    ]
},

如果你不知道如何使用css module 請參閱Vue官方文件 -> 單檔案組建 -> 針對高階使用者 -> VueLoader -> css module

css提取

npm install -D mini-css-extract-plugin
{
    test: /\.scss$/,
    use: [
        MiniCssExtractPlugin.loader,
        {
            loader: 'css-loader',
            options: {
                modules: true
            }
        },
        {
            loader: 'px2rem-loader',
            options: {
                remUnit: 75,
                remPrecision: 6
            }
        },
        'sass-loader'
    ]
},
plugins: [
    // ... 
    new MiniCssExtractPlugin({
      filename: 'style.css'
    })
  ]

區分生產環境和開發環境

webpack3

我們需要使用webpack的DefinePlugin建立一個在編譯時可以配置的全域性常量。在webpack.config.js頭部引入webpack

const webpack = require('webpack');

接下來我們把module.exports的值改為箭頭函式,並傳入一個引數env

module.exports = env => { 
    if (!env) { env = {} }
    return {
        // 原來的配置
    }
}

我們先來做一個示例,例如我們在開發環境不需要css提取

module.exports = env => {
    if (!env) { env = {} }

    let plugins = [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './views/index.html'
        }),
        new VueLoaderPlugin(),
    ];

    if (env.production) {
        plugins.push(
            new webpack.DefinePlugin({
                'process.env': {
                    NODE_ENV: 'production'
                }
            }),
            new MiniCssExtractPlugin({
                filename: 'style.css'
            })
        )
    }
  • process 物件是屬於node的一個全域性變數
  • 我們只需要根據是否傳入了env.production,然後給plugins陣列push生產環境下需要的MiniCssExtractPlugin外掛

對應的我們還有修改部分原來的程式碼

{
test: /.scss$/,
use: [

  •   env.production?MiniCssExtractPlugin.loader:'vue-style-loader',
      {
          loader: 'css-loader',
          options: {
              modules: true
          }
      },
      {
          loader: 'px2rem-loader',
          options: {
              remUnit: 75,
              remPrecision: 6
          }
      },
      'sass-loader'
    ]
    },
以及原來的plugins配置我們直接將它的值變為我們上面定義的plugins。  
package.json中我們需要新增命令
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open",
    "watch": "webpack --watch",
    "build" : "webpack --env.production"
  },

注意我們給webpack 傳遞了引數,我們就是利用這個引數來區分環境。

npm start


控制檯我們可以看到
css樣式以style標籤插入,並沒有被提取,說明MiniCssExtractPlugin外掛沒有執行

npm run build

執行打包後的index.html,css樣式以link標籤插入,說明css被提取合併為一個檔案,說明生產環境下MiniCssExtractPlugin外掛運行了

webpack4

上面是的做法看起來更好理解,webpack4中我們可以直接利用mode來區分開發環境和生產環境。頭部我們不需要引入webpack了, 因為我們不需要依賴 DefinePlugin。
配置中新增:

mode: 'development' //預設是 development
module.exports = (env, argv) => {
  if (argv.mode === 'production') {
    //...
  }

  return config;
};

使用eslint

npm i eslint -D

eslint支援多種格式的配置檔案,同時支援把配置直接寫在package.json中,我們直接在寫在package.json中,如何配置呢?
vue專案可以直接使用vue官方推薦的外掛

npm i eslint-plugin-vue -D

package.json新增如下:

{ 
  // 其他配置
 "eslintConfig": {
    "root": true,
    "parserOptions": {
      "ecmaVersion": 2017
    },
    "extends": [
      "mysticatea",
      "mysticatea/modules",
      "plugin:vue/recommended"
    ],
    "plugins": [
      "node"
    ],
    "env": {
      "browser": false
    },
    "globals": {
      "applicationCache": false,
      "atob": false,
      "btoa": false,
      "console": false,
      "document": false,
      "location": false,
      "window": false
    },
    "rules": {
      "node/no-extraneous-import": "error",
      "node/no-missing-import": "error",
      "node/no-unpublished-import": "error",
      "vue/html-indent": [
        "error",
        4
      ],
      "vue/max-attributes-per-line": "off"
    }
  },
  "eslintIgnore": [
    "node_modules",
    "webpack.config.js"
  ]
}