uni-app.02.提交form表单的两种方式_uniapp form表单提交-CSDN博客

mikel阅读(263)

来源: uni-app.02.提交form表单的两种方式_uniapp form表单提交-CSDN博客

uni-app提交form表单的两种方式
form表单元素较少
比如用户登录,如下图
前端代码举例
此处省略了部分多余代码
<template>
<view style=”padding:50rpx;”>
<view style=”margin-top:60rpx;”>
<form @submit=”submit”>
<view class=”gui-border-b gui-form-item” style=”margin-top:80rpx;”>
<view class=”gui-form-body”>
<input type=”number” class=”gui-form-input” v-model=”driverTel” name=”driverTel” placeholder=”手机号” placeholder-style=”color:#CACED0″/>
</view>
</view>
<view class=”gui-border-b gui-form-item” style=”margin-top:30rpx;”>
<view class=”gui-form-body”>
<input type=”password” class=”gui-form-input” v-if=”isPwd”
v-model=”password” name=”password” placeholder=”密码” placeholder-style=”color:#CACED0″/>
<input type=”text” class=”gui-form-input” v-if=”!isPwd” :disabled=”true”
v-model=”password” name=”password” placeholder=”密码” placeholder-style=”color:#CACED0″/>
</view>
<text class=”gui-form-icon gui-icons gui-text-center”
@click=”changePwdType” :style=”{color:isPwd?’#999999′:’#008AFF’}”>&#xe609;</text>
</view>
<view style=”margin-top:50rpx;”>
<button type=”default” class=”gui-button gui-bg-blue msgBtn” formType=”submit” style=”border-radius:50rpx;”>
<text class=”gui-color-white gui-button-text-max”>登录</text>
</button>
</view>
</form>
</view>
</view>
</template>
<script>
uni.request({
url: _self.server_host + “/app/driver/login/password”,
method:’POST’,
header:{‘content-type’ : “application/x-www-form-urlencoded”},
data:{
// 对于上面的form表单提交,我们可以直接在uni.request的data属性中直接提交就行了
driverTel: _self.driverTel,
password: _self.password
},
success: (res) => {
// 服务器返回结果
}
})
</script>
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
后端代码举例
/**
* 这里可以以一个实体类来接收,实体类必须包含前端传参参数的对应字段
*/
@PostMapping(“/password”)
public Result loginByPassword(LoginUserVO loginUserVO) {
// 处理业务逻辑
}
/**
* 也可以按照字段名来接收
*/
@PostMapping(“/password”)
public Result loginByPassword(String username, String passsword) {
// 处理业务逻辑
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
form表单元素较多
上面的方法在form表单元素较多时处理起来就比较费事了,一般像新增用户、商品之类的form表单少则十几个,多则几十个。如下图:
如果按照上面的写法,不仅前端写起来费事不雅观,后台接受也要一个字段一个字段的接收也煞是费劲,这个时候我们可以定义一个对象formData,将数据保存到里面提交时直接提交JSON字符串到后端,后端接收到JSON字符串后转成JSON对象,然后再进行自己的业务逻辑处理
前端代码举例:
<!– 表单元素核心区域 –>
<scroll-view :scroll-y=”true” :show-scrollbar=”false” :style=”{height:mainHeight+’px’}”>
<!– 第1步 –>
<view class=”gui-padding” v-if=”step == 1″>
<view class=”gui-form-item gui-border-b”>
<text class=”gui-form-label”>所属客户</text>
<view class=”gui-form-body”>
<picker mode=”selector” :range=”tenantList” :value=”tenantIndex” @change=”tenantChange($event,tenantList)” :range-key=”‘tenantName'”>
<view class=”gui-flex gui-rows gui-nowrap gui-space-between gui-align-items-center”>
<text class=”gui-text”>{{tenantList[tenantIndex].tenantName}}</text>
<text class=”gui-form-icon gui-icons gui-text-center gui-color-gray”>&#xe603;</text>
</view>
</picker>
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>姓名</text>
<view class=”gui-form-body”>
<input type=”text” class=”gui-form-input” v-model=”formData.driverName” placeholder=”请输入姓名” />
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>手机号</text>
<view class=”gui-form-body”>
<input type=”text” class=”gui-form-input” v-model=”formData.driverTel” placeholder=”请输入手机号” />
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>身份证号码</text>
<view class=”gui-form-body”>
<input type=”text” class=”gui-form-input” v-model=”formData.idNumber” placeholder=”请输入身份证号码” />
</view>
</view>
<view class=”gui-margin-top”>
<text class=”gui-form-label” style=”width: 100%;”>身份证照片(个人信息面)</text>
</view>
<view class=”gui-idcard-items gui-img-in gui-flex gui-rows gui-justify-content-center”>
<view class=”gui-idcard-items-image” @tap=”selectIdPhotoPositive”>
<gui-image :src=”formData.idPhotoPositive” :width=”380″></gui-image>
</view>
</view>
<view class=”gui-margin-top”>
<text class=”gui-form-label” style=”width: 100%;”>身份证照片(国徽图案面)</text>
</view>
<view class=”gui-idcard-items gui-img-in gui-flex gui-rows gui-justify-content-center”>
<view class=”gui-idcard-items-image” @tap=”selectIdPhotoReverse”>
<gui-image :src=”formData.idPhotoReverse” :width=”380″></gui-image>
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>证件有效期</text>
<view class=”gui-form-body”>
<picker class=”gui-form-picker” mode=”date” @change=”idNumberValidUntilChange”>
<text class=”gui-text”>{{formData.idNumberValidUntil}}</text>
<text class=”gui-icons icon-arrow-down” style=”margin-left:5px;”></text>
</picker>
</view>
</view>
<view class=”gui-form-item gui-border-b”>
<text class=”gui-form-label”>收款人</text>
<view class=”gui-form-body”>
<picker mode=”selector” :range=”payeeList” :value=”payeeIndex” @change=”payeeChange($event,payeeList)” :range-key=”‘payeeName'”>
<view class=”gui-flex gui-rows gui-nowrap gui-space-between gui-align-items-center”>
<text class=”gui-text”>{{payeeList[payeeIndex].payeeName}}</text>
<text class=”gui-form-icon gui-icons gui-text-center gui-color-gray”>&#xe603;</text>
</view>
</picker>
</view>
</view>
</view>
<!– 第2步 –>
<view class=”gui-padding” v-if=”step == 2″>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>驾驶证编号</text>
<view class=”gui-form-body”>
<input type=”text” class=”gui-form-input” v-model=”formData.drivingLicenseNumber” placeholder=”请输入驾驶证编号” />
</view>
</view>
<view class=”gui-margin-top”>
<text class=”gui-form-label” style=”width: 100%;”>驾驶证(主页)</text>
</view>
<view class=”gui-idcard-items gui-img-in gui-flex gui-rows gui-justify-content-center”>
<view class=”gui-idcard-items-image” @tap=”selectDrivingLicensePhoto”>
<gui-image :src=”formData.drivingLicensePhoto” :width=”380″></gui-image>
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>有效期开始</text>
<view class=”gui-form-body”>
<picker class=”gui-form-picker” mode=”date” @change=”drivingLicenseValidityStartChange”>
<text class=”gui-text”>{{formData.drivingLicenseValidityStart}}</text>
<text class=”gui-icons icon-arrow-down” style=”margin-left:5px;”></text>
</picker>
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>有效期结束</text>
<view class=”gui-form-body”>
<picker class=”gui-form-picker” mode=”date” @change=”drivingLicenseValidityEndChange”>
<text class=”gui-text”>{{formData.drivingLicenseValidityEnd}}</text>
<text class=”gui-icons icon-arrow-down” style=”margin-left:5px;”></text>
</picker>
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>发证机关</text>
<view class=”gui-form-body”>
<input type=”text” class=”gui-form-input” v-model=”formData.drivingLicenseIssuingOrg” placeholder=”请输入驾驶证发证机关” />
</view>
</view>
<view class=”gui-form-item gui-border-b”>
<text class=”gui-form-label”>准驾车型</text>
<view class=”gui-form-body”>
<picker mode=”selector” :range=”vehicleTypeList” :value=”vehicleTypeIndex” @change=”vehicleTypeChange($event,vehicleTypeList)” :range-key=”‘codeSetName'”>
<view class=”gui-flex gui-rows gui-nowrap gui-space-between gui-align-items-center”>
<text class=”gui-text”>{{vehicleTypeList[vehicleTypeIndex].codeSetName}}</text>
<text class=”gui-form-icon gui-icons gui-text-center gui-color-gray”>&#xe603;</text>
</view>
</picker>
</view>
</view>
<view class=”gui-form-item gui-border-b”>
<text class=”gui-form-label”>关联车辆</text>
<view class=”gui-form-body”>
<picker mode=”selector” :range=”vehicleList” :value=”vehicleIndex” @change=”vehicleChange($event,vehicleList)” :range-key=”‘carNumber'”>
<view class=”gui-flex gui-rows gui-nowrap gui-space-between gui-align-items-center”>
<text class=”gui-text”>{{vehicleList[vehicleIndex].carNumber}}</text>
<text class=”gui-form-icon gui-icons gui-text-center gui-color-gray”>&#xe603;</text>
</view>
</picker>
</view>
</view>
</view>
<!– 第3步 –>
<view class=”gui-padding” v-if=”step == 3″>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>资格证号码</text>
<view class=”gui-form-body”>
<input type=”text” class=”gui-form-input” v-model=”formData.roadTransportQualificationCertificateNumber” placeholder=”请输入从业资格证编号” />
</view>
</view>
<view class=”gui-margin-top”>
<text class=”gui-form-label” style=”width: 100%;”>从业资格证</text>
</view>
<view class=”gui-idcard-items gui-img-in gui-flex gui-rows gui-justify-content-center”>
<view class=”gui-idcard-items-image” @tap=”selectRoadTransportQualificationCertificatePhoto”>
<gui-image :src=”formData.roadTransportQualificationCertificatePhoto” :width=”380″></gui-image>
</view>
</view>
<view class=”gui-form-item gui-margin-top gui-border-b”>
<text class=”gui-form-label”>证件有效期</text>
<view class=”gui-form-body”>
<picker class=”gui-form-picker” mode=”date” @change=”roadTransportQualificationCertificateValidUntilChange”>
<text class=”gui-text”>{{formData.roadTransportQualificationCertificateValidUntil}}</text>
<text class=”gui-icons icon-arrow-down” style=”margin-left:5px;”></text>
</picker>
</view>
</view>
</view>
</scroll-view>
<script>
export default {
data() {
return {
// 表单数据记录
formData: {
// 第一步
tenantId: ”,// 所属客户
payeeId: ”,// 收款人
driverName: ”,// 司机姓名
driverTel: ”,// 司机电话
idNumber: ”,// 身份证号码
idNumberValidUntil:’请选择证件有效期’,// 身份证有效期
idPhotoPositive: ‘https://www.zzwlnet.com/files/images/upload_identity_card_front.png’,// 身份证正面(个人信息面)
idPhotoReverse: ‘https://www.zzwlnet.com/files/images/upload_identity_card_contrary.png’,// 身份证反面(国徽面)
// 第二步
drivingLicenseNumber: ”,// 驾驶证编号
drivingLicensePhoto: ‘https://www.zzwlnet.com/files/images/upload_driving_license.png’,// 驾驶证图片
drivingLicenseValidityStart: ‘请选择证件有效期开始时间’, // 驾驶证有效期开始
drivingLicenseValidityEnd: ‘请选择证件有效期结束时间’,// 驾驶证有效期结束
drivingLicenseIssuingOrg: ”,// 驾驶证发证机关
quasiDrivingType: ”,// 准驾车型
vehicleId: ”,// 关联车辆
// 第三步
roadTransportQualificationCertificateNumber: ”,// 从业资格证号
roadTransportQualificationCertificatePhoto: ‘https://www.zzwlnet.com/files/images/upload_road_transport_qualification_certificate.png’,// 从业资格证图片
roadTransportQualificationCertificateValidUntil: ‘请选择证件有效期’,// 从业资格证有效期
},
}
},
methods: {
submit: function() {
uni.request({
url: _self.server_host + ‘/api’,
method: ‘POST’,
header: {‘content-type’ : “application/x-www-form-urlencoded”},
data: {
// 传参方式一:以JSON字符串的形式提交form表单数据
formData: JSON.stringify(_self.formData),
// 传参方式二:将form表单数据以对象形式传递
// formData: _self.formData,
},
success: res => {
// 服务器返回数据,后续业务逻辑处理
},
fail: () => {
uni.showToast({ title: “服务器响应失败,请稍后再试!”, icon : “none”});
},
complete: () => {}
});
}
}
}
</script>
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
后端java代码举例
// 针对传参方式一:后台以String的方式接收
public Result add(String formData){
// 将JSON字符串转换成JSONObject
JSONObject jsonObject= JSONObject.parSEObject(formData);
// 继续后续业务逻辑处理
return Results.success();
}
// 针对传参方式二:后台以Object的方式接收
public Result add(Object driverObj){
// 继续后续业务逻辑处理
return Results.success();
}
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/bbxylqf126com/article/details/114951576

uni-app的H5版使用注意事项 - DCloud问答

mikel阅读(419)

来源: uni-app的H5版使用注意事项 – DCloud问答

uni-app x的web版运行注意事项,另见https://doc.dcloud.net.cn/uni-app-x/web/

HBuilderX 1.2开始包含了uni-app的web平台支持。

使用方式

  1. 打开uni-app项目下的vue文件
  2. 点击菜单 运行->运行到浏览器->Chrome
  3. 在Chrome內打开调试模式(右键->检查)开启设备模拟,模拟移动设备(如果UI变形刷新即可)
  4. HBuilderX修改代码后会自动刷新chrome的页面
  5. 审查元素

    在chrome控制台安装vue devtools后可查看节点关系。
    安装方式自行搜索。
    每个页面都在page节点下,pageHead是微信和app下的原生导航栏,即pages.json里配的导航栏。
    pageBody是导航栏下的页面内容。
    所有标签为了避免和标准H5标签冲突,都加了U前缀。

  6. 断点Debug
    点chrome控制台的source,可以给js打断点调试。
    找到同名的文件,如果没有同名vue文件,一般会有一个同文件名的js文件,此时会提示检测到sourcemap,是否引入,点允许。然后就会有同名的vue文件。如果找不到,则把焦点放到source的代码区,然后敲ctrl+p打开文件查找窗口,然后敲入vue页面名字,然后打开vue页面。
    这个vue里,只有js,没有tag和css,但可以打断点调试。

发布方式

  1. 配置发行后的路径(发行在网站根目录可不配置),比如发行网站路径是www.xxx.com/html5,在manifest.json可视化界面 – H5配置 – 运行的基础路径中设置,也可以在源码视图内编辑h5节点,router下增加base属性为html5。
    可视化界面设置:

    源码视图设置:

  2. 点击菜单 发行->H5
  3. 在当下项目下的unpackage/dist/build/h5目录找到出的资源,部署服务器(或者使用本地服务器预览,不要直接在浏览器打开html文件)。如果发布使用的history模式,需要服务端配合,参考:后端配置方式

跨端注意

uni-app由uni的通用api和平台专有api组成,H5版也不例外。可以使用uni的通用api完成很多工作,也可以在条件编译里调用H5版的浏览器专有api。
虽然dom、window都可以用了,但如果要跨端,还是少写这样的代码好。
H5仍应该使用pages.json管理页面,强烈不建议使用浏览器的跳转页面的api。
H5的条件编译写法是把之前的app-plus换成H5。敲ifdef会有代码助手提示。

复制代码//#ifdef H5  
this.titleHeight = 44  
//#endif

条件编译目前有7个平台,APP-PLUS、APP-PLUS-NVUE、MP-WEIXIN、H5、MP、MP-BAIDU、MP-ALIPAY。
其中APP-PLUS-NVUE是APP-PLUS的子集,用于weex下单独写专用代码。
为了方便多平台选择,还引入了~#ifndef~,也就是ifdef的not,反向选择。以及或语法,及||。这些命名都是c语言条件编译的标准命名。

复制代码// #ifndef H5  
console.log("这段代码编译到非H5平台");  
// #endif

开发者之前为微信或app写的代码,H5的平台不支持时,需要注意把这些代码放到条件编译里。
经过这样的处理,之前做好的App或小程序才能正常运行到H5版里。

小程序版在UI上,尤其是导航栏上限制较多,H5在这里是参考了app,默认解析了pages.json下的app-plus的节点,实现了titleNView、buttons、下拉刷新(下拉刷新只有circle方式,因为只有这样的下拉刷新在H5版上可以保障流畅体验)

组件和API支持情况

目前的H5版,还没有100%实现uni的所有api,但大部分已经完成,具体参考uniapp文档。

第三方组件支持

  • 支持mpvue组件
  • 支持普通vue组件(仅H5平台)
  • 支持微信小程序组件(HBuilderX2.5.0+开始支持编译到H5)
  • 支持nvue

vue语法支持

H5版支持完整的vue语法,同时校验器也校验了更严格的vue语法,有些写法不规范会报警。比如data后面写对象会报警,必须写function。

注意事项(必看)

  • 编译为H5版后生成的是单页应用,SPA。如果想要SEO优化,首页可以在template模板中配置keyword。二级页不支持配置。但一个更酷的方式是用uni-app直接发布一版百度小程序,搜索权重更高。
  • 编译后看日志和错误,要看浏览器的控制台,而不是HBuilderX的控制台。浏览器的控制台会有错误提示。
  • 网络请求(request、uploadFile、downloadFile等)在浏览器存在跨域限制(CORS、Cross-Origin),解决方案详见:https://ask.dcloud.net.cn/article/35267
  • APP 和微信的原生导航栏和tabbar下,元素区域坐标是不包含原生导航栏和tabbar的。而 H5 里原生导航栏和tabbar是 div 模拟实现的,所以元素坐标会包含导航栏和tabbar的高度。为了优雅的解决多端高度定位问题,uni-app新增了2个css变量:–window-top和–window-bottom,这代表了页面的内容区域距离顶部和底部的距离。举个实例,如果你想在原生tabbar上方悬浮一个菜单,之前写bottom:0。这样的写法编译到h5后,这个菜单会和tabbar重叠,位于屏幕底部。而改为使用bottom:var(–window-bottom),则不管在app下还是在h5下,这个菜单都是悬浮在tabbar上浮的。这就避免了写条件编译代码。当然你也仍然可以使用 H5 的条件编译处理界面的不同。
  • CSS內使用vh单位的时候注意100vh包含导航栏,使用时需要减去导航栏和tabBar高度,部分浏览器还包含浏览器操作栏高度,使用时请注意。
  • event 对象上使用的 mpvue 独有的属性需调整(比如 event.pageY,可能需要加上44px的导航栏高度)。
  • fixed定位的组件有可能遮挡框架内置UI组件,如果不希望遮挡可以分平台判断,在H5平台避开内置UI。
  • 正常支持rpx。px是真实物理像素。暂不支持通过设manifest的”transformPx” : true,把px当动态单位使用。
  • 使用罗盘、地理位置、加速计等相关接口需要使用https协议,本地预览(localhost)可以使用 http 协议。
  • PC 端 Chrome 浏览器模拟器设备测试的时候,获取定位 API 需要连接谷歌服务器,需要翻墙。
  • 组件内(页面除外)不支持onLoad生命周期。
  • 为避免和内置组件冲突,自定义组件请加上前缀(但不能是u和uni)。比如可使用的自定义组件名称:my-view、m-input、we-icon,例如不可使用的自定义组件名称:u-view、uni-input。如果已有项目使用了可能造成冲突的名称,请修改名称。另外微信小程序下自定义组件名称不能以wx开头。
  • 在tabBar页面,如果page高度设置为100%时,页面超出滚动会导致底部被tabbar遮挡,可在tabbar页面去掉height:100%或者改用min-height:100%。
  • 编写组件时需要遵守vue的规范,之前在app端和小程序端能使用的一些不规范写法需要纠正,比如:不要修改props的值、组件最外层template节点下不允许包含多个节点。
  • 开发App时,不可在H5预览后直接云打包。需在HBuilderX里点运行-选择运行到手机,真机调试无误后再打包。
  • H5端 “网络不给力” 原因及解决办法:https://ask.dcloud.net.cn/article/37065。

相关

uni-app编译H5底层技术解析:https://juejin.im/post/5c1b0d715188256973244377

uni-app踩坑+小改造- 掘金

mikel阅读(388)

来源: uni-app踩坑+小改造最近团队内部一直在试点用uni-app去做一些小需求,但主要是先在H5上做试点,之后再按计划编 – 掘金

背景

最近团队内部一直在试点用uni-app去做一些小需求,但主要是先在H5上做试点,之后再按计划编译成小程序去发布。这回分享几个遇到的小问题和解决方案。

下面说到的问题主要在用uni-app开发H5平台时才会遇到,非H5平台可忽略。

跨域的问题

首先,在本地开发时,不同于直接用小程序IDE进行开发,在开发H5平台时,需要使用浏览器进行调试,而浏览器会有跨域的问题。比如直接通过本地IP地址去访问开发中的页面,同时这个页面会调一些现有的接口时,就面临着跨域的问题。

官方的解决方案

uni-app官方介绍了一些解决跨域问题的方法,比如服务端开启CORS,给浏览器安装跨域插件等,详见uni-app的H5版使用注意事项。但里面并没有提到(应该是很久未更新文档导致)的是,如果不想这么麻烦去解决,还有个更方便的办法,也就是用webpack-dev-server去代理即可解决。

更方便的解决方案

根据官方文档的描述,devServer配置被要求在manifest.json去配置,并且由于这个配置文件是json格式的,所以只能对简单类型进行配置。但对于proxy这项配置来说也是足够了的。直接如下方式配置即可解决:

代码解读
复制代码
// manifest.json
{
    "h5": {
        "devServer": {
            "proxy": {
                "/prefix/api/user/list": {
                    "target": "https://api-remote.xxxx.com",
                    "pathRewrite": {
                        "^/prefix": ""
                    }
                }
            }
        }
    }
}

另一种解决方案

直接创建一个vue.config.js文件,并在里面配置devServer,直接上代码

代码解读
复制代码
// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '/prefix/api/user/list': {
        target: 'https://api-remote.xxxx.com',
        pathRewrite: {
          '^/prefix': ''
        }
      }
    },
  }
}

