SKU開發是小程序中最難的一部分,思路在分析中已經記錄過了,這里主要看一下代碼的實現,感覺老師寫的代碼太棒了,很優雅!主要想記錄一下寫代碼的思路,對面向對象編程的實踐。
一、代碼結構的分析
1、說明幾個關鍵詞
搞清楚sku的概念,搞清楚我們抽象出來的realm組件、fence組件、cell組件以及他們對應的模型類,這里模型類放到models文件夾中
realm組件 --- fence-group.js中的FenceGroup模型
fence組件 --- fence.js中的Fence模型
cell組件 --- cell.js中Cell模型
除此之外,還有
矩陣的處理模型:matrix.js中的Matrix模型
總控制模型(負責方法的調用):judger.js中的Judger模型
sku-code處理模型:sku-code.js中的SkuCode模型
2019年12月10日11:04:41截止,可能后續還會有處理sku規格值的狀態的模型,后續再補充...
2、分析他們之間的聯系(做的圖示)
說明:
圖中所示的箭頭的流向是從用戶的角度來看,當點擊規格值進行選擇時,數據的流向
感想總結:這個結構這樣抽象出來,感覺太清晰了,彼此之前是獨立的,可擴展的,但是彼此之間是有聯系的,各司其職,哇,感覺這樣寫出來的代碼太美好了,原來寫代碼可以這么舒服,好的代碼,好的架構真的讓人耳目一新,回味無窮啊!今后奮斗的方向,寫出好的代碼,優雅而強大!
二、代碼的編寫
這也不是完整的SKU代碼,只是一部分的代碼,只是用來記錄一下整個SKU開發的代碼的結構,看一下簡單的代碼(從realm組件到cell組件),實現SKU規格值的提取(SKU狀態的確定代碼就不記錄了,太復雜了,不過之后可能記錄一下其中編碼思路),順序是按照圖示的順序(開發的過程,並不是嚴格按照這個順序進行編碼的,開發是按照從fence-group出發,細化,抽象出fence,再進一步細化,抽象出cell)
1、cell組件
這里不考慮SKU規格值的狀態的確定,所以cell組件的代碼就相對來說特別簡單,只是來負責將規格值顯示出來(代碼中沒有樣式代碼)
1 // index.wxml代碼 2 <!--規格值組件--> 3 <view bind:tap="onTap" class="container"> 4 <view class="inner-container"> 5 <text>{{cell.title}}</text> 6 </view> 7 </view> 8 9 // index.js代碼 創建cell屬性 10 properties: { 11 cell:Object 12 },
2、cell模型(cell.js)
創建Cell類,有構造方法以及id(規格值id)和title(規格值名稱)兩個屬性
1 class Cell{ 2 id // 規格值的主鍵id 3 title // 規格值的名字 4 5 constructor(spec){ 6 this.title = spec.value 7 this.id = spec.value_id 8 } 9 } 10 11 export { 12 Cell 13 }
3、fence組件
fence組件需要引入cell組件,需要用fence屬性來傳遞數據
1 // index.wxml 2 <view class="container"> 3 <!--規格名--> 4 <view class="title">{{fence.title}}</view> 5 <!--規格名下的所有規格值--> 6 <view class="row-container"> 7 <block wx:for="{{fence.cells}}" wx:key="{{index}}"> 8 <!--規格值組件--> 9 <s-cell class="cell" cell="{{item}}"></s-cell> 10 </block> 11 <view class="hr"></view> 12 </view> 13 </view> 14 15 // index.js 16 properties: { 17 fence: Object 18 }, 19 20 // index.json 21 { 22 "component": true, 23 "usingComponents": { 24 "s-cell":"/components/cell/index" 25 } 26 }
4、fence模型(fence,js)
創建Fence類,有構造方法,cells屬性(存放一個規格名下的一組規格值),specs屬性(spu(商品)的一個確定的規格值的組合,比如:金屬灰-七龍珠-小號 S),title(規格名名稱)以及id(規格名id)以及初始化cell的方法
1 import {Cell} from "./cell"; 2 3 class Fence { 4 5 cells = [] 6 specs 7 title // 規格名的名字 8 id // 規格名的主鍵id 9 10 constructor(specs) { 11 this.specs = specs 12 this.title = specs[0].key 13 this.id = specs[0].key_id 14 } 15 16 init() { 17 this._initCells() 18 } 19 20 _initCells(){ 21 this.specs.forEach(s=>{ 22 // 去重判斷 23 const existed = this.cells.some(c=>{ 24 return c.id === s.value_id 25 }) 26 if(existed){ 27 return 28 } 29 const cell = new Cell(s) 30 this.cells.push(cell) 31 }) 32 } 33 } 34 35 export { 36 Fence 37 }
補充說明數組(Array)中的some方法:
鏈接地址詳解:https://www.runoob.com/jsref/jsref-some.html
some() 方法用於檢測數組中的元素是否滿足指定條件(函數提供)。
some() 方法會依次執行數組的每個元素:
- 如果有一個元素滿足條件,則表達式返回true , 剩余的元素不會再執行檢測。
- 如果沒有滿足條件的元素,則返回false。
注意: some() 不會對空數組進行檢測。
注意: some() 不會改變原始數組。
5、realm組件(對應着fence-group.js)
realm組件需要引用fence組件,需要通過spu屬性來傳遞數據,並且需要監聽spu
1 // index.wxml代碼 2 <view class="container"> 3 <view> 4 <image></image> 5 </view> 6 <block wx:for="{{fences}}" wx:key="{{index}}"> 7 <s-fence fence="{{item}}"></s-fence> 8 </block> 9 <view class="counter-container"> 10 <!--<l-counter></l-counter>--> 11 </view> 12 </view> 13 14 // index.js代碼 15 properties: { 16 spu: Object 17 }, 18 19 data: { 20 judger:Object 21 }, 22 23 observers: { 24 'spu': function (spu) { 25 if (!spu) { 26 return 27 } 28 const fenceGroup = new FenceGroup(spu) 29 fenceGroup.initFences() 30 // judge在這里並沒有用到 31 // const judger = new Judger(fenceGroup) 32 // this.data.judger = judger 33 this.bindInitData(fenceGroup) 34 } 35 }, 36 methods: { 37 bindInitData(fenceGroup) { 38 this.setData({ 39 fences:fenceGroup.fences 40 }) 41 }, 42 } 43 44 // index.json 45 { 46 "component": true, 47 "usingComponents": { 48 "s-fence":"/components/fence/index" 49 } 50 }
補充說明一下,小程序中的observer監聽函數的詳解:
組件數據字段監聽器,用於監聽 properties 和 data 的變化,參見 數據監聽器
數據監聽器鏈接地址:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html
6、fence-group模型(fence-group.js)
創建FenceGroup類,創建spu屬性、skuList屬性以及fences屬性,還有初始化的fence方法(這里用到了矩陣中的轉置方法,具體就不記錄了)
1 import {Matrix} from "./matrix"; 2 import {Fence} from "./fence"; 3 4 class FenceGroup { 5 spu 6 skuList = [] 7 fences 8 9 constructor(spu) { 10 this.spu = spu 11 this.skuList = spu.sku_list 12 } 13 14 initFences() { 15 const matrix = this._createMatrix(this.skuList) 16 const fences = [] 17 // 進行矩陣的轉置操作 18 const AT = matrix.transpose() 19 AT.forEach(r => { 20 const fence = new Fence(r) 21 fence.init() 22 fences.push(fence) 23 }) 24 this.fences = fences 25 } 26 27 _createMatrix(skuList) { 28 const m = [] 29 skuList.forEach(sku => { 30 m.push(sku.specs) 31 }) 32 return new Matrix(m) 33 } 34 35 } 36 37 export { 38 FenceGroup 39 }
說明:矩陣的思想其實在這里簡化了實現思路,可以看一下具體的矩陣是如何進行裝置操作的,可看一下百度百科中的說明:
鏈接地址:https://baike.baidu.com/item/轉置矩陣
三、重要總結
數據的流向問題在啰嗦一下:
一切都是從商品的詳情頁面進行發起的,當用戶點擊商品的時候,跳轉到詳情頁面,用戶點擊加入購物車的功能,商品的規格信息進行顯示,初始化商品的規格信息,這里觸發了realm組件的監聽spu的方法,從而引發多米諾骨效應,從FenceGroup模型的創建,到Fence模型的創建,再到Cell模型的創建,當數據最終創建之后,在通過組件一步一步的渲染。最終呈現在用戶的面前就是可選的一組規格(當然這個功能完善之后,用戶看到的是一組選擇好的規格路徑,這里沒實現),這就是完整的一個過程,當然僅僅只是一個規格值的處理提取的過程。
內容出處:七月老師《從Java后端到全棧》視頻課程
七月老師課程鏈接:https://class.imooc.com/sale/javafullstack