风袖电商小程序笔记第一周【走进Web全栈工程师】

购买了慕课网7七月老师的Java全栈课程,本着花了钱就认真学,在这里分周做笔记,也写一下这个过程遇到的坑(毕竟是一个6年前的课程了),怎么解决的,好在以后方便查看(我可不想看第二遍,也看不下去)

2025.11.25补充:还是需要重新完善一下内容,方便以后看懂,或者完全依靠看文章就可以把项目写完……

Java全栈序章

及时当勉励,岁月不待人

  • 与以往的课程不同,Java全栈课程是把主流的Web技术串在一起,做一个真正的产品——前端的数据均有CMS控制

  • 整个课程分4个阶段:第一阶段小程序,第二阶段小程序后端API,第三阶段CMS后端API,第四阶段CMS前端;有一种前后后前的开发流程;(小程序-C端API-CMS前端-CMS-API;)

  • 课程覆盖小程序、Vue、Java和Spring Boot等知识面;

  • 课程中的CMS的远比C端开发难,CMS会涉及到权限的访问和数据的修改等;先行体验CMS:CMS地址:http://sleeve.7yue.pro (旧的不能访问);新访问地址:http://sleeve.talelin.com/

  • 既能做一个有成就感的大项目又能学习技术;课程比较均衡:小程序-Java Spring Boot-Vue3-CMS

长期课程规划与前置要求

非快餐课,长期维护,后续可能增加第二期,比如Spring Cloud;

前置基础要求:

  • 小程序要求:JS、CSS、小程序基础、自定义组件、ES6、Promise;(不管基础跟着项目走、自学自补、参考《纯正商业应用微信小程序实战》)
  • Java&Java SpringBoot:良好的Java语法基础(注解、接口、类、IOC),MySQL的基础知识
  • Vue:有良好的小程序基础Vue其实就差不多的,初中高级Vue技术点会讲到

第一次作业(风袖细节分析)

研究线上风袖小程序的功能,制作思维导图;

重点:SKU和SPU的设计;

举例1:默认SKU和选择SKU:在淘宝一般都是逛的心态,所以一般不会有默认SKU(当然2025年淘宝是默认SKU的),需要用户自己去选择,而在京东,SKU一般是默认选择好的,用户看中就下单(2025年京东也是以SKU为主);

举例2:SKU的规格数量计算:在一些产品上,比如衣服,有3个颜色,每一个颜色都有3个图案,每一个图案有3个尺码,但是其中一个颜色的一个图案库存是0,这个时候就不能让用户去选择这个图案,包括这个图案下面的所有尺码,其他图案可以正常选择;

举例3:SKU可以被反选;

举例4:SKU要选择完整才可以购买和加入购物车,否则会提示你需要再选择哪个规格,并且阻止你加购物车或者购买,比如上面衣服例子,只选择颜色和图案,不选尺码,点击加购物车就会提示你请选择尺码;

举例5:在库存充足的情况下显示库存多少,在库存紧缺的时候,显示仅剩XX件并且标红,营造商品火爆的氛围;

举例6:在库存只有7件的时候,可以加购7件,但是写到8件,下方的加入购物车或者立即下单就会变成暂时缺货并且禁止点击;

举例7:可视规格->SKU图片

思维导图:

小程序注册与新建项目

微信公众平台:https://mp.weixin.qq.com/cgi-bin/loginpage?url=/cgi-bin/home/t=home/index&lang=zh_CN

小程序注册:https://mp.weixin.qq.com/wxopen/waregister?action=step1&source=mpregister&token=&lang=zh_CN(建议使用企业资质)

获取AppID(小程序ID):开发与服务-开发管理-开发者ID

一定要写自己的AppID,否则一些微信接口可能后续无法使用。

首页布局详尽分析

主要分析首页的主题Theme和Banner:(为什么这里需要分析和区分这些,因为这些在后面的数据库设计会提到,这里的规划和数据库相关,具体可以参考第三周的———-学会抽象来简化数据库表设计)

  • 每周上新有两个,但是Banner的每周上新,每一张图片可以跳转到主题或者商品详情页上去,

  • 而主题只需要多了一个查看更多,并且只需要一张入口图,就可以跳转到不同的商品详情页。

