Write the Code. Change the World.

11月 30

数组

sort() 方法对数组的项目进行排序。
排序顺序可以是按字母或数字,也可以是升序(向上)或降序(向下)。
默认情况下,sort() 方法将按字母和升序将值作为字符串进行排序。

如:

let fruits = ["Banana", "Orange", "Apple", "Mango"];

fruits.sort();

# 输出 ['Apple', 'Banana', 'Mango', 'Orange']

fruits.reverse();
# 输出 ['Orange', 'Mango', 'Banana', 'Apple']

不过基于数字的排序,是按字符串来排序的。比如 33 排序比 123 要后。 如果非要基于数字大小来排。可以通过传入函数的方式来解决。

比如:

let arr = [1, 123, 22,5, 32];
arr.sort((a,b)=>(a-b));
# 输出 1, 5, 22, 32, 123
# 这样就可以基于数字来排序了

如果是基于数组里的 object ,也可以通过这种方式来排序。

Object 呢

对 object 按键值排序,一般在做加密验证的时候会用到。

有了数组的排序,object 也一样可以。

let param = {
    name: 'vini123',
    gender: 1,
    time: 1669796346
}

let sorted = {};
Object.keys(param).sort().forEach(key => {
    sorted[key] = param[key];
});
11月 21

uniapp 打包 app 时,默认安全区域的颜色是白色。如果页面和安全区域接触的区域不是白色,就会出现色差,这个时候体验就真心不好啊。不过,可以设置去搞掉。
我也是在做聊天功能时才碰触到颜色的问题。

https://uniapp.dcloud.net.cn/collocation/manifest-app.html#full-manifest

在文档中,找到 safearea 配置,复制过去在 manifest.json 找到 app-plus 添加进去去配置。

        "safearea": {                            
            "bottom": {                                            
                "offset": "none"
            }
        },

可以这样设置。

参考

https://blog.csdn.net/CSDN877425287/article/details/107484287

11月 19

时间,日期相关的处理, carbon 的确是个好东西。感觉离不开呀。

Carbon 是对 PHP DateTime 模块的二次扩展;提供时间格式化,时间计算的功能;

官方主页:http://carbon.nesbot.com/;

github: https://github.com/briannesbitt/Carbon

参考

https://www.jianshu.com/p/7e45bc4d0006

https://www.php.cn/phpkj/laravel/484191.html

https://learnku.com/articles/62292

11月 19

在微信环境,通过分享方式(非用户搜索,非小程序盒子), 能打开目标小程序,进入目标页面的方式。除了扫小程序码还有小程序链接方式。生成小程序性链接的能力有各种控制,链接的作用和生命也各有控制。做一个体验好,效果好的分享,得细心和用心。还得处处找一切能突破微信自己的阻碍。

主要是小程序分享到朋友圈从来就没一个好的体验。可以通过海报的方式,但用户得长按海报。不是所有人都知道要长按,也不是所有人都愿意长按。其他途径,比如小程序链接。虽然用户可以点击进入,但每次都弹个窗让用户确认。从体验感来说是不好,从用户意愿度(广告)来讲,算是一次告知。可是,既然用户愿意点了,意愿度还是有的,何必弹个窗来恶心人呢。每个人都有每个人的考虑,人在屋檐下,不得不低头。

https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-scheme/queryScheme.html

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/url-scheme.html

开始

看了下边的各种说明,只想说在微信内,打开小程序的途径 scheme 码和 urllink 都不好。这种码 A 打开了,其他人就打不开了。怎么发朋友圈宣传。短链接看起来是最好的,可短链接不是谁都有权限生成的,只有特殊服务类目才可以。就像获取用户当前位置一样,需要特殊服务目录(这个还得去申请接口权限)才有资格用。说来说去,微信内朋友圈宣传只能短链接或海报了。只能去争取申请能获取该接口的服务了。

微信外打开小程序 scheme 码 ios 是直接可以,android 需要借助 h5。 urllink 必须都得先跳转到 h5。只是跳转到 h5,然后再跳转到小程序。不必在 h5 那边再点击再跳转。就是 scheme 码和 urllink 只能一个人打开就有点恶心。