这种办法的好处显而易见,用js而非json去配置会更加的灵活,需要注意的是以上两种方案不能同时使用,第一种会覆盖第二种方案。

Mock的问题

这可能也不是什么大问题,毕竟现在有很多像Easy Mock这样的在线Mock平台。但有时我们可能嫌麻烦,不想离开代码编辑窗口去注册,编写在线Mock数据,更想一切都用代码解决,那么同样可以用上面的第二种方案来搞定

解决方案

借助mocker-apimockjs这两个工具,直接配置devServerbefore选项即可,代码如下:

代码解读
复制代码
// vue.config.js
const webpackApiMocker = require('mocker-api')

module.exports = {
  devServer: {
    before (app) {
      webpackApiMocker(app, path.resolve('./mock/index.js'))
    }
  }
}

// mock/index.js
const Mock = require('mockjs')

const Random = Mock.Random
const mock = Mock.mock

const proxy = {
  'GET /api/user/list': mock({
    'array|3': [
      {
        id: 1,
        username: 'kenny',
        sex: 'male'
      }
    ]
  }),
  'POST /api/login/account': (req, res) => {
    return res.json({
      status: 'ok',
      data: {
        id: Random.id(),
        userName: Random.cname(),
        city: Random.city()
      }
    })
  }
}

