Vue 2.0已經(jīng)正式發(fā)布好長時間了绢淀。想找一個Vue.js Vuex vue-route的學(xué)習(xí)項(xiàng)目來練手萤悴。做個電商App吧,上gitHub搜索了一下搜到一大堆皆的,不過基本上都不是使用單文件組件開發(fā)的覆履,更不用說基于Vue.js全家桶了。本項(xiàng)目不一樣的地方在于使用vue-cli + webpack template人開發(fā)模式,還要求Web和移動端一體化硝全,也就是響應(yīng)式Web栖雾,而且不能Mock數(shù)據(jù),額外需要有一個提供restful web api的后端應(yīng)用伟众。哈哈析藕,就是搞全棧呀。
一赂鲤、后端服務(wù)
采用的是Express + mLab + Mongoose的技術(shù)棧噪径。http模塊加上Express自帶路由利用中間件思想可以很方便地基于Node開發(fā)提供restful web api的Web應(yīng)用∈酰考慮到方便在線演示部署找爱,數(shù)據(jù)庫選用了MongoDB的云服務(wù)mLab。當(dāng)然泡孩,提交數(shù)據(jù)到數(shù)據(jù)庫時一般要選用便捷操作的對象模型工具(ORM)车摄。在node.js和MongoDB數(shù)據(jù)庫環(huán)境下,Mongoose作為就是不二之選了仑鸥。具體實(shí)現(xiàn)請看https://github.com/szriafan/vue-shop-api吮播。另外,我將node.js代碼免費(fèi)托管到了https://heroku.com/(主機(jī)數(shù)據(jù)庫都免費(fèi)眼俊,爽呀)∫夂荩現(xiàn)在直接在瀏覽器訪問https://vue-shop-api.herokuapp.com/v1/products就可以返回商品列表所需要的數(shù)據(jù)了,當(dāng)然更好的選擇是使用Postman之類的HTTP請求調(diào)試工具疮胖。如果我們不了解后端和mLab环戈,只要在ations.js手動將axios的baseURL改為https://vue-shop-api.herokuapp.com/v1就可以了∨炀模或者對數(shù)據(jù)API足夠了解院塞,也可以不寫Node.js應(yīng)用的。為了方便大家全面學(xué)習(xí)測試性昭,建議大家最好訪問本地的restful應(yīng)用拦止。注意,訪問不同端口的本地服務(wù)或不同域名的遠(yuǎn)程服務(wù)都有跨域問題糜颠,使用CORS可以很好解決這個問題汹族。app.all('/*', function(req, res, next) {? res.header('Access-Control-Allow-Origin', '*');? res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');? res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');? if (req.method == 'OPTIONS') {? ? res.status(200).end();? } else {? ? next();? }});好了,接下來集中精力開發(fā)前端其兴。
二鞠抑、前端
1. 運(yùn)行vue-cli + webpack template就得到了一個Vue.js Vuex vue-route全家桶腳手架項(xiàng)目。我們要完成兩個部分:前臺展示和后臺管理忌警。
2. 前臺展示有兩個功能:展示商品列表和詳情的功能、添加商品到購物車的功能。商品列表和詳情頁面都能添加商品到購物車法绵,商品列表頁只能通過點(diǎn)擊按鈕一次添加一件商品箕速,但詳情頁面可以通過+-按鈕或文本框輸入來改變商品數(shù)量(不能大于庫存),再通過點(diǎn)擊添加按鈕一次性添加多件商品朋譬。購物車列表頁也可以可以通過+-按鈕或文本框輸入來改變商品數(shù)量盐茎,與詳情頁面操作不一樣的是購物車列表頁改變商品數(shù)量時會同步保存數(shù)據(jù),即使重新刷新頁面或關(guān)閉瀏覽器徙赢,都會顯示已保存的數(shù)據(jù)字柠。而但詳情頁面如果改變數(shù)量后沒有按添加按鈕就不會添加商品到購物車。
3. 后臺管理有的功能就是實(shí)現(xiàn)商品及品牌的CRUD操作狡赐。在新增修改商品時窑业,會通過下拉框選擇品牌,而下拉框用品牌數(shù)據(jù)來填充的枕屉。因此商品數(shù)據(jù)是依賴品牌數(shù)據(jù)的常柄,因此后臺管理時先應(yīng)該添加商品數(shù)據(jù)。
4. 明確了上述需求后搀擂,先看到一下最終效果吧西潘。
5. 我們來說一說具體實(shí)現(xiàn)。SPA一般都有路由哨颂,復(fù)雜Vue.js應(yīng)用頁面之間的導(dǎo)航都要用到vue-route喷市。vue-rout使用JavaScript動態(tài)初始化配置全局對象,將組件(components)映射到路由(routes)威恼,然后告訴vue-router在哪里渲染它們品姓。通過注入路由器,我們還可以在任何組件內(nèi)通過this.$router訪問路由器沃测,也可以通過 this.$route訪問當(dāng)前路由缭黔。比如說當(dāng)添加或修改商品成功后我們就是通過this.$router回到商品列表頁的。模板中的組件讓用戶在具有路由功能的應(yīng)用中(點(diǎn)擊)導(dǎo)航蒂破,其中to表示目標(biāo)路由的鏈接馏谨,這個值可以是一個字符串或者是描述目標(biāo)位置的對象,對應(yīng)路由配置對象的path屬性值或name對象附迷。比如說App模板中的組件to屬性設(shè)置為{name: 'Home'}惧互,通過路由映射會導(dǎo)航到Home組件所在的頁面。建議設(shè)置to的屬性值為name對象喇伯,因?yàn)檫@個對象一般很少改動喊儡,而首頁鏈接設(shè)置成'/',同樣是因?yàn)檫@個值很少改動稻据。另外首頁鏈接還得添加exact屬性來精確匹配鏈接艾猜,大家都知道,其它path值肯定包含/,不設(shè)置該屬性會造成導(dǎo)航鏈接樣式都添加激活鏈接類名匆赃。
6. 如下面的文件路徑樹所示淤毛,本項(xiàng)目使用了大量的嵌套單文件組件,下面的項(xiàng)目components和pages文件夾包含了大量的單文件組件算柳。嵌套單文件組件提高了代碼復(fù)用性低淡,讓分模塊團(tuán)隊(duì)開發(fā)變得容易,這對于開發(fā)大型Vue.js應(yīng)用尤為重要瞬项。不過Vue組件使用單向數(shù)據(jù)流向下進(jìn)行組件數(shù)據(jù)通信蔗蹋,大量的會讓代碼難以維護(hù),此時我們就應(yīng)該考慮用Vuex來實(shí)現(xiàn)多個組件共享狀態(tài)囱淋。
├─components
│ ├─cart
│ │ CartControl.vue
│ │ CartItem.vue
│ ├─common
│ │ Loading.vue
│ ├─manufacturer
│ │ ManufacturerForm.vue
│ └─product
│ AddToCartButton.vue
│ ProductDetails.vue
│ ProductForm.vue
│ ProductItem.vue
│ ProductList.vue
├─pages
│ │ Cart.vue
│ │ Details.vue
│ │ Home.vue
│ └─admin
│ │ Index.vue
│ ├─manufacturer
│ │ Edit.vue
│ │ Manufacturers.vue
│ │ New.vue
│ └─product
│ Edit.vue
│ New.vue
│ Products.vue
如下面的文件路徑樹所示烟勋,本項(xiàng)目Vuex代碼集中store文件夾中乏冀。Vuex應(yīng)用的核心store(倉庫)褐耳,包含應(yīng)用中大部分的狀態(tài)骑脱,如商品、品牌称鳞、購物車數(shù)組涮较, 我們是在index.js中定義的; Action函數(shù)接受一個與store實(shí)例具有相同方法和屬性的context對象冈止,可以包含任意異步操作狂票,如對后臺數(shù)據(jù)訪問商品和品牌數(shù)據(jù)的異步操作,我們是在action.js中定義的熙暴;Vuex中的Mutation非常類似于事件:每個Mutation都有一個字符串的事件類型 (type) 和一個回調(diào)函數(shù)(handler)闺属。如對異步回調(diào)函數(shù)和添加商品到購物車時的同步函數(shù),我們是在mutations.js中定義的周霉;Vuex允許定義getter從store中的state中派生出一些狀態(tài)(計(jì)算屬性)掂器,如從商品和品牌數(shù)組狀態(tài)中派生出根據(jù)id查詢到的狀態(tài),我們是在getters.js中定義的俱箱;另外国瓮,我們的mutation-types.js中使用常量替代mutation事件類型以方便多人協(xié)作。在任意組件中訪問store的計(jì)算屬性時可使用mapState和mapGetters輔助函數(shù)狞谱,分發(fā)Action乃摹、Mutation時使用mapAction、mapMutation輔助函數(shù)跟衅。它們本質(zhì)是都是語法糖孵睬,將store中的state、getter映射到局部計(jì)算屬性伶跷,或?qū)utations和actions映射到局部方法掰读,從而避免使用this.$store的方式來訪問相應(yīng)的函數(shù)秘狞。當(dāng)然,并不是所有情況下都能使用輔助函數(shù)蹈集,如created生命周期鉤子函數(shù)中或條件語句中谒撼。請注意,使用Vuex并不意味著我們需要將所有的狀態(tài)放入Vuex雾狈。如果有些狀態(tài)嚴(yán)格屬于單個組件,最好還是作為組件的局部狀態(tài)抵皱。我們應(yīng)該根據(jù)應(yīng)用開發(fā)需要進(jìn)行權(quán)衡和確定善榛。如CartControl組件中的count狀態(tài)和每個cart組件綁定不能單例化,因此不需要用放入Vuex來管理呻畸。
├─store
│ actions.js
│ getters.js
│ index.js
│ mutation-types.js
│ mutations.js
7. 如下面的文件路徑樹所示移盆,本項(xiàng)目plugins文件夾中有兩個插件。插件會為Vue.js添加全局功能伤为,本質(zhì)上就是封裝更好咒循,復(fù)用性更強(qiáng)的組件,Vuex和vue-route就是這樣兩個優(yōu)秀的插件绞愚。如果我們想在任意組件中彈出模態(tài)框和信息提示叙甸、控制加載動畫等,怎么辦位衩?每個組件都加一個布爾控制變量肯定導(dǎo)致代碼冗余難以維護(hù)裆蒸。用Vuex集中在根組件中處理也好不到哪里去,因?yàn)橐獙懞芏啻a來判斷事件類型糖驴。所以我們得自己開發(fā)插件僚祷,感興趣的話可參考vue-dialog和vue-toast的代碼自行開發(fā)一個Loading插件。那樣就可以在任意組件中控制加載動畫了贮缕。├─plugins
│ ├─vue-dialog
│ │ │ index.js
│ │ │ VueDialog.vue
│ │ └─themes
│ │ default.css
│ │ ios.css
│ └─vue-toast
│ │ index.js
│ └─components
│ Notification.vue
│ VueToast.vue
8. 當(dāng)我們build好代碼辙谜,在IE或手機(jī)自帶的Android瀏覽器上運(yùn)行時,發(fā)現(xiàn)添加商品到購物車的功能用不了感昼。報(bào)錯:Vuex requires a Promise polyfill in this browser装哆,原來Vuex依賴Promise。如果瀏覽器并沒有實(shí)現(xiàn)Promise抑诸,那么可以使用一個 polyfill的庫烂琴,例如babel-polyfill。第一步: 安裝npm install --save babel-polyfill第二步:在webpack.config.js文件中蜕乡,將entry的代碼改為如下:entry: {? ? app: ["babel-polyfill", "./src/main.js"] }或者將下列代碼添加到使用Vuex之前的一個地方:import 'babel-polyfill'
三奸绷、有待實(shí)現(xiàn)和改進(jìn)功能:
1. 登錄注冊模塊
2. 會員模塊
3. 支付模塊
4. 列表搜索排序功能
5. 圖片列表的懶加載
6. 輪播圖效果
7. 加入購物車動畫
8. 頁面切換動畫
Vue.js devtools是一個很好的測試Vue.js應(yīng)用的工具,對Vuex也支持得很好层玲,值得擁有号醉。