第二次作业(LinUI瀑布流)

LinUI开源地址:https://github.com/TaleLin/lin-ui

LinUI文档:https://doc.mini.talelin.com/

  • 安装:
1
2
npm init
npm install lin-ui

执行成功后,会在根目录里生成项目依赖文件夹 node_modules/lin-ui (小程序IDE的目录结构里不会显示此文件夹)。
然后用小程序官方IDE打开我们的小程序项目,找到 工具 选项,点击下拉选中 构建npm ,等待构建完成即可。

  • 根目录创建components文件夹放自定义组件,创建l-demo文件夹放l-demo自定义组件。

    l-demo.js

    在properties添加data,属性值Object来接受父组件传递的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // components/l-demo/l-demo.js
    Component({
    /**
    * 组件的属性列表
    */
    properties: {
    data:Object//接受父组件传递的数据
    },
    /**
    * 组件的初始数据
    */
    data: {

    },

    /**
    * 组件的方法列表
    */
    methods: {
    },
    })

    l-demo.json

    引入组件l-card和l-price

    1
    2
    3
    4
    5
    6
    7
    {
    "component": true,
    "usingComponents": {
    "l-card":"/miniprogram_npm/lin-ui/card/index",
    "l-price":"/miniprogram_npm/lin-ui/price/index"
    }
    }

    l-demo.wxml

    自定义组件基本结构,这里使用到l-card和l-price这两个组件,一定要引入(当时没有引入l-pric导致显示不了价格)

    这里还需要注意data.XXXX里面不能为空,否则会警告,解决方式就是后面加上|| ‘’

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <l-card full type="cover" image="{{data.image|| ''}}" title="{{data.title|| ''}}" l-class="life-container" l-img-class="life-img" l-title-class="life-title" bindtap="onProduct">
    <view class='life-product-contianer'>
    <view class='art-content'>{{data.describe}}</view>
    <view class='price-container'>
    <l-price unit="¥" value="{{data.count|| ''}}" value-color="#ad0e11" unit-color="#ad0e11" value-size="36"></l-price>
    <l-price l-class="del-price" unit="¥" value="{{data.delCount|| ''}}" value-color="#ad0e11" unit-color="#ad0e11" deleted del-color="#666" value-color="#666" unit-color="#666"></l-price>
    </view>
    </view>
    </l-card>

    l-demo.wxss

    自定义组件样式

    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
    /* demo.wxss */
    .life-container {
    width: 350rpx !important;
    padding: 0 0 20rpx 0 !important;
    background-color: #fff !important;
    box-shadow: 0px 8rpx 20rpx 0px rgba(9, 36, 66, 0.04) !important;
    border-radius: 16rpx !important;
    overflow: hidden;
    margin-bottom: 20rpx;
    }

    .life-img {
    width: 350rpx !important;
    height: 380rpx !important;
    }

    .life-product-contianer {
    padding: 0 10rpx;
    display: flex;
    flex-direction: column;
    }

    .life-title {
    margin-left: 10rpx !important;
    }

    .tag {
    height: 30rpx !important;
    line-height: 30rpx !important;
    }

    .art-content {
    font-size: 28rpx;
    color: #999;
    margin-top: 15rpx;
    display: flex;
    flex-wrap: wrap;
    }
  • 在根目录创建文件夹img,存放一张名为demo.png的图片

  • 修改page/index页面

    index.js

    这里需要注意:/img/demo.png,要写相对路径不能写绝对路径

    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    // pages/index/index.js
    Page({

    /**
    * 页面的初始数据
    */
    data: {
    demo:[{
    image: '/img/demo.png',
    title: '显瘦中长款系带风衣',
    describe: '柔软顺滑、上身挺括显瘦,垂坠飘逸、不易皱好打理。',
    count: '888',
    delCount: '666'
    },
    {
    image: '/img/demo.png',
    title: '显瘦中长款系带风衣',
    describe: '柔软顺滑、上身挺括显瘦,垂坠飘逸、不易皱好打理。柔软顺滑、上身挺括显瘦,垂坠飘逸、不易皱好打理。',
    count: '888',
    delCount: '666'
    },
    {
    image: '/img/demo.png',
    title: '显瘦中长款系带风衣',
    describe: '柔软顺滑、上身挺括显瘦,垂坠飘逸、不易皱好打理。',
    count: '888',
    delCount: '666'
    },
    {
    image: '/img/demo.png',
    title: '显瘦中长款系带风衣',
    describe: '柔软顺滑、上身挺括显瘦,垂坠飘逸、不易皱好打理。',
    count: '888',
    delCount: '666'
    }]

    },

    /**
    * 生命周期函数--监听页面加载
    */
    onLoad(options) {
    wx.lin.renderWaterFlow(this.data.demo, false ,()=>{
    console.log('渲染成功')
    })
    },

    /**
    * 生命周期函数--监听页面初次渲染完成
    */
    onReady() {

    },

    /**
    * 生命周期函数--监听页面显示
    */
    onShow() {

    },

    /**
    * 生命周期函数--监听页面隐藏
    */
    onHide() {

    },

    /**
    * 生命周期函数--监听页面卸载
    */
    onUnload() {

    },

    /**
    * 页面相关事件处理函数--监听用户下拉动作
    */
    onPullDownRefresh() {

    },

    /**
    * 页面上拉触底事件的处理函数
    */
    onReachBottom() {

    },

    /**
    * 用户点击右上角分享
    */
    onShareAppMessage() {

    }
    })

    index.json

    引入组件这里引入了瀑布流和自定义两个组件,可以在index页面使用

    1
    2
    3
    4
    5
    6
    {
    "usingComponents": {
    "l-water-flow":"/miniprogram_npm/lin-ui/water-flow/index",
    "l-demo":"/components/l-demo/l-demo",
    }
    }

    index.wxml

    使用自定义组件:

    1
    <l-water-flow generic:l-water-flow-item="l-demo"/>

