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 组件的代码就相对来说特别简单, 只是来负责将规格值显示出来(代码中没有样式代码)
- // index.wxml 代码
- <!-- 规格值组件 -->
- <view bind:tap="onTap" class="container">
- <view class="inner-container">
- <text>{{cell.title}}</text>
- </view>
- </view>
- // index.JS 代码 创建 cell 属性
- properties: {
- cell:Object
- },
2,cell 模型(cell.JS)
创建 Cell 类, 有构造方法以及 id(规格值 id)和 title(规格值名称)两个属性
- class Cell{
- id // 规格值的主键 id
- title // 规格值的名字
- constructor(spec){
- this.title = spec.value
- this.id = spec.value_id
- }
- }
- export {
- Cell
- }
3,fence 组件
fence 组件需要引入 cell 组件, 需要用 fence 属性来传递数据
- // index.wxml
- <view class="container">
- <!-- 规格名 -->
- <view class="title">{{fence.title}}</view>
- <!-- 规格名下的所有规格值 -->
- <view class="row-container">
- <block wx:for="{{fence.cells}}" wx:key="{{index}}">
- <!-- 规格值组件 -->
- <s-cell class="cell" cell="{{item}}"></s-cell>
- </block>
- <view class="hr"></view>
- </view>
- </view>
- // index.JS
- properties: {
- fence: Object
- },
- // index.JSON
- {
- "component": true,
- "usingComponents": {
- "s-cell":"/components/cell/index"
- }
- }
4,fence 模型(fence,JS)
创建 Fence 类, 有构造方法, cells 属性 (存放一个规格名下的一组规格值),specs 属性(spu(商品) 的一个确定的规格值的组合, 比如: 金属灰 - 七龙珠 - 小号 S),title(规格名名称)以及 id(规格名 id)以及初始化 cell 的方法
- import {Cell} from "./cell";
- class Fence {
- cells = []
- specs
- title // 规格名的名字
- id // 规格名的主键 id
- constructor(specs) {
- this.specs = specs
- this.title = specs[0].key
- this.id = specs[0].key_id
- }
- init() {
- this._initCells()
- }
- _initCells(){
- this.specs.forEach(s=>{
- // 去重判断
- const existed = this.cells.some(c=>{
- return c.id === s.value_id
- })
- if(existed){
- return
- }
- const cell = new Cell(s)
- this.cells.push(cell)
- })
- }
- }
- export {
- Fence
- }
补充说明数组 (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
- // index.wxml 代码
- <view class="container">
- <view>
- <image></image>
- </view>
- <block wx:for="{{fences}}" wx:key="{{index}}">
- <s-fence fence="{{item}}"></s-fence>
- </block>
- <view class="counter-container">
- <!--<l-counter></l-counter>-->
- </view>
- </view>
- // index.JS 代码
- properties: {
- spu: Object
- },
- data: {
- judger:Object
- },
- observers: {
- 'spu': function (spu) {
- if (!spu) {
- return
- }
- const fenceGroup = new FenceGroup(spu)
- fenceGroup.initFences()
- // judge 在这里并没有用到
- // const judger = new Judger(fenceGroup)
- // this.data.judger = judger
- this.bindInitData(fenceGroup)
- }
- },
- methods: {
- bindInitData(fenceGroup) {
- this.setData({
- fences:fenceGroup.fences
- })
- },
- }
- // index.JSON
- {
- "component": true,
- "usingComponents": {
- "s-fence":"/components/fence/index"
- }
- }
补充说明一下, 小程序中的 observer 监听函数的详解:
组件数据字段监听器, 用于监听 properties 和 data 的变化, 参见 数据监听器
数据监听器链接地址:
6,fence-group 模型(fence-group.JS)
创建 FenceGroup 类, 创建 spu 属性, skuList 属性以及 fences 属性, 还有初始化的 fence 方法(这里用到了矩阵中的转置方法, 具体就不记录了)
- import {Matrix} from "./matrix";
- import {Fence} from "./fence";
- class FenceGroup {
- spu
- skuList = []
- fences
- constructor(spu) {
- this.spu = spu
- this.skuList = spu.sku_list
- }
- initFences() {
- const matrix = this._createMatrix(this.skuList)
- const fences = []
- // 进行矩阵的转置操作
- const AT = matrix.transpose()
- AT.forEach(r => {
- const fence = new Fence(r)
- fence.init()
- fences.push(fence)
- })
- this.fences = fences
- }
- _createMatrix(skuList) {
- const m = []
- skuList.forEach(sku => {
- m.push(sku.specs)
- })
- return new Matrix(m)
- }
- }
- export {
- FenceGroup
- }
说明: 矩阵的思想其实在这里简化了实现思路, 可以看一下具体的矩阵是如何进行装置操作的, 可看一下百度百科中的说明:
链接地址: https://baike.baidu.com/item / 转置矩阵
三, 重要总结
数据的流向问题在啰嗦一下:
一切都是从商品的详情页面进行发起的, 当用户点击商品的时候, 跳转到详情页面, 用户点击加入购物车的功能, 商品的规格信息进行显示, 初始化商品的规格信息, 这里触发了 realm 组件的监听 spu 的方法, 从而引发多米诺骨效应, 从 FenceGroup 模型的创建, 到 Fence 模型的创建, 再到 Cell 模型的创建, 当数据最终创建之后, 在通过组件一步一步的渲染. 最终呈现在用户的面前就是可选的一组规格(当然这个功能完善之后, 用户看到的是一组选择好的规格路径, 这里没实现), 这就是完整的一个过程, 当然仅仅只是一个规格值的处理提取的过程.
内容出处: 七月老师《从 Java 后端到全栈》视频课程
七月老师课程链接: https://class.imooc.com/sale/javafullstack
全栈之路 - 微信小程序 - SKU 开发(代码)
来源: http://www.bubuko.com/infodetail-3325999.html