HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

用unicloud做了一个ditto云同步工具

源码分享 uniCloud

ditto是一个电脑端剪切板增强工具,非常好用,码农必备神器
可惜不支持云同步,那就自己做一个

功能

  • 上传到云服务器
  • 从云服务器下载
  • 多台电脑同步
  • 仅支持私有部署(隐私起见)
  • 存储所有历史剪切板

解决的场景

  • 本地存储有限,历史剪切板丢失
  • 家庭、办公多台电脑,剪切板不同步

主要参数

  • 因为免费,云存储使用unicloud的免费数据库(私有部署)
  • 从云端下载仅限最新500条(云端太多,没必要)
  • 上传服务器不限条数,全部存储
  • 没有窗口,后台运行,进程可见
  • 配置文件: config.json
  • 运行记录及提示都在日志文件 ditto-sync-2022-01-27.log

私有云端部署及代码 详见

https://www.yuque.com/dittosync/help/01

继续阅读 »

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

【感谢】感谢社区回复的大大

问题反馈 确认bug DCloud

之前反馈了一个bug,一直以为社区没人回复,后来发现第二天就回复我了,而且当时问题就确认修复了,感谢各位的辛苦付出

之前反馈了一个bug,一直以为社区没人回复,后来发现第二天就回复我了,而且当时问题就确认修复了,感谢各位的辛苦付出

uniapp+uview后台管理系统|uni-app手机端后台uniUadmin

uniapp模板 uniapp

前段时间有给大家分享一个uni-app仿抖音小视频,今天分享一个uniapp移动端后台管理uni-uadmin实例项目。

uni-uadmin 一款基于uni-app+uviewUI+uniUI+mockjs+echarts等技术开发架构的手机移动端后台管理系统模板。包含了图表、自定义表格、表单、瀑布流及图文编辑器等业务模块,动态权限管理,错误页处理,可编译至H5+小程序+APP端。

uni-app+uview短视频+直播聊天实例

img

技术栈

  • 编辑器:HbuilderX3.3.5
  • 使用技术:vue+uniapp+uViewUI+mockjs
  • 弹窗组件:ua-popup(基于uni-app跨端弹框组件)
  • 表格组件:ua-table(基于uni-app封装的多功能表格)
  • 自定义组件:uaDock全新的dock风格tabbar组件
  • uniapp图表组件:u-charts图表库

img

img

img

项目构建目录

img

img

img

img

img

img

共用模板

整个页面结构布局分为顶部自定义导航+内容区域+底部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 ref="uapopup" />](/img/bVcXG8X)  

        <!-- //换肤弹框模板 -->  
        <ua-popup v-model="isVisibleSkin" position="right">  
            <Skin />  
        </ua-popup>  
    </view>  
</template>

img

img

img

uniapp-uadmin 还支持动态权限控制。

img

img

uniapp自定义菜单tab组件ua-dock

img

<!-- //底部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

img

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端。

uni-app+uview短视频+直播聊天实例

img

技术栈

  • 编辑器:HbuilderX3.3.5
  • 使用技术:vue+uniapp+uViewUI+mockjs
  • 弹窗组件:ua-popup(基于uni-app跨端弹框组件)
  • 表格组件:ua-table(基于uni-app封装的多功能表格)
  • 自定义组件:uaDock全新的dock风格tabbar组件
  • uniapp图表组件:u-charts图表库

img

img

img

项目构建目录

img

img

img

img

img

img

共用模板

整个页面结构布局分为顶部自定义导航+内容区域+底部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 ref="uapopup" />](/img/bVcXG8X)  

        <!-- //换肤弹框模板 -->  
        <ua-popup v-model="isVisibleSkin" position="right">  
            <Skin />  
        </ua-popup>  
    </view>  
</template>

img

img

img

uniapp-uadmin 还支持动态权限控制。

img

img

uniapp自定义菜单tab组件ua-dock

img

<!-- //底部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

img

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性能再次提升

vue3

uni-appvue3 & 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)
  • 支持jsxtsx(h5,app 平台支持,小程序不支持)