至此使用linui瀑布流基本结束!

项目必须是动态的CMS可控

  • 一个真实可用的项目,不会在本地写死图片和数据的,比如首页的活动图片,今天可能是这个活动,后天是另外活动,图片数据这些都是可以变化的,一切都是由后端的CMS决定的。比如现在的淘宝、京东都是这样,想要修改什么,只需要后台修改就可以;
  • 这样做的好处个人觉得是可以软更新,快速更新,当然这里不只是指电商,电商和小程序是不得不这样操作的,小程序不可能给你那么多图片写死在上面,小程序对包的大小是有限制的,那可以用外链?可以是可以,但是这个链接写死在代码,以后更改只能又重新修改上传代码非常麻烦,电商更是如此,电商不可能总是那几张图片的,每天每周都有可能更新价格图片等;除了小程序和电商这种业务,一般的APP其实也可以这样做,只不过对带宽的要求更高,因为每一步操作都需要去请求后端数据;好处就是不用用户去重新下载更新app;

WebStorm开发小程序必备设置(废弃)

不使用WebStorm来开发小程序,改用为vscode,这里使用的是大前端Brian老师的vscode插件,可以在根目录创建.prettierrc文件,就可以实现文件保存自动格式化的功能:

.prettierrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"overrides": [
{
"files": "*.wxml",
"options": { "parser": "html" }
},
{
"files": "*.wxss",
"options": { "parser": "css" }
},
{
"files": "*.wxs",
"options": { "parser": "babel" }
}
]
}

临时补充:appkey的申请

之前的是http://7yue.pro 现在改为:https://talelin.unna.com.cn 其实截止2025年已经废弃;最新的还是https://talelin.com/

appkey申请课程补充资料

调用服务端API获取数据

获取了appid,这里做第一次请求数据,根目录下创建config文件夹,config文件夹下创建config.js文件,作为配置文件

config\config.js

1
2
3
4
5
const config = {
appkey: "yourappid",
apiBase: "http://se.talelin.com/",
};
export { config };