module.exports = proxy

publicPath的问题

到了测试阶段,我们需要将代码部署到CDN上提测,不同的环境对应不同的CDN域名,官方通过manifest.json的方式配置publicPath显然非常的不灵活,我们希望publicPath是动态的,与环境,仓库,工程名甚至开发分支有关,而且不需要开发人员去关心。

解决方案

要解决这个问题就需要对脚手架做一些小改造了。

  • 首先,我们将publicPath这项配置拿出来单独放在一个配置文件中,比如project-config.js,并放在工程根目录下
代码解读
复制代码
const projectName = 'xxx' // 当前工程名,此处自由发挥即可
const isDev = isDev() // 是否为本地开发环境,此处自由发挥即可
const CDN_HOST = process.env.CDN_HOST   // build时指定的CDN域名
const APP_ENV = process.env.APP_ENV // build时指定的自定义环境

module.exports = {
  publicPath: isDev
    ? '/'
    : `//${CDN_HOST}/static/${projectName}/${APP_ENV}/`,
}
  • 其次,我们fork了一版官方的uni-app源码,并对@dcloudio/vue-cli-plugin-uni/index.js做了点改动
代码解读
复制代码
// @dcloudio/vue-cli-plugin-uni/index.js#L30
// 获取本地的project-config配置
module.exports = (api, options) => {
    const projectConfig = require(api.resolve('project-config'))

    Object.assign(options, { 
        outputDir: process.env.UNI_OUTPUT_TMP_DIR || process.env.UNI_OUTPUT_DIR,
        assetsDir
    }, vueConfig, {
        // 重新对publicPath进行覆盖
        publicPath: process.env.NODE_ENV === 'production' ? projectConfig.publicPath : '/'
    })
}