另外,在小程序平台,新版uni-app也扩展了更多的语法,如:

  • 更完善的模板语法支持(如 classstyle 支持函数、变量等,不再局限数组、对象类型)
  • 更完整的 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.6vue 3.x的编译时间
  • 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译时间,和uni-appvue 3.x版本进行对比

uni-app 历史版本纵向对比

我们选择uni-app默认模板uni-starterhello-uniapp三个项目模板,分别测试vue 2.6vue 3.x的编译时间。

uni-app项目编译时间的采集方式:

  • vue 2.6版本编译时间 = webpack 的 stats.endTime - stats.startTime
  • vue 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平台,多次编译测试,并求其均值后,获得如下数据:

vue3-08

从上图对比数据来看,我们可以得出结论:App平台,vue 3.x版本下的运行编译,相比vue 2.6版本,编译性能提升将近50%。

虽没有H5平台的十倍效率提升那么刺激,但将近50%的速度提升,经常开发小程序/App的小伙伴,还不心动?

业内优秀框架横向对比

除了采用不同版本的uni-app进行纵向对比外,我们还使用业内优秀的跨端框架Taro,创建空的项目模板,进行横向对比测试。

具体测试方案:

  1. 安装Taro的最新cli,本文测试时使用的版本为"@Tarojs/Taro": "3.3.16"
  2. 使用Taro init命令,分别选择reactvuevue3框架,创建三个默认项目模板,三个项目名称分别为taro3-reacttaro3-vuetaro3-vue3,如下图:
  3. 使用npm run dev:h5,运行到H5平台进行预览,记录每次预览编译时间,重复执行,求其均值

关于Taro编译时间的计算方案:

  1. 开发一个Taro扩展插件,插件规范参考Taro官网 - 插件功能
  2. ctx.onBuildStart中记录开始编译时间
  3. ctx.onBuildFinish中记录编译结束时间
  4. 两者的时间差,即为编译过程消耗时间

然后使用uni-appcli命令行,创建基于vue3.x的空项目模板,项目命名为uni-app-vue3

我们使用各自框架的命令行,将如上创建的5个项目分别编译到H5平台和小程序平台,多次测试,并求其均值。

同框架版本在H5平台上的编译时间,结果如下:

从图中可以看出,uni-appvue3版本,在H5平台上的首页编译预览性能是遥遥领先的。这个遥遥有多远呢?这么讲吧,你都编译20次了,友商第一次还没完呢。

继续编译到小程序平台,多次测试,求其均值,结果如下:

从图中可以看出,uni-appvue3版本,在小程序平台上的编译性能也是遥遥领先的,这个遥遥也不近。

更好的运行速度

开发环节编译快了,那面向最终用户的软件,运行性能怎么样?

我们进入性能测试章节。

测试方案:

  • 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
  • 界面如下:
  • 测试机型:小米 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.xuni-app项目,点赞组件的响应时间跟页面条数无关,一直保持极高的响应灵敏度,性能体验远高于vue 2.6版本。

从这个常见的长列表组件响应实验来看,vue 3.x的性能体验要远高于vue 2.6版本。

更小的代码体积

项目发行后的代码体积,是一个很重要的考量指标:

  • H5平台:更小的代码体积,可以帮助开发者节省服务端带宽及CDN流量,可实现更快的资源加载及页面渲染;
  • 小程序平台:更小的代码体积,可加速小程序包的下载(甚至可能免了分包加载的繁琐),帮助用户更快进入小程序业务界面;
  • App平台:更小的代码体积,可实现更快的App启动,帮助用户更快进入App首页

为了测试vue 3.x新版升级后,代码体积的变化,我们同样做了两个维度的测试:

  • 纵向对比:选择uni-app常用项目模板,在H5、小程序、App平台,分别测试vue 2.6vue 3.x的编译包大小
  • 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译后的包体积大小,和uni-app版本进行对比

Tips:

  • 开发阶段重在编译速度,对应npm run dev操作
  • 发行阶段重在编译包大小,对应npm run build操作

