来源: uni-app.02.提交form表单的两种方式_uniapp form表单提交-CSDN博客
uni-app的H5版使用注意事项 - DCloud问答
来源: uni-app的H5版使用注意事项 – DCloud问答
uni-app x的web版运行注意事项,另见https://doc.dcloud.net.cn/uni-app-x/web/
HBuilderX 1.2开始包含了uni-app的web平台支持。
使用方式
- 打开uni-app项目下的vue文件
- 点击菜单 运行->运行到浏览器->Chrome

- 在Chrome內打开调试模式(右键->检查)开启设备模拟,模拟移动设备(如果UI变形刷新即可)
- HBuilderX修改代码后会自动刷新chrome的页面
- 审查元素
在chrome控制台安装vue devtools后可查看节点关系。
安装方式自行搜索。
每个页面都在page节点下,pageHead是微信和app下的原生导航栏,即pages.json里配的导航栏。
pageBody是导航栏下的页面内容。
所有标签为了避免和标准H5标签冲突,都加了U前缀。 - 断点Debug
点chrome控制台的source,可以给js打断点调试。
找到同名的文件,如果没有同名vue文件,一般会有一个同文件名的js文件,此时会提示检测到sourcemap,是否引入,点允许。然后就会有同名的vue文件。如果找不到,则把焦点放到source的代码区,然后敲ctrl+p打开文件查找窗口,然后敲入vue页面名字,然后打开vue页面。
这个vue里,只有js,没有tag和css,但可以打断点调试。
发布方式
- 配置发行后的路径(发行在网站根目录可不配置),比如发行网站路径是www.xxx.com/html5,在manifest.json可视化界面 – H5配置 – 运行的基础路径中设置,也可以在源码视图内编辑h5节点,router下增加base属性为html5。
可视化界面设置:源码视图设置:
- 点击菜单 发行->H5
- 在当下项目下的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踩坑+小改造- 掘金
来源: 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-api和mockjs这两个工具,直接配置devServer的before选项即可,代码如下:
// 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 - 博客园
来源: 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博客
uni-app 分不清的全局变量this, uni, $u, vm, uni.$u, this.$u-CSDN博客
来源: 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博客
来源: 【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问答
来源: 关于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个问题:
- 同样是vue组件,only for web的和全端的有什么区别?
传统的vue组件,比如elementUI,都是only for web的,里面有大量dom和window对象操作。但小程序和App是没有dom这些api的,自然无法跨端使用。
想要跨端,其实也不难,做一套无dom的vue组件即可。官方的uni-ui即是如此。还有众多开发者在插件市场提交了更多这种类型的库。 - vant是分web版和weapp版的,千万别搞混
vant的web版操作了dom,所以只能用于web端;而vant weapp是微信小程序组件规范,可以用于微信、App、H5;vant自身并没有提供全端可用的无dom vue组件。
除了兼容性,在性能和生态完善度层面,不同类型组件有什么差别?
- 性能:
- vue组件性能好于小程序自定义组件。这是因为uni-app在底层对vue数据更新使用了自动差量更新的机制。而小程序自定义组件,默认的setData写法是没有差量数据更新的,需要写代码手动实现差量更新才能达到相同性能。
- 生态完善度
- 首先除了微信小程序,其他几个平台的小程序几乎是没有三方组件和模板生态的。开发其他端小程序,得靠uni-app的生态
- 再说微信小程序生态,之前在微信小程序平台上一些有名的库(比如wxParse、wx-Echart),实际上在性能、功能、技术支持上,大多做的不如uni-app生态下的新库好。而vant、iview的weapp版,其性能也均不如uni ui。
- 其他指标
再来看看各种成套UI的优劣分析
uni ui
DCloud官方出了一套扩展组件,即uni-ui
这些扩展组件支持单个组件从插件市场下载,也支持npm引入uni ui,当然更方便的是在HBuilderX新建项目时直接选择uni ui项目模板
uni ui有如下优势:
- 优化逻辑层和视图层的通信折损:非H5端的各个平台,包括App和各种小程序,其逻辑层和视图层是分离的,两层之间通信交互会有折损,导致诸如跟手滑动不流畅。uni ui在底层会利用wxs等技术,把适当的js代码运行在视图层,减少通信折损,保证诸如swiperAction左滑菜单等跟手操作流畅顺滑
- 自动差量diff数据:在uni-app下,开发App和小程序,不需要手动setData,底层自动会差量更新数据。但如果使用了小程序组件,则需要按小程序的setData方式来更新数据,很难做到自动diff更新数据。
- 背景停止:很多ui组件是会一直动的,比如轮播图、跑马灯。即便这个窗体被新窗体挡住,它在背景层仍然在消耗着硬件资源。在Android的webview版本为chrome66以上,背景操作ui会引发很严重的性能问题,造成前台界面明显卡顿。而uni ui的组件,会自动判断自己的显示状态,在组件不再可见时,不会再做动画消耗硬件资源。
- 纯vue语法:uni ui的引用、开发都是纯vue方式。而小程序组件的引用注册、开发都是小程序语法,两种语法混合在一个工程,写的也不舒服,维护也麻烦。
- 与uni统计自动整合:比如使用uni ui的导航栏组件,就不需要写统计的自定义事件来触发页面标题上报。uni统计会自动识别导航栏组件的标题。类似的,收藏组件、购物车组件,都可以免打点直接使用。
- uni ui兼容Android 4.4等低端机webview,没有浏览器兼容问题。
- uni ui支持nvue:App端,uni-app同时支持webview渲染和原生渲染,而uni ui是可以一套代码同时支持webview渲染和原生渲染的。为了兼容原生渲染,uni ui也做到了纯flex布局。
- uni ui内置vue doc,使用组件时有良好的代码提示
- 支持easycom规范,使用非常简单
- 支持datacom规范,云端一体全部封装掉
- 支持uni_module规范,方便插件的更新
推荐在HBuilderX新建项目时,直接选择uni ui项目模板,然后在代码里直接敲u,所有组件都拉出来,不用引用、不用注册,直接就用。