一个完整的网络get请求:

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
11
12
onLoad(options) {
wx.request({
url: "http://se.talelin.com/v1/theme/by/names",
method: "GET",
data: {
names: "t-1",
},
header: {
appkey: "yourappid",
},
});
},

小程序开发前必备配置

需要开启不校验合法域名、web-view(业务域名)、TLS版本以及 HTTPS 证书

配置文件与第一次调用服务端API

上面写了config配置文件,接下来用这个配置文件来完成调用后端的API接口,并在首页显示折扣图片

pages\home\home.js

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// pages/home/home.js
import { config } from "../../config/config";
Page({
/**
* 页面的初始数据
*/
data: {
topTheme: null,
},

/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
wx.request({
url: `${config.apiBaseUrl}theme/by/names`,
method: "GET",
data: {
names: "t-1",
},
header: {
appkey: config.appkey,
},
success: (res) => {
// console.log(res);
this.setData({
topTheme: res.data[0],
});
},
});
},

/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},

/**
* 生命周期函数--监听页面显示
*/
onShow() {},

/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {},

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},

/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});

pages\home\home.wxml

1
2
3
4

<view>
<image class="top-theme" src="{{topTheme.entrance_img}}" />
</view>

业务对象的重要性

对于前端page的js文件应该承担什么样的工作,page文件下的wxss写样式,wxml写骨架,json写页面的配置,一般情况下,js应该是写逻辑的,但是page下的js不应该把逻辑写在里面,因为这会导致代码混论,后期难以维护;页面的JS应该写数据绑定,可以理解为view视图层和业务逻辑之间的桥梁,或者说是中间层

后端常见的分层结构->MVC,页面的JS很像C->Controller,Controller不应该写业务,Model写业务,Model可以分Model、Logic、Service

前端虽然不用像后端那样有严格的分层,但是最少也得分出一层Model层来写业务

Model层按照业务类型来写

上面是视频里面的大概意思,但是按照我的想法,其实就是一个数据接口请求的逻辑,负责处理请求数据的一些操作;在后面还会学习到wxs,这个也是js,但是是属于页面上的逻辑

在根目录创建model文件夹,创建theme.js文件,存放获取主题的业务逻辑。

model\theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { config } from "../../config/config";
//业务对象
// theme、banner、spu、sku、address、user
class Theme {
// 不用实例化可以直接调用
static getHomeLocationA() {
wx.request({
url: `${config.apiBaseUrl}theme/by/names`,
method: "GET",
data: {
names: "t-1",
},
header: {
appkey: config.appkey,
},
success: (res) => {
// console.log(res);
this.setData({
topTheme: res.data[0],
});
},
});
}
}

封装HTTP请求

上面的theme.js是有问题的,this.setData不能直接在model使用,所以需要改写:

model\theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//业务对象
// theme、banner、spu、sku、address、user
import { config } from "../config/config";
class Theme {
// 不用实例化可以直接调用
static getHomeLocationA(callback) {
wx.request({
url: `${config.apiBaseUrl}theme/by/names`,
method: "GET",
data: {
names: "t-1",
},
header: {
appkey: config.appkey,
},
success: (res) => {
// console.log(res);
callback(res.data);
},
});
}
}
// 导出
export { Theme };

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
Theme.getHomeLocationA((data) => {
this.setData({
topTheme: data[0],
});
});
},

第一次封装:

在utils文件夹下新建http.js文件

utils\http.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { config } from "../config/config";
class Http {
static request({ url, data, callback, method = "GET" }) {
wx.request({
url: `${config.apiBaseUrl}${url}`,
data,
method,
header: {
appkey: config.appkey,
},
success(res) {
callback(res.data);
},
});
}
}
export { Http };

重写theme.js

model\theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//业务对象
// theme、banner、spu、sku、address、user
import { config } from "../config/config";
import { Http } from "../utils/http";
class Theme {
// 不用实例化可以直接调用
static getHomeLocationA(callback) {
Http.request({
url: "theme/by/names",
data: {
names: "t-1",
},
callback: (data) => {
callback(data);
},
});
}
}
// 导出
export { Theme };