这样会使manifest.json中的配置失效,也就是如果使用HBuilder开发的话会受到点影响。

作者:前端论道
链接:https://juejin.cn/post/6844904063855755271
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

uni-app <image>和vue中img标签动态绑定src不显示本地图片 - web_pzj - 博客园

mikel阅读(331)

来源: uni-app <image>和vue中img标签动态绑定src不显示本地图片 – web_pzj – 博客园

因为需要根据后端返回的数据渲染不同的图片,所有要对imge标签的src进行动态绑定。

以下是静态绑定时的代码,静态绑定无任何问题。

 

下方是静态绑定时的正常图片:

 

 

以下是动态绑定时的代码,但是图片无法显示。

 

无法显示效果图:

 

解决办法一:用将路径放入require()中。

 

 

解决办法二:不要使用../,改为根目录/

uniapp 中出现 Undefined variable $u-type-primary-light 问题_undefined variable: "$u-type-primary-light".-CSDN博客

mikel阅读(287)

来源: uniapp 中出现 Undefined variable $u-type-primary-light 问题_undefined variable: “$u-type-primary-light”.-CSDN博客

问题描述
uniapp 引入 uview-ui 后 一顿操作,出现 Undefined variable: “$u-type-primary-light” 错误。
解决问题
第一步:确保 HBuilder X 是安装了 scss/sass 编译插件
第二步:确保在根目录下 uni.scss 文件中引入 theme.scss,
@import “uview-ui/theme.scss”; // 放在代码首行
1
第三步:确保在根目录下 App.vue 文件中添加 lang=”scss” 和 index.scss
<style lang=”scss”>
@import “uview-ui/index.scss”;
</style>
1
2
3
问题得以解决。
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_49175501/article/details/118157121

