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后端到全栈》视频课程