三层调用:home.js调用Theme.js,Theme.js调用http.js;这里回调函数里面又有回调函数,看着很混乱,后续需要再优化……

小程序中使用async和await的难点分析

处理异步最基本的方式是callback,进阶一点的是使用promise,最后是async await;上面的封装就是使用最基础的callback回调函数的方式;

这节中将使用async/await封装,async/await是基于promise;async 声明一个函数是异步的,await 等待一个Promise完成,并返回结果;但是截止2025年11月7号,微信小程序的网络请求接口也不支持返回promise,所以需要封装一个函数,让微信小程序的网络请求接口支持返回promise,

使用LinUI Promisic 让小程序内置API支持promise

在LinUI 有一个函数库Promisic,可以让微信小程序的网络请求接口支持返回promise。具体用法:

utils/util.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const promisic = function (func) {
return function (params = {}) {
return new Promise((resolve, reject) => {
const args = Object.assign(params, {
success: (res) => {
resolve(res);
},
fail: (error) => {
reject(error);
}
});
func(args);
});
};
},
px2rpx = function (n) {
const { screenWidth: t } = wx.getSystemInfoSync();
return (750 / t) * n;
};
export { promisic, px2rpx };

utils/http.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { config } from "../config/config";
import { promisic } from "../utils/util";
class Http {
static async request({ url, data, method = "GET" }) {
const res = await promisic(wx.request)({
url: `${config.apiBaseUrl}${url}`,
data,
method,
header: {
appkey: config.appkey,
},
});
return res.data;
}
}
export { Http };

model/theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Http } from "../utils/http";
class Theme {
// static,不用实例化可以直接调用;Http.reques已经返回是一个promise,在这里直接返回就可以
static getHomeLocationA() {
return Http.request({
url: "theme/by/names",
data: {
names: "t-1",
},
});
}
}
// 导出
export { Theme };

home.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 页面的初始数据
*/
data: {
topTheme: null,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
// await去接受promise
const data = await Theme.getHomeLocationA();
console.log("传回数据:", data);
this.setData({
topTheme: data[0],
});
},

将回调函数全部替换为async和await

在上一节中已经替换。

获取banner数据

上面已经封装了http,也让微信小程序的网络请求接口支持返回promise,可以直接使用async/await的写法,那接下来写banner数据的获取就简单多了

model\banner.js

1
2
3
4
5
6
7
8
9
10
11
12
import { Http } from "../utils/http";
class Banner {
static LocationB = "b-1";
static getHomeLocationB() {
return Http.request({
url: `banner/name/${Banner.LocationB}`,
});
}
}
// 导出
export { Banner };

pages\home\home.js

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
// pages/home/home.js
import { Banner } from "../../model/banner";
import { Theme } from "../../model/theme";
Page({
/**
* 页面的初始数据
*/
data: {
themeA: null,
bannerB: null,
},

/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
this.initAllData();
},
// 初始化数据
async initAllData() {
// await去接受promise;获取顶部图片
const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
this.setData({
themeA: themeA[0],
bannerB: bannerB,
});
},

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},

/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});

pages\home\home.wxml

1
2
3
4

<view>
<image class="top-theme" src="{{themeA.entrance_img}}" />
</view>

主要添加了Banner这个业务对象来请求Banner数据,对home.js进行了优化,把初始化数据统一写到一个函数里面。然后在onLoad的时候调用,优化了themeA和bannerB命名,需要在home.wxml修改为themeA.entrance_img,

banner轮播图实现与插槽的基本使用

获取到banner数据后,接下来完成banner的展示:

pages\home\home.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<view>
<image class="top-theme" src="{{themeA.entrance_img}}" />

<swiper
class="swiper"
indicator-dots
indicator-active-color="#157658"
autoplay
circular
>
<block wx:for="{{bannerB.items}}" wx:key="index">
<swiper-item>
<image class="swiper" src="{{item.img}}"></image>
</swiper-item>
</block>
</swiper>
</view>

