最近感覺自己越來越像一個API調(diào)用程序員,很多基礎的原理以及項目構建都沒實際操作過艺蝴,所以這里動手自己去搭建了一個vue項目贺嫂,從webpack配置到vue配置,以及構建的優(yōu)化拾碌,雖然寫得并不好吐葱,但是自己在這個過程中也學到了一些東西,以此記錄校翔。
由于是真的從零開始弟跑,所以長文預警!7乐ⅰ孟辑!??
初始化項目
首先??哎甲,在命令行中創(chuàng)建文件夾并進入,使用npm命令初始化項目:
mkdir vue-starter && cd vue-starter
npm init
然后扑浸,創(chuàng)建index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue Starter</title>
</head>
<body>
<h1>First Step!</h1>
</body>
</html>
創(chuàng)建src文件夾烧给,并在src文件中創(chuàng)建main.js文件:
mkdir src && cd src
touch main.js
src/main.js中寫入:
window.onload = () => {
console.log('load');
};
這個時候如果在index.html中引入./src/main.js,并在瀏覽器中運行index.html會發(fā)現(xiàn)控制臺中打印了‘load’喝噪。代碼少看起來并不復雜础嫡,但是當我們業(yè)務變復雜,之后代碼量過大酝惧,就需要我們進行打包構建了??榴鼎。
所以下面進行webpack配置。
webpack第一步
首先??晚唇,安裝webpack 和webpack-cli:
npm i -D webpack webpack-cli
然后巫财,在package.json中配置運行webpack的腳本命令:
"scripts": {
"build": "cross-env NODE_ENV=production webpack --mode=production --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
這里用了新依賴,cross-env哩陕,進行安裝:
npm i -D cross-env
關于build腳本命令解釋:
- cross-env依賴用于跨平臺設置環(huán)境變量平项。在多數(shù)情況下,在windows平臺下使用類似于: NODE_ENV=production的命令行指令會卡缀芳啊闽瓢;windows平臺與POSIX在使用命令行時有許多區(qū)別(POSIX,使用$ENV_VAR心赶;windows使用%ENV_VAR%)扣讼。cross-env就是解決這類跨平臺問題的,統(tǒng)一每個平臺的命令缨叫。
- NODE_ENV=development 設置 NODE 的環(huán)境變量為開發(fā)環(huán)境
- --mode=procution配置為生產(chǎn)環(huán)境模式
- --config webpack.config.js 指明配置文件位置(相對路徑)
創(chuàng)建webpack.config.js椭符,webpack配置如下:
const path = require('path');
const config = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist/')
}
};
module.exports = config;
上面是最基本的webpack配置,大概意思就是根據(jù)entry的./src/main.js內(nèi)容進行打包構建輸出到bundle.js.
然后運行npm run build命令會耻姥,會得到dist文件夾以及dist/bundle.js文件销钝,dist/bundle.js就是src/main.js打包構建之后的內(nèi)容。
現(xiàn)在將index.html中的src/main.js改成dist/bundle.js琐簇。
<script src="./dist/bundle.js"></script>
再在瀏覽器中打開index.html曙搬,可以看到也得到了相同的效果。
生成的bundle文件添加hash
為什么添加hash鸽嫂?
是為了防止瀏覽器緩存機制阻止文件的更新纵装,為打包文件添加hash后綴之后,每次構建打包生成的文件名的hash都會發(fā)生改變据某,強制瀏覽器進行刷新挽唉,獲取當前最新的文件就可以防止使用緩存文件匠童。
如何添加严拒?
在output中設置:
output: {
filename: 'bundle.[hash].js',
path: path.resolve(__dirname, './dist/'),
}
設置好hash之后挤牛,運行npm run build命令會發(fā)現(xiàn)dist下生成的bundle帶上了hash。
自動生成html文件
在npm run build之后,dist文件夾中并沒有index.html文件长窄,要想引用打包的文件疮绷,需要手動引用懂算,并且由于上一步為bundle添加了hash,所以每次構建都需要手動修改script標簽的src路徑航棱。
使用HtmlWebpackPlugin可以自動生成html文件并注入打包的文件。
安裝包:
npm i -D html-webpack-plugin
在webpack.config.js中引入并在plugins中添加配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPlugin = new HtmlWebpackPlugin({
// 生成的html的title
title: 'Vue Starter',
// 生成的html的文件名
filename: 'index.html',
// 注入bundle到body中
inject: 'body'
});
const config = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash].js',
path: path.resolve(__dirname, './dist/')
},
plugins: [
htmlPlugin
]
};
module.exports = config;
現(xiàn)在運行npm run build可以看到生成了index.html并且自動引用了帶hash后綴的bundle.[hash].js馁蒂。
引入vue
首先饵隙,安裝vue與vue-loader:
npm i -D vue vue-loader
安裝成功之后會看到控制臺有一個warning??:
npm WARN vue-loader@15.6.2 requires a peer of css-loader@* but none is installed. You must install peer dependencies yourself.
意思是安裝的包還需要依賴css-loader,所以繼續(xù)安裝css-loader:
npm i -D css-loader
然后在webpack.config.js中添加vue相關的loader:
module: {
rules: [
{
test: /\.vue$/,
loader:'vue-loader'
}
]
}
src下創(chuàng)建App.vue:
<template>
<div>
Second Step!
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
src下創(chuàng)建main.js:
import Vue from 'vue';
import App from './App.vue';
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App)
}).$mount(root)
這時候我們運行npm run build命令會得到以下報錯??:
Module Error (from ./node_modules/vue-loader/lib/index.js):
[vue-loader] vue-template-compiler must be installed as a peer dependency, or a compatible compiler implementation must be passed via options.
提示vue-loader需要依賴vue-template-compiler包免姿,安裝該包即可:
npm i -D vue-template-compiler
然后運行npm run build又報錯??啦:
ERROR in ./src/App.vue
Module Error (from ./node_modules/vue-loader/lib/index.js):
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
@ ./src/main.js 2:0-28 8:21-24
ERROR in ./src/App.vue?vue&type=template&id=4fa9bc52& 2:0
Module parse failed: Unexpected token (2:0)
You may need an appropriate loader to handle this file type.
這個bug是因為vue-loader版本問題引起的,v15版本需要依賴VueloaderPlugin包,解決這個問題的辦法是使用v14版本的vue-loader槽片,在package.json中修改vue-loader的版本為14.2.2
传轰,然后npm install一下就可以了勃刨。具體的解決辦法參考??enableVueLoader does not include VueLoaderPlugin?
然后再運行npm run build,可以看到生成了dist文件夾與dist文件夾下的bundle.js。
然后在瀏覽器中運行index.html可以看到叁巨,頁面中內(nèi)容為“Second Step庶橱!”苏章,表示我們的npm run build成功構建了我們的vue代碼啦??寂嘉。
webpack開發(fā)環(huán)境(npm run dev)配置
但是,這樣的開發(fā)方式還不是很方便枫绅,每次寫完代碼之后泉孩,需要手動npm run build構建打包,再手動刷新頁面撑瞧。所以我們需要配置開發(fā)環(huán)境的運行腳本棵譬。
首先显蝌,在package.json中添加dev命令腳本:
"scripts": {
"build": "cross-env NODE_ENV=production --mode=production webpack --config webpack.config.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server --mode=development --config webpack.config.js --open",
"test": "echo \"Error: no test specified\" && exit 1"
}
關于dev腳本命令解釋:
- 與build不同的是预伺,dev腳本使用了webpack-dev-server,webpack-dev-server是一個小型的Node.js Express服務器,它使用webpack-dev-middleware來服務于webpack的包曼尊,用于開發(fā)者在開發(fā)中配置使用酬诀。
- --mode=development設置模式為開發(fā)環(huán)境
- --open設置后,會在服務器啟動之后立即打開頁面
關于webpack-dev-server的理解與使用骆撇,可以參考這篇文章??詳解webpack-dev-server的使用瞒御。
webpack-dev-server是獨立的包,所以進行安裝:
npm i -D webpack-dev-server
對App.vue進行修改神郊,將Second Step肴裙!修改為Third Step趾唱!
然后命令行中運行npm run dev,服務器啟動成功蜻懦,命令行中出現(xiàn):Project is running at http://localhost:8080/甜癞,從瀏覽器中進入http://localhost:8080/訪問頁面,但是頁面中的內(nèi)容依舊是Second Step宛乃!悠咱。
服務器運行成功,但是頁面內(nèi)容未更新征炼,這是什么原因呢析既?
檢查根目錄下的index.html看到我們引入js的路徑為/dist/bundle.js,但是進行npm run dev命令并沒有更新dist的bundle.js谆奥。
這是因為webpack-dev-server打包的內(nèi)容是放在內(nèi)存中的眼坏,并不會在真實的目錄中生成。
這里只需要把index.html中的引入bundle.js的script標簽刪除即可雄右。因為在前面加了html-webpack-plugin包空骚,在運行過程中會自動對內(nèi)存中的index.html插入js。所以不需要再手動插入擂仍。
注意:
如果還未使用html-webpack-plugin囤屹,則需要用publicPath來解決。設置devServer的publicPath為/dist/即可逢渔。
關于publicPath的理解可以參考這里??Webpack中publicPath詳解
引入webpack-dev-server的目的就是為了在開發(fā)階段根據(jù)修改快速更新頁面肋坚,先試一下效果。修改App.vue內(nèi)容為Third Step Updated!肃廓,然后Ctrl + s保存看看頁面是否更新智厌,在控制臺中可以看到這樣的提示:
[WDS] App updated. Recompiling...
bundle.js:7 [WDS] App hot update...
可以看到進行了重新編譯和更新,頁面內(nèi)容也進行了刷新盲赊,不需要重新運行npm run dev铣鹏。
進階配置
自動清理dist文件夾
前面添加了hash的設置,每次npm run build的時候都會生成新的hash后綴的文件哀蘑,不會覆蓋之前的bundle.[hash].js诚卸,導致dist文件夾的內(nèi)容越來越多。
這里就可以使用clean-webpack-plugin包實現(xiàn)每次構建的時候自動清理dist文件夾绘迁,首先安裝clean-webpack-plugin包:
npm i -D clean-webpack-plugin
webpack.config.js中引入clean-webpack-plugin包并在plugins中配置:
const CleanWebpackPlugin = require('clean-webpack-plugin');
if (process.env.NODE_ENV === 'production') {
config.plugins.push(new CleanWebpackPlugin());
}
配置之后合溺,每次運行npm run build就可以清理dist文件夾之前的內(nèi)容了。
添加css-loader
一開始就安裝了css-loader沒使用缀台,就算在App.vue中添加了樣式也不會出錯棠赛,那么css-loader到底是干什么的呢?
webpack的官方解釋:
css-loader 解釋(interpret) @import 和 url() ,會 import/require() 后再解析(resolve)它們睛约。
在某些情況下鼎俘,可能需要在js中引用css文件,例如添加一些全局的樣式配置或者是通過引入css文件達到css修改熱更新的目的等辩涝。這時候需要在js中通過require('xxx.css')引入而芥,但是運行項目會出現(xiàn)以下錯誤。
ERROR in ./src/text.css 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
這時候在 webpack.config.js 中添加 css-loader 就能解決這個問題:
{
test: /\.css$/,
loader:'css-loader'
}
所以 css-loader 是處理 css 文件膀值,將 css 裝載到 javascript棍丐。
注意
安裝最新版本的css-loader(2.1.0)在構建(npm run build)的時候會出現(xiàn)如下錯誤:
ValidationError: CSS Loader Invalid Options
options should NOT have additional properties
由于目前暫未找到解決方法,所以暫時安裝指定的舊版本(1.0.1)沧踏,等找到解決方法之后會更新歌逢。
關于css-loader理解參考這里??你真的知道 css-loader 怎么用嗎?
關于style-loader理解參考這里??style-loader詳細使用說明
關于樣式相關的loader對比可以參考這里??style-loader翘狱、css-loader秘案、mini-css-extract-plugin 區(qū)別
添加圖片處理loader
<template>
<div>
Third Step!
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
div {
width: 200px;
height: 200px;
background: url("./logo.png");
}
</style>
其中的關鍵是background的url設置,運行npm run dev會發(fā)現(xiàn)報錯??:
ERROR in ./src/logo.png 1:0
Module parse failed: Unexpected character '?' (1:0)
You may need an appropriate loader to handle this file type.
這個問題是項目不能識別圖片后綴的原因潦匈,所以添加引用資源的loader:
npm i -D url-loader
webpack.config.js配置圖片相關的loader:
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader'
}
然后項目就可以成功運行且引入圖片了?阱高。
引入less
安裝less和less-loader:
npm i -D less less-loader
webpack.config.js中添加less的loader配置:
{
test: /\.less$/,
loader: 'style!css!less'
}
然后既可以使用less了??!
提取css
extract-text-webpack-plugin只支持 webpack 4 以下提取 CSS 文件茬缩,webpack 4使用extract-text-webpack-plugin包的alpha版本赤惊,安裝:
npm i -D extract-text-webpack-plugin@next
在webpack.config.js中首先對vue文件中的樣式做etract處理, 添加extractCSS的配置:
{
test: /\.vue$/,
loader:'vue-loader',
options: {
extractCSS: true
}
}
然后在plugins中使用extract-text-webpack-plugin:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
plugins: [
htmlPlugin,
new ExtractTextPlugin('style.css')
]
運行npm run build就可以成功單獨提取css了凰锡。
關于npm安裝@next的解釋參考這里??npm使用小技巧
自動解析確定的擴展
當前配置中未舟,引用.vue等后綴的文件,不能省略后綴掂为,必須明確寫出裕膀,否則,無法識別勇哗。
可以使用resolve的extensions配置昼扛,在webpack.config.js中添加下面的配置即可。
// other configurations
resolve: {
extensions: ['*', '.js', '.vue', '.json']
}
// other configurations
現(xiàn)在就可以愉快的引入文件且不添加后綴啦欲诺。
分離生產(chǎn)環(huán)境和開發(fā)環(huán)境的webpack配置抄谐,使用webpack-merge合并通用配置
這一部分官網(wǎng)具體有說:https://webpack.docschina.org/guides/production/
在上面的配置中,使用 npm run dev 運行開發(fā)環(huán)境瞧栗,npm run build 運行生產(chǎn)環(huán)境斯稳。開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置都在 webpack.config.js 中海铆,對于兩種環(huán)境不同的配置迹恐,使用 if 邏輯進行了判斷與單獨配置。當配置邏輯逐漸增加卧斟,if 中的邏輯會逐漸臃腫殴边,所以有必要對生產(chǎn)環(huán)境和開發(fā)環(huán)境的配置進行分離憎茂。
創(chuàng)建webpack.config.dev.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const htmlPlugin = new HtmlWebpackPlugin({
// 生成的html的title
title: 'Vue Starter',
// 生成的html的文件名
filename: 'index.html',
// 注入bundle到body中
inject: 'body'
});
const config = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash].js',
path: path.resolve(__dirname, './dist/')
},
module: {
rules: [
{
test: /\.vue$/,
loader:'vue-loader',
options: {
extractCSS: true
}
},
{
test: /\.css$/,
loader:'css-loader'
},
{
test: /\.less$/,
loader: 'style!css!less'
},
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader'
}
]
},
plugins: [
htmlPlugin,
new ExtractTextPlugin('style.[hash].css')
],
resolve: {
extensions: ['*', '.js', '.vue', '.json']
},
devtool: false,
devServer: {
noInfo: true
}
};
module.exports = config;
去掉了生產(chǎn)環(huán)境判斷添加配置的邏輯。
創(chuàng)建webpack.config.pro.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const htmlPlugin = new HtmlWebpackPlugin({
// 生成的html的title
title: 'Vue Starter',
// 生成的html的文件名
filename: 'index.html',
// 注入bundle到body中
inject: 'body'
});
const config = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash].js',
path: path.resolve(__dirname, './dist/')
},
module: {
rules: [
{
test: /\.vue$/,
loader:'vue-loader',
options: {
extractCSS: true
}
},
{
test: /\.css$/,
loader:'css-loader'
},
{
test: /\.less$/,
loader: 'style!css!less'
},
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader'
}
]
},
plugins: [
htmlPlugin,
new ExtractTextPlugin('style.[hash].css'),
new CleanWebpackPlugin()
],
resolve: {
extensions: ['*', '.js', '.vue', '.json']
},
devtool: '#source-map',
devServer: {
noInfo: true
}
};
module.exports = config;
將 if 邏輯刪除锤岸,直接配置生產(chǎn)環(huán)境需要的 plugins竖幔。
修改 package.json 中開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置文件:
"dev": "cross-env NODE_ENV=development webpack-dev-server --mode=development --config webpack.config.dev.js --open",
"build": "cross-env NODE_ENV=production webpack --mode=production --config webpack.config.pro.js",
另外,除了一些特殊配置是偷,可以看到還有很多相同的重復配置拳氢,本著 DRY 原則,可以提取通用的配置蛋铆,然后使用 webpack-merge 進行合并馋评。
首先安裝 webpack-merge:
npm i -D webpack-merge
然后,將之前的 webpack.config.js 改名為 webpack.common.js刺啦,修改代碼為 生產(chǎn)環(huán)境和開發(fā)環(huán)境通用的配置留特,主要是通用的 enter、output玛瘸、module 和通用的 plugins蜕青。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const htmlPlugin = new HtmlWebpackPlugin({
// 生成的html的title
title: 'Vue Starter',
// 生成的html的文件名
filename: 'index.html',
// 注入bundle到body中
inject: 'body'
});
const config = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash].js',
path: path.resolve(__dirname, './dist/')
},
module: {
rules: [
{
test: /\.vue$/,
loader:'vue-loader',
options: {
extractCSS: true
}
},
{
test: /\.css$/,
loader:'css-loader'
},
{
test: /\.less$/,
loader: 'style!css!less'
},
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader'
}
]
},
plugins: [
htmlPlugin,
new ExtractTextPlugin('style.[hash].css')
],
resolve: {
extensions: ['*', '.js', '.vue', '.json']
},
};
module.exports = config;
現(xiàn)在可以在 生產(chǎn)環(huán)境和開發(fā)環(huán)境的配置文件中使用 webpack-merge 和 通用的 common 配置。
webpack.config.dev.js:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: '#eval-source-map',
devServer: {
noInfo: true,
open: true
}
});
webpack.config.pro.js:
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
plugins: [
new CleanWebpackPlugin()
],
devtool: '#source-map',
});
然后修改 package.json糊渊,目的在于將之前的 mode 設置直接放到配置文件中右核,這樣可以集中處理生產(chǎn)環(huán)境和開發(fā)環(huán)境的區(qū)別配置。
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.config.pro.js",
這樣就完成了渺绒,生產(chǎn)環(huán)境與開發(fā)環(huán)境的配置分離啦~??
關于 source map
首先什么是 source map蒙兰?
一句話來說,source map 就是一個信息文件芒篷,用來記錄打包文件轉換前的位置信息搜变,便于調(diào)試。
詳情參考阮一峰大大的博文:JavaScript Source Map 詳解
關于 webpack 中如何配置 source-map针炉?
參考:Webpack中的sourcemap以及如何在生產(chǎn)和開發(fā)環(huán)境中合理的設置sourcemap的類型
添加eslint
使用eslint可以保證保證編碼規(guī)范挠他,自動檢驗代碼,安裝:
npm i -D eslint eslint-config-standard babel-eslint eslint-config-vue eslint-plugin-vue eslint-plugin-standard eslint-plugin-promise eslint-plugin-import
然后使用命令初始化eslint篡帕,會生成.eslintrc.js:
eslint --init
在.eslintrc.js 寫入如下配置:
module.exports = {
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
},
extends: ['vue', 'standard', 'plugin:vue/recommended'],
plugins: [
'vue',
"standard",
"promise"
],
'rules': {
"indent": ["error", 4, { "SwitchCase": 1 }],
'arrow-parens': 0,
'generator-star-spacing': 0,
"semi": ["error", "always", { "omitLastInOneLineBlock": true }],
"no-lone-blocks": "error",
"no-multi-spaces": "error",
"no-multiple-empty-lines": ["error", { "max": 2 }],
"no-param-reassign": "warn",
"no-spaced-func": "error",
"no-use-before-define": "error",
"no-unused-vars": "error",
"no-with": "error",
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
"comma-spacing": ["error", { "before": false, "after": true }],
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}
然后在 package.json 中添加 lint 腳本命令:
"lint": "eslint --ext .js,.vue src"
就可以通過npm run lint進行檢查殖侵。
為了能在編寫代碼的過程中編輯器就提醒錯誤,需要在webstorm -> Preferences -> Languages & Frameworks -> Javascript -> Code Quality Tools -> ESLint中勾選enable即可镰烧。
參考
本文相關的git地址:vue-starter