uni-app 不同版本纵向对比

我们复用之前创建的uni-app默认模板uni-starterhello-uniapp三个项目模板,分别测试vue 2.6vue 3.x的编译包体积。

uni-app项目编译包体积的采集方式:编译到对应平台后,记录编译后文件夹的大小。

H5平台

H5平台编译后代码体积记录如下:

从统计结果来看,uni-appvue3.x版本,在H5平台上的编译包体积至少瘦身30%以上。

H5平台的瘦身优化,主要得益于uni-app框架的底层全面重构,实现了更彻底的摇树优化。

小程序平台

小程序平台编译后代码体积记录如下:

从统计结果来看,uni-appvue3.x版本,在小程序平台上也有大幅瘦身。

App平台

App平台编译后代码体积记录如下:

从统计结果来看,uni-appvue3.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-appvue3 & 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)
  • 支持jsxtsx(h5,app 平台支持,小程序不支持)

另外,在小程序平台,新版uni-app也扩展了更多的语法,如:

  • 更完善的模板语法支持(如 classstyle 支持函数、变量等,不再局限数组、对象类型)
  • 更完整的 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.6vue 3.x的编译时间
  • 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译时间,和uni-appvue 3.x版本进行对比

uni-app 历史版本纵向对比

我们选择uni-app默认模板uni-starterhello-uniapp三个项目模板,分别测试vue 2.6vue 3.x的编译时间。

uni-app项目编译时间的采集方式:

  • vue 2.6版本编译时间 = webpack 的 stats.endTime - stats.startTime
  • vue 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平台,多次编译测试,并求其均值后,获得如下数据:

vue3-08

从上图对比数据来看,我们可以得出结论:App平台,vue 3.x版本下的运行编译,相比vue 2.6版本,编译性能提升将近50%。

虽没有H5平台的十倍效率提升那么刺激,但将近50%的速度提升,经常开发小程序/App的小伙伴,还不心动?

业内优秀框架横向对比

除了采用不同版本的uni-app进行纵向对比外,我们还使用业内优秀的跨端框架Taro,创建空的项目模板,进行横向对比测试。

具体测试方案:

  1. 安装Taro的最新cli,本文测试时使用的版本为"@Tarojs/Taro": "3.3.16"
  2. 使用Taro init命令,分别选择reactvuevue3框架,创建三个默认项目模板,三个项目名称分别为taro3-reacttaro3-vuetaro3-vue3,如下图:
  3. 使用npm run dev:h5,运行到H5平台进行预览,记录每次预览编译时间,重复执行,求其均值

关于Taro编译时间的计算方案:

  1. 开发一个Taro扩展插件,插件规范参考Taro官网 - 插件功能
  2. ctx.onBuildStart中记录开始编译时间
  3. ctx.onBuildFinish中记录编译结束时间
  4. 两者的时间差,即为编译过程消耗时间

然后使用uni-appcli命令行,创建基于vue3.x的空项目模板,项目命名为uni-app-vue3

我们使用各自框架的命令行,将如上创建的5个项目分别编译到H5平台和小程序平台,多次测试,并求其均值。

同框架版本在H5平台上的编译时间,结果如下:

从图中可以看出,uni-appvue3版本,在H5平台上的首页编译预览性能是遥遥领先的。这个遥遥有多远呢?这么讲吧,你都编译20次了,友商第一次还没完呢。

继续编译到小程序平台,多次测试,求其均值,结果如下:

从图中可以看出,uni-appvue3版本,在小程序平台上的编译性能也是遥遥领先的,这个遥遥也不近。

更好的运行速度

开发环节编译快了,那面向最终用户的软件,运行性能怎么样?

我们进入性能测试章节。

测试方案:

  • 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
  • 界面如下:
  • 测试机型:小米 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.xuni-app项目,点赞组件的响应时间跟页面条数无关,一直保持极高的响应灵敏度,性能体验远高于vue 2.6版本。