pages\home\home.wxss

1
2
3
4
5
6
7
8
9
/* pages/home/home.wxss */
.top-theme {
width: 100%;
height: 260rpx;
}
.swiper{
width: 100%;
height: 360rpx;
}

这里主要使用到微信小程序内置的swiper组件,由于轮播图数量是后端决定,所以这里需要用到for循环,indicator-dots是显示圆点,indicator-active-color=”#157658” 设置圆点颜色,autoplay自动播放,circular循环播放

npm的Semver语法规则

接下来需要完成的是首页的六宫格,这里需要考虑的一点,这个项目是真实的项目,大都是由CMS控制的,这样做的好处是,可以“软更新”,说回九宫格,以后这里可能是3个分类,或者12个分类,都要确保这个宫格是均分的,所以这里就需要使用LinUI的组件,所以这节也不是讲的是LinUI组件的安装与使用,安装与使用在下一节课,六宫格实现的章节在随后。

  • 安装nodejs LTS版本(建议使用nvm)视频安装版本:node=>10.16.0,npm=>6.9.0
  • 项目根目录 npm init
  • 安装LinUI:npm install lin-ui
  • 执行成功后,会在根目录里生成项目依赖文件夹 node_modules/lin-ui (小程序IDE的目录结构里不会显示此文件夹)。
    然后用小程序官方IDE打开我们的小程序项目,找到 工具 选项,点击下拉选中 构建npm ,等待构建完成即可。

Semver语义化版本:

  1. 主版本号:当你做了不兼容的 API 修改,
  2. 次版本号:当你做了向下兼容的功能性新增,
  3. 修订号:当你做了向下兼容的问题修正

package.json

1
2
3
4
"dependencies": {
"lin-ui": "^0.9.13",
"lin-ui": "~0.9.13"
}

^0.9.13向上箭头是指当这个组件次要版本升级的时候,在重新安装的时候会自动安装最新的次要版本,如最新的是0.10.1,那这里虽然写的是0.9.13,实际安装的是最新的0.10.1,如果只是想升级到最新的修订号,可以写号,如 `”lin-ui”: “0.9.13”`

LinUI安装、主题色配置与按需加载

具体参考文档;https://doc.mini.talelin.com/start/

LinUI Grid组件构建六宫格(上)

根目录创建components文件夹,存放自定义组件,新建category-grid文件夹,编写自定义组件

想要使用linui的 Grid组件,首先需要引入,所以这里可以在app.json下全局引入这个组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"pages": [
"pages/home/home"
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin",
"navigationBarBackgroundColor": "#ffffff"
},
"style": "v2",
"componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json",
"usingComponents": {
"l-grid": "/miniprogram_npm/lin-ui/grid/index",
"l-grid-item": "/miniprogram_npm/lin-ui/grid-item/index"
},
"lazyCodeLoading": "requiredComponents"
}

LinUI Grid组件构建六宫格(中)

自定义组件的图片和文字是由后端决定的,那也就是这里需要发送一次请求,有两种方式,一种是在自定义组件的jS去请求接口,这样方便,另外一种是由调用方去请求接口,相对复杂一点,但是严谨,也确保自定义组件的纯粹性;这里采用第二种;

创建category业务对象接口

model\category.js

1
2
3
4
5
6
7
8
9
10
import { Http } from "../utils/http";

class Category {
static getHomeLocationC() {
return Http.request({
url: "category/grid/all",
});
}
}
export { Category };

home页面发送请求

pages\home\home.js

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
/**
* 页面的初始数据
*/
data: {
themeA: null,
bannerB: null,
grid: [],
},

/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
this.initAllData();
},
// 初始化数据
async initAllData() {
// await去接受promise;获取顶部图片
const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
this.setData({
themeA: themeA[0],
bannerB: bannerB,
grid: grid,
});
},

注册自定义组件:

1
2
3
4
5
{
"usingComponents": {
"s-category-grid": "/components/category-grid/index"
}
}

骨架,子组件接受grid数据