插件市场更多组件
插件市场,https://ext.dcloud.net.cn,有各种玲琅满目的组件、模板。
其中成套的全端兼容ui库包括:
- uViewUI:组件丰富、文档清晰,支持nvue
- colorUI css库:颜值很高,css库而非组件
- unify UI:全端支持的组件库,侧重nvue
- mypUI:全端支持的组件库,侧重nvue
- ThorUI组件库
- graceUI商业库
- nPro全端nvue组件与模版库:云端一体、nvue优质商业库,https://ext.dcloud.net.cn/plugin?id=5169
- first UI:分开源版和商业版
- 图鸟UI:高颜值UI库
- s-ui
其他
- 如果你仍坚持使用微信小程序的自定义组件ui,插件市场也有很多vant weapp版的集成示例https://ext.dcloud.net.cn/search?q=vant。同时要注意,小程序自定义组件的性能不如vue组件。
- 如果你的nvue文件使用weex编译模式,也支持weex ui。三方商业ui库有graceUI weex版。但weex编译模式属于被淘汰技术,不再提供技术支持,nvue开发请使用uni-app编译模式。
综上,官方对组件的使用建议是:
- 首先使用内置组件
- 然后使用uni ui扩展组件
- 其他需求依靠插件市场其他组件灵活补充
使用uniapp创建项目,并使用uni-ui,引入uView及遇到的一些问题_全局使用uni-ui-CSDN博客
来源: 使用uniapp创建项目,并使用uni-ui,引入uView及遇到的一些问题_全局使用uni-ui-CSDN博客
百万商品查询,性能提升了10倍 - 苏三说技术 - 博客园
来源: 百万商品查询,性能提升了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<Unit> optional = 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监听Mysql的binlog日志。当product表有数据新增时,需要同时查询出单位、品牌和分类的数据,生成一个新的结果集,保存到ClickHouse当中。
查询数据时,从ClickHouse当中查询,这样使用count(*)的查询效率能够提升N倍。
需要特别提醒一下:使用ClickHouse时,新增数据不要太频繁,尽量批量插入数据。
其实如果查询条件非常多,使用ClickHouse也不是特别合适,这时候可以改成ElasticSearch,不过它跟Mysql一样,存在深分页问题。
9 数据库读写分离
有时候,分页查询接口性能差,是因为用户并发量上来了。
在系统的初期,还没有多少用户量,读数据请求和写数据请求,都是访问的同一个数据库,该方式实现起来简单、成本低。
刚开始分页查询接口性能没啥问题。
但随着用户量的增长,用户的读数据请求和写数据请求都明显增多。
我们都知道数据库连接有限,一般是配置的空闲连接数是100-1000之间。如果多余1000的请求,就只能等待,就可能会出现接口超时的情况。
因此,我们有必要做数据库的读写分离。写数据请求访问主库,读数据请求访问从库,从库的数据通过binlog从主库同步过来。
根据不同的用户量,可以做一主一从,一主两从,或一主多从。
数据库读写分离之后,能够提升查询接口的性能。
如果你对性能优化比较感兴趣,可以看看《性能优化35讲》,里面有更多干货内容。
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
Mikel