其实还有一种方式,是可以的。就是使用微信开放标签。需要借助服务号(个人可以借助云开发的静态网站),需要额外调用 js。请看下边的链接。

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html#%E5%BC%80%E6%94%BE%E6%A0%87%E7%AD%BE%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/introduction.html

scheme 码

该接口用于获取小程序 scheme 码,适用于短信、邮件、外部网页、微信内等拉起小程序的业务场景。通过该接口,可以选择生成到期失效和永久有效的小程序码,有数量限制,目前仅针对国内非个人主体的小程序开放

# 重点说明
自 2022 年 4 月 11 日起,URL Scheme有效期最长 30 天,不再支持永久有效的URL Scheme、不再区分短期有效URL Scheme与长期有效URL Scheme。若在微信外打开,用户可以在浏览器页面点击进入小程序。每个独立的URL Scheme被用户访问后,仅此用户可以再次访问并打开对应小程序,其他用户无法再次通过相同URL Scheme打开该小程序。 在本次规则调整生效前已经生成的URL Scheme,如果有效期超过30天或长期会被降级为30天有效,只能被1个用户访问,开始时间从调整日期开始计算。

每天生成 URL Scheme 和 URL Link 总数量上限为50万

iOS系统支持识别 URL Scheme,可在短信等应用场景中直接通过 Scheme 跳转小程序。
Android系统不支持直接识别 URL Scheme,用户无法通过 Scheme 正常打开小程序,开发者需要使用 H5 页面中转,再跳转到 Scheme 实现打开小程序

urllink

获取小程序 URL Link,适用于短信、邮件、网页、微信内等拉起小程序的业务场景。通过该接口,可以选择生成到期失效和永久有效的小程序链接,有数量限制,目前仅针对国内非个人主体的小程序开放

# 重点说明
自 2022 年 4 月 11 日起,URL Link有效期最长 30 天,不再支持永久有效的URL Link、不再区分短期有效URL Link与长期有效URL Link。若在微信外打开,用户可以在浏览器页面点击进入小程序。每个独立的URL Link被用户访问后,仅此用户可以再次访问并打开对应小程序,其他用户无法再次通过相同URL Link打开该小程序。 在本次规则调整生效前已经生成的URL Link,如果有效期超过30天或长期会被降级为30天有效,只能被1个用户访问,开始时间从调整日期开始计算。 

每天生成 URL Scheme 和 URL Link 总数量上限为50万

只能生成已发布的小程序的 URL Link。
在微信内或者安卓手机打开 URL Link 时,默认会先跳转官方 H5 中间页,如果需要定制 H5 内容,可以使用云开发静态网站。

short link

获取小程序 Short Link,适用于微信内拉起小程序的业务场景。目前只开放给电商类目(具体包含以下一级类目:电商平台、商家自营、跨境电商)。通过该接口,可以选择生成到期失效和永久有效的小程序短链

通过服务端接口可以获取打开小程序任意页面的 Short Link。适用于微信内拉起小程序的业务场景。

目前只开放给电商类目小程序,具体包含以下一级类目:电商平台、商家自营、跨境电商。

最终

说了好多啰嗦的话。微信内推广,宣传,短链接肯定是最好用的。可得去申请服务类目。官方说只对电商开放。不过,看到其他非电商类目的也可以(比如招聘)。

11月 11

只要 websocket 服务。最简单化的 websocket 服务。不需要其他基于 swoole 的其他框架应用。仅仅 swoole 扩展 + php + nginx 完成 websocket 的服务。就是想最简单简洁的实现。

就像官方的实例一样 https://wiki.swoole.com/#/start/start_ws_server,就这么些代码。

$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502);

//监听WebSocket连接打开事件
$ws->on('Open', function ($ws, $request) {
    $ws->push($request->fd, "hello, welcome\n");
});

