從零搭建Vue開發環境:webpack4 + vue-loader + koa2 + babel-loader v8 + Babel v7 + eslint + git hooks + editorconfigcss
「不積跬步無以致千里,不積小流無以成江海」html
建立一個工程目錄 vue-structurevue
mkdir vue-structure && cd vue-structure
複製代碼
npm i webpack webpack-cli -D
複製代碼
建立build目錄node
mkdir build
複製代碼
在build目錄裏, 建立webpack.config.jswebpack
cd build && touch webpack.config.js
複製代碼
建立入口文件 src/main.jsios
mkdir src
cd src && touch main.js
複製代碼
main.jscss3
alert('hello world!')
複製代碼
配置npm scriptsgit
// package.json
"scripts": {
"build": "webpack --config build/webpack.config.js --progress --mode production"
}
複製代碼
npm i webpack-dev-server -D
複製代碼
配置npm scriptsgithub
"scripts": {
...
"dev": "webpack-dev-server --config build/webpack.config.js --progress --mode development"
}
複製代碼
html 插件web
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/
到這裏webpack開發服務基本跑通了
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
ok 沒問題
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單文件組件
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.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被成功掛載到頁面
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 提供了一個解析器,它可以將 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樣式
此時 npm run dev 能夠看到 自動添加了瀏覽器前綴
將圖片資源轉換成base64 URI
// webpack.config.js
module: {
rules: [
{
test: /\.(png|svg|jpe?g)$/,
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
複製代碼
轉換後圖片地址變爲了base64 URI
加載圖標字體
module: {
rules: [
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
}
]
}
複製代碼
設置命令運行時的環境變量 以便在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"
},
複製代碼
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文件
生產環境下 爲何要清理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')
},
}
複製代碼
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
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的
在不一樣的編輯器和IDEs中爲多個從事同一項目的開發人員保持一致的編碼風格。
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
複製代碼
安裝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實時編譯,進行代碼效驗
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
eslint基本配置完了 可是我想在控制檯報錯信息更友好些
一個簡單的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
此時命令行中報錯信息更加友好 顯示rules詳細規則
把編譯錯誤,直接顯示到瀏覽器頁面上。
webpack.config.js
module.exports = {
devServer: {
overlay: {
errors: true,
warnings: true
}
}
}
複製代碼
再次npm run dev 這樣就能夠直接在頁面中看到錯誤信息了
"scripts": {
"lint": "eslint --ext .js,.vue src"
},
複製代碼
經過npm 單獨進行效驗代碼也能夠
npm run lint
複製代碼
Eslint檢測出的問題如何自動修復
"scripts": {
"lint": "eslint --ext .js,.vue src",
"lint:fix": "eslint --fix --ext .js,.vue src"
},
複製代碼
npm run lint:fix
複製代碼
會把你代碼中一些常規錯誤 進行自動修復
Git 能在特定的重要動做發生時觸發自定義腳本。 相似於框架中的生命週期
npm install husky --save-dev
複製代碼
建立.huskyrc
// .huskyrc
{
"hooks": {
"pre-commit": "npm run lint"
}
}
複製代碼
package.json
"scripts": {
"lint": "eslint --ext .js,.vue src"
},
複製代碼
當每次git commit時 自動執行npm run lint效驗
代碼分離 屬於性能優化 (業務代碼 第三方代碼 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
webpack4中 optimization選項更多配置請看官方文檔
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, '../')
// })
]
}
複製代碼
其餘更多配置留到下一章