看vue.js有幾天了,之前也零零散散的瞅過,不過一直沒有動手去寫過demo,這幾天后台事比較少,一直在討論各種需求(其實公司對需求還是比較重視與嚴謹的,一個項目需求討論就差不多一周了,這要擱之前,天哪。。。),於是就琢磨着把vue簡單的過下,如下所講只是個人一些理解,不到的地方還望園友指正,涉及到的東西有vue、vue-router、vuex、axios以及nodejs一些后台東西,廢話不說了直接上菜吧。
一、vue.js
1、項目搭建使用vue-cli腳手架,首先必須安裝vue、vue-cli:cnpm i vue vue-cli -g,全局安裝完成之后我們便可以使用vue-cli腳手架進行項目結構搭建,具體如下:
vue init webpack frontend,如下:
這里我們環境部分選擇“運行環境+編譯環境”,這樣,后面直接可以使用webpack進行編譯后部署,十分方便,再繼續:
一般每個團隊都有自己的代碼規范,這時候打開eslint,配置自己團隊的代碼規范就顯得尤為重要,下面說下vscode編輯器下如何配置eslint,在之前項目基礎上我們配置下eslint,首先是vscode安裝eslint插件:
然后在用戶設置欄配置具體的eslint規則:
依次打開vscode的“文件”->“首選項”->“用戶設置”,在右側編輯區輸入我們定義好的eslint規則:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
{
"editor.fontSize"
: 17,
"editor.tabSize"
: 2,
"editor.formatOnSave"
:
false
,
"files.associations"
: {
"*.vue"
:
"vue"
},
"eslint.validate"
: [
"javascript"
,
"javascriptreact"
,
"html"
,
"vue"
,
{
"language"
:
"html"
,
"autoFix"
:
true
}
],
"emmet.syntaxProfiles"
: {
"vue-html"
:
"html"
,
"vue"
: [
"css"
,
"html"
,
"less"
]
},
"editor.fontFamily"
:
"Source Code Pro, 'Courier New', monospace"
,
"files.autoSave"
:
"off"
,
"workbench.iconTheme"
:
"vscode-icons"
}
|
這里我們設置字體大小為17,tab縮進為2個空格,eslint的校驗適用於js、html、vue,設置vscode圖標為vscode-icons,其它規則可以參考下eslint官方說明。
2、項目結構
前面通過vue-cli生成的項目結構如下:
主要是src文件夾,我們對其進行了一些拓展,其中api文件夾用來存放前端各種請求api模塊,components文件夾如下:
用來存放各種頁面組件,其中base為公共組件,比如一些頁頭、頁尾、分頁組件等等,Home為主界面,該頁面通過router路由來整合其它組件,Login為登錄組件。mock文件夾用來進行mock.js配置,這樣前端可以獨立於后端接口開發,使用虛擬數據不依賴后端從而更加高效。router文件夾用來配置vue-router下的各種前端路由,vuex文件夾主要用來配置vuex狀態管理相關:
其中modules文件夾用來存放各個vuex的module,分模塊配置的話有個好處就是項目比較龐大的時候便於數據查看與管理,mutation.types.js用來存放各種vuex的mutation類型常量,關於vuex后面繼續介紹。
3、關於vue.js一些知識點
1)、組件概念
組件這個比較好解釋,簡單理解就是一個個通過vue自己的方式注冊的頁面(可以是公有頁面也可以是單個頁面),組件化開發時通過template包裹的一系列部分功能頁面都可以稱作組件,比如我們的App.vue:
當然,每個組件都有屬於自己作用域,組件下的各個數據及相關操作均寫在當前組件下的script標簽中,如下:
可以看到如上為App.vue組件的數據結構,這里通過ES6的export default導出當前vue實例,組件下包含很多東西,比如data(data是一個function,通過return一個數據對象來表示當前組件都有哪些數據實例)、components(組件是可以被其它組件導入使用的,components就是用來聲明當前組件導入的子組件)、computed(計算屬性,和data類似,本質返回的是一個數據對象)、watch(監聽,主要是監聽數據對象的變化,有變化則執行對應的function)、mounted(鈎子函數,組件初始化時調用)、methods(用得最多,當前組件下的私有方法,可通過this.methodName調用)、events(事件,當前組件下的事件函數),這里還有很多,具體可以參考官網。
當然,每個組件都有屬於自己的style樣式,使用如下定義:
這里的scoped表示下方樣式僅僅當前組件有效,否則全局有效,通過@import我們可以導入外來樣式文件(這里公共資源我們一般定義在src的assets文件夾下,不用寫在static文件夾下,因為assets被打包時會自動打包,為了統一還是放在assets下面)
2)、組件間的數據交換
首先是父子組件間的數據交換:
父組件好比我們這里的App.vue組件,子組件就好比我們import進來的那些組件,vue.js提供了我們很便捷的方式進行跨組件間的通信,對於父子組件那便是:子組件dispatch事件到父組件,父組件broadcast事件到下面的所有子組件,默認事件傳輸為冒泡傳輸。什么意思呢,就是說在App.vue里面我們可以直接通過dispatch方法向上派發事件,前提是父組件需要在methods或者events那里配置好改事件(只有配置了該事件才能接收到,推薦寫在events里面),當然派發時可以傳遞一些數據,同理父組件也是通過broadcast廣播事件到子組件。二是我們可以通過props屬性進行,子組件在script標簽中寫明需要prop的哪些屬性,父組件在調用子組件的地方直接寫上該prop(如果添加v-bind:prop則為動態prop),那么該數據便直接從父組件傳遞到了子組件中三是我們可以通過全局的$emit進行事件通信,這個可以參考官網文檔。
其次是任意無關系的組件如何通信:
這個時候需要使用中間組件進行數據傳輸,相當於搭建起一個中央數據總線,比如A組件需要和C組件進行通信,那么我們可以在定義一個空的組件為B,那么在A組件中導入B組件,同時向B組件派發事件,同時在C組件中導入B組件,並在此進行事件接收即可。當然了解vuex的話,使用vuex可以輕松解決任意組件間的通信問題,這個后面說。
3)、vue.js實例屬性
一個是.parent和.parent和.children,這兩個一個是獲取當前組件的父實例,一個是獲取當前組件的所有子組件,獲取到組件后便可以方便訪問實例的方法、數據資源等了。.refs是在父組件調用子組件的地方,為了區分各個子組件,可以為組件指定不同的ref屬性,然后在通過this..refs是在父組件調用子組件的地方,為了區分各個子組件,可以為組件指定不同的ref屬性,然后在通過this.refs.xxx獲得該子組件實例,然后可以進行各種實例操作。
4、運行效果
剛剛說了那么多,我們的項目差不多搭起來了,通過命令行:npm run dev,然后直接在瀏覽器中可以查看效果:
默認啟動的是8080端口,訪問下:
到這里,一切都ok了,簡單的vue.js腳手架項目就搭建完畢了。
二、vue-router
看到router,顧名思義“路由”的意思,vue-router賦予SPA應用前端路由的權利,從而實現自定義頁面跳轉而不請求服務端,主要記錄如下:
1、配置vue-router
在前面的腳手架項目里面我們已經生成了自帶vue-router的腳手架項目,下面主要是對其進行一些修改說明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import
Vue from
'vue'
import
Router from
'vue-router'
// import store from '../vuex'
// import App from '../App.vue'
// import _ from 'lodash'
Vue.use(Router)
export
const router =
new
Router({
mode:
'history'
,
routes: [
// 組件懶加載,這樣可防止組件太多時首屏打開慢的問題
{ path:
'/user'
, component: resolve => require([
'../components/user/User.vue'
], resolve) },
{ path:
'*'
, component:
null
}
]
})
router.beforeEach((to, from, next) => {
// to and from are Route Object,next() must be called to resolve the hook
// if (!store.getters.logined) {
// } else {
// next()
// }
next()
})
|
router必須使用vue.use進行全局注冊方可使用,mode參數用來配置router的模式,默認為帶“#”的hash模式,當然配置為history則使用H5的history模式,路由更像是普通url。routes用來配置各種具體路由信息,這里routes是一個對象數組,每一個對象都是一個路由對象,其中包括path(路由路徑,支持正則匹配)、component(該路由對應的組件實例),注意的是一般我們會對路由組件做懶加載處理,以便加快首屏渲染速度。
2、在組件中配置使用
router-view是用來做路由視圖的顯示的,第一步配置了每個路由對應的路由,那么一旦某個頁面使用router-view進行視圖顯示的話,恰好該路由匹配了上面path,那么path對應的組件將會展示在router-view的位置,相當於路由組件的填位吧。當然一個頁面或組件可以配置多個router-view,那么相應的就需要引入多個組件實例了。
router-link是一個路由跳轉標簽,可以理解為我們html中的a標簽,router-link標簽內部包含屬性to,同樣可以路由配置。
關於路由方法:
router.push()、router.replace():路由跳轉
router.back():路由回退
router.forward():forward跳轉
其它參考官網說明,應用較多的是beforeEach方法,所有路由調用之前均執行該方法,這里我們一般可以做一些權限判斷、登錄判斷之類的操作:
三、vuex狀態管理
vuex主要用來進行全局狀態管理,可以理解為全局的數據管理,vuex主要由幾部分組成:action、mutation、getters、state組成,一般的使用流程是:組件中可以直接調用上面四個部分,比如調用action,可以使用:this.$store.actionName,mutation也是一樣,不同的是action支持異步調用,mutation下的操作完全同步,也就是說,action下可以調用各種api調用(api方法一般都是異步的,返回promise對象)。組件訪問getters:this.store.getters.gettersName,組件中調用state:this.store.getters.gettersName,組件中調用state:this.store.moduleName.stateName(模塊化配置vuex時這樣訪問)。action一般commit事件到mutation,然后再在mutation中操作state中的數據,最后通過getters暴露state中的數據給組件使用,如果不涉及到異步操作的話,可以直接在組件中dispatch到相應的mutation而跳過action直接操作state。
1、配置vuex
首先是module下的user模塊內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
import
* as types from
'../mutation.types'
import
* as http from
'../../api/http'
// import _ from 'lodash'
const state = {
userinfo: JSON.stringify(localStorage.getItem(
'userinfo'
) || {})
}
const actions = {
setUserInfo ({ commit }, userinfo) {
localStorage.setItem(
'userinfo'
, userinfo)
commit(types.SET_USERINFO, userinfo)
},
login ({dispatch, commit, getters}, plyload) {
return
http.get(
'/user/caiya'
, {})
}
}
const mutations = {
[types.SET_USERINFO] (state, userinfo) {
state.userinfo = userinfo
},
[types.LOGOUT] (state) {
localStorage.setItem(
'userinfo'
,
''
)
state.userinfo =
''
}
}
const getters = {
logined (state) {
return
state.userinfo !==
''
&& state.userinfo !==
'{}'
},
userinfo (state) {
if
(state.userinfo !==
''
&& state.userinfo !==
'{}'
&&
typeof
state.userinfo ===
'string'
) {
return
JSON.parse(state.userinfo)
}
else
if
(
typeof
state.userinfo ===
'object'
) {
return
state.userinfo
}
return
null
}
}
export
default
{
state,
actions,
mutations,
getters
}
|
每個module都有相應的四個組成部分,分別定義如上,然后再在index.js中配置出該模塊:
1
2
3
4
5
6
7
8
9
10
11
12
|
import
Vue from
'vue'
import
Vuex from
'vuex'
import
user from
'./modules/user'
Vue.use(Vuex)
export
default
new
Vuex.Store({
modules: {
user
}
})
|
最后再將該store配置到全局的vue實例中:
2、actions
actions接收鍵值對函數,該函數第一個參數為vuex的context對象,其中包括dispatch、commit、getters對象函數,通過ES6結構的方式可以直接取到:
然后可以直接進行相關操作,也可以在此將payload數據commit到mutation中在此進行處理。
3、mutation
mutation一般用來對state中的數據進行修改操作,其中第一個參數為state對象,后面參數不定,均為action傳過來的參數或者頁面通過dispatch直接傳過來的參數
4、getters
可以看到getters其實就是用來過濾處理state中的數據的,每個getters函數第一個參數為當前module下的state對象,定義好getters后組件中直接獲取getters即可獲取到state中的過濾后的數據:
5、最后看下要操作的state
其實state就是json對象,用來保存任意狀態的:
6、組件輔助函數
輔助函數是干嘛的呢,比如之前我們定義好的action、mutation、getters、state后,在組件中想使用他們要怎么做呢?必須使用this.store.getters.xxx來獲取gettersxxx,要調用action必須使用this.store.getters.xxx來獲取gettersxxx,要調用action必須使用this.store.actionName來調用,mutation同理,獲取state要使用this.$store.state.moduleName.stateName獲取指定moduleName下的stateName這個state數據,這樣調用比較麻煩,所以輔助函數就出現了。
比如我們Login.vue組件需要調用actions中的Login方法,如下:
同樣地方式我們可以這樣直接調用mutation:
再來看個getters的:
四、axios
axios是一個http請求包,類似於vue-resource(該包已停止維護),vue官網推薦使用axios進行http調用,因為axios壓縮后體積更小,支持restful方法調用,關於axios的使用看如下代碼,其中有詳盡介紹:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
import
axios from
'axios'
axios.defaults.baseURL =
'https://cnodejs.org/api/v1'
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN
axios.defaults.headers.post[
'Content-Type'
] =
'application/x-www-form-urlencoded'
const ACCESS_TOKEN =
'accesstoken=ed20aac8-9fd8-45bf-8112-62fd2425b4a5'
// 添加請求攔截器
axios.interceptors.request.use(config => {
// 在發送請求之前做些什么
config.url = `${config.url}?${ACCESS_TOKEN}`
return
config
}, error => {
// 對請求錯誤做些什么
return
Promise.reject(error)
})
// 添加響應攔截器
axios.interceptors.response.use(response => {
// 對響應數據做點什么
return
response
}, error => {
// 對響應錯誤做點什么
return
Promise.reject(error)
})
export
function
fetch (url, params) {
return
new
Promise((resolve, reject) => {
axios.post(url, params).then(res => {
resolve(res)
}).
catch
(err => {
reject(err)
})
})
}
export
function
get (url, params) {
return
new
Promise((resolve, reject) => {
axios.get(url, {
params: params
}).then(res => {
resolve(res)
}).
catch
(err => {
reject(err)
})
})
}
|
這樣將該http.js文件導出后,可以直接在action中調用,不用全局vue配置,十分方便。axios.interceptors.request方法主要用來給所有的請求添加攔截器,這里可以進行各種請求權限校驗、方法重寫等操作,axios.interceptors.response主要是對響應做攔截處理,比如說我們服務端返回狀態碼為403,表示無權限信息,那么這里可以直接進行相關錯誤信息展示,或者直接跳轉至登錄或其它頁面。
五、一個簡單應用
閑暇時間做了個簡單的登錄及后台管理系統(ps:剛開始做),簡單效果如下,github地址:https://github.com/caiya/frontend:
六、最后想多說幾句
1、任何技術,想學就得多動手去做,實踐出真知啊,光看不做經不起實戰的考驗
2、多寫博客還是不錯的哈哈哈,不光是練文筆,陶冶情操也是有的,更別提那些大無畏的分享精神了