//监听WebSocket消息事件
$ws->on('Message', function ($ws, $frame) {
    echo "Message: {$frame->data}\n";
    $ws->push($frame->fd, "server: {$frame->data}");
});

//监听WebSocket连接关闭事件
$ws->on('Close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});

$ws->start();

我们不喜欢用 ip 来连接,我们用域名。我们不喜欢用 http,我们用 https。我们不喜欢加端口号,我们用代理。这里我们用 nginx 来搞定。比如吧,我们自己的域名是: xx.com, nginx 就可以像下边这样配置:

map $http_upgrade $connection_upgrade {  
    default upgrade;  
    '' close;  
}

server 
{    
    listen 443 ssl http2; 
    server_name xx.com;

    ssl_certificate         /usr/local/server/nginx/conf/ssl/xx.com.pem;
    ssl_certificate_key     /usr/local/server/nginx/conf/ssl/xx.com.key;

    charset utf-8;
    index index.php index.html index.htm;
    root /www/xx/xx.com/public;

    location /wss {
        # 代理到上边的自定义的地址
        proxy_pass http://127.0.0.1:9502;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location / {
        try_files $uri $uri/  /index.php?$query_string; 
    }

    location ~ \.php($|/) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param HTTPS $https if_not_empty;
        fastcgi_split_path_info ^(.+\.php)(.*)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

服务端的配置构建的重要部分就算好了。需要注意的是,创建 websocket 的时候,一定不要带上 ssl 相关的参数和证书。这样你 nginx 这边请求代理过去是 http 请求,就会出错。

websocket 这里没有心跳功能,可以手动模拟创建一个,去实现类似的业务。

还有,在服务端可以使用 swoole 自己的存储结构。 https://wiki.swoole.com/#/memory/table

旧的文章

https://blog.vini123.com/405

https://blog.vini123.com/482

https://blog.vini123.com/294

11月 10

要实现 app 内版本更新得走以下几个流程:

  1. 对系统的获取(android 或 ios)。

  2. 对系统当前 app 版本的获取(1.0.1 + '.' + 100)(版本名称 + 版本号控制)。

  3. 对服务端接口请求 app 版本信息(定义当前最新版本号)。比如 app 之前的版本是 1.0.1.100,返回给前端就是这个。后来你新提交了一个版本 1.0.2.100,并审核通过了。当审核通过时,你修改后端配置,将版本号改成 1.0.2.100,这个时候返回前端就是 1.0.2.100。

  4. 用当前 app 版本信息和服务端版本信息进行对比。如果不一样,则表示服务端有新的版本。应弹窗提示用户进行 app 更新。(版本可以定义两个,android 和 ios 各一个,每个系统同一时间可能会不一样)。

  5. 弹窗提示用户是否要更新。用户点击确定。如果是 ios 系统,则跳转到商店更新(亲自测试可行)。如果是 android 系统,则下载安装包更新(没亲测过),ios 是必须要走商店,且只有一个商店,比较好弄,也正规。android 打个包就可以装,上商店也很多家都可以。说它简单是只要个正常的包就可以装,说它不简单就是商店太多,审核太麻烦,还得要软著,太心累。一般不到万不得已,根本就不想上 android 商店。

上边这个流程是在客户端做判断,并去处理升级。感觉不太好,因为客户端一旦上架你短时间内很难修改和改变。如果把判断放在服务端,你可以随时随地的修改。以备异常情况。(也是文章写着写着,想法也就变了)

运行环境管理对象(获取版本信息等能力)

官方文章

App升级中心

虽然 uniapp 官方提供了套件,但还是个人用户实现的更自由些(套件的升级 ui 界面 好像不能任意自定义)。

代码操作

一定要使用条件编译,也就是 app 才需要这样。因为有些 api 只有 app 端才有的。

// #ifdef APP-PLUS
# 从服务端获取 app 版本信息
GetAppVersion().then(res => {
   // 获取当前已安装的 app 的版本信息
    let curVersion = plus.runtime.version + '.' + plus.runtime.versionCode;
    let osName = uni.getSystemInfoSync().platform;
    if (osName == 'ios') {
        if (res.ios_version && res.ios_version != curVersion) {
            // 弹提示是否要更新。如果更新,就跳转到苹果应用商店
            uni.showModal({
                title: '更新提示',
                content: '发现新版本,是否要更新?',
                cancelColor: '#383838',
                confirmColor: '#ffc80b',
                success(res) {
                    if (res.confirm) {
                        let appleId = 6443910575;
                        plus.runtime.launchApplication(
                            {
                                action: `itms-apps://itunes.apple.com/cn/app/id${appleId}?mt=8`
                            },
                            function(e) {
                                console.log(e.message);
                            }
                        );
                    }
                }
            });
        }
    } else if (osName == 'android') {
        if (res.android_version && res.android_version != curVersion && res.downUrl) {
                // 弹窗提示是否要更新。如果更新,跳转到特定商店更新或下载更新。(弹窗省略了,去商店也省了)
                var downTask = plus.downloader.createDownload(url, {}, function(d, status) {
                //d为下载的文件对象
                if (status == 200) {
                    //下载成功,d.filename是文件在保存在本地的相对路径,使用下面的API可转为平台绝对路径
                    var fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);
                    plus.runtime.openFile(d.filename); //选择软件打开文件
                    uni.showToast({
                        icon: 'none',
                        title: '更新成功'
                    })
                } else {
                    //下载失败
                    plus.downloader.clear(); //清除下载任务
                    uni.showToast({
                        icon: 'none',
                        title: '更新失败'
                    })
                }
            })
            //开始下载
            downTask.start();

            //监听下载进度
            downTask.addEventListener('statechanged', (task) => {
                let percentage = parseInt(
                    (parseFloat(task.downloadedSize) /
                        parseFloat(task.totalSize)) *
                    100
                );
                if (percentage == 100) {
                    // 更新完成
                }
            })
        }
    }
});
// #endif

上边仅供参考,实际情况需调试。后一个思路,由服务端判断是否要更新,并提供更新信息(下载地址,商店地址等)这种方式比较好。还有 android 这边如果自己做下载更新,还得做个好看的 ui 升级界面。有进度条显示那种。

11月 09

app 接受到推送消息后,如果用户点击消息,一般是到达目标页面。这个时候解析消息并进行处理。

unipush 消息推送

操作流程

  1. 当用户点击消息栏的消息时,通过 uni.onPushMessage 方法来接受消息。
  2. 获取接受消息里边的 payload 数据。payload 里边可以传递一些自定义的参数过来。
  3. 我们可以将想跳转的页面和参数定义在 page 里。并对 page 进行判断,如果 page 是 tab 页的,就做 tab 页跳转,非 tab 页就做非 tab 页跳转。
  4. 对消息数目角标清零 plus.runtime.setBadgeNumber(0);
            // #ifdef APP-PLUS
            let goPage = (value) => {
                let tabbars = ['pages/home/index', 'pages/pdd/index', 'pages/message/index', 'pages/mine/index'];
                let isTab = false;
                for(let tabbar of tabbars) {
                    if (value.indexOf(tabbar) >= 0) {
                        isTab = true;
                        break;
                    }
                }

                if (isTab) {
                    uni.switchTab({
                        url: value
                    });
                } else {
                    uni.navigateTo({
                        url: value
                    });
                }
            }

            // 通过推送消息进来的
            uni.onPushMessage((res) => {

                plus.runtime.setBadgeNumber(0);

                if (res && res.data && res.data.payload && res.data.payload.page) {
                    goPage(res.data.payload.page)
                }
            });
            // #endif

消息推送补充

消息推送那边,我们把 page 参数 放在 payload 里。

{
    "appId": "__UNI__XXXX",
    "push_clientid": "6eb119fabc0123f545086eexxxxxxxx",
    "title": "小区宝来电",
    "content": "人生若只如初见,何事秋风悲画扇。",
    "payload": { "page": "pages/home/topic/detail?id=6" }
}