uni-app 分不清的全局变量this, uni, $u, vm, uni.$u, this.$u-CSDN博客

mikel阅读(277)

来源: uni-app 分不清的全局变量this, uni, $u, vm, uni.$u, this.$u-CSDN博客

项目引入了uview,并将uview所有模块指给uniapp全局变量uni

uni.$u=$u

 

在登录页面,或者APP.vue打印以下变量:

this, uni, $u, vm, uni.$u, this.$u

// this,$u,vm,uni, this.$u, uni.$u全局变量说明
console.log(“>>this”, this)
console.log(“>>uni”, uni)
console.log(“>>this===uni”, this === uni) //false
console.log(“>>this.$u “, this.$u)
console.log(“>>uni.$u “, uni.$u)
console.log(“>>this.$u===uni.&u”, this.$u === uni.$u) //ture

try {//$u is not defined
console.log(“>>$u “, $u)
} catch (e) {
console.log(e)
}
try { //vm is not defined
console.log(“>>this.vm”, this.vm)
console.log(“>>vm “, vm)
} catch (e) {
console.log(e)
}
运行结果
$u 是uview挂载到uni上的,方便使用uni来操作uview组件。

uni. 对象是uni-app框架实例。

this. 在.vue页面,对应就是当前vue的实例对象。

this.$u 与uni.$u 在vue页面是同一对象,指向 uview

 

$u来源
uniapp-master\uview-ui\index.vue,在此vue里,通过xx.prototype.$u的方法 ,通过uni.$u方法同时挂载到全局里。

// 引入全局mixin
import mixin from ‘./libs/mixin/mixin.js’

// 防抖方法
import debounce from ‘./libs/function/debounce.js’
// 节流方法
import throttle from ‘./libs/function/throttle.js’
import {fixMoney2Decimal, formatInputMoney, toFixed2Decimal} from “@/uview-ui/libs/function/numberform”;
import {positiveNumber} from “@/uview-ui/libs/function/inputRegExp”;
//…
//…

// 配置信息
import config from ‘./libs/config/config.js’
// 各个需要fixed的地方的z-index配置文件
import zIndex from ‘./libs/config/zIndex.js’
import {timeFormatCmm} from “@/uview-ui/libs/function/timeFormatStr”;

const $u = {
queryParams: queryParams,
positiveNumber: positiveNumber,
route: route,
timeFormat: timeFormat,
date: timeFormat, // 另名date
timeFormatCmm: timeFormatCmm,
timeFrom,
colorGradient: colorGradient.colorGradient,
colorToRgba: colorGradient.colorToRgba,
guid,
color,
sys,
os,
type2icon,
randomArray,
wranning,
get: http.get,
post: http.post,
put: http.put,
‘delete’: http.delete,
hexToRgb: colorGradient.hexToRgb,
rgbToHex: colorGradient.rgbToHex,
test,
random,
deepClone,
deepMerge,
getParent,
$parent,
addUnit,
trim,
type: [‘primary’, ‘success’, ‘error’, ‘warning’, ‘info’],
http,
toast,
toastErr,
toastS,
modal,
config, // uView配置信息相关,比如版本号
zIndex,
debounce,
throttle,

//–
toFixed2Decimal,
fixMoney2Decimal,
formatInputMoney
}

// $u挂载到uni对象上
uni.$u = $u

const install = Vue => {
Vue.mixin(mixin)
if (Vue.prototype.openShare) {
Vue.mixin(mpShare);
}
// Vue.mixin(vuexStore);
// 时间格式化,同时两个名称,date和timeFormat
Vue.filter(‘timeFormat’, (timestamp, format) => {
return timeFormat(timestamp, format)
})
Vue.filter(‘date’, (timestamp, format) => {
return timeFormat(timestamp, format)
})
// 将多久以前的方法,注入到全局过滤器
Vue.filter(‘timeFrom’, (timestamp, format) => {
return timeFrom(timestamp, format)
})
Vue.prototype.$u = $u
}

export default {
install
}
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/LlanyW/article/details/132522228

【uni-app项目如何引入 uView组件库】_uview安装到uni-app项目-CSDN博客

mikel阅读(309)

