那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

布羅利發表於2019-01-25

從零搭建Vue開發環境:webpack4 + vue-loader + koa2 + babel-loader v8 + Babel v7 + eslint + git hooks + editorconfig

從2009到2019社會在不斷進步 技術也在不斷進步 我們當然也不能落後

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

“不積跬步無以至千里,不積小流無以成江海”

先開始webpack基本構建

建立一個工程目錄 vue-structure

mkdir vue-structure && cd vue-structure
複製程式碼

安裝webpack

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

建立build目錄

mkdir build
複製程式碼

在build目錄裡, 建立webpack.config.js

cd build && touch webpack.config.js
複製程式碼

建立入口檔案 src/main.js

mkdir src

cd src && touch main.js
複製程式碼

main.js

alert('hello world!')
複製程式碼

配置npm scripts

  // package.json 
  "scripts": {
    "build": "webpack --config build/webpack.config.js --progress --mode production"
  }
複製程式碼

配置devServer

npm i webpack-dev-server -D
複製程式碼

配置npm scripts

  "scripts": {
    ...
    "dev": "webpack-dev-server --config build/webpack.config.js --progress --mode development"
  }
複製程式碼

html 外掛

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

webpack配置

// build/webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const resolve = dir => path.join(__dirname, '..', dir)

module.exports = {
  entry: resolve('src/main.js'),
  output: {
    filename: '[name].[hash:5].js',
    path: resolve('dist')
  },
  devServer: {
    host: '0.0.0.0',
    port: 7000,
    open: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: resolve('index.html')
    })
  ]
}

複製程式碼

執行webpack dev server

npm run dev
複製程式碼

瀏覽器自動開啟 http://0.0.0.0:7000/

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

到這裡webpack開發服務基本跑通了

配置babel v7

webpack 4.x | babel-loader 8.x | babel 7.x

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

babel plugin 支援動態import()

npm i @babel/plugin-syntax-dynamic-import -D
複製程式碼

配置webpack.config.js

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

建立.babelrc檔案

{
  "plugins": [
    "@babel/plugin-syntax-dynamic-import"
  ],
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ]
  ]
}

複製程式碼

測試下ES6程式碼

test.js

  // src/test.js
  export default () => alert('hello vue!')
複製程式碼

index.html

  // src/index.html
  <body>
    <div id="app">請說say</div>
  </body>
複製程式碼

main.js

 // src/main.js
 document.querySelector('#app').addEventListener('click', () => {
  import('./test.js').then(res => {
    res.default()
  })
})
複製程式碼

執行下dev

npm run dev
複製程式碼

點選頁面div

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

ok 沒問題

配置Vue Loader

Vue Loader 是什麼?

Vue Loader 是一個 webpack 的 loader,它允許你以一種以單檔案元件(*.vue檔案) 的格式撰寫 Vue 元件:

建立App.vue根元件

<template>
  <div class="example">{{ msg }}</div>
</template>

<script>
export default {
  data () {
    return {
      msg: 'Hello Vue!'
    }
  }
}
</script>

<style>
.example {
  color: red;
}
</style>

複製程式碼

安裝Vue

npm i vue
複製程式碼

src/main.js

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

new Vue({
  render: h => h(App)
}).$mount('#app')
複製程式碼

修改index.html

<body>
  <div id="app"></div>
</body>
複製程式碼

執行dev

npm run dev
複製程式碼

結果報錯了 webpack預設只能識別JavaScript檔案,不能解析.vue檔案(vue單檔案元件 是Vue獨有的),於是作者提供了vue-loader。

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

Vue單檔案元件

cn.vuejs.org/v2/guide/si…

配置vue-loader

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

vue-template-compiler (peer dependency) 是vue-loader的同版本依賴

webpack.config.js

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  module: {
    rules: [
      // ... 其它規則
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 請確保引入這個外掛!
    new VueLoaderPlugin()
  ]
}
複製程式碼

vue單檔案元件中css 也需要css-loader解析

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

配置webpack

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // 它會應用到普通的 `.js` 檔案
      // 以及 `.vue` 檔案中的 `<script>` 塊
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // 它會應用到普通的 `.css` 檔案
      // 以及 `.vue` 檔案中的 `<style>` 塊
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // 請確保引入這個外掛來施展魔法
    new VueLoaderPlugin()
  ]
}
複製程式碼

此時執行npm run dev OK了,App.vue被成功掛載到頁面

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

css前處理器配置