pages\home\home.wxml

1
<s-category-grid grid="{{grid}}"></s-category-grid>

子组件接受数据:

components\category-grid\index.js

1
2
3
4
5
6
/**
* 组件的属性列表
*/
properties: {
grid: Array,
},

自定义子组件骨架:

components\category-grid\index.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--comment4-->
<view class="container">
<l-grid wx:for="{{grid}}">
<block>
<l-grid-item>
<view>
<image src="{{item.img}}"></image>
<text>{{item.title}}</text>
</view>
</l-grid-item>
</block>
</l-grid>
</view>

至此,六宫格的数据请求和显示已经完成,接下来是完成六宫格的样式

LinUI Grid组件构建六宫格(下)

自定义组件不能直接设置class,如需要设置,需要使用外部样式类,插槽可以自定义内容,外部样式类可以自定义样式

components\category-grid\index.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<view class="container">
<l-grid l-class="innder-container">
<block wx:for="{{grid}}" wx:key="index">
<l-grid-item key="{{index}}" slot="{{index}}">
<view class="grid-item">
<image src="{{item.img}}" class="img"></image>
<text class="text">{{item.title}}</text>
</view>
</l-grid-item>
</block>
</l-grid>
</view>

components\category-grid\index.wxss

grid-item设置width是为了点击区域扩大

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
/* components/category-grid/index.wxss */
.container{
height: 320rpx;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.innder-container{
width: 730rpx;
height: 300rpx;
border: 1px dashed #deded6;

}
.grid-item{
width: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.img{
height: 60rpx;
width: 60rpx;
}
.text{
margin-top: 6rpx;
font-size: 24rpx;
color: #333333;
}

组件设计与linui使用的几个非常重要原则

必须在组件的灵活性和易用性之间做出一个选择,找到一个平衡点:

  • 越是灵活的组件,易用性就越差(复杂度高),越是简单的组件,灵活性和定制性就不够
  • 组件的默认值应该选在一个 能普遍适应50%,否则不是一个好用的组件,每一个组件都需要自定义非常繁琐。

组件到底意义是什么:

  • 组件从三方面:样式、骨架、业务逻辑上对代码进行封装,方便开发者在日后复用样式、骨架、业务逻辑

组件必须要提供给用户的几个特点与方法:

  • 组件必须允许用户通过某种方式对组件进行“自定义”,不能自定义或者自定义很弱的组件不是一个好的组件设计,自定义通常包括以下3种:

    1. 对样式自定义
    2. 对骨架自定义
    3. 对业务逻辑自定义

    从现有技术手段。对于以上的自定义,微信小程序提供了以下几种方式来支持自定义:

    1. 外部样式类(对样式自定义)
    2. Slot插槽(对骨架自定义)
    3. 业务逻辑(Behavior行为,一般通过预设好的几种方式,然后给组件传参数选择)

    但是正如课程中所谈到,业务逻辑自定义存在以下2个问题:

    1. Behavior依然繁琐
    2. 组件的业务逻辑自定义其实从某种角度来讲,是一个伪命题

    为什么是伪命题,因为组件A之所以是组件A,而不是组件B,正是由于他自己独特的业务逻辑,如果改变了业务逻辑,那么组件A可能就不应该是组件A了,所以,组件对于业务逻辑性的自定义有待商榷

没有明确理由不要固定高和宽

页面布局的时候,如果不加宽高就能实现的效果,那建议就不加,让页面自适应,因为加了之后不好修改。

  • 固定宽高,必须要有理由,比如为了点击时间的区域更大
  • 固定宽高,弊端就是版本更迭繁琐,修改和维护不方便
  • 固定宽高,数值难以确定

周总结

在这一周内,主要完成了HTTP的封装,让让微信小程序的网络请求接口支持返回promise,引入LinUI组件,完成首页的顶部图片,轮播图和六宫格;

其中重点是学习封装HTTP请求、promise和小程序中使用async和await

就项目本身而言,其实讲解到的内容很少,主要时长都是对内容深度的探讨,对涉及到项目整体宽度反而较少。