从这个常见的长列表组件响应实验来看,vue 3.x的性能体验要远高于vue 2.6版本。

更小的代码体积

项目发行后的代码体积,是一个很重要的考量指标:

  • H5平台:更小的代码体积,可以帮助开发者节省服务端带宽及CDN流量,可实现更快的资源加载及页面渲染;
  • 小程序平台:更小的代码体积,可加速小程序包的下载(甚至可能免了分包加载的繁琐),帮助用户更快进入小程序业务界面;
  • App平台:更小的代码体积,可实现更快的App启动,帮助用户更快进入App首页

为了测试vue 3.x新版升级后,代码体积的变化,我们同样做了两个维度的测试:

  • 纵向对比:选择uni-app常用项目模板,在H5、小程序、App平台,分别测试vue 2.6vue 3.x的编译包大小
  • 横向对比:使用业内优秀的其它跨端框架,创建默认项目模板,记录其编译后的包体积大小,和uni-app版本进行对比

Tips:

  • 开发阶段重在编译速度,对应npm run dev操作
  • 发行阶段重在编译包大小,对应npm run build操作

uni-app 不同版本纵向对比

我们复用之前创建的uni-app默认模板uni-starterhello-uniapp三个项目模板,分别测试vue 2.6vue 3.x的编译包体积。

uni-app项目编译包体积的采集方式:编译到对应平台后,记录编译后文件夹的大小。

H5平台

H5平台编译后代码体积记录如下:

从统计结果来看,uni-appvue3.x版本,在H5平台上的编译包体积至少瘦身30%以上。

H5平台的瘦身优化,主要得益于uni-app框架的底层全面重构,实现了更彻底的摇树优化。

小程序平台

小程序平台编译后代码体积记录如下:

从统计结果来看,uni-appvue3.x版本,在小程序平台上也有大幅瘦身。

App平台

App平台编译后代码体积记录如下:

从统计结果来看,uni-appvue3.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开发方式总结和对比

混合开发 uniapp React Native weex flutter

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;

注:

  1. 微信小程序类似于RN/Weex开发方式,也分为逻辑层和视图层;
  2. 微信小程序的页面属于混合渲染,什么是混合渲染?看后文总结;

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开发最大的好处就是省成本和不错的生态:

  1. 成本,包括学习成本、开发成本,时间成本,招人成本等;
  2. 生态,包括开发者数量,社区活跃度,文档是否齐全等;

总结

目前主流的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;

注:

  1. 微信小程序类似于RN/Weex开发方式,也分为逻辑层和视图层;
  2. 微信小程序的页面属于混合渲染,什么是混合渲染?看后文总结;

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开发最大的好处就是省成本和不错的生态:

  1. 成本,包括学习成本、开发成本,时间成本,招人成本等;
  2. 生态,包括开发者数量,社区活跃度,文档是否齐全等;

总结

目前主流的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毫秒?

rtsp Vue

近期研究在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

背景

参考问题:uniapp开发微信小程序如何使用分包异步化特性,目前(2022/02/07)uniapp中的pages.json配置不支持分包异步化的特性(按照微信官方文档配置,构建后并不会在app.json文件生成对应的配置,猜测是因为分包异步化中的pages为空,构建代码过滤了,有空查看构建源码确认一下)。

这里应该由uniapp官方支持一下这个功能,目前项目需要,先用下面的临时方案

解决方案

思路:在uniapp构建完成后,添加自己的构建脚本,做以下的事情

  1. 读取pages.json
  2. 判断pages.json中是否有配置分包异步化
  3. 把分包异步化相关配置写入app.json
  4. 寻找用到分包组件的地方(小程序的页面 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构建完成后,添加自己的构建脚本,做以下的事情

  1. 读取pages.json
  2. 判断pages.json中是否有配置分包异步化
  3. 把分包异步化相关配置写入app.json
  4. 寻找用到分包组件的地方(小程序的页面 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,但测试过单个分包配置的场景,项目使用的时候请谨慎。

最后还是希望官方大佬支持一下分包异步化这个特性。

收起阅读 »