npm i stylus stylus-loader
複製程式碼

webpack.config.js

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

vue元件中使用

<style lang='stylus' scoped>
.example
  .title
    color: red
</style>
複製程式碼

postcss配置

postcss 提供了一個解析器,它能夠將 CSS 解析成抽象語法樹(AST)。

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

autoprefixer(外掛) 它可以解析CSS檔案並且新增瀏覽器字首到CSS內容裡

npm i -D autoprefixer
複製程式碼

建立postcss.config.js

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

配置webpack

// webpack.config.js
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [
          devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.styl(us)?$/,
        use: [
          devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'stylus-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  }
複製程式碼

給App.vue新增css3樣式

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

此時 npm run dev 可以看到 自動新增了瀏覽器字首

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

圖片資源載入配置

url-loader

將圖片資源轉換成base64 URI

// webpack.config.js

module: {
  rules: [
    {
      test: /\.(png|svg|jpe?g)$/,
      loader: 'url-loader',
      options: {
        limit: 8192
      }
    }
  ]
}
複製程式碼

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

轉換後圖片地址變為了base64 URI

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

file-loader

載入圖示字型

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

cross-env

設定命令執行時的環境變數 以便在webpack配置檔案中區分環境

npm i cross-env -D
複製程式碼

package.json

  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js --progress --mode production",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js --progress --mode development"
  },
複製程式碼

css提取

webpack4

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

webpack.config.js

// 區分當前環境是 development 還是 production
const devMode = process.env.NODE_ENV === 'development'

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
        'css-loader'
      ]
    },
    {
      test: /\.styl(us)?$/,
      use: [
        devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
        'css-loader',
        'stylus-loader'
      ]
    }
  ]
},

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

開發環境下一般不開啟css提取,要不然每次程式碼改動重新編譯速度會慢。通常在生成環境下打包時 才開啟css提取。

此時npm run build, css被單獨打包成一個css檔案

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

清理dist目錄

生產環境下 為什麼要清理dist目錄 因為我們的輸出檔案為了快取再檔名拼接上hash值,只要檔案有改動就會產生新的hash值,dist目錄下每次都會新增一份輸出檔案 但我們只要編譯後的最終的那個就可以了

npm run build 三次 dist目錄如下

dist
├── app.bundle.0e380cea371d050137cd.js
├── app.bundle.259c34c1603489ef3572.js
├── app.bundle.e56abf8d6e5742c78c4b.js
├── index.html
└── style.css
複製程式碼
module.exports = {
    output: {
    filename: '[name].[hash:6].js',
    path: resolve('dist')
  },
}
複製程式碼

利用webpack外掛清理

clean-webpack-plugin

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

webpack配置

// build/webpack.config.js

const CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
  plugins: [
    new CleanWebpackPlugin(['dist'], {
      root: path.join(__dirname, '../')
    })
  ]
}
複製程式碼

然後執行npm run build 每次打包前就會把之前的dist目錄清理下

第二種方式 用rimraf命令 清理dist目錄

rimraf

The UNIX command rm -rf for node.

npm i rimraf -D
複製程式碼

修改package.json

"scripts": {
    "clean": "rimraf dist",
    "build": "npm run clean && cross-env NODE_ENV=production webpack --config build/webpack.config.js --progress --mode production",
}

複製程式碼

npm run build 也是ok的

.editorconfig

在不同的編輯器和IDEs中為多個從事同一專案的開發人員保持一致的編碼風格。

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
複製程式碼

editorconfig.org/

程式碼校驗 (Linting)

安裝eslint

 npm i eslint eslint-plugin-vue -D
複製程式碼

eslint各種安裝

npm i -D babel-eslint eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node
複製程式碼

建立.eslintrc檔案

{
  root: true,
  env: {
    node: true
  },
  parserOptions: {
    parser: "babel-eslint",
    sourceMap: "module"
  },
  extends: [
    "plugin:vue/essential",
    "standard"
  ],
  rules: {}
}
複製程式碼

webpack中配置eslint

通過webpack實時編譯,進行程式碼效驗

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

webpack.config.js

module: {
  rules: [
    {
      {
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
      },
      ......
    }
  ]
}
複製程式碼

在src/main.js中定義一個未使用變數

let title = 'eslint'
複製程式碼

執行 npm run dev

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

eslint基本配置完了 但是我想在控制檯報錯資訊更友好些

eslint-friendly-formatter