来源: 【uni-app项目如何引入 uView组件库】_uview安装到uni-app项目-CSDN博客

uniapp项目引入uView组件库
一、uView官方文档
二、公司项目中引入并使用uView
一、uView官方文档
uView官方文档

二、公司项目中引入并使用uView
第一步:

在公司创建完成uniapp项目后引入uView

 

第二步:

您如果是使用HBuilder X创建的uniapp项目,使用uView组件库的话需要在HBuilder X下载插件库 下载 uni_modules ,(如果这个看不懂可以看官方文档)

(1) HBuilder X插件库中下载 uni_modules

 

 

 

 

 

 

 

现在是已经将 uView导入到项目中了,接下来还需要对项目中uView组件库样式的导入,(具体步骤官方文档里都有)

 

如果之前安装过scss,可以直接跳过

这里的scss仅限于是通过Vcode开发uniapp的

 

通过HBuilder X 下载scss

 

 

在项目根目录中的main.js中,引入并使用uView的JS库,注意这两行要放在import Vue之后。

// main.js
import uView from ‘@/uni_modules/uview-ui’
Vue.use(uView)

在项目根目录的uni.scss中引入此文件。

/* uni.scss */
@import ‘@/uni_modules/uview-ui/theme.scss’;

 

 

到这一步可以直接启动项目,里边的组件均可使用
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/Lnbd_/article/details/129470614

关于uni-app的ui库、ui框架、ui组件 - DCloud问答

mikel阅读(306)

来源: 关于uni-app的ui库、ui框架、ui组件 – DCloud问答

组件的概念

组件是现代开发的重要里程碑。组件重构了分工模型,让大量的轮子出现,让开发者可以拿来轮子直接用,大幅提升了整个产业的效率。

uni-app是有内置组件的。这和web开发不一样。
web开发基本上不用基础组件,都是找一个三方ui库,全套组件都包含。那是因为html的基础组件默认样式不适配手机风格。
但uni-app体系不是这样,内置组件就是为手机优化的。

但内置组件只能满足基础需求,更多场景,需要扩展组件。
扩展组件是基于内置组件的二次封装,从性能上来讲,扩展组件的性能略低于内置组件,所以开发者切勿抛弃内置组件,直接全套用三方UI组件库。

uni-app的插件市场,有很多扩展组件,有的是单独的,有的是成套的。
有些开发者喜欢成套的组件,但注意成套扩展组件也不可能覆盖所有需求,很多场景还是需要单独下载专业组件。

扩展组件的选择

众多扩展组件如何选择?我们首先要搞清楚组件的分类。
组件分2大类:1、vue组件(文件后缀为vue);2、小程序自定义组件(文件后缀为wxml或其他小程序平台特有后缀名称)

  • vue组件又分为2个细项:only for web、全端兼容
  • 小程序组件又分为:微信/QQ小程序组件、阿里小程序组件、百度小程序组件、字节跳动小程序组件。
    这些组件uni-app都支持,但受组件本身技术特点限制,在不同端有不一样的支持度。
    下面这张表格,可以清楚的表达不同类型的组件的兼容性。

 

 

从表格中可以很明显看出,更推荐使用的是全端兼容的uni规范组件。

很多人容易搞错2个问题:

  1. 同样是vue组件,only for web的和全端的有什么区别?
    传统的vue组件,比如elementUI,都是only for web的,里面有大量dom和window对象操作。但小程序和App是没有dom这些api的,自然无法跨端使用。
    想要跨端,其实也不难,做一套无dom的vue组件即可。官方的uni-ui即是如此。还有众多开发者在插件市场提交了更多这种类型的库。
  2. vant是分web版和weapp版的,千万别搞混
    vant的web版操作了dom,所以只能用于web端;而vant weapp是微信小程序组件规范,可以用于微信、App、H5;vant自身并没有提供全端可用的无dom vue组件。

除了兼容性,在性能和生态完善度层面,不同类型组件有什么差别?

  1. 性能:
    • vue组件性能好于小程序自定义组件。这是因为uni-app在底层对vue数据更新使用了自动差量更新的机制。而小程序自定义组件,默认的setData写法是没有差量数据更新的,需要写代码手动实现差量更新才能达到相同性能。
  2. 生态完善度
    • 首先除了微信小程序,其他几个平台的小程序几乎是没有三方组件和模板生态的。开发其他端小程序,得靠uni-app的生态
    • 再说微信小程序生态,之前在微信小程序平台上一些有名的库(比如wxParse、wx-Echart),实际上在性能、功能、技术支持上,大多做的不如uni-app生态下的新库好。而vant、iview的weapp版,其性能也均不如uni ui。
  3. 其他指标
    • vue doc:HBuilderX支持vue doc,组件作者在vue组件源码里编写vue doc,可以让组件使用者写代码时得到良好的代码提示。
    • easycom:uni-app支持easycom,可以大幅简化组件的使用
    • nvue支持:如果开发App,可能会涉及到nvue原生渲染页面,这种渲染方式支持的css有限,此时就要甄别组件是否兼容nvue。

再来看看各种成套UI的优劣分析

uni ui

DCloud官方出了一套扩展组件,即uni-ui
这些扩展组件支持单个组件从插件市场下载,也支持npm引入uni ui,当然更方便的是在HBuilderX新建项目时直接选择uni ui项目模板
uni ui有如下优势:

  1. 优化逻辑层和视图层的通信折损:非H5端的各个平台,包括App和各种小程序,其逻辑层和视图层是分离的,两层之间通信交互会有折损,导致诸如跟手滑动不流畅。uni ui在底层会利用wxs等技术,把适当的js代码运行在视图层,减少通信折损,保证诸如swiperAction左滑菜单等跟手操作流畅顺滑
  2. 自动差量diff数据:在uni-app下,开发App和小程序,不需要手动setData,底层自动会差量更新数据。但如果使用了小程序组件,则需要按小程序的setData方式来更新数据,很难做到自动diff更新数据。
  3. 背景停止:很多ui组件是会一直动的,比如轮播图、跑马灯。即便这个窗体被新窗体挡住,它在背景层仍然在消耗着硬件资源。在Android的webview版本为chrome66以上,背景操作ui会引发很严重的性能问题,造成前台界面明显卡顿。而uni ui的组件,会自动判断自己的显示状态,在组件不再可见时,不会再做动画消耗硬件资源。
  4. 纯vue语法:uni ui的引用、开发都是纯vue方式。而小程序组件的引用注册、开发都是小程序语法,两种语法混合在一个工程,写的也不舒服,维护也麻烦。
  5. uni统计自动整合:比如使用uni ui的导航栏组件,就不需要写统计的自定义事件来触发页面标题上报。uni统计会自动识别导航栏组件的标题。类似的,收藏组件、购物车组件,都可以免打点直接使用。
  6. uni ui兼容Android 4.4等低端机webview,没有浏览器兼容问题。
  7. uni ui支持nvue:App端,uni-app同时支持webview渲染和原生渲染,而uni ui是可以一套代码同时支持webview渲染和原生渲染的。为了兼容原生渲染,uni ui也做到了纯flex布局。
  8. uni ui内置vue doc,使用组件时有良好的代码提示
  9. 支持easycom规范,使用非常简单
  10. 支持datacom规范,云端一体全部封装掉
  11. 支持uni_module规范,方便插件的更新

