webpack + vue專案搭建
原文使用的版本比較老,且看且珍惜。
之前說到的vue.js的安裝–vue-cli腳手架採用的vue-cli
腳手架進行安裝的自動構建的專案,但是實際專案中可能會有特殊的要求,所以沒有使用到腳手架
推薦vue專案目錄結構:
|-- ./config // 全域性變數
|-- ./dist // 編譯後的專案程式碼
|-- ./src // 專案原始碼
|-- apis // api封裝
|-- components // Vue元件
|-- lib // js工具類
| -- router // 路由
|-- store // Vuex的store
|-- modules // Vuex模組
|-- style // css
|-- views // 頁面元件
|-- index.js // 入口檔案
|-- ./webpack.config // Webpack各種環境的配置檔案
|-- ./package.json
首先安裝Node.js,選擇需要的版本自行安裝,這裡不多贅述。
本文原始碼在這:webpack + vue專案搭建,只要有安裝Node.js,下載後執行npm install
npm run dev
即可。
初始化專案
為了方便閱讀,這裡的專案目錄結構使用的是上面的推薦目錄。
在專案的根目錄處(此處為./
)使用使用命令列執行npm init -y
(-y
的作用是跳過資訊填寫)建立package.json
入口檔案
1、在根目錄
下直接建立一個index.html
,作為頁面的入口檔案。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>myProject</title>
</head>
<body>
<div id="app">{{ message }}</div> <!-- Vue模板入口 -->
<script src="dist/index.js" charset="utf-8"></script>
</body>
</html>
2、在src
下建立一個index.js
,作為Vue的入口檔案
// import...from的語法是ES6的,需要用到babel,後面再說
// require的語法是Commonjs的,webpack已經實現了,可以直接使用
const Vue = require('vue')
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
3、安裝模組
安裝Vue:npm install --save vue
安裝Webpack:npm install --save-dev webpack
4、使用webpack編譯打包
除非在全域性安裝webpack
,使用本地安裝的webpack
你需要寫一長串的命令.\node_modules\.bin\webpack src\index.js dist\index.js
,所以建議在package.json
的script
加入執行指令碼,新增之後package.json
如下:
{
"name": "myProject",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack src/index.js dist/index.js" // <---新增這句
},
"keywords": [],
"author": "Headmaster_Tan",
"license": "ISC",
"dependencies": {
"vue": "^2.4.2"
},
"devDependencies": {
"webpack": "^3.5.5"
}
}
然後你可以執行npm run dev
命令進行打包,此時開啟index.html
,如無意外應該是會報錯的。這是一個[email protected] -> [email protected]的版本升級問題,報錯內容如下:
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
解決辦法是使用下面的webpack
配置檔案對'vue'
進行定義,報錯原因詳情可以看這裡:Vue 2.0 升(cai)級(keng)之旅
編寫webpack配置檔案
上一步中直接使用webpack執行指令碼webpack [入口檔案] [出口檔案]
,顯然對於後期新增webpack外掛和不同環境的配置是不行的。
1、在專案根目錄下建立webpack.config
資料夾專門用於存放webpack的配置檔案(當然也是完全可以只建一個webpack.config.js檔案的)
2、為了讓配置檔案不同的編譯環境能夠複用(例如loaders
的配置,不管在開發環境還是生產環境肯定都是一樣的),在webpack.config
中首先建立一個base.js
:
const path = require('path')
const root = path.resolve(__dirname, '..') // 專案的根目錄絕對路徑
module.exports = {
entry: path.join(root, 'src/index.js'), // 入口檔案路徑
output: {
path: path.join(root, 'dist'), // 出口目錄
filename: 'index.js' // 出口檔名
}
}
上面這段配置就實現了webpack src/index.js dist/index.js
的功能,還可以額外拓展一下,變成:
const path = require('path')
const root = path.resolve(__dirname, '..') // 專案的根目錄絕對路徑
module.exports = {
entry: path.join(root, 'src/main.js'), // 入口檔案路徑
output: {
path: path.join(root, 'dist'), // 出口目錄
filename: 'main.js' // 出口檔名
},
resolve: {
alias: { // 配置目錄別名
// 在任意目錄下require('components/example') 相當於require('專案根目錄/src/components/example')
components: path.join(root, 'src/components'),
views: path.join(root, 'src/views'),
styles: path.join(root, 'src/styles'),
store: path.join(root, 'src/store'),
'vue': 'vue/dist/vue.js' // 這裡就是解決上面[email protected] -> [email protected]版本升級的報錯問題
},
// 這裡注意在新版本的webpack中,extensions是不能包含''空串的
extensions: ['.js', '.vue'], // 引用js和vue檔案可以省略字尾名
},
module: { // 配置loader
// 在這裡 -loader 不能忽略,乖乖加上吧
loaders: [
{test: /\.vue$/, loader: 'vue-loader'}, // 所有.vue結尾的檔案,使用vue-loader
{test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/} // .js檔案使用babel-loader,切記排除node_modules目錄
]
}
}
babel-loader
就是將ES6的語法解析為ES5,要用到babel
則根目錄下要新建.babelrc
檔案用於配置babel
:
{
"presets": ["es2015"]
}
使用了
vue-loader
和babel-loader
需要安裝包:
npm install --save-dev vue-loader babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 css-loader vue-style-loader vue-hot-reload-api vue-html-loader
3、在webpack.config
資料夾中建立dev.js
檔案:
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./base')
const root = path.resolve(__dirname, '..')
module.exports = merge(baseConfig, {})
上面程式碼僅僅是匯出了跟base.js
一模一樣的配置,下面我們新增更多用於dev
(開發環境)的配置。
webpack-merge 用於合併兩個配置檔案,需要安裝
npm install --save-dev webpack-merge
4、使用webpack-dev-server
,開啟一個小型伺服器,不需要再手動開啟index.html
進行除錯了,修改配置檔案為:
module.exports = merge(baseConfig, {
devServer: {
historyApiFallback: true, // 404的頁面會自動跳轉到/頁面
inline: true, // 檔案改變自動重新整理頁面
port: 8080, // 伺服器埠
},
devtool: 'source-map' // 用於標記編譯後的檔案與編譯前的檔案對應位置,便於除錯
})
5、新增熱替換配置,每次改動檔案不會再整個頁面都重新整理,並使用HtmlWebpackPlugin
,實現js入口檔案自動注入
安裝webpack-dev-server
: npm install --save-dev webpack-dev-server
// ...同上
const HtmlWebpackPlugin = require('html-webpack-plugin') // 實現js入口檔案自動注入
module.exports = merge(baseConfig, {
entry: [
'webpack/hot/dev-server', // 熱替換處理入口檔案
path.join(root, 'src/index.js')
],
devServer: { /* 同上 */ },
plugins: [
new webpack.HotModuleReplacementPlugin(), // 新增熱替換外掛
new HtmlWebpackPlugin({
template: path.join(root, 'index.html'), // 模板檔案
inject: 'body' // js的script注入到body底部
})
]
})
這裡的HotModuleReplacementPlugin是webpack內建的外掛,不需要安裝
但HtmlWebpackPlugin需要自行安裝:
npm install --save-dev html-webpack-plugin
在檔案頭中引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
修改index.html
,去掉入口檔案的javascript引入:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>myProject</title>
</head>
<body>
<div id="app">{{ message }}</div>
</body>
</html>
6、最後修改package.json
中的webpack執行指令碼為:
{
"dev": "webpack-dev-server --config webpack.config/dev.js"
}
為了測試webpack配置是否都生效了,下面建立一個vue元件src/components/Hello.vue
:
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data: () => ({ message: 'Hello Vue.js!' })
}
</script>
既然用到了vue元件,那自然需要安裝vue-template-compiler
: npm install --save-dev vue-template-compiler
修改index.js
:
import Vue from 'vue'
import Hello from './components/Hello.vue'
new Vue({
el: '#app',
template: '<div><hello></hello></div>',
components: { Hello }
})
執行npm run dev
,開啟瀏覽器localhost:8080
,檢視結果:
Hello Vue.js!
如果這裡出現了報錯,而且長這個樣子的:
Error: Cannot find module 'webpack/bin/config-yargs'
at Function.Module._resolveFilename (module.js:485:15)
at Function.Module._load (module.js:437:25)
at Module.require (module.js:513:17)
······balabala
那是因為webpack
和webpack-dev-server
的版本不相容導致的,檢視下版本重新下載就好了。
配置路由
1、安裝vue-router
: npm install --save vue-router
2、在src
目錄下建立views
(用於存放頁面元件)和router
(用於存放所有路由相關的配置)資料夾。
3、新增頁面元件src/views/Home.vue
:
<template>
<div><hello></hello></div>
</template>
<script>
import Hello from 'components/Hello'
export default {
components: { Hello }
}
</script>
下面的幾個檔案因為是使用的新版環境,所以和原文的會有很大出入。
新增專案路由配置檔案src/router/routes.js
:
import Home from 'views/Home'
export default [{
path: '/', // 代表訪問 '/' 的時候其實是訪問Home.vue頁面
name: 'home',
component: Home
}]
新增路由入口檔案src/router/index.js
:
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router)
const router = new Router({
hashbang: false, // 關閉hash模式
history: true, // 開啟html5 history模式
linkActiveClass: 'active', // v-link啟用時新增的class,預設是`v-link-active`
routes
})
// 全域性導航鉤子
router.beforeEach((to, from, next) => {
console.log('---------> ' + to.path) // 每次調整路由時列印,便於除錯
next()
})
export default router
修改src/index.js
import Vue from 'vue'
import router from './router'
const App = new Vue({
router
}).$mount('#app')
最後別忘了index.html
:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>myProject</title>
</head>
<body>
<div id="app">
<router-view></router-view> <!--路由替換位置-->
</div>
</body>
</html>
重新執行npm run dev
,開啟瀏覽器localhost:8080
(訪問Home.vue
頁面)檢視效果
Hello Vue.js
配置Vuex
vuex通常用於存放和管理不同元件中的共用狀態,例如不同路由頁面之間的公共資料
vuex中的幾個概念:
- state: 狀態,即資料
- store: 資料的集合,一個vuex引用,僅有一個store,包含n個state
- getters:state不能直接取值,使用getters返回需要的state
- mutations: state不能直接賦值,通過mutation定義最基本的操作
- actions: 在action中呼叫一個或多個mutation
- modules: store和state之間的一層,便於大型專案管理,store包含多個module,module包含state、mutation、action
本教程將以一個全域性計數器作為例子
1、安裝vuex
: npm install --save vuex
2、新增src/store
資料夾,存放vuex相關檔案,新增src/store/modules
用於vuex分模組管理。
新增src/store/types.js
,vuex的所有mutation type
都放在一起,不建議分開多個檔案,有效避免重名情況:
export const INCREASE = 'INCREASE' // 累加
export const RESET = 'RESET' // 清零
3、編寫vuex模組,新增counter.js
模組目錄store/modules/counter.js
import * as types from 'store/types'
// state
const state = {
count: 0
}
// getters
const getters = {
getCount: state => state.count
}
// actions
const actions = {
increase({ commit }) {
commit(types.INCREASE) // 呼叫type為INCREASE的mutation
},
reset({ commit }) {
commit(types.RESET) // 呼叫type為RESET的mutation
}
}
// mutations
const mutations = {
[types.INCREASE](state) {
state.count++
},
[types.RESET](state) {
state.count = 0
}
}
export default {
state,
getters,
actions,
mutations
}
4、新增store/index.js
,作為vuex入口檔案(共用的getters和actions可拆分到store/getters.js
和store/actions.js
,然後在store/index.js
進行匯入,這裡因為沒有,所以就直接寫了):
import Vue from 'vue'
import Vuex from 'vuex'
import counter from './modules/counter'
Vue.use(Vuex) // 確保在new Vuex.Store()之前
export default new Vuex.Store({
getters: {},
actions: {},
modules: {
counter
}
})
5、修改src/index.js
,將store引入並新增到App中:
import Vue from 'vue'
import router from './router'
import store from 'store'
const app = new Vue({
router,
store
}).$mount('#app')
6、最後改造一下components/Hello.vue
,可以看到vuex v2變化了蠻多:
<template>
<div>
<p>{{ message }}</p>
<p>click count: {{ getCount }}</p>
<button @click="increase">increase</button>
<button @click="reset">reset</button>
</div>
</template>
<script>
// vuex 提供了獨立的構建工具函式 Vuex.mapGetters, Vuex.mapActions
import { mapGetters, mapActions } from 'vuex'
export default {
data: () => {
return {
message: 'Hello Vue.js!'
}
},
computed: mapGetters({
getCount: 'getCount'
}),
methods: mapActions([
'increase',
'reset'
])
}
</script>
重新執行npm run dev
,開啟瀏覽器localhost:8080
,檢視效果。
配置eslint
這不是必須的,不想要可以跳過。
雖然
eslint
不是必須的,但是強烈建議用在所有的JavaScript專案中。對於個人開發,可以在程式設計過程中發現並提示語法錯誤,有效過濾各種低階錯誤
對於團隊開發,強制採用一致的編碼風格,保證專案的一致性,有效避免各種任性行為
但是一定要注意,
eslint
定義的只是編碼風格,規則是死的,人是活的,學會利用自定義規則的功能,增減規則同時要知道,
eslint
檢測不通過不一定就是不能執行的,可能只是這種寫法違背了編碼風格,學會檢視控制的查詢具體錯誤原因想要更好的
eslint
體驗,請根據不同編輯器安裝對應的eslint外掛,主流的編輯器已有相應的外掛
1、選擇合適的編碼風格
eslint
提供了很多rules,可以直接在.eslintrc
檔案的rules
中一個一個地配置顯然我們大多數情況下不需要這麼做,晚上已經有一些比較多人使用的風格了,本文推薦使用standard,點開這個連結,可以看到支援的編輯器和對應的外掛名,自行去安裝
2、配置.eslintrc
檔案,在根目錄下建立.eslintrc
檔案:
{
"parser": "babel-eslint", // 支援babel
"extends": "standard", // 使用eslint-config-standard的配置
"plugins": [
"html" // 支援.vue檔案的檢測
],
"env": {
"browser": true, // 不會將window上的全域性變數判斷為未定義的變數
"es6": true // 支援es6的語法
},
"rules": { // 自定義個別規則寫在這,0忽略,1警告,2報錯
"no-unused-vars": 1 // 將”未使用的變數“調整為警告級別,原為錯誤級別,更多規則請看官網
}
}
結合不同編輯器的外掛
,開啟js和vue檔案中,就可以看到提示了
根據使用的不同風格,安裝所需的包,本文安裝:
npm install --save-dev eslint babel-eslint eslint-config-standard eslint-plugin-standard eslint-plugin-html eslint-plugin-promise
webpack生產環境配置
前面已經配置過了開發環境下使用的配置檔案dev.js
,對於生產環境,通常需要對編譯出來的檔案戶必須不過壓縮處理,提取公共模組等,就需要專門提供一個配置檔案。
1、新增webpack.config/pro.js
檔案,把生產環境用不到的刪除,比如webpack-dev-server
, webpack-hot-replacement
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseConfig = require('./base')
const root = path.resolve(__dirname, '..')
module.exports = merge(baseConfig, {
plugins: [
new HtmlWebpackPlugin({
template: path.join(root, 'index.html'), // 模板檔案
inject: 'body' // js的script注入到body底部
})
]
})
webpack常用外掛
- extract-text-webpack-plugin 提取css到單獨的檔案
- compression-webpack-plugin 壓縮gzip
- webpack.optimize.UglifyJsPlugin 壓縮js檔案,內建外掛
- webpack.DefinePlugin 定義全域性變數,內建外掛
- webpack.optimize.CommonsChunkPlugin 提取公共依賴,內建外掛
根據專案需求新增相應的外掛,外掛配置引數請檢視官方文件,這裡不進行羅列
2、在package.json
中新增指令碼:"build": "webpack --config webpack.config/pro.js"
3、執行npm run build
,可以在dist資料夾中看到打包好的檔案。
完