一個簡單的eslint格式設定工具/報告器,它使用高階文字和iterm2“點選開啟檔案”功能友好

安裝

npm i -D eslint-friendly-formatter
複製程式碼

修改webpack配置

// build/webpack.config.js

module: {
  rules: [
    {
      {
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/,
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      ......
    }
  ]
}

複製程式碼

再次 npm run dev

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

此時命令列中報錯資訊更加友好 顯示rules詳細規則

devServer.overlay

把編譯錯誤,直接顯示到瀏覽器頁面上。

webpack.config.js

module.exports = {
  devServer: {
    overlay: {
      errors: true,
      warnings: true
    }
  }
}

複製程式碼

再次npm run dev 這樣就可以直接在頁面中看到錯誤資訊了

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

package.json中配置eslint

  "scripts": {
    "lint": "eslint --ext .js,.vue src"
  },
複製程式碼

通過npm 單獨進行效驗程式碼也可以

npm run lint
複製程式碼

eslint自動修復

Eslint檢測出的問題如何自動修復

  "scripts": {
    "lint": "eslint --ext .js,.vue src",
    "lint:fix": "eslint --fix --ext .js,.vue src"
  },
複製程式碼
npm run lint:fix
複製程式碼

會把你程式碼中一些常規錯誤 進行自動修復

Git Hooks

Git 能在特定的重要動作發生時觸發自定義指令碼。 類似於框架中的生命週期

git-scm.com/book/zh/v2/…

husky

npm install husky --save-dev
複製程式碼

www.npmjs.com/package/hus…

建立.huskyrc

// .huskyrc
{
  "hooks": {
    "pre-commit": "npm run lint"
  }
}
複製程式碼

package.json

  "scripts": {
    "lint": "eslint --ext .js,.vue src"
  },
複製程式碼

當每次git commit時 自動執行npm run lint效驗

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

webpack程式碼分離

程式碼分離 屬於效能優化 (業務程式碼 第三方程式碼 webpack執行時生成程式碼...)

webpack4中直接配置就可以

// build/webpack.config.js

module.exports = {
    optimization: {
    splitChunks: {
      // 預設將node_modules中依賴打包到venders.js
      chunks: 'all'
    },
    // 將webpack執行時生成程式碼打包到runtime.js
    runtimeChunk: true
  },
}
複製程式碼

此時 npm run build 會看到vendors.js 和 runtime.js

那些花兒,從零構建Vue工程(webpack4 Eslint git hooks...)

webpack4中 optimization選項更多配置請看官方文件

webpack.js.org/configurati…

目前完整配置檔案

package.json

  "scripts": {
    "clean": "rimraf dist",
    "build": "npm run clean && cross-env NODE_ENV=production webpack --config build/webpack.config.js --progress --mode production",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js --progress --mode development",
    "lint": "eslint --ext .js,.vue src",
    "lint:fix": "eslint --fix --ext .js,.vue src"
  },
複製程式碼

build/webpack.config.js

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

const resolve = dir => path.join(__dirname, '..', dir)

const devMode = process.env.NODE_ENV === 'development'

module.exports = {
  entry: resolve('src/main.js'),
  output: {
    filename: '[name].[hash:6].js',
    path: resolve('dist')
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/,
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        use: 'vue-loader',
        exclude: /node_modules/
      },
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.styl(us)?$/,
        use: [
          devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'stylus-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.(png|svg|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 8192
        }
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }
    ]
  },
  optimization: {
    splitChunks: {
      // 預設將node_modules中依賴打包到venders.js
      chunks: 'all'
    },
    // 將webpack執行時程式碼打包到runtime.js
    runtimeChunk: true
  },
  devServer: {
    host: '0.0.0.0',
    port: 7000,
    open: true,
    overlay: {
      warnings: true,
      errors: true
    }
  },
  plugins: [
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css'
    }),
    new HtmlWebpackPlugin({
      template: resolve('index.html')
    })
    // new CleanWebpackPlugin(['dist'], {
    //   root: path.join(__dirname, '../')
    // })
  ]
}

複製程式碼

未完待續

  • webpack配置根據環境進行拆分(生產、開發)
  • 動態連結庫
  • vue路由整合程式碼組織
  • axios二次封裝
  • api管理
  • Vue外掛應用
  • Vuex Vue-Route整合

其他更多配置留到下一章

webpack配置原始碼地址

github.com/Lwenli1224/…

每當到了夜晚時分 就是內心最安靜的時候心無雜念。

相關文章