推荐在HBuilderX新建项目时,直接选择uni ui项目模板,然后在代码里直接敲u,所有组件都拉出来,不用引用、不用注册,直接就用。

 

插件市场更多组件

插件市场,https://ext.dcloud.net.cn,有各种玲琅满目的组件、模板。
其中成套的全端兼容ui库包括:

其他

  • 如果你仍坚持使用微信小程序的自定义组件ui,插件市场也有很多vant weapp版的集成示例https://ext.dcloud.net.cn/search?q=vant。同时要注意,小程序自定义组件的性能不如vue组件。
  • 如果你的nvue文件使用weex编译模式,也支持weex ui。三方商业ui库有graceUI weex版。但weex编译模式属于被淘汰技术,不再提供技术支持,nvue开发请使用uni-app编译模式。

综上,官方对组件的使用建议是:

  1. 首先使用内置组件
  2. 然后使用uni ui扩展组件
  3. 其他需求依靠插件市场其他组件灵活补充

使用uniapp创建项目,并使用uni-ui,引入uView及遇到的一些问题_全局使用uni-ui-CSDN博客

mikel阅读(552)

来源: 使用uniapp创建项目,并使用uni-ui,引入uView及遇到的一些问题_全局使用uni-ui-CSDN博客

一、uniapp中使用uni-ui
1.创建uniapp项目
点击 文件 —— 新建 —— 项目
选择 默认模板 给项目命名
2.运行项目
点击 运行 —— 运行到浏览器 —— Chrome
看到后台提示编译成功
项目的页面展示
3.引入uni-ui
在cmd中输入命令
npm i @dcloudio/uni-ui
1
4.在项目中全局使用uni-ui
在pages.json中进行配置easycom,
“easycom”: {
“autoscan”: true,
“custom”: {
// uni-ui 规则如下配置
“^uni-(.*)”: “@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue”
}
},
二、uniapp中使用uView
1.创建uniapp项目,步骤同上
点击 文件 —— 新建 —— 项目——选择 默认模板——给项目命名
2.引入uView,步骤同上
在cmd中输入命令
初始化,创建package.json文件
npm init -y
1
安装uview-ui组件库
npm install uview-ui
1
3.在main.js中全局引入uview组件
ps:注意uview引入的位置,必须是在import Vue from “vue” 之后,并且是在 app.$mount()之前,否则在使用uview组件时会报错!!!
在uni.scss中全局引入uview-ui样式
4.使用uview组件
效果图如下
uview相关的问题
1.u-tabs子项水平居中
u-tabs默认是这样的,想着把子项水平居中
这是修改后的
代码如下
.mytabs ::v-deep .uni-scroll-view-content {
display: flex;
justify-content: space-around;
}
.mytabs ::v-deep .u-tabs__wrapper__nav {
flex:none !important;
}
style flex属性
https://www.runoob.com/jsref/prop-style-flex.html
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/blue__k/article/details/122921646

百万商品查询,性能提升了10倍 - 苏三说技术 - 博客园

mikel阅读(385)

来源: 百万商品查询,性能提升了10倍 – 苏三说技术 – 博客园

前言

最近在我的知识星球中,有个小伙伴问了这样一个问题:百万商品分页查询接口,如何保证接口的性能?

这就需要对该分页查询接口做优化了。

这篇文章从9个方面跟大家一起聊聊分页查询接口优化的一些小技巧,希望对你会有所帮助。

图片

1 增加默认条件

对于分页查询接口,如果没有特殊要求,我们可以在输入参数中,给一些默认值。

这样可以缩小数据范围,避免每次都count所有数据的情况。

对于商品查询,这种业务场景,我们可以默认查询当天上架状态的商品列表。

例如:

select * from product 
where edit_date>='2023-02-20' and edit_date<'2023-02-21' and status=1

如果每天有变更的商品数量不多,通过这两个默认条件,就能过滤掉绝大部分数据,让分页查询接口的性能提升不少。

温馨提醒一下:记得给时间状态字段增加一个联合索引

2 减少每页大小

分页查询接口通常情况下,需要接收两个参数:pageNo(即:页码)和pageSize(即:每页大小)。

如果分页查询接口的调用端,没有传pageNo默认值是1,如果没有传pageSize也可以给一个默认值10或者20。

不太建议pageSize传入过大的值,会直接影响接口性能。

在前端有个下拉控件,可以选择每页的大小,选择范围是:10、20、50、100。

前端默认选择的每页大小为10

不过在实际业务场景中,要根据产品需求而且,这里只是一个参考值。

3 减少join表的数量

有时候,我们的分页查询接口的查询结果,需要join多张表才能查出数据。

比如在查询商品信息时,需要根据商品名称、单位、品牌、分类等信息查询数据。

这时候写一条SQL可以查出想要的数据,比如下面这样的:

select 
  p.id,
  p.product_name,
  u.unit_name,
  b.brand_name,
  c.category_name
from product p
inner join unit u on p.unit_id = u.id
inner join brand b on p.brand_id = b.id
inner join category c on p.category_id = c.id
where p.name='测试商品' 
limit 0,20;

使用product表去join了unit、brand和category这三张表。

其实product表中有unit_id、brand_id和category_id三个字段。

我们可以先查出这三个字段,获取分页的数据缩小范围,之后再通过主键id集合去查询额外的数据。

我们可以把SQL改成这样:

