
用unicloud做了一个ditto云同步工具
ditto是一个电脑端剪切板增强工具,非常好用,码农必备神器
可惜不支持云同步,那就自己做一个
功能
- 上传到云服务器
- 从云服务器下载
- 多台电脑同步
- 仅支持私有部署(隐私起见)
- 存储所有历史剪切板
解决的场景
- 本地存储有限,历史剪切板丢失
- 家庭、办公多台电脑,剪切板不同步
主要参数
- 因为免费,云存储使用unicloud的免费数据库(私有部署)
- 从云端下载仅限最新500条(云端太多,没必要)
- 上传服务器不限条数,全部存储
- 没有窗口,后台运行,进程可见
- 配置文件:
config.json
- 运行记录及提示都在日志文件
ditto-sync-2022-01-27.log
私有云端部署及代码 详见
ditto是一个电脑端剪切板增强工具,非常好用,码农必备神器
可惜不支持云同步,那就自己做一个
功能
- 上传到云服务器
- 从云服务器下载
- 多台电脑同步
- 仅支持私有部署(隐私起见)
- 存储所有历史剪切板
解决的场景
- 本地存储有限,历史剪切板丢失
- 家庭、办公多台电脑,剪切板不同步
主要参数
- 因为免费,云存储使用unicloud的免费数据库(私有部署)
- 从云端下载仅限最新500条(云端太多,没必要)
- 上传服务器不限条数,全部存储
- 没有窗口,后台运行,进程可见
- 配置文件:
config.json
- 运行记录及提示都在日志文件
ditto-sync-2022-01-27.log
私有云端部署及代码 详见
https://www.yuque.com/dittosync/help/01
收起阅读 »
让App消息推送听得见!详解个推SDK通知栏铃声功能及接入
今天是2月14日情人节,一个表达心动和爱意的日子。
表白招式千千万,而真正要打动一个人的心,其实并不容易。对于App开发者而言,要收获千万量级用户的芳心,那就更加困难了。
本文为大家分享个推消息推送SDK【通知栏铃声】功能的使用窍门,帮助开发者用简单10行代码,即可以声传意,轻松收获用户喜爱。