select 
  p.id,
  p.product_id,
  u.unit_id,
  b.brand_id,
  c.category_id
from product
where name='测试商品'
limit 0,20;

这个例子中,分页查询之后,我们获取到的商品列表其实只要20条数据。

再根据20条数据中的id集合,获取其他的名称,例如:

select id,name 
from unit
where id in (1,2,3);

然后在程序中填充其他名称。

伪代码如下:

List<Product> productList = productMapper.search(searchEntity);
List<Long> unitIdList = productList.stream().map(Product::getUnitId).distinct().collect(Collectors.toList());
List<Unit> unitList = UnitMapper.queryUnitByIdList(unitIdList);
for(Product product: productList) {
   Optional<Unitoptional = unitList.stream().filter(x->x.getId().equals(product.getId())).findAny();
   if(optional.isPersent()) {
      product.setUnitName(optional.get().getName());
   } 
}

这样就能有效的减少join表的数量,可以一定的程度上优化查询接口的性能。

4 优化索引

分页查询接口性能出现了问题,最直接最快速的优化办法是:优化索引

因为优化索引不需要修改代码,只需回归测试一下就行,改动成本是最小的。

我们需要使用explain关键字,查询一下生产环境分页查询接口的执行计划

看看有没有创建索引,创建的索引是否合理,或者索引失效了没。

索引不是创建越多越好,也不是创建越少越好,我们需要根据实际情况,到生产环境测试一下SQL的耗时情况,然后决定如何创建或优化索引。

建议优先创建联合索引

如果你对explain关键字的用法比较感兴趣,可以看看我的这篇文章《explain | 索引优化的这把绝世好剑,你真的会用吗?》。

如果你对索引失效的问题比较感兴趣,可以看看我的这篇文章《聊聊索引失效的10种场景,太坑了》。

5 用straight_join

有时候我们的业务场景很复杂,有很多查询sql,需要创建多个索引。

在分页查询接口中根据不同的输入参数,最终的查询sql语句,MySQL根据一定的抽样算法,却选择了不同的索引。

不知道你有没有遇到过,某个查询接口,原本性能是没问题的,但一旦输入某些参数,接口响应时间就非常长。

这时候如果你此时用explain关键字,查看该查询sql执行计划,会发现现在走的索引,跟之前不一样,并且驱动表也不一样。

之前一直都是用表a驱动表b,走的索引c。

此时用的表b驱动表a,走的索引d。

为了解决Mysql选错索引的问题,最常见的手段是使用force_index关键字,在代码中指定走的索引名称。

但如果在代码中硬编码了,后面一旦索引名称修改了,或者索引被删除了,程序可能会直接报错。

这时该怎么办呢?

答:我们可以使用straight_join代替inner join

straight_join会告诉Mysql用左边的表驱动右边的表,能改表优化器对于联表查询的执行顺序。

之前的查询sql如下:

select p.id from product p
inner join warehouse w on p.id=w.product_id;
...

我们用它将之前的查询sql进行优化:

select p.id from product p
straight_join warehouse w on p.id=w.product_id;
...

6 数据归档

随着时间的推移,我们的系统用户越来越多,产生的数据也越来越多。

单表已经到达了几千万。

这时候分页查询接口性能急剧下降,我们不能不做分表处理了。

做简单的分表策略是将历史数据归档,比如:在主表中只保留最近三个月的数据,三个月前的数据,保证到历史表中。

我们的分页查询接口,默认从主表中查询数据,可以将数据范围缩小很多。

如果有特殊的需求,再从历史表中查询数据,最近三个月的数据,是用户关注度最高的数据。

7 使用count(*)

在分页查询接口中,需要在sql中使用count关键字查询总记录数

目前count有下面几种用法:

  • count(1)
  • count(id)
  • count(普通索引列)
  • count(未加索引列)

那么它们有什么区别呢?

  • count(*) :它会获取所有行的数据,不做任何处理,行数加1。
  • count(1):它会获取所有行的数据,每行固定值1,也是行数加1。
  • count(id):id代表主键,它需要从所有行的数据中解析出id字段,其中id肯定都不为NULL,行数加1。
  • count(普通索引列):它需要从所有行的数据中解析出普通索引列,然后判断是否为NULL,如果不是NULL,则行数+1。
  • count(未加索引列):它会全表扫描获取所有数据,解析中未加索引列,然后判断是否为NULL,如果不是NULL,则行数+1。

由此,最后count的性能从高到低是:

count(*) ≈ count(1) > count(id) > count(普通索引列) > count(未加索引列)

所以,其实count(*)是最快的。

我们在使用count统计总记录数时,一定要记得使用count(*)。

8 从ClickHouse查询

有些时候,join的表实在太多,没法去掉多余的join,该怎么办呢?

答:可以将数据保存到ClickHouse

ClickHouse是基于列存储的数据库,不支持事务,查询性能非常高,号称查询十几亿的数据,能够秒级返回。

为了避免对业务代码的嵌入性,可以使用Canal监听Mysqlbinlog日志。当product表有数据新增时,需要同时查询出单位、品牌和分类的数据,生成一个新的结果集,保存到ClickHouse当中。

查询数据时,从ClickHouse当中查询,这样使用count(*)的查询效率能够提升N倍。

需要特别提醒一下:使用ClickHouse时,新增数据不要太频繁,尽量批量插入数据。

其实如果查询条件非常多,使用ClickHouse也不是特别合适,这时候可以改成ElasticSearch,不过它跟Mysql一样,存在深分页问题。

9 数据库读写分离

有时候,分页查询接口性能差,是因为用户并发量上来了。

在系统的初期,还没有多少用户量,读数据请求和写数据请求,都是访问的同一个数据库,该方式实现起来简单、成本低。

刚开始分页查询接口性能没啥问题。

但随着用户量的增长,用户的读数据请求和写数据请求都明显增多。

我们都知道数据库连接有限,一般是配置的空闲连接数是100-1000之间。如果多余1000的请求,就只能等待,就可能会出现接口超时的情况。

因此,我们有必要做数据库的读写分离。写数据请求访问主库,读数据请求访问从库,从库的数据通过binlog从主库同步过来。

根据不同的用户量,可以做一主一从,一主两从,或一主多从。

数据库读写分离之后,能够提升查询接口的性能。

如果你对性能优化比较感兴趣,可以看看《性能优化35讲》,里面有更多干货内容。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。