一、功能介绍
众所周知,消息推送是App和用户之间交互的桥梁。App通过通知栏消息,低成本、高效率地向用户告知产品功能更新、运营活动上线、日常新闻动态等重要消息。
为了使用户不错过重要通知,个推SDK还支持App开发者自定义个性化的通知栏铃声(区别于系统提示音),通过声音的方式,更加有效地提醒用户及时查阅关键消息。比如个推某生活服务类App客户使用该功能,将自制的温馨提示音作为通知栏铃声,告知其App用户“您的外卖到了”,从而拉近了和用户之间的距离。
接下来,本文就围绕该功能的接入方式、通知栏铃声的配置方式及相关注意事项等方面展开,帮助广大开发者快速解锁该功能,通过设置好听、个性化的“通知栏铃声”,为用户提供高效和贴心服务,打动用户芳心,提升App用户黏性和活跃度。
二、功能接入
个推消息推送SDK“通知栏铃声”功能供开发者免费使用,开发者需要在客户端和服务端同时做相应设置,才能实现该功能。
2.1 客户端
Android
在客户端app/raw目录下放置铃声文件(支持mp3、wav、mpeg等多种格式的音频文件),例如设置铃声文件名为:ringing.mp3。
iOS
在客户端把自制好的的铃声添加到项目的main bundle中后,在项目设置Build Phases-->Copy Bundle Resources中添加您的铃声文件(支持 aiff、wav、caf、mp3等格式的音频)。
2.2 服务端
开发者可通过调用个推服务端API(https://docs.getui.com/getui/server/rest_v2/push)或者登陆个推开发者中心,从页面https://dev.getui.com/?activityId=xuetang#/overviewIndex下发消息。针对这两种消息下发方式,开发者都可以通过简单配置,自定义消息下发时的通知栏铃声。
方式一:服务端API设置
由于默认的厂商策略是:在成功集成多厂商的前提下且App在线(即App在前台打开运行)时,下发push_message内的消息;App离线(App在后台、锁屏、进程关闭)时,下发push_channel内的消息。因此开发者在调用个推服务端API下发消息时,需要同时在push_message和push_channel内指定该铃声。
联系@个推技术支持,获取配置说明:
个推技术支持企业微信
温馨提示:
使用自定义铃声前,必须完成个推SDK和个推消息推送多厂商版本SDK集成,建议使用最新版个推SDK:https://docs.getui.com/getui/mobile/android/androidstudio
options内相关参数的生成,可参考个推文档中心详解:https://docs.getui.com/getui/server/rest_v2/third_party
通过以上方式实现后,App在线时:iOS、Android都支持自定义铃声;App离线时:iOS完全支持,Android 暂时只有小米、华为机型支持自定义铃声。其它安卓机型,等待对应厂商平台开放相关功能后会进行适配。
方式二:个推开发者中心配置
Step1:
登陆个推开发者中心(https://dev.getui.com/?activityId=xuetang#/overviewIndex),进入个推消息推送dos页面,点击【创建推送】后在Android配置模块中选择【通知渠道模板】。一般默认为响铃、震动、唤醒屏幕。
Step2:
通过设置通知渠道模板来设置通知的提醒方式。可以自主选择响铃、震动、浮动、唤醒屏幕等多种方式对用户进行消息弱提醒、强提醒。如果是重要的消息内容,还可以针对通知栏铃声进行自定义设置,提醒相应用户群体及时关注消息;如果没有进行自定义设置,则通知栏铃声默认为系统声音。
Step3:
自定义铃声设置:在渠道模板【提醒方式】中选择通知铃声,输入自定义文件名。
总结
个推消息推送SDK不仅支持对通知栏消息设置自定义铃声,还支持自定义角标,提供大图、动态视频等富媒体形式的消息推送服务,让App开发者可以自主设置通知栏的展示样式,和用户建立更加有趣、有效、有爱的互动联结。
这个情人节,快来体验个推消息推送SDK的【通知栏铃声功能】,用开发者的别样浪漫,收获用户芳心!
登陆个推开发者中心(https://dev.getui.com),获取个推消息推送SDK最新版本,解锁App用户触达新姿势。
今天是2月14日情人节,一个表达心动和爱意的日子。
表白招式千千万,而真正要打动一个人的心,其实并不容易。对于App开发者而言,要收获千万量级用户的芳心,那就更加困难了。
本文为大家分享个推消息推送SDK【通知栏铃声】功能的使用窍门,帮助开发者用简单10行代码,即可以声传意,轻松收获用户喜爱。
一、功能介绍
众所周知,消息推送是App和用户之间交互的桥梁。App通过通知栏消息,低成本、高效率地向用户告知产品功能更新、运营活动上线、日常新闻动态等重要消息。
为了使用户不错过重要通知,个推SDK还支持App开发者自定义个性化的通知栏铃声(区别于系统提示音),通过声音的方式,更加有效地提醒用户及时查阅关键消息。比如个推某生活服务类App客户使用该功能,将自制的温馨提示音作为通知栏铃声,告知其App用户“您的外卖到了”,从而拉近了和用户之间的距离。
接下来,本文就围绕该功能的接入方式、通知栏铃声的配置方式及相关注意事项等方面展开,帮助广大开发者快速解锁该功能,通过设置好听、个性化的“通知栏铃声”,为用户提供高效和贴心服务,打动用户芳心,提升App用户黏性和活跃度。
二、功能接入
个推消息推送SDK“通知栏铃声”功能供开发者免费使用,开发者需要在客户端和服务端同时做相应设置,才能实现该功能。
2.1 客户端
Android
在客户端app/raw目录下放置铃声文件(支持mp3、wav、mpeg等多种格式的音频文件),例如设置铃声文件名为:ringing.mp3。
iOS
在客户端把自制好的的铃声添加到项目的main bundle中后,在项目设置Build Phases-->Copy Bundle Resources中添加您的铃声文件(支持 aiff、wav、caf、mp3等格式的音频)。
2.2 服务端
开发者可通过调用个推服务端API(https://docs.getui.com/getui/server/rest_v2/push)或者登陆个推开发者中心,从页面https://dev.getui.com/?activityId=xuetang#/overviewIndex下发消息。针对这两种消息下发方式,开发者都可以通过简单配置,自定义消息下发时的通知栏铃声。
方式一:服务端API设置
由于默认的厂商策略是:在成功集成多厂商的前提下且App在线(即App在前台打开运行)时,下发push_message内的消息;App离线(App在后台、锁屏、进程关闭)时,下发push_channel内的消息。因此开发者在调用个推服务端API下发消息时,需要同时在push_message和push_channel内指定该铃声。
联系@个推技术支持,获取配置说明:
个推技术支持企业微信
温馨提示:
使用自定义铃声前,必须完成个推SDK和个推消息推送多厂商版本SDK集成,建议使用最新版个推SDK:https://docs.getui.com/getui/mobile/android/androidstudio
options内相关参数的生成,可参考个推文档中心详解:https://docs.getui.com/getui/server/rest_v2/third_party
通过以上方式实现后,App在线时:iOS、Android都支持自定义铃声;App离线时:iOS完全支持,Android 暂时只有小米、华为机型支持自定义铃声。其它安卓机型,等待对应厂商平台开放相关功能后会进行适配。
方式二:个推开发者中心配置
Step1:
登陆个推开发者中心(https://dev.getui.com/?activityId=xuetang#/overviewIndex),进入个推消息推送dos页面,点击【创建推送】后在Android配置模块中选择【通知渠道模板】。一般默认为响铃、震动、唤醒屏幕。
Step2:
通过设置通知渠道模板来设置通知的提醒方式。可以自主选择响铃、震动、浮动、唤醒屏幕等多种方式对用户进行消息弱提醒、强提醒。如果是重要的消息内容,还可以针对通知栏铃声进行自定义设置,提醒相应用户群体及时关注消息;如果没有进行自定义设置,则通知栏铃声默认为系统声音。
Step3:
自定义铃声设置:在渠道模板【提醒方式】中选择通知铃声,输入自定义文件名。
总结
个推消息推送SDK不仅支持对通知栏消息设置自定义铃声,还支持自定义角标,提供大图、动态视频等富媒体形式的消息推送服务,让App开发者可以自主设置通知栏的展示样式,和用户建立更加有趣、有效、有爱的互动联结。
这个情人节,快来体验个推消息推送SDK的【通知栏铃声功能】,用开发者的别样浪漫,收获用户芳心!
登陆个推开发者中心(https://dev.getui.com),获取个推消息推送SDK最新版本,解锁App用户触达新姿势。
收起阅读 »
uni-app html5+ 实现input 获取焦点聚焦但不弹出键盘,不需要原生插件,input框聚焦时不显示软键盘的方法
uni-app html5+ 实现input 获取焦点聚焦但不弹出键盘,不需要原生插件,input框聚焦时不显示软键盘的方法
有偿提供代码
附件是视频,可以看下,是否符合需求
QQ:543610866
uni-app html5+ 实现input 获取焦点聚焦但不弹出键盘,不需要原生插件,input框聚焦时不显示软键盘的方法
有偿提供代码
附件是视频,可以看下,是否符合需求
QQ:543610866

uniapp+uview后台管理系统|uni-app手机端后台uniUadmin
前段时间有给大家分享一个uni-app仿抖音小视频,今天分享一个uniapp移动端后台管理uni-uadmin实例项目。
uni-uadmin 一款基于uni-app+uviewUI+uniUI+mockjs+echarts等技术开发架构的手机移动端后台管理系统模板。包含了图表、自定义表格、表单、瀑布流及图文编辑器等业务模块,动态权限管理,错误页处理,可编译至H5+小程序+APP端。
技术栈
- 编辑器:HbuilderX3.3.5
- 使用技术:vue+uniapp+uViewUI+mockjs
- 弹窗组件:ua-popup(基于uni-app跨端弹框组件)
- 表格组件:ua-table(基于uni-app封装的多功能表格)
- 自定义组件:uaDock全新的dock风格tabbar组件
- uniapp图表组件:u-charts图表库
项目构建目录
共用模板
整个页面结构布局分为顶部自定义导航+内容区域+底部dock tab菜单三大部分。
<!-- 公共页面模板 -->
<template>
<view class="ua__pageview flexbox flex-col" :style="{'--SKIN': $store.state.skin, 'background': bgcolor, 'color': color}">
<slot name="header" />
<!-- //主容器 -->
<view class="ua__scrollview flex1">
<slot />
</view>
<!-- //底部 -->
<slot name="footer" />
<!-- //dock菜单 -->
<ua-dock v-if="dock && dock != 'false'" @click="handleDockClick" />
<!-- //函数式弹框 -->
<!-- //换肤弹框模板 -->
<ua-popup v-model="isVisibleSkin" position="right">
<Skin />
</ua-popup>
</view>
</template>
uniapp-uadmin 还支持动态权限控制。
uniapp自定义菜单tab组件ua-dock
<!-- //底部dock菜单 -->
<template>
<view class="ua__dockbar">
<scroll-view class="ua__dock-scroll ua__filter" :class="platform" scroll-x :style="{'background': bgcolor}">
<view class="ua__dock-wrap">
<!-- Tab菜单项 -->
<block v-for="(item, index) in menu" :key="index">
<view v-if="item.type == 'divider'" class="ua__dock-divider"></view>
<view v-else class="ua__dock-item" :class="currentTabIndex == index ? 'cur' : ''" @click="switchTab(index, item)">
<text v-if="item.icon" class="iconfont nvuefont" :class="item.icon">{{item.icon}}</text>
<image v-if="item.img" :src="item.img" class="iconimg" :style="{'font-size': item.iconSize}" />
<text v-if="item.badge" class="ua__badge ua__dock-badge">{{item.badge}}</text>
<text v-if="item.dot" class="ua__badge-dot ua__dock-badgeDot"></text>
</view>
</block>
</view>
</scroll-view>
</view>
</template>
props: {
// 当前索引
current: { type: [Number, String], default: 0 },
// 背景色
bgcolor: { type: String, default: null },
/**
* [ 菜单选项 ]
type 菜单类型 type: 'tab'支持uni.switchTab切换 type: 'divider'分割线
path 菜单页面地址
icon 菜单图标-iconfont图标
img 菜单图片
color 菜单图标颜色
title 标题
badge 圆点数字
dot 小红点
*/
menu: {
type: Array,
default: () => [
/* Tab菜单 */
{
type: 'tab',
path: '/pages/index/index',
icon: `\ue619`,
color: '#2979ff',
title: '首页',
},
{
type: 'tab',
path: '/pages/component/index',
icon: 'icon-component',
color: '#17c956',
title: '组件',
badge: 5,
},
{
type: 'tab',
path: '/pages/permission/index',
icon: 'icon-auth',
color: '#f44336',
title: '权限管理',
},
{
type: 'tab',
path: '/pages/setting/index',
icon: 'icon-wo',
color: '#8d1cff',
title: '设置',
dot: true,
},
{
path: '/pages/error/404',
img: require('@/static/mac/keychain.png'),
title: '错误页面',
},
{ type: 'divider' },
/* Nav菜单 */
{
img: require('@/static/logo.png'),
title: 'github',
},
{
img: 'https://www.uviewui.com/common/logo.png',
title: 'gitee',
},
{
img: require('@/static/mac/colorsync.png'),
title: '皮肤',
},
{
img: require('@/static/mac/info.png'),
title: '关于',
},
{ type: 'divider' },
{
img: require('@/static/mac/bin.png'),
title: '回收站',
badge: 12,
},
]
},
},
uniapp自定义多功能表格组件ua-table
ua-table 支持多行、多列,表头固定,自定义slot插槽内容,点击行列返回数据等功能。
<ua-table
:columns="columns"
headerBgColor="#eee"
:headerBold="true"
stripe
padding="5px 0"
:data="data.list"
height="450rpx"
>
</ua-table>
<script>
import Mock from 'mockjs'
export default {
data() {
return {
columns: [
{type: 'index', align: 'center', width: 100, fixed: true}, // 索引序号
{prop: 'title', label: '标题', align: 'left', width: '350'},
{prop: 'num', label: '搜索量', align: 'center', width: 120},
],
data: Mock.mock({
total: 100,
page: 1,
pagesize: 10,
'list|10': [
{
id: '@id()',
title: '@ctitle(10, 20)',
num: '@integer(1000,10000)'
}
]
}),
}
}
}
</script>
如果想实现一些自定义插槽内容,则可以通过如下方法实现。
<ua-table
:columns="columns"
headerBgColor="#eee"
:headerBold="true"
:stripe="true"
:data="data.list"
@row-click="handleRowClick"
@select="handleCheck"
height="750rpx"
style="border:1px solid #eee"
>
<template #default="{row, col, index}">
<block v-if="col.slot == 'image'">
<u-image :src="row.image" :lazy-load="true" height="100rpx" width="100rpx" @click="previewImage(row.image)" />
</block>
<block v-if="col.slot == 'switch'">
<u-switch v-model="row.switch" inactive-color="#fff" :size="36"></u-switch>
</block>
<block v-if="col.slot == 'tags'">
<u-tag :text="row.tags" bg-color="#607d8b" color="#fff" mode="dark" size="mini" />
</block>
<block v-if="col.slot == 'progress'">
<u-line-progress active-color="#1fb925" :percent="row.progress" :show-percent="false" :height="16"></u-line-progress>
</block>
<block v-if="col.slot == 'btns'">
<view class="ua__link success" @click.stop="handleFormEdit(row)">编辑</view>
<view class="ua__link error" @click.stop="handleDel(row, index)">删除</view>
</block>
</template>
</ua-table>
<script>
/**
* uniapp自定义表格
* @author XY Q:282310962
*/
import Mock from 'mockjs'
export default {
data() {
return {
columns: [
{type: 'selection', align: 'center', width: 80, fixed: true}, // 多选
{type: 'index', align: 'center', width: 80, fixed: true}, // 索引序号
{prop: 'author', label: '作者', align: 'center', width: 120},
{prop: 'title', label: '标题', align: 'left', width: 350},
{slot: 'image', label: '图片', align: 'center', width: 120},
{slot: 'switch', label: '推荐', align: 'center', width: 100},
{slot: 'tags', label: '标签', align: 'center', width: 100},
{slot: 'progress', label: '热度', align: 'center', width: 150},
{prop: 'date', label: '发布时间', align: 'left', width: 300}, // 时间
{slot: 'btns', label: '操作', align: 'center', width: 150, fixed: 'right'}, // 操作
],
data: Mock.mock({
total: 100,
page: 1,
pagesize: 10,
'list|30': [
{
id: '@id()',
author: '@cname()',
title: '@ctitle(10, 20)',
image: 'https://picsum.photos/400/400?random=' + '@guid()',
switch: '@boolean()',
'tags|1': ['admin', 'test', 'dev'],
progress: '@integer(30, 90)',
date: '@datetime()'
}
]
}),
}
}
}
</script>
OK,基于uni-app+uview-ui开发原生移动端中后台管理系统就分享到这里。
链接:https://juejin.cn/post/7058793828123148325
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
前段时间有给大家分享一个uni-app仿抖音小视频,今天分享一个uniapp移动端后台管理uni-uadmin实例项目。
uni-uadmin 一款基于uni-app+uviewUI+uniUI+mockjs+echarts等技术开发架构的手机移动端后台管理系统模板。包含了图表、自定义表格、表单、瀑布流及图文编辑器等业务模块,动态权限管理,错误页处理,可编译至H5+小程序+APP端。
技术栈
- 编辑器:HbuilderX3.3.5
- 使用技术:vue+uniapp+uViewUI+mockjs
- 弹窗组件:ua-popup(基于uni-app跨端弹框组件)
- 表格组件:ua-table(基于uni-app封装的多功能表格)
- 自定义组件:uaDock全新的dock风格tabbar组件
- uniapp图表组件:u-charts图表库
项目构建目录
共用模板
整个页面结构布局分为顶部自定义导航+内容区域+底部dock tab菜单三大部分。
<!-- 公共页面模板 -->
<template>
<view class="ua__pageview flexbox flex-col" :style="{'--SKIN': $store.state.skin, 'background': bgcolor, 'color': color}">
<slot name="header" />
<!-- //主容器 -->
<view class="ua__scrollview flex1">
<slot />
</view>
<!-- //底部 -->
<slot name="footer" />
<!-- //dock菜单 -->
<ua-dock v-if="dock && dock != 'false'" @click="handleDockClick" />
<!-- //函数式弹框 -->
<!-- //换肤弹框模板 -->
<ua-popup v-model="isVisibleSkin" position="right">
<Skin />
</ua-popup>
</view>
</template>
uniapp-uadmin 还支持动态权限控制。
uniapp自定义菜单tab组件ua-dock
<!-- //底部dock菜单 -->
<template>
<view class="ua__dockbar">
<scroll-view class="ua__dock-scroll ua__filter" :class="platform" scroll-x :style="{'background': bgcolor}">
<view class="ua__dock-wrap">
<!-- Tab菜单项 -->
<block v-for="(item, index) in menu" :key="index">
<view v-if="item.type == 'divider'" class="ua__dock-divider"></view>
<view v-else class="ua__dock-item" :class="currentTabIndex == index ? 'cur' : ''" @click="switchTab(index, item)">
<text v-if="item.icon" class="iconfont nvuefont" :class="item.icon">{{item.icon}}</text>
<image v-if="item.img" :src="item.img" class="iconimg" :style="{'font-size': item.iconSize}" />
<text v-if="item.badge" class="ua__badge ua__dock-badge">{{item.badge}}</text>
<text v-if="item.dot" class="ua__badge-dot ua__dock-badgeDot"></text>
</view>
</block>
</view>
</scroll-view>
</view>
</template>
props: {
// 当前索引
current: { type: [Number, String], default: 0 },
// 背景色
bgcolor: { type: String, default: null },
/**
* [ 菜单选项 ]
type 菜单类型 type: 'tab'支持uni.switchTab切换 type: 'divider'分割线
path 菜单页面地址
icon 菜单图标-iconfont图标
img 菜单图片
color 菜单图标颜色
title 标题
badge 圆点数字
dot 小红点
*/
menu: {
type: Array,
default: () => [
/* Tab菜单 */
{
type: 'tab',
path: '/pages/index/index',
icon: `\ue619`,
color: '#2979ff',
title: '首页',
},
{
type: 'tab',
path: '/pages/component/index',
icon: 'icon-component',
color: '#17c956',
title: '组件',
badge: 5,
},
{
type: 'tab',
path: '/pages/permission/index',
icon: 'icon-auth',
color: '#f44336',
title: '权限管理',
},
{
type: 'tab',
path: '/pages/setting/index',
icon: 'icon-wo',
color: '#8d1cff',
title: '设置',
dot: true,
},
{
path: '/pages/error/404',
img: require('@/static/mac/keychain.png'),
title: '错误页面',
},
{ type: 'divider' },
/* Nav菜单 */
{
img: require('@/static/logo.png'),
title: 'github',
},
{
img: 'https://www.uviewui.com/common/logo.png',
title: 'gitee',
},
{
img: require('@/static/mac/colorsync.png'),
title: '皮肤',
},
{
img: require('@/static/mac/info.png'),
title: '关于',
},
{ type: 'divider' },
{
img: require('@/static/mac/bin.png'),
title: '回收站',
badge: 12,
},
]
},
},
uniapp自定义多功能表格组件ua-table
ua-table 支持多行、多列,表头固定,自定义slot插槽内容,点击行列返回数据等功能。
<ua-table
:columns="columns"
headerBgColor="#eee"
:headerBold="true"
stripe
padding="5px 0"
:data="data.list"
height="450rpx"
>
</ua-table>
<script>
import Mock from 'mockjs'
export default {
data() {
return {
columns: [
{type: 'index', align: 'center', width: 100, fixed: true}, // 索引序号
{prop: 'title', label: '标题', align: 'left', width: '350'},
{prop: 'num', label: '搜索量', align: 'center', width: 120},
],
data: Mock.mock({
total: 100,
page: 1,
pagesize: 10,
'list|10': [
{
id: '@id()',
title: '@ctitle(10, 20)',
num: '@integer(1000,10000)'
}
]
}),
}
}
}
</script>
如果想实现一些自定义插槽内容,则可以通过如下方法实现。
<ua-table
:columns="columns"
headerBgColor="#eee"
:headerBold="true"
:stripe="true"
:data="data.list"
@row-click="handleRowClick"
@select="handleCheck"
height="750rpx"
style="border:1px solid #eee"
>
<template #default="{row, col, index}">
<block v-if="col.slot == 'image'">
<u-image :src="row.image" :lazy-load="true" height="100rpx" width="100rpx" @click="previewImage(row.image)" />
</block>
<block v-if="col.slot == 'switch'">
<u-switch v-model="row.switch" inactive-color="#fff" :size="36"></u-switch>
</block>
<block v-if="col.slot == 'tags'">
<u-tag :text="row.tags" bg-color="#607d8b" color="#fff" mode="dark" size="mini" />
</block>
<block v-if="col.slot == 'progress'">
<u-line-progress active-color="#1fb925" :percent="row.progress" :show-percent="false" :height="16"></u-line-progress>
</block>
<block v-if="col.slot == 'btns'">
<view class="ua__link success" @click.stop="handleFormEdit(row)">编辑</view>
<view class="ua__link error" @click.stop="handleDel(row, index)">删除</view>
</block>
</template>
</ua-table>
<script>
/**
* uniapp自定义表格
* @author XY Q:282310962
*/
import Mock from 'mockjs'
export default {
data() {
return {
columns: [
{type: 'selection', align: 'center', width: 80, fixed: true}, // 多选
{type: 'index', align: 'center', width: 80, fixed: true}, // 索引序号
{prop: 'author', label: '作者', align: 'center', width: 120},
{prop: 'title', label: '标题', align: 'left', width: 350},
{slot: 'image', label: '图片', align: 'center', width: 120},
{slot: 'switch', label: '推荐', align: 'center', width: 100},
{slot: 'tags', label: '标签', align: 'center', width: 100},
{slot: 'progress', label: '热度', align: 'center', width: 150},
{prop: 'date', label: '发布时间', align: 'left', width: 300}, // 时间
{slot: 'btns', label: '操作', align: 'center', width: 150, fixed: 'right'}, // 操作
],
data: Mock.mock({
total: 100,
page: 1,
pagesize: 10,
'list|30': [
{
id: '@id()',
author: '@cname()',
title: '@ctitle(10, 20)',
image: 'https://picsum.photos/400/400?random=' + '@guid()',
switch: '@boolean()',
'tags|1': ['admin', 'test', 'dev'],
progress: '@integer(30, 90)',
date: '@datetime()'
}
]
}),
}
}
}
</script>
OK,基于uni-app+uview-ui开发原生移动端中后台管理系统就分享到这里。
链接:https://juejin.cn/post/7058793828123148325
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

vue3和vite双向加持,uni-app性能再次提升
uni-app
对vue3 & Vite
的升级,是一个渐进式过程:
- 2020年9月:小程序平台支持 vue3 开发,小程序平台编译器依然使用webpack;
- 2021年5月:H5平台支持 vue3 开发,H5平台编译器升级为 Vite;
- 2021年8月:App平台支持 vue3 开发,App平台编译器升级为 Vite;
- 2021年11月:小程序平台编译器升级为 Vite;
至此,uni-app
在全平台支持了 Vite
编译及Vue 3.x
运行。
so,这场持续一年之久的大版本升级,究竟给uni-app
项目带来了哪些提升?
是时候总结(秀)一波了。
新版 uni-app 框架主要做了三大改进:
- 重写框架内核:基于
vue3 + ts
重写内置组件和API,实现更彻底、更高效的tree-shaking
; - 新增支持 Vite 构建工具,在H5平台实现秒开预览;
- 新增支持 Vue3.x,实现更灵活的开发方式,及更高的运行性能;
基于这三大改进,uni-app项目获得了多快好省四大收益:
- 更多的语法支持,支持组合式API,业务聚焦,开发效率更高;
- 更快的编译速度,H5平台十倍加速,小程序、App加速30%以上;
- 更好的运行性能,用户端响应更快,体验更好;
- 更小的代码体积,瘦身30%以上,更省体积、更省流量
更多的语法支持
新版uni-app
支持Vue 3.x框架,支持组合式API,可实现更聚焦的业务开发。
Vue 3.x的一些新增特性,uni-app
也已经完全支持,如:
- 支持
<script setup>
- 支持
<style scoped>
、<style module>
、State-Driven Dynamic CSS(v-bind)
- 支持
jsx
、tsx
(h5,app 平台支持,小程序不支持)
另外,在小程序平台,新版uni-app
也扩展了更多的语法,如:
- 更完善的模板语法支持(如
class
、style
支持函数、变量等,不再局限数组、对象类型) - 更完整的
props
支持(如传递函数) - 更完善的
slot
支持(如作用域插槽)
更快的编译速度
开发者日常工作中,最无聊的就是等待编译构建。
某乎上还有一个”程序员在等待编译的时候都做什么?“的讨论帖,可见编译时间对开发者而言,是一个多么尴尬无聊的碎片时间。
uni-app
本次升级vue3 & Vite
后,在编译时间上有多少改进?带给开发者多少福利?我们安排真实测试,以数据说话。
测试环境说明:
硬件:RedmiBook 14 二代
处理器:Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
内存:16.0 GB
操作系统:Windows 11 专业版 64 位操作系统
关于编译速度,我们做了两个维度的对比:
- 纵向对比:挑选
uni-app
常用项目模板,在H5、小程序、App平台,分别测试vue 2.6
和vue 3.x
的编译时间 - 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译时间,和
uni-app
的vue 3.x
版本进行对比
uni-app 历史版本纵向对比
我们选择uni-app默认模板
、uni-starter
、hello-uniapp
三个项目模板,分别测试vue 2.6
和vue 3.x
的编译时间。
uni-app项目编译时间的采集方式:
vue 2.6
版本编译时间 = webpack 的 stats.endTime - stats.startTimevue 3.x
版本编译时间 = 构建工具入口处记录 global.vite_start_time = performance.now(),构建工具编译完成时:performance.now() - global.vite_start_time
H5平台
对uni-app
的三个项目模板分别运行到H5平台,进行多次编译测试,并求其均值后,获得如下数据:
由此,我们可以观察到:
- 在
vue 2.6
环境下,随着项目复杂度的提升,H5首页预览所需编译时间会直线增加;这是因为在vue 2.6
版本下,虽然仅预览首页,但依然会使用webpack
编译整个项目资源;故项目越复杂,编译时间越长; - 在
vue 3.x
环境下,H5首页预览的编译时间跟项目复杂度也有关系,但增幅不大;这是因为在vue 3.x
版本下,使用Vite
进行构建,预览首页时仅编译首页及首页所依赖资源,不会编译其它页面资源。
通过图表对比,我们可以直观得出结论:vue 3.x
环境下的首页编译时间,平均不到vue 2.6
环境下的十分之一。
换言之,vue 3.x
版本下的首页编译速度,相比vue 2.6
版本,有十倍效率提升。
这个十倍效率提升,主要得益于新版采用Vite
作为构建工具,由此带来了两大好处:
- 使用原生 ESM 文件,无需打包,实现极速的服务启动;
- 预览(运行)使用
esbuild
作为打包工具,相比vue 2.6
环境下的webpack
,构建速度快 10-100 倍(这不是我们夸大,详见esbuild)
本着这个十倍效率提升,小伙伴们还不赶紧上手试试?
小程序平台
对uni-app
的三个模板项目运行到小程序平台,多次编译测试,并求其均值后,获得如下数据:
从上图对比数据来看,我们可以得出结论:小程序平台,vue 3.x
版本下的运行编译,相比vue 2.6
版本,编译性能至少提升30%;且项目越复杂,编译性能提升越明显,可以达到40% ~ 50%。
App平台
对uni-app
的三个项目模板继续运行到App平台,多次编译测试,并求其均值后,获得如下数据:
从上图对比数据来看,我们可以得出结论:App平台,vue 3.x
版本下的运行编译,相比vue 2.6
版本,编译性能提升将近50%。
虽没有H5平台的十倍效率提升那么刺激,但将近50%的速度提升,经常开发小程序/App的小伙伴,还不心动?
业内优秀框架横向对比
除了采用不同版本的uni-app
进行纵向对比外,我们还使用业内优秀的跨端框架Taro
,创建空的项目模板,进行横向对比测试。
具体测试方案:
- 安装
Taro
的最新cli,本文测试时使用的版本为"@Tarojs/Taro": "3.3.16" - 使用
Taro init
命令,分别选择react
、vue
、vue3
框架,创建三个默认项目模板,三个项目名称分别为taro3-react
、taro3-vue
、taro3-vue3
,如下图:
- 使用
npm run dev:h5
,运行到H5平台进行预览,记录每次预览编译时间,重复执行,求其均值
关于Taro
编译时间的计算方案:
- 开发一个
Taro
扩展插件,插件规范参考Taro官网 - 插件功能 - 在
ctx.onBuildStart
中记录开始编译时间 - 在
ctx.onBuildFinish
中记录编译结束时间 - 两者的时间差,即为编译过程消耗时间
然后使用uni-app
的cli
命令行,创建基于vue3.x
的空项目模板,项目命名为uni-app-vue3
。
我们使用各自框架的命令行,将如上创建的5个项目分别编译到H5平台和小程序平台,多次测试,并求其均值。
同框架版本在H5平台上的编译时间,结果如下:
从图中可以看出,uni-app
的vue3
版本,在H5平台上的首页编译预览性能是遥遥领先的。这个遥遥有多远呢?这么讲吧,你都编译20次了,友商第一次还没完呢。
继续编译到小程序平台,多次测试,求其均值,结果如下:
从图中可以看出,uni-app
的vue3
版本,在小程序平台上的编译性能也是遥遥领先的,这个遥遥也不近。
更好的运行速度
开发环节编译快了,那面向最终用户的软件,运行性能怎么样?
我们进入性能测试章节。
测试方案:
- 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
- 界面如下:
- 测试机型:小米 Mi 10 pro、MIUI 12.5 (21.11.3 开发版) 、微信版本 8.0.16
- 准备工作:每次开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。
- 评测点:长列表中的某个组件,比如点赞组件,点击时是否能及时的修改未赞和已赞状态?
测试计时方式:
- 选中某微博,点击“点赞”按钮,实现点赞状态状态切换(已赞高亮、未赞灰色),
- 点赞按钮 onclick函数开头开始计时,setData回调函数开头结束计时;
在小米手机上进行多次测试,求其平均值,结果如下:
记录条数 | 200 | 400 | 600 | 800 | 1000 |
---|---|---|---|---|---|
vue2 | 30ms | 43ms | 56ms | 72ms | 90ms |
vue3 | 8ms | 9ms | 9ms | 8ms | 9ms |
从表格中可以看出:
- 随着页面记录的增加,
vue 2.6
版本的uni-app
项目,点赞组件响应时间快速增加,响应越来越慢; - 基于
vue 3.x
的uni-app
项目,点赞组件的响应时间跟页面条数无关,一直保持极高的响应灵敏度,性能体验远高于vue 2.6
版本。
从这个常见的长列表组件响应实验来看,vue 3.x
的性能体验要远高于vue 2.6
版本。
更小的代码体积
项目发行后的代码体积,是一个很重要的考量指标:
- H5平台:更小的代码体积,可以帮助开发者节省服务端带宽及CDN流量,可实现更快的资源加载及页面渲染;
- 小程序平台:更小的代码体积,可加速小程序包的下载(甚至可能免了分包加载的繁琐),帮助用户更快进入小程序业务界面;
- App平台:更小的代码体积,可实现更快的App启动,帮助用户更快进入App首页
为了测试vue 3.x
新版升级后,代码体积的变化,我们同样做了两个维度的测试:
- 纵向对比:选择
uni-app
常用项目模板,在H5、小程序、App平台,分别测试vue 2.6
和vue 3.x
的编译包大小 - 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译后的包体积大小,和
uni-app
版本进行对比
Tips:
- 开发阶段重在编译速度,对应
npm run dev
操作 - 发行阶段重在编译包大小,对应
npm run build
操作
uni-app 不同版本纵向对比
我们复用之前创建的uni-app默认模板
、uni-starter
、hello-uniapp
三个项目模板,分别测试vue 2.6
和vue 3.x
的编译包体积。
uni-app
项目编译包体积的采集方式:编译到对应平台后,记录编译后文件夹的大小。
H5平台
H5平台编译后代码体积记录如下:
从统计结果来看,uni-app
的vue3.x
版本,在H5平台上的编译包体积至少瘦身30%以上。
H5平台的瘦身优化,主要得益于uni-app
框架的底层全面重构,实现了更彻底的摇树优化。
小程序平台
小程序平台编译后代码体积记录如下:
从统计结果来看,uni-app
的vue3.x
版本,在小程序平台上也有大幅瘦身。
App平台
App平台编译后代码体积记录如下:
从统计结果来看,uni-app
的vue3.x
版本,在App平台上根据项目不同,会有不同幅度的瘦身。
从理论上来讲,项目中的页面模板越复杂,App平台的瘦身效果越明显。
业内优秀框架横向对比
关于编译后的代码体积,我们也和业内优秀的跨端框架Taro
进行了对比,复用前面章节创建的三个Taro
项目,分别编译到H5平台和小程序平台,计算其编译后的源码文件夹大小。
从图中可以看出,uni-app
的vue3版本,在H5平台上编译包体积是最小的,只有友商的十分之一左右。
我们继续测试,不同版本框架发行到微信小程序平台,记录其编译包大小:
从图中可以看出,uni-app
的vue3版本,在小程序平台上编译包体积也是最小的。
Tips:细心的开发者会发现,所有框架版本编译到小程序上的代码包体积都远小于其在H5平台上的包体积,这是因为小程序由平台厂商提供内置组件及接口实现,而H5平台则需跨端框架自己实现内置组件及接口,故H5平台的代码包普遍要大一些。
总结
综上,我们以数字说话,阐述了vue3版本uni-app
开发的诸多好处,再回顾一遍:
- 更多的语法
- 更快的编译
- 更好的运行
- 更少的代码
你还不赶紧升级新版uni-app
来试试吗?
对文本测试过程及结果有疑问的同学,欢迎到github上提交issue,欢迎指正。
uni-app
对vue3 & Vite
的升级,是一个渐进式过程:
- 2020年9月:小程序平台支持 vue3 开发,小程序平台编译器依然使用webpack;
- 2021年5月:H5平台支持 vue3 开发,H5平台编译器升级为 Vite;
- 2021年8月:App平台支持 vue3 开发,App平台编译器升级为 Vite;
- 2021年11月:小程序平台编译器升级为 Vite;
至此,uni-app
在全平台支持了 Vite
编译及Vue 3.x
运行。
so,这场持续一年之久的大版本升级,究竟给uni-app
项目带来了哪些提升?
是时候总结(秀)一波了。
新版 uni-app 框架主要做了三大改进:
- 重写框架内核:基于
vue3 + ts
重写内置组件和API,实现更彻底、更高效的tree-shaking
; - 新增支持 Vite 构建工具,在H5平台实现秒开预览;
- 新增支持 Vue3.x,实现更灵活的开发方式,及更高的运行性能;
基于这三大改进,uni-app项目获得了多快好省四大收益:
- 更多的语法支持,支持组合式API,业务聚焦,开发效率更高;
- 更快的编译速度,H5平台十倍加速,小程序、App加速30%以上;
- 更好的运行性能,用户端响应更快,体验更好;
- 更小的代码体积,瘦身30%以上,更省体积、更省流量
更多的语法支持
新版uni-app
支持Vue 3.x框架,支持组合式API,可实现更聚焦的业务开发。
Vue 3.x的一些新增特性,uni-app
也已经完全支持,如:
- 支持
<script setup>
- 支持
<style scoped>
、<style module>
、State-Driven Dynamic CSS(v-bind)
- 支持
jsx
、tsx
(h5,app 平台支持,小程序不支持)
另外,在小程序平台,新版uni-app
也扩展了更多的语法,如:
- 更完善的模板语法支持(如
class
、style
支持函数、变量等,不再局限数组、对象类型) - 更完整的
props
支持(如传递函数) - 更完善的
slot
支持(如作用域插槽)
更快的编译速度
开发者日常工作中,最无聊的就是等待编译构建。
某乎上还有一个”程序员在等待编译的时候都做什么?“的讨论帖,可见编译时间对开发者而言,是一个多么尴尬无聊的碎片时间。
uni-app
本次升级vue3 & Vite
后,在编译时间上有多少改进?带给开发者多少福利?我们安排真实测试,以数据说话。
测试环境说明:
硬件:RedmiBook 14 二代
处理器:Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
内存:16.0 GB
操作系统:Windows 11 专业版 64 位操作系统
关于编译速度,我们做了两个维度的对比:
- 纵向对比:挑选
uni-app
常用项目模板,在H5、小程序、App平台,分别测试vue 2.6
和vue 3.x
的编译时间 - 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译时间,和
uni-app
的vue 3.x
版本进行对比
uni-app 历史版本纵向对比
我们选择uni-app默认模板
、uni-starter
、hello-uniapp
三个项目模板,分别测试vue 2.6
和vue 3.x
的编译时间。
uni-app项目编译时间的采集方式:
vue 2.6
版本编译时间 = webpack 的 stats.endTime - stats.startTimevue 3.x
版本编译时间 = 构建工具入口处记录 global.vite_start_time = performance.now(),构建工具编译完成时:performance.now() - global.vite_start_time
H5平台
对uni-app
的三个项目模板分别运行到H5平台,进行多次编译测试,并求其均值后,获得如下数据:
由此,我们可以观察到:
- 在
vue 2.6
环境下,随着项目复杂度的提升,H5首页预览所需编译时间会直线增加;这是因为在vue 2.6
版本下,虽然仅预览首页,但依然会使用webpack
编译整个项目资源;故项目越复杂,编译时间越长; - 在
vue 3.x
环境下,H5首页预览的编译时间跟项目复杂度也有关系,但增幅不大;这是因为在vue 3.x
版本下,使用Vite
进行构建,预览首页时仅编译首页及首页所依赖资源,不会编译其它页面资源。
通过图表对比,我们可以直观得出结论:vue 3.x
环境下的首页编译时间,平均不到vue 2.6
环境下的十分之一。
换言之,vue 3.x
版本下的首页编译速度,相比vue 2.6
版本,有十倍效率提升。
这个十倍效率提升,主要得益于新版采用Vite
作为构建工具,由此带来了两大好处:
- 使用原生 ESM 文件,无需打包,实现极速的服务启动;
- 预览(运行)使用
esbuild
作为打包工具,相比vue 2.6
环境下的webpack
,构建速度快 10-100 倍(这不是我们夸大,详见esbuild)
本着这个十倍效率提升,小伙伴们还不赶紧上手试试?
小程序平台
对uni-app
的三个模板项目运行到小程序平台,多次编译测试,并求其均值后,获得如下数据:
从上图对比数据来看,我们可以得出结论:小程序平台,vue 3.x
版本下的运行编译,相比vue 2.6
版本,编译性能至少提升30%;且项目越复杂,编译性能提升越明显,可以达到40% ~ 50%。
App平台
对uni-app
的三个项目模板继续运行到App平台,多次编译测试,并求其均值后,获得如下数据:
从上图对比数据来看,我们可以得出结论:App平台,vue 3.x
版本下的运行编译,相比vue 2.6
版本,编译性能提升将近50%。
虽没有H5平台的十倍效率提升那么刺激,但将近50%的速度提升,经常开发小程序/App的小伙伴,还不心动?
业内优秀框架横向对比
除了采用不同版本的uni-app
进行纵向对比外,我们还使用业内优秀的跨端框架Taro
,创建空的项目模板,进行横向对比测试。
具体测试方案:
- 安装
Taro
的最新cli,本文测试时使用的版本为"@Tarojs/Taro": "3.3.16" - 使用
Taro init
命令,分别选择react
、vue
、vue3
框架,创建三个默认项目模板,三个项目名称分别为taro3-react
、taro3-vue
、taro3-vue3
,如下图:
- 使用
npm run dev:h5
,运行到H5平台进行预览,记录每次预览编译时间,重复执行,求其均值
关于Taro
编译时间的计算方案:
- 开发一个
Taro
扩展插件,插件规范参考Taro官网 - 插件功能 - 在
ctx.onBuildStart
中记录开始编译时间 - 在
ctx.onBuildFinish
中记录编译结束时间 - 两者的时间差,即为编译过程消耗时间
然后使用uni-app
的cli
命令行,创建基于vue3.x
的空项目模板,项目命名为uni-app-vue3
。
我们使用各自框架的命令行,将如上创建的5个项目分别编译到H5平台和小程序平台,多次测试,并求其均值。
同框架版本在H5平台上的编译时间,结果如下:
从图中可以看出,uni-app
的vue3
版本,在H5平台上的首页编译预览性能是遥遥领先的。这个遥遥有多远呢?这么讲吧,你都编译20次了,友商第一次还没完呢。
继续编译到小程序平台,多次测试,求其均值,结果如下:
从图中可以看出,uni-app
的vue3
版本,在小程序平台上的编译性能也是遥遥领先的,这个遥遥也不近。
更好的运行速度
开发环节编译快了,那面向最终用户的软件,运行性能怎么样?
我们进入性能测试章节。
测试方案:
- 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
- 界面如下:
- 测试机型:小米 Mi 10 pro、MIUI 12.5 (21.11.3 开发版) 、微信版本 8.0.16
- 准备工作:每次开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。
- 评测点:长列表中的某个组件,比如点赞组件,点击时是否能及时的修改未赞和已赞状态?
测试计时方式:
- 选中某微博,点击“点赞”按钮,实现点赞状态状态切换(已赞高亮、未赞灰色),
- 点赞按钮 onclick函数开头开始计时,setData回调函数开头结束计时;
在小米手机上进行多次测试,求其平均值,结果如下:
记录条数 | 200 | 400 | 600 | 800 | 1000 |
---|---|---|---|---|---|
vue2 | 30ms | 43ms | 56ms | 72ms | 90ms |
vue3 | 8ms | 9ms | 9ms | 8ms | 9ms |
从表格中可以看出:
- 随着页面记录的增加,
vue 2.6
版本的uni-app
项目,点赞组件响应时间快速增加,响应越来越慢; - 基于
vue 3.x
的uni-app
项目,点赞组件的响应时间跟页面条数无关,一直保持极高的响应灵敏度,性能体验远高于vue 2.6
版本。
从这个常见的长列表组件响应实验来看,vue 3.x
的性能体验要远高于vue 2.6
版本。
更小的代码体积
项目发行后的代码体积,是一个很重要的考量指标:
- H5平台:更小的代码体积,可以帮助开发者节省服务端带宽及CDN流量,可实现更快的资源加载及页面渲染;
- 小程序平台:更小的代码体积,可加速小程序包的下载(甚至可能免了分包加载的繁琐),帮助用户更快进入小程序业务界面;
- App平台:更小的代码体积,可实现更快的App启动,帮助用户更快进入App首页
为了测试vue 3.x
新版升级后,代码体积的变化,我们同样做了两个维度的测试:
- 纵向对比:选择
uni-app
常用项目模板,在H5、小程序、App平台,分别测试vue 2.6
和vue 3.x
的编译包大小 - 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译后的包体积大小,和
uni-app
版本进行对比
Tips:
- 开发阶段重在编译速度,对应
npm run dev
操作 - 发行阶段重在编译包大小,对应
npm run build
操作
uni-app 不同版本纵向对比
我们复用之前创建的uni-app默认模板
、uni-starter
、hello-uniapp
三个项目模板,分别测试vue 2.6
和vue 3.x
的编译包体积。
uni-app
项目编译包体积的采集方式:编译到对应平台后,记录编译后文件夹的大小。
H5平台
H5平台编译后代码体积记录如下:
从统计结果来看,uni-app
的vue3.x
版本,在H5平台上的编译包体积至少瘦身30%以上。
H5平台的瘦身优化,主要得益于uni-app
框架的底层全面重构,实现了更彻底的摇树优化。
小程序平台
小程序平台编译后代码体积记录如下:
从统计结果来看,uni-app
的vue3.x
版本,在小程序平台上也有大幅瘦身。
App平台
App平台编译后代码体积记录如下:
从统计结果来看,uni-app
的vue3.x
版本,在App平台上根据项目不同,会有不同幅度的瘦身。
从理论上来讲,项目中的页面模板越复杂,App平台的瘦身效果越明显。
业内优秀框架横向对比
关于编译后的代码体积,我们也和业内优秀的跨端框架Taro
进行了对比,复用前面章节创建的三个Taro
项目,分别编译到H5平台和小程序平台,计算其编译后的源码文件夹大小。
从图中可以看出,uni-app
的vue3版本,在H5平台上编译包体积是最小的,只有友商的十分之一左右。
我们继续测试,不同版本框架发行到微信小程序平台,记录其编译包大小:
从图中可以看出,uni-app
的vue3版本,在小程序平台上编译包体积也是最小的。
Tips:细心的开发者会发现,所有框架版本编译到小程序上的代码包体积都远小于其在H5平台上的包体积,这是因为小程序由平台厂商提供内置组件及接口实现,而H5平台则需跨端框架自己实现内置组件及接口,故H5平台的代码包普遍要大一些。
总结
综上,我们以数字说话,阐述了vue3版本uni-app
开发的诸多好处,再回顾一遍:
- 更多的语法
- 更快的编译
- 更好的运行
- 更少的代码
你还不赶紧升级新版uni-app
来试试吗?
对文本测试过程及结果有疑问的同学,欢迎到github上提交issue,欢迎指正。
收起阅读 »
uni-app 清爽型商城app开源代码,支持小程序,h5,Android,ios
https://github.com/gooking/uni-app--mini-mall
[码云镜像:] https://gitee.com/javazj/uni-app--mini-mall
直接贴 GitHub 和 码云的下载地址吧,上面有演示二维码可以扫码体验,做的还是挺不错的
https://github.com/gooking/uni-app--mini-mall
[码云镜像:] https://gitee.com/javazj/uni-app--mini-mall
直接贴 GitHub 和 码云的下载地址吧,上面有演示二维码可以扫码体验,做的还是挺不错的
收起阅读 »
APP开发为什么选择uni-app,目前主流的APP开发方式总结和对比
Native App
使用原生语言开发的应用;
性能和体验都是最好,但开发和发布成本最高;
常用的开发技术:Swift,OC, Java
Web App
移动端的网站,常被称为H5应用,即运行在移动端浏览器的网站应用,一般泛指SPA模式开发的网站,与MPA对应,代表:微信公众号里的H5应用(微信公众号的H5又可以调用Native API,也可以认为是Hybrid App);
开发和发布成本最低,但性能最差;
常用的开发技术:VueJS、ReactJS等;
Hybrid App
混合模式移动应用,介于WebApp、Native App两者之间的App开发技术;
原理:JS写逻辑且可以通过JSBridge调用Native的API,用HTML+CSS编写界面,并由webview渲染界面;
渲染方式:webview渲染;
JSBridge统一封装了IOS和Android的API,因此Hybrid App具有跨平台效果;
JS逻辑的执行由webview内置的JS引擎决定,调用Native API是通过JSBridge来实现;
开发和发布成本介于Native App 和 WebApp之间。
热更新:支持
常用开发技术:PhoneGap、ApiCloud、MUI、Wex5、AppCan等;
React Native App
RN是Facebook开发并开源的一款UI框架,以解决Hybrid存在的缺陷与不足;
原理:JS写逻辑且运行在JS引擎中,底层自动把JS代码解析成对应平台(ios、安卓)的原生API,调用Native的API绘制原生UI,即原生渲染界面,这是与Hybird App最大的不同,因此性能好于Hybrid App。
渲染方式:原生渲染;
JS引擎为:ios为JSCore,andorid为v8,最新版rn开始在andorid上搞自己的js引擎Hermes
界面:由JSX语言写界面
布局:Flexbox;
基于的开发技术:ReactJS
热更新:支持;
思想:learn once, write anywhere; 注:不敢说write once,因为RN要针对ios和安卓各写一套代码;
Weex App
与React Native App类似,由阿里开发并开源一款UI框架;
原理:跟RN类似;
渲染方式:原生渲染
跟RN最大不同:Weex写一套代码即可运行在IOS和安卓中,RN要写两套代码,IOS一套,安卓一套;
JS引擎为:ios为JSCore,andorid为v8
界面:由Vue编写界面;
布局:Flexbox;
基于的开发技术:VueJS;
热更新:支持;
思想:write once, run anywhere;
注:
- 微信小程序类似于RN/Weex开发方式,也分为逻辑层和视图层;
- 微信小程序的页面属于混合渲染,什么是混合渲染?看后文总结;
Flutter APP
由Google开发并开源的一套UI框架,使用dart语言;逻辑和界面使用Flutter Engine;
Flutter使用Engine来绘制Widget(Flutter的显示单元),即Widget渲染界面,Dart代码都是通过AOT(Ahead Of Time)编译为平台的原生代码,所以Flutter可以直接与平台通信,不需要JS引擎的桥接。
Widget是不可变的,仅支持一帧,并且在每一帧上不会直接更新,要更新而必须使用Widget的状态。无状态和有状态widget的核心特性是相同的,每一帧他们都会重新构建,有一个State对象,它可以跨帧存储状态数据并恢复它。
渲染方式:Widget渲染界面
性能:Flutter APP是除了Native APP以外性能最好的;
热更新:不支持;
Uni-App
DCloud公司开发的一款基于vue.js的跨端的框架;
渲染方式:混合渲染、weex原生渲染、webview渲染。小程序和app-vue页面属于混合渲染,app-nvue页面全部是weex原生渲染。H5全部为webview渲染;
uni-app里的App端原生插件,这类插件使用IOS或者Android原生语言编写,封装成插件,供其他开发者使用js来调用;
原生插件分为原生组件component和原生模块module;
原生组件component只能在App-nvue页面中使用;
uni-app插件市场的大部分原生插件大部分属于原生模块module;
uni-app开发app性能足够好,用官方原话说是:点击跳转原文
> 当然,uni-app的app引擎并没有吊炸天。App平台,所有跨平台工具都还比不过原生,这是客观事实。只是,如果uni-app不能满足你的需求,你没有必要去用其他跨平台工具,直接上原生吧。
笔者认为使用uni-app开发最大的好处就是省成本和不错的生态:
- 成本,包括学习成本、开发成本,时间成本,招人成本等;
- 生态,包括开发者数量,社区活跃度,文档是否齐全等;
总结
目前主流的3大渲染引擎有:webview、React Native/weex、Flutter,复杂程度依次降低、渲染性能依次上升
混合渲染:主体为webview渲染,部分元素为原生渲染,比如导航栏、tabbar、video、map使用了原生控件
例如:微信小程序,uniapp发布的app-vue页面都属于混合渲染;
混合渲染虽然提升了性能,但也带来了其他问题,点击查看
Flutter的逻辑层和视图层统一,运行在同一套dart虚拟机下。
rn和weex使用原生渲染,性能高于webview,但是同为原生渲染,rn和weex怎么会慢于flutter呢?其实并不是原生渲染慢,而是js和原生通信慢
rn和weex分为js引擎和原生渲染层两个运行环境,当js引擎联网获取数据后,通知原生视图层更新界面时,有一个跨环境的通信折损。同样,但用户在屏幕上操作原生视图层时,要给js引擎发送通知,也会产生通信折损。
这个通信折损,普遍存在于所有逻辑和视图分离的框架中,各家小程序因为也使用这个架构,所以也存在这个问题。
因为flutter只有一个dart引擎,所有没有来回通信参数的性能问题,所以性能比rn和weex跟高;
这个通信的折损特别表现在跟手势的js响应操作绘制帧动画,或者说js连续操作界面元素方面。场景如:界面可拖动的浮动球、可拖动的滑块等。
为了解决通信的折损,RN搞了lottie的动画库,weex搞了BindingX,微信小程序搞了wxs,百度小程序搞了Filter,阿里小程序搞了SJS,uniapp若使用weex渲染时使用BindingX,使用app-vue时使用renderjs或wxs,renderjs和wxs是一种运行在视图层的js,不和逻辑层通信。
Native App
使用原生语言开发的应用;
性能和体验都是最好,但开发和发布成本最高;
常用的开发技术:Swift,OC, Java
Web App
移动端的网站,常被称为H5应用,即运行在移动端浏览器的网站应用,一般泛指SPA模式开发的网站,与MPA对应,代表:微信公众号里的H5应用(微信公众号的H5又可以调用Native API,也可以认为是Hybrid App);
开发和发布成本最低,但性能最差;
常用的开发技术:VueJS、ReactJS等;
Hybrid App
混合模式移动应用,介于WebApp、Native App两者之间的App开发技术;
原理:JS写逻辑且可以通过JSBridge调用Native的API,用HTML+CSS编写界面,并由webview渲染界面;
渲染方式:webview渲染;
JSBridge统一封装了IOS和Android的API,因此Hybrid App具有跨平台效果;
JS逻辑的执行由webview内置的JS引擎决定,调用Native API是通过JSBridge来实现;
开发和发布成本介于Native App 和 WebApp之间。
热更新:支持
常用开发技术:PhoneGap、ApiCloud、MUI、Wex5、AppCan等;
React Native App
RN是Facebook开发并开源的一款UI框架,以解决Hybrid存在的缺陷与不足;
原理:JS写逻辑且运行在JS引擎中,底层自动把JS代码解析成对应平台(ios、安卓)的原生API,调用Native的API绘制原生UI,即原生渲染界面,这是与Hybird App最大的不同,因此性能好于Hybrid App。
渲染方式:原生渲染;
JS引擎为:ios为JSCore,andorid为v8,最新版rn开始在andorid上搞自己的js引擎Hermes
界面:由JSX语言写界面
布局:Flexbox;
基于的开发技术:ReactJS
热更新:支持;
思想:learn once, write anywhere; 注:不敢说write once,因为RN要针对ios和安卓各写一套代码;
Weex App
与React Native App类似,由阿里开发并开源一款UI框架;
原理:跟RN类似;
渲染方式:原生渲染
跟RN最大不同:Weex写一套代码即可运行在IOS和安卓中,RN要写两套代码,IOS一套,安卓一套;
JS引擎为:ios为JSCore,andorid为v8
界面:由Vue编写界面;
布局:Flexbox;
基于的开发技术:VueJS;
热更新:支持;
思想:write once, run anywhere;
注:
- 微信小程序类似于RN/Weex开发方式,也分为逻辑层和视图层;
- 微信小程序的页面属于混合渲染,什么是混合渲染?看后文总结;
Flutter APP
由Google开发并开源的一套UI框架,使用dart语言;逻辑和界面使用Flutter Engine;
Flutter使用Engine来绘制Widget(Flutter的显示单元),即Widget渲染界面,Dart代码都是通过AOT(Ahead Of Time)编译为平台的原生代码,所以Flutter可以直接与平台通信,不需要JS引擎的桥接。
Widget是不可变的,仅支持一帧,并且在每一帧上不会直接更新,要更新而必须使用Widget的状态。无状态和有状态widget的核心特性是相同的,每一帧他们都会重新构建,有一个State对象,它可以跨帧存储状态数据并恢复它。
渲染方式:Widget渲染界面
性能:Flutter APP是除了Native APP以外性能最好的;
热更新:不支持;
Uni-App
DCloud公司开发的一款基于vue.js的跨端的框架;
渲染方式:混合渲染、weex原生渲染、webview渲染。小程序和app-vue页面属于混合渲染,app-nvue页面全部是weex原生渲染。H5全部为webview渲染;
uni-app里的App端原生插件,这类插件使用IOS或者Android原生语言编写,封装成插件,供其他开发者使用js来调用;
原生插件分为原生组件component和原生模块module;
原生组件component只能在App-nvue页面中使用;
uni-app插件市场的大部分原生插件大部分属于原生模块module;
uni-app开发app性能足够好,用官方原话说是:点击跳转原文
> 当然,uni-app的app引擎并没有吊炸天。App平台,所有跨平台工具都还比不过原生,这是客观事实。只是,如果uni-app不能满足你的需求,你没有必要去用其他跨平台工具,直接上原生吧。
笔者认为使用uni-app开发最大的好处就是省成本和不错的生态:
- 成本,包括学习成本、开发成本,时间成本,招人成本等;
- 生态,包括开发者数量,社区活跃度,文档是否齐全等;
总结
目前主流的3大渲染引擎有:webview、React Native/weex、Flutter,复杂程度依次降低、渲染性能依次上升
混合渲染:主体为webview渲染,部分元素为原生渲染,比如导航栏、tabbar、video、map使用了原生控件
例如:微信小程序,uniapp发布的app-vue页面都属于混合渲染;
混合渲染虽然提升了性能,但也带来了其他问题,点击查看
Flutter的逻辑层和视图层统一,运行在同一套dart虚拟机下。
rn和weex使用原生渲染,性能高于webview,但是同为原生渲染,rn和weex怎么会慢于flutter呢?其实并不是原生渲染慢,而是js和原生通信慢
rn和weex分为js引擎和原生渲染层两个运行环境,当js引擎联网获取数据后,通知原生视图层更新界面时,有一个跨环境的通信折损。同样,但用户在屏幕上操作原生视图层时,要给js引擎发送通知,也会产生通信折损。
这个通信折损,普遍存在于所有逻辑和视图分离的框架中,各家小程序因为也使用这个架构,所以也存在这个问题。
因为flutter只有一个dart引擎,所有没有来回通信参数的性能问题,所以性能比rn和weex跟高;
这个通信的折损特别表现在跟手势的js响应操作绘制帧动画,或者说js连续操作界面元素方面。场景如:界面可拖动的浮动球、可拖动的滑块等。
为了解决通信的折损,RN搞了lottie的动画库,weex搞了BindingX,微信小程序搞了wxs,百度小程序搞了Filter,阿里小程序搞了SJS,uniapp若使用weex渲染时使用BindingX,使用app-vue时使用renderjs或wxs,renderjs和wxs是一种运行在视图层的js,不和逻辑层通信。

如何在VUE中播放RTSP监控视频,且延迟小于300毫秒?
近期研究在VUE中播放RTSP实时视频,客户要求延迟低于300毫秒,并且要求支持多路同时播放,支持H.265格式视频,比较了下目前市场上常见的几种方案,以供大家参考!
一、海康威视官方WEB解决方案:
海康威视官方提供了两种WEB解决方案,即无插件方案和有插件方案。
1.无插件方案,实际采用的是服务器转码推流的方式,因为需要转码两次,导致延迟比较高,多路播放或者播放高清视频容易卡顿或者花屏,无法满足客户需求。
2.有插件版方案,虽然延迟比较低,但要求浏览器需要支持NPAPI插件,所以只能运行在IE和低版本Chrome等浏览器,IE明年就彻底不能用了,低版本的浏览器漏洞也比较多,商用还是有很大风险,只能忍痛放弃!
二、低版本浏览器VLC播放方案:
2015年之前Chrome等浏览器还未取消对NPAPI插件支持的时主流方案,继续使用低版本Chrome浏览器,通过VLC原生播放器直接播放,也不需要服务器支持,延迟非常低,终端也可以使用硬件的加速能力,多路播放也毫无压力。
缺点也非常明显:无法使用最新的浏览器和操作系统,不适合商用。如果能解决高版本的Chrome、Firefox、Edge等浏览器使用,此方案无疑是最佳选择!
三、猿大师VLC播放程序方案:
猿大师的VLC播放程序是基于猿大师中间件提供的内嵌网页播放的专利技术,底层调用VLC客户端的ActiveX控件可实现在Chrome等高版本浏览器中内嵌播放海康威视、大华等摄像头的RTSP视频流,可以以做到低延迟(300毫秒),支持多路同时播放,支持H.264和H.265格式,支持2K、4K等高清视频,兼容主流浏览器的老版本和最新版本,不用担心浏览器升级导致不能用的问题。
猿大师官网:http://www.yuanmaster.com
1.猿大师与大华官方网页延迟对比:https://www.bilibili.com/video/BV1ff4y1j7qg/
2.猿大师VLC播放程序与海康威视官方网页延迟对比:https://www.bilibili.com/video/BV1mr4y127oX/
3.猿大师VLC播放程序VUE测试页面效果演示:https://www.bilibili.com/video/BV1Y34y197Z3
近期研究在VUE中播放RTSP实时视频,客户要求延迟低于300毫秒,并且要求支持多路同时播放,支持H.265格式视频,比较了下目前市场上常见的几种方案,以供大家参考!
一、海康威视官方WEB解决方案:
海康威视官方提供了两种WEB解决方案,即无插件方案和有插件方案。
1.无插件方案,实际采用的是服务器转码推流的方式,因为需要转码两次,导致延迟比较高,多路播放或者播放高清视频容易卡顿或者花屏,无法满足客户需求。
2.有插件版方案,虽然延迟比较低,但要求浏览器需要支持NPAPI插件,所以只能运行在IE和低版本Chrome等浏览器,IE明年就彻底不能用了,低版本的浏览器漏洞也比较多,商用还是有很大风险,只能忍痛放弃!
二、低版本浏览器VLC播放方案:
2015年之前Chrome等浏览器还未取消对NPAPI插件支持的时主流方案,继续使用低版本Chrome浏览器,通过VLC原生播放器直接播放,也不需要服务器支持,延迟非常低,终端也可以使用硬件的加速能力,多路播放也毫无压力。
缺点也非常明显:无法使用最新的浏览器和操作系统,不适合商用。如果能解决高版本的Chrome、Firefox、Edge等浏览器使用,此方案无疑是最佳选择!
三、猿大师VLC播放程序方案:
猿大师的VLC播放程序是基于猿大师中间件提供的内嵌网页播放的专利技术,底层调用VLC客户端的ActiveX控件可实现在Chrome等高版本浏览器中内嵌播放海康威视、大华等摄像头的RTSP视频流,可以以做到低延迟(300毫秒),支持多路同时播放,支持H.264和H.265格式,支持2K、4K等高清视频,兼容主流浏览器的老版本和最新版本,不用担心浏览器升级导致不能用的问题。
猿大师官网:http://www.yuanmaster.com
1.猿大师与大华官方网页延迟对比:https://www.bilibili.com/video/BV1ff4y1j7qg/
2.猿大师VLC播放程序与海康威视官方网页延迟对比:https://www.bilibili.com/video/BV1mr4y127oX/
3.猿大师VLC播放程序VUE测试页面效果演示:https://www.bilibili.com/video/BV1Y34y197Z3

苹果内购项目怎样获取货币符号
let codes=[];
codes.push({country:'USD',code:'$ '})
codes.push({country:'AED',code:'AED'})
codes.push({country:'EGP',code:'EGP'})
codes.push({country:'EUR',code:'€ '})
codes.push({country:'AUD',code:'$ '})
codes.push({country:'PKR',code:'Rs '})
codes.push({country:'BRL',code:'R$ '})
codes.push({country:'BGN',code:'лв '})
codes.push({country:'PLN',code:'zł '})
codes.push({country:'DKK',code:'kr '})
codes.push({country:'RUB',code:'₽ '})
codes.push({country:'PHP',code:'₱ '})
codes.push({country:'COP',code:'$ '})
codes.push({country:'KZT',code:'₸ '})
codes.push({country:'KRW',code:'₩ '})
codes.push({country:'CAD',code:'$ '})
codes.push({country:'CZK',code:'Kč '})
codes.push({country:'QAR',code:'QAR'})
codes.push({country:'HRK',code:'kn '})
codes.push({country:'RON',code:'lei'})
codes.push({country:'MYR',code:'RM '})
codes.push({country:'PEN',code:'S/''})
codes.push({country:'MXN',code:'$ '})
codes.push({country:'ZAR',code:'R '})
codes.push({country:'NGN',code:'₦ '})
codes.push({country:'NOK',code:'kr '})
codes.push({country:'JPY',code:'¥ '})
codes.push({country:'SEK',code:'kr '})
codes.push({country:'CHF',code:'CHF'})
codes.push({country:'SAR',code:'SR '})
codes.push({country:'TWD',code:'NT '})
codes.push({country:'THB',code:'฿ '})
codes.push({country:'TZS',code:'TZS'})
codes.push({country:'TRY',code:'₺ '})
codes.push({country:'HKD',code:'HK$'})
codes.push({country:'SGD',code:'S$ '})
codes.push({country:'NZD',code:'$ '})
codes.push({country:'HUF',code:'Ft '})
codes.push({country:'ILS',code:'₪ })'
codes.push({country:'INR',code:'₹ '})
codes.push({country:'IDR',code:'Rp '})
codes.push({country:'GBP',code:'£ '})
codes.push({country:'VND',code:' đ '})
codes.push({country:'CLP',code:' $ '})
codes.push({country:'CNY',code:'¥ '})
大神们有没有更好的方式。原生的有,但是uniapp不知道怎样调用,获取各国的货币符号
let codes=[];
codes.push({country:'USD',code:'$ '})
codes.push({country:'AED',code:'AED'})
codes.push({country:'EGP',code:'EGP'})
codes.push({country:'EUR',code:'€ '})
codes.push({country:'AUD',code:'$ '})
codes.push({country:'PKR',code:'Rs '})
codes.push({country:'BRL',code:'R$ '})
codes.push({country:'BGN',code:'лв '})
codes.push({country:'PLN',code:'zł '})
codes.push({country:'DKK',code:'kr '})
codes.push({country:'RUB',code:'₽ '})
codes.push({country:'PHP',code:'₱ '})
codes.push({country:'COP',code:'$ '})
codes.push({country:'KZT',code:'₸ '})
codes.push({country:'KRW',code:'₩ '})
codes.push({country:'CAD',code:'$ '})
codes.push({country:'CZK',code:'Kč '})
codes.push({country:'QAR',code:'QAR'})
codes.push({country:'HRK',code:'kn '})
codes.push({country:'RON',code:'lei'})
codes.push({country:'MYR',code:'RM '})
codes.push({country:'PEN',code:'S/''})
codes.push({country:'MXN',code:'$ '})
codes.push({country:'ZAR',code:'R '})
codes.push({country:'NGN',code:'₦ '})
codes.push({country:'NOK',code:'kr '})
codes.push({country:'JPY',code:'¥ '})
codes.push({country:'SEK',code:'kr '})
codes.push({country:'CHF',code:'CHF'})
codes.push({country:'SAR',code:'SR '})
codes.push({country:'TWD',code:'NT '})
codes.push({country:'THB',code:'฿ '})
codes.push({country:'TZS',code:'TZS'})
codes.push({country:'TRY',code:'₺ '})
codes.push({country:'HKD',code:'HK$'})
codes.push({country:'SGD',code:'S$ '})
codes.push({country:'NZD',code:'$ '})
codes.push({country:'HUF',code:'Ft '})
codes.push({country:'ILS',code:'₪ })'
codes.push({country:'INR',code:'₹ '})
codes.push({country:'IDR',code:'Rp '})
codes.push({country:'GBP',code:'£ '})
codes.push({country:'VND',code:' đ '})
codes.push({country:'CLP',code:' $ '})
codes.push({country:'CNY',code:'¥ '})
大神们有没有更好的方式。原生的有,但是uniapp不知道怎样调用,获取各国的货币符号
收起阅读 »
uniapp使用微信小程序分包异步化能力临时方案
背景
参考问题:uniapp开发微信小程序如何使用分包异步化特性,目前(2022/02/07)uniapp中的pages.json配置不支持分包异步化的特性(按照微信官方文档配置,构建后并不会在app.json文件生成对应的配置,猜测是因为分包异步化中的pages为空,构建代码过滤了,有空查看构建源码确认一下)。
这里应该由uniapp官方支持一下这个功能,目前项目需要,先用下面的临时方案
解决方案
思路:在uniapp构建完成后,添加自己的构建脚本,做以下的事情
- 读取pages.json
- 判断pages.json中是否有配置分包异步化
- 把分包异步化相关配置写入app.json
- 寻找用到分包组件的地方(小程序的页面 or 组件json配置文件),注入组件占位(不注入的话小程序会报错导致分包内组件无法使用)
构建脚本源码
/* eslint-disable @typescript-eslint/no-require-imports */
const fs = require('fs');
const path = require('path');
console.log('开始处理异步化分包...');
// 读取pages.json
const pagesConfig = (() => {
const configPath = path.resolve(__dirname, '../../../pages.json'); // @NOTE 这里要根据脚本执行的路径改一下
const pages = fs.readFileSync(configPath, 'utf8');
// @NOTE 移除注释
let pagesJson = pages.replace(/\/\*.*\*\//g, '');
pagesJson = pagesJson.replace(/\/\/.*/g, '');
return JSON.parse(pagesJson);
})();
// 读取page.json中的异步分包(没有配置pages)
const asyncPackages = (pagesConfig.subPackages || []).filter(package => !package.pages || package.pages.length === 0);
// console.log(pagesConfig, asyncPackages);
// 写入app.json
const distPath = path.resolve(__dirname, '../../../../dist/build/mp-weixin'); // @NOTE 这里要根据脚本执行的路径改一下
const appJsonPath = path.resolve(distPath, 'app.json');
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf8'));
if (!appJson.subPackages) {
appJson.subPackages = [];
}
asyncPackages.forEach((package) => {
const hasInject = appJson.subPackages.find(pack => pack.root === package.root);
if (hasInject) {
return;
}
appJson.subPackages.push({
root: package.root,
pages: [],
});
});
fs.writeFileSync(appJsonPath, JSON.stringify(appJson));
// 寻找用到分包组件的地方,注入组件占位(不注入的话小程序会报错导致分包内组件无法使用)
const ignorePaths = [];
ignorePaths.push(appJsonPath); // 过滤app.json
asyncPackages.forEach((package) => {
ignorePaths.push(path.join(distPath, package.root)); // 过滤分包的内容
});
const injectPlaceholder = (filepath) => {
// 判断是否用到了分包的组件
const jsonConfig = require(filepath);
if (!jsonConfig.usingComponents) {
return;
}
const subPackageComponents = [];
// @TODO 可以考虑使用map来加快查找速度
Object.keys(jsonConfig.usingComponents).forEach((componentName) => {
const componentPath = jsonConfig.usingComponents[componentName];
const targetSubPackage = asyncPackages.find(package => componentPath.startsWith(`/${package.root}`));
if (targetSubPackage) {
// 防止重复添加
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (jsonConfig.componentPlaceholder && jsonConfig.componentPlaceholder[componentName]) {
return;
}
subPackageComponents.push(componentName);
}
});
if (subPackageComponents.length === 0) {
return;
}
console.log('开始处理: ', filepath);
if (!jsonConfig.componentPlaceholder) {
jsonConfig.componentPlaceholder = {};
}
subPackageComponents.forEach((name) => {
jsonConfig.componentPlaceholder[name] = 'view'; // 占位符全用view组件
});
fs.writeFileSync(filepath, JSON.stringify(jsonConfig));
console.log('处理完成: ', filepath);
};
findJSON(distPath, ignorePaths, injectPlaceholder);
console.log('异步化分包处理完成');
function findJSON(folder, ignorePaths, cb) {
fs.readdirSync(folder).forEach((filename) => {
const filepath = path.join(folder, filename);
const isIgnore = ignorePaths.some(ignorePath => filepath.startsWith(ignorePath));
if (isIgnore) {
return;
}
const stat = fs.statSync(filepath);
if (filename.endsWith('.json')) {
cb(filepath);
return;
}
if (stat.isDirectory()) {
findJSON(filepath, ignorePaths, cb);
}
});
}
最后
目前用这个方式解决了分包异步化中使用分包内的组件问题,至于分包内的js使用,大家可以验证一下,我暂时没有这个场景,所以没有验证。
上面的代码编写没有review,但测试过单个分包配置的场景,项目使用的时候请谨慎。
最后还是希望官方大佬支持一下分包异步化这个特性。
背景
参考问题:uniapp开发微信小程序如何使用分包异步化特性,目前(2022/02/07)uniapp中的pages.json配置不支持分包异步化的特性(按照微信官方文档配置,构建后并不会在app.json文件生成对应的配置,猜测是因为分包异步化中的pages为空,构建代码过滤了,有空查看构建源码确认一下)。
这里应该由uniapp官方支持一下这个功能,目前项目需要,先用下面的临时方案
解决方案
思路:在uniapp构建完成后,添加自己的构建脚本,做以下的事情
- 读取pages.json
- 判断pages.json中是否有配置分包异步化
- 把分包异步化相关配置写入app.json
- 寻找用到分包组件的地方(小程序的页面 or 组件json配置文件),注入组件占位(不注入的话小程序会报错导致分包内组件无法使用)
构建脚本源码
/* eslint-disable @typescript-eslint/no-require-imports */
const fs = require('fs');
const path = require('path');
console.log('开始处理异步化分包...');
// 读取pages.json
const pagesConfig = (() => {
const configPath = path.resolve(__dirname, '../../../pages.json'); // @NOTE 这里要根据脚本执行的路径改一下
const pages = fs.readFileSync(configPath, 'utf8');
// @NOTE 移除注释
let pagesJson = pages.replace(/\/\*.*\*\//g, '');
pagesJson = pagesJson.replace(/\/\/.*/g, '');
return JSON.parse(pagesJson);
})();
// 读取page.json中的异步分包(没有配置pages)
const asyncPackages = (pagesConfig.subPackages || []).filter(package => !package.pages || package.pages.length === 0);
// console.log(pagesConfig, asyncPackages);
// 写入app.json
const distPath = path.resolve(__dirname, '../../../../dist/build/mp-weixin'); // @NOTE 这里要根据脚本执行的路径改一下
const appJsonPath = path.resolve(distPath, 'app.json');
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf8'));
if (!appJson.subPackages) {
appJson.subPackages = [];
}
asyncPackages.forEach((package) => {
const hasInject = appJson.subPackages.find(pack => pack.root === package.root);
if (hasInject) {
return;
}
appJson.subPackages.push({
root: package.root,
pages: [],
});
});
fs.writeFileSync(appJsonPath, JSON.stringify(appJson));
// 寻找用到分包组件的地方,注入组件占位(不注入的话小程序会报错导致分包内组件无法使用)
const ignorePaths = [];
ignorePaths.push(appJsonPath); // 过滤app.json
asyncPackages.forEach((package) => {
ignorePaths.push(path.join(distPath, package.root)); // 过滤分包的内容
});
const injectPlaceholder = (filepath) => {
// 判断是否用到了分包的组件
const jsonConfig = require(filepath);
if (!jsonConfig.usingComponents) {
return;
}
const subPackageComponents = [];
// @TODO 可以考虑使用map来加快查找速度
Object.keys(jsonConfig.usingComponents).forEach((componentName) => {
const componentPath = jsonConfig.usingComponents[componentName];
const targetSubPackage = asyncPackages.find(package => componentPath.startsWith(`/${package.root}`));
if (targetSubPackage) {
// 防止重复添加
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (jsonConfig.componentPlaceholder && jsonConfig.componentPlaceholder[componentName]) {
return;
}
subPackageComponents.push(componentName);
}
});
if (subPackageComponents.length === 0) {
return;
}
console.log('开始处理: ', filepath);
if (!jsonConfig.componentPlaceholder) {
jsonConfig.componentPlaceholder = {};
}
subPackageComponents.forEach((name) => {
jsonConfig.componentPlaceholder[name] = 'view'; // 占位符全用view组件
});
fs.writeFileSync(filepath, JSON.stringify(jsonConfig));
console.log('处理完成: ', filepath);
};
findJSON(distPath, ignorePaths, injectPlaceholder);
console.log('异步化分包处理完成');
function findJSON(folder, ignorePaths, cb) {
fs.readdirSync(folder).forEach((filename) => {
const filepath = path.join(folder, filename);
const isIgnore = ignorePaths.some(ignorePath => filepath.startsWith(ignorePath));
if (isIgnore) {
return;
}
const stat = fs.statSync(filepath);
if (filename.endsWith('.json')) {
cb(filepath);
return;
}
if (stat.isDirectory()) {
findJSON(filepath, ignorePaths, cb);
}
});
}
最后
目前用这个方式解决了分包异步化中使用分包内的组件问题,至于分包内的js使用,大家可以验证一下,我暂时没有这个场景,所以没有验证。
上面的代码编写没有review,但测试过单个分包配置的场景,项目使用的时候请谨慎。
最后还是希望官方大佬支持一下分包异步化这个特性。
收起阅读 »