- 从0到1开发一个小程序cli脚手架(二) --版本发布/管理篇
接上文 《从0到1开发一个小程序cli脚手架(一)–创建页面/组件模版篇》 github地址:https://github.com/jinxuanzheng01/xdk-cli 觉得有用的朋友帮忙给项目一个star,谢谢 上文大家应该大致学会了怎么搭建一个cli脚手架,包括实现了一个快速生成启动模版的功能,本质上作为脚手架应该可以做更多的事情,本篇文章会实现一些新的功能,例如:自动发布体验版,版本号控制,环境变量控制 痛点 不知道大家有没有一天发多次版本或者一天给多个小程序发版的经历,按照微信正常的发布流程,需要: 修改版本号/版本描述 修改发布环境 点击微信开发者工具上传体验版 提交审核 确认环境/版本 点击发布 其中所有的1,2步为手工修改config文件,第5步是确认手工修改config文件的正确性,毕竟人总会犯错,作者表示就干过线下环境发布到测试环境的事情,而且这是在做了第5步的情况下,很遗憾没有仔细核对 为了不再次发生同样的事情导致引咎辞职,那么有没有更好的方法呢 ?自然是有的,既然人不可靠,那么直接把它流程化自然就可以了 准备工作 最好阅读了上篇文章《从0到1开发一个小程序cli脚手架(一)–创建页面/组件模版篇》,并搭建了一个简单的demo 需要了解微信小程序提供的一些cli能力, 点击这里 Let‘ go 后续的很多流程上的实操代码为了缩短篇幅会以伪代码的形式来进行描述,强烈推荐先阅读上文,如果需要更详细的实操代码请去github仓库查看 实际效果图:[图片] 梳理流程 识别命令行 询问问题,拿到版本号和版本描述 调用微信提供的cli能力,进行体验版上传 是不是发现非常简单,事实也是如此,整个功能做下来也就60行代码 ~ 目录结构 项目结构分为入口文件,配置文件 [代码]- lib - publish-weapp.js - index.js - config.js - node_modules - package.json [代码] config.js用来记录一些基础常量和默认项的配置,例如项目路径,执行路径等 [代码]module.exports = { // 根目录 root: __dirname, // 执行命令目录路径 dir_root: process.cwd(), // 小程序项目路径 entry: './', // 项目编译输出文件夹 output: './', } [代码] 识别命令行 在入口文件(index.js)处利用第三方库 commander 识别命令行参数,同时作为路由进行任务分发, [代码]#!/usr/bin/env node const version = require('./package').version; // 版本号 /* = package import -------------------------------------------------------------- */ const program = require('commander'); // 命令行解析 /* = task events -------------------------------------------------------------- */ const publishWeApp = require('./lib/publish-weapp'); // 发布体验版 program .command('publish') .description('发布微信小程序体验版') .action((cmd, options) => publishWeApp(); /* = main entrance -------------------------------------------------------------- */ program.parse(process.argv) [代码] 创建交互命令 接下来是一个QA环节,这时候需要根据自己的实际需要安排自己需要的命令,可以回想一下要解决的问题: 修改版本号/版本描述 修改发布环境 根据cli自动上传体验版 大概得出这样一个队列: [代码]function getQuestion({version, versionDesc} = {}) { return [ // 确定是否发布正式版 { type: 'confirm', name: 'isRelease', message: '是否为正式发布版本?', default: true }, // 设置版本号 { type: 'list', name: 'version', message: `设置上传的版本号 (当前版本号: ${version}):`, default: 1, choices: getVersionChoices(version), filter(opts) { if (opts === 'no change') { return version; } return opts.split(': ')[1]; }, when(answer) { return !!answer.isRelease } }, // 设置上传描述 { type: 'input', name: 'versionDesc', message: `写一个简单的介绍来描述这个版本的改动过:`, default: versionDesc }, ] } [代码] 最后获得的json对象,是这个样子: [代码]{isRelease: true, version: '1.0.1', versionDesc: '这是一个体验版'} [代码] 确定是否发布正式版 主要是因为体验版并非完全是发行版,公司内部测试的时候也是需要发布测试用体验版的,但是又涉及只有正式版本修改本地版本文件,那么就只能多此一问作为区分了 设置版本号 / 设置版本信息 [图片] 设置上述如图的 体验版上的版本号和描述信息,这里有个case是只有第一个问题选择发布正式版才会让你设置版本号,测试版本使用默认版本号0.0.0,这也是区分体验版的一种方式 [代码] when(answer) { return !!answer.isRelease } [代码] 这里只设置了三个问题:是否发布正式版,设置版本号,设置上传描述,当然你也可以设置自己想做的其他事情 上传体验版 翻了下小程序的文档,大概犹如下几个关键点: 找到cli工具 小程序cli并非全局安装,需要自己去索引路径,命令行工具所在位置:macOS: [代码]<安装路径>/Contents/MacOS/cli[代码] Windows: [代码]<安装路径>/cli.bat[代码] mac的 安装路径 如果是默认安装的话,是/Applications/wechatwebdevtools.app/, 外加cli的位置是: /Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli windows 的话作者表示没有这个环境,只能大家自己探索了 拼凑上传命令 官方文档给了非常详细的描述: [图片] [代码]# 上传路径 /Users/username/demo 下的项目,指定版本号为 1.0.0,版本备注为 initial release cli -u 1.0.0@/Users/username/demo --upload-desc 'initial release' # 上传并将代码包大小等信息存入 /Users/username/info.json cli -u 1.0.0@/Users/username/demo --upload-desc 'initial release' --upload-info-output /Users/username/info.json [代码] 编写上传逻辑 基本流程: 获取cli -> 获取当前版本配置 -> 问题队列(获取上传信息)-> 执行上传(cli命令)-> 修改本地版本文件 -> 成功提示 [代码]// ./lib/publish-weapp.js 文件 module.exports = async function(userConf) { // cli路径 const cli = `/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli`; // 版本配置文件路径 const versionConfPath = Config.dir_root + '/xdk.version.json'; // 获取版本配置 const versionConf = require(versionConfPath); // 开始执行问题队列 anser case: {isRelease: true, version: '1.0.1', versionDesc: '这是一个体验版'} let answer = await inquirer.prompt(getQuestion(versionConf)); versionConf.version = answer.version || '0.0.0'; versionConf.versionDesc = answer.versionDesc; //上传体验版 let res = spawn.sync(cli, ['-u', `${versionConf.version}@${Config.output}`, '--upload-desc', versionConf.versionDesc], { stdio: 'inherit' }); if (res.status !== 0) process.exit(1); // 修改本地版本文件 (当为发行版时) !!answer.isRelease && await rewriteLocalVersionFile(versionConfPath, versionConf); // success tips Log.success(`上传体验版成功, 登录微信公众平台 https://mp.weixin.qq.com 获取体验版二维码`); } // 修改本地版本文件 function rewriteLocalVersionFile(filepath, versionConf) { return new Promise((resolve, reject) => { fs.writeFile(filepath, jsonFormat(versionConf), err => { if(err){ Log.error(err); process.exit(1); }else { resolve(); } }) }) } [代码] 注意这里需要在项目根目录下创建一个xdk.version.json的版本文件,详细配置说明见仓库 大致是这样的结构: [代码]// xdk.version.json { "version": "0.12.2", "versionDesc": "12.2版本" } [代码] 到这里基本就完成了上传的所有步骤,可以当前项目下键入[代码]xdk-cli publish[代码]查看一下程序是否正常运行,注意上传出错记得检查是否处于登录状态 扩展:关于版本号自增 可以看到在版本号环节使用的是list类型,而非input类型,这是因为手写版本号有写错的风险, 还是让我做选择题吧 emmm… 最终效果: [图片] 就不在这里展开了,可以下, 搜索[代码]getVersionChoices[代码]函数: https://github.com/jinxuanzheng01/xdk-cli/blob/master/lib/publish-weapp.js 解决打包工具的问题 你会发现是运行cli命令就直接发布了,那么用gulp,webpack等工具项目因为需要上传dist目录而非src的原因,需要先进行打包再进行发布: gulp -> xdk-cli publish ,将一个动作拆成了两个 遗忘了一个怎么办?纠结了 不知道大家对微信开发者工具的上传钩子还有没有印象,要实现的大概就是这么个东西, 一个上传前预处理的钩子 [图片] 这个实现起来也很简单,首先给用户的配置文件(xdk.config.js)开一个可配置项: [代码]// xdk.config.js { // 发布钩子 publishHook: { async before(answer) { this.spawnSync('gulp'); return Promise.resolve(); }, async after(answer) { this.log('发布后的钩子执行了~'); return Promise.resolve(); } } } [代码] 在publish-weapp.js中去识别钩子即可: [代码] // 前置钩子函数 await userConf.publishHook.before.call(originPrototype, answer); //上传体验版 let res = spawn.sync(cli, ['-u', `${versionConf.version}@${Config.output}`, '--upload-desc', versionConf.versionDesc], { stdio: 'inherit' }); // 后置钩子函数 await userConf.publishHook.after.call(originPrototype, answer); [代码] 当然,你需要判断下钩子是否存在,类型判断,包括需要返回给用户配置里一些基本的方法和属性,例如:日志输出,命令行执行等 我这边以gulp为例,可以看到在publsih前先执行[代码]gulp[代码],后进行发布,最后log出一行提示信息 完全符合预期 解决环境变量切换问题 解决了自动上传的问题,接下来就要解决环境变量切换的问题了,这里还要借用下刚才写的钩子函数: [代码]{ // 发布钩子 publishHook: { async before(answer) { this.spawnSync('gulp', [`--env=${answer.isRelease ? 'online' : 'stage'}`]); return Promise.resolve(); }, async after(answer) { this.log.success('发布后的钩子执行了~'); return Promise.resolve(); } } } [代码] 以gulp为例,根据之前的回答的结果answer.isRelease,判断是否为使用正式环境 如果你没有使用编译工具,也可以提出一个env的config文件,使用fs模块直接重写该环境变量 下面是一串伪代码: [代码]目录结构 - app (小程序项目) - page - app.json ... - env.js - xdk.config.js - xdk.version.js - envConf.js // envConf.js module.exports = { ['online']: { appHost1: 'https://app.xxxxxxxxx.com', appHost2: 'https://app.xxxxxxxxx.com', }, ['stage']: { appHost1: 'https://stage.xxxxxxxxx.com', appHost2: 'https://stage.xxxxxxxxx.com', } }; // xdk.config.js publishHook: { async before(answer) { let config = require('envConf.js')[answer.isRelease ? 'online' : 'stage']; fs.writeFile('./app/env.js', jsonFormat(jsonConf), (err) => { if(err){ this.log.error('写入失败') }else { this.log.success('写入成功); } }); return Promise.resolve(); } [代码] 打包工具的问题还有环境变量的问题,我都没有写在cli里面,是因为不同项目之间对环境变量的写法,格式都不尽相同,具体要怎么做还是要留给开发者自己来确定吧,这样看起来更灵活一些 最后 总之利用脚手架解决了从发布到上线的一连串问题,使得不再担心因为切换环境导致线上bug,也不再担心写版本号写错的问题,确认线上环境这个环境也变成了一个非强需求的事情 整个上线流程也只需要:xdk-cli publish -> 提交审核即可,而且整个代码也并不复杂,publish-weapp.js整个文件算上注释也就60行代码,何乐不为呢? 注:下篇会讲一下如何做自定义指令,帮助小伙伴可以更自由的适配不同的项目~
2019-08-05 - 视频号本地生活小店最新API文档
视频号本地生活小店最新接口文档: https://docs.qq.com/doc/DSGRGTVZnakhCaURS?u=1bccddf3447545b5ba3dd058c6b3c9d9 主要支持:商品,价库,发券同步。 核心业务时序图[图片]
06-14 - 个人开发者把小程序发布到 App Store 5个步骤(保姆级教程)
用完即走,小程序已经成为连接用户与服务的重要桥梁,无论是购物、出行还是娱乐,小程序都以其便捷性和高效性赢得了用户的青睐。 那小程序是否有边界,能否拓展到 App? 微信开发者工具的最新升级,为这一问题提供了创新的解答。现在,开发者们可以轻松将已有的小程序代码构建为全功能的 App,而无需从零开始开发,这不仅节省了大量的人力和时间成本,更为小程序开发者们打开了通往 App Store 巨大流量的大门。 在这篇文章中,我们将深入探讨微信开发者工具支持小程序 App 化的五大步骤,分析其潜在价值,并通过实际案例来展示这一过程的效果。 背景:个人开发者,将小程序代码构建为 iOS App,以下为整体流程,适合 iOS 开发 / 上架零基础的同学。 [图片] 缘起 一个周末,突然奇想,我还没有搞过 App,要不搞搞玩玩😄 从 0 开始学还是很慢的,毕竟时间有限,好在了解过提示工程 [代码]《ChatGPT 进阶:提示工程入门 陈颢鹏 李子菡》[代码],问了一下助手 ChatGPT 几个常见的问题。 开发适用于 iPhone 的 App 的流程是什么 注册开发者账号 -> 下载 Xcode -> 学习 Swift -> 设计 + 编码 + 测试 -> TestFlight 内测 -> 准备上架 (准备) -> 提交审核 -> 应用上架 -> 应用更新和维护 很好,请给出可运行的应用实例,完成查询本机 IP 地址 我是一个新手,请问在 XCode 中如何运行你提供的代码 几轮对话后,大约用了 1 个小时,一个 iOS Demo 在模拟器上跑成功了,有点意思😄 不过功能有点简单,几年前用 Vue 写过一个还在跑的网站,同时我知道 ChatGPT 的编程能力,于是我丢了一个问题给他。 [代码]你是一个开发,会 Vue 、iOS 开发(使用 SwiftUI 框架 )两种语言,现在需要你根据 Vue 的代码重写为 iOS 代码,以下是 Vue 代码 [代码] [图片] 笔者是一个运维平台的产品,为了不忘记运维场景和技能,自己维护一个业务场景,开发语言:golang + Vue,部署架构:腾讯云 CLB+TKE+ 服务网格,运营系统:CLS+ 云函数 +Kafka+Doris+Flink+Hadoop+Streamsets。 结果惊奇的发现,真的可以执行,不服不行 👍 [图片] 1. 转折:把小程序代码构建为 iOS App 测试包(1 小时) 如果仅仅只是这样,那么这篇文章标题就该叫“GPT 如何将 Vue 改写为 App”。 万万没想到,过了 2 周从朋友那里获悉 微信开发者工具可以直接将小程序代码构建为 App,就像 Golang 一样,可以通过参数 [代码]GOARCH[代码] 控制构建的程序是在跑在 [代码]amd64[代码],还是 [代码]arm64[代码] CPU 架构上。 [图片] 现实就是这么巧,几年前使用 Vue 开发站点时,同时也开发了同款小程序。 有点意思,参照文档 构建你的第一个应用 花了 1 个小时,在我的 iPhone 上跑了 测试版 的 App. [图片] 此处应该给多端应用的产品和开发点个赞👍🏻 搞到这里,我其实进入了这款的第一个哎哈时刻,确实很爽,因为我不需要花心思用 GPT 来迁移 Vue 程序,直接用微信开发者工具构建为 App 即可,交互完全一致。 另外记录构建过程中遇到的两个问题 问题 1:小程序的图片在 App 中无法渲染 启用 Media SDK 即可 [图片] 问题 2:App 带有 Vconsole 入口 一开始以为在模拟器中才有,最后发现是一个配置,需要自己主动关闭。 [图片] 2. 构建正式包 谁不想在 App Store 能搜到自己的 App 呢,第二步,构建正式包。 2.1 准备苹果开发者账号 在 MacBook Air 或 iPhone 中安装 Apple Developer,然后注册苹果开发者账号 [图片] 一年 688 元会费 [图片] 正常情况下,交完会费后,第二天会收到一封欢迎加入 Apple Developer Program 的邮件,代表苹果开发者账号注册成功。 很遗憾,我注册时提示“未知错误,请再试一次” 找 Apple Developer 客服反馈,最后答复 [代码]由于一个或多个原因,您无法完成 Apple Developer Program 的注册。我们目前无法继续处理您的注册。[代码]。 好吧,估计是被风控命中了,于是找了家人的账号来注册,直接成功😄 2.2 生成 Bundle ID/ 证书 /Profile 生成 App 备案和构建正式包都需要的 Bundle ID/ 证书 /Profile。 生成 Bundle ID Bundle ID 是一个唯一的标识符,用来识别你的应用程序。它通常采用反向域名格式,例如 com.example.myapp。在开发和发布应用程序时,你需要在苹果的开发者账户中注册一个 Bundle ID,这样苹果的服务才能识别出你的应用程序。 参照 文档 生成 Bundle ID。 生成 证书 /Profile 证书(Certificates)用于建立开发者的身份,并确保应用是由已注册的开发者发布的。开发者需要从苹果开发者中心申请证书,用来对应用进行签名,这样 iOS 设备才会信任并运行这个应用程序。 配置文件(Provisioning Profiles)是一个包含证书、应用程序 ID、设备 ID 和其他信息的文件,它告诉 iOS 设备一个应用程序可以被安装和运行。配置文件将应用、开发者和设备联系起来,控制哪些设备可以安装和运行你的应用程序。 参照 文档 生成 iOS 证书和 Provisioning Profile。 [图片] 拓展资料:创建证书签名请求 问题:申请的 iPhone Distribution 证书不受信任 导入 Apple WWDRCA 证书 即可,可能原因:大致是分发的根证书没有导入你的 Mac 上。 更多资料详见 Apple PKI。 [图片] 2.3 备案(10 天 +) App 如果没有备案,在中国大陆将无法上架,这是苹果官方的说明。 中国工业和信息化部(MIIT)要求 App 必须具备有效的互联网信息服务提供者(ICP)备案号,了解更多 [图片] 其实备案比较简单,参照 App 备案 ,使用上一部分申请的 [代码]Bundle ID[代码]、证书(可查看 [代码]公钥[代码]、[代码]签名 MD5 值[代码])即可,不需要把 App 开发完,再来备案。 备案最长需要 20 个工作日,笔者用了 10 个工作日,在一个周五的下午收到了工信部发来的备案通过短信。 2.4 创建移动应用 移动应用是为了让 App 能用上微信的能力(比如分享到朋友圈或发送给朋友、微信登录 / 支付等),在移动应用中同时登记了 Bundle ID 和 Universal Links,这将会传递给下一步的多端框架,这是构建可正式包(采用苹果的分发证书)的必备条件。 先介绍一下 Universal Links。当用户使用 iPhone 手机访问你的网站,同时安装了 App 时,能在网站顶部快速跳转到 App。具体可以看下苹果官方的文档 Supporting associated domains 你需要有一个网站,未来要放 Universal Links 要用到的 [代码]apple-app-site-association[代码] 文件,不过对于我来说,这个功能好像用处不大,我更需要的是当用户用 iPhone 访问网站,引导他去 Apple Store 安装 App. 这里有一个关键信息,如果你不需要微信支付 / 微信登录 / 微信卡券的能力,不需要做开发者认证(开发者认证不能是个人主体) 访问 微信开放平台,创建移动应用,提交审核,几个小时就审核通过了。 [图片] 2.5 绑定多端框架 在 Donut 开发平台 中将 多端应用绑定上一步创建的移动应用,这样可以用到移动应用中登记的 Bundle ID 和 Universal Links,官方这么做比较合理,关键信息必须通过移动应用这关人工审核来起到一定的约束。 [图片] 绑定后,在多端应用中可以看到 Bundle ID 和 Universal Links 了。 [图片] 2.6 准备 App icon 等资料 App Icon 先用工具为你的 App 设计一个 1024px X 1024px 的图标,然后在 App Icon Generator 上生成 iPhone 所有规格的图标,之后在 [代码]project.miniapp.json[代码] 配置。 [图片] 启动图片 App 启动一般需要 2~4 秒,如果没有启动图片是白屏,用户会有点慌,不知道当前 App 是否正在启动,启动图片就是解决这个问题,同时在启动图片中传达 App 的价值主张。 我是直接用 Sketch 设计的,分辨率为 1290px x 2796px,这是兼容性最强的 6.7 寸(iPhone 15 Pro Max/15 Plus/14 Pro Max)手机的分辨率。 考虑到启动图片在不同机型上的兼容性,如果你用 Xcode 开发,苹果官方会推荐使用 Launch Screen Storyboard 隐私信息访问许可描述 小程序虽然没有用到摄像头、麦克风等权限,但多端的 SDK 中有(具体详见 Donut 官方文档 上架应用市场常见问题),所以得提前申明,不然把包通过 [代码]Transporter[代码] 上传后,会收到苹果发出的不合规邮件。 [图片] 以下是根据苹果官方打回的邮件中定义的隐私信息访问许可描述,应该是最基础的了,可以贴到你的 [代码]project.miniapp.json[代码] 文件中(用编辑器打开)。 [代码]{ "privateDescriptions": { "NSBluetoothPeripheralUsageDescription": "为了提供完整的功能,我们的应用程序需要访问蓝牙外设。这将用于与其他设备进行通信和数据交换。我们承诺保护用户隐私和数据安全。", "NSMicrophoneUsageDescription": "为了提供完整的功能,我们的应用程序需要访问麦克风。这将用于录制音频和进行语音交互。我们承诺保护用户隐私和数据安全。", "NSCalendarsUsageDescription": "为了提供完整的功能,我们的应用程序需要访问日历。这将用于提醒和日程管理。我们承诺保护用户隐私和数据安全。", "NSLocationAlwaysAndWhenInUseUsageDescription": "","NSBluetoothAlwaysUsageDescription":" 为了提供完整的功能,我们的应用程序需要始终访问蓝牙外设。这将用于与其他设备进行通信和数据交换。我们承诺保护用户隐私和数据安全。","NSPhotoLibraryUsageDescription":" 为了提供完整的功能,我们的应用程序需要始终访问相册。这将用于 IP 查询时显示 ISP 的图标。我们承诺保护用户隐私和数据安全。","NSCameraUsageDescription":" 为了提供完整的功能,我们的应用程序需要访问摄像头。这将用于录制视频。我们承诺保护用户隐私和数据安全。","NSLocationWhenInUseUsageDescription":" 为了提供完整的功能,我们的应用程序需要在使用时访问位置信息。这将用于提供定位服务和相关功能。我们承诺保护用户隐私和数据安全。" } } [代码] 2.7 构建正式版版本包 参照 打包生成 IPA 生成正式版的版本,注意使用分发证书。 [图片] 报错:file must be in miniprogram project 解决:把 mobileprovision 放在 miniprogram 目录下,因为 profile 不像 App icon 一样会自动上传到 miniprogram/ 目录下。 2.8 使用 Transporter 上传版本 参照 官方文档 上传正式版的 APK 包。 [图片] 遇到问题: Transporter,无法为 App “comxxxx.ipa” 创建临时 .itmsp 软件包。No suitable application records were found. Verify your bundle identifier ‘com.xxxx’ is correct and that you are signed into Xcode with an Apple ID that has access to the app in App Store Connect. [图片] 解决办法:去 App Store Connect 添加 App,绑定 [代码]Bundle id[代码],这样 Transporter 可以验证包在 App Store Connect 中已注册。 3. 使用 TestFlight 测试 在 App Store Connect 的 TestFlight 页面,可以选择内部、外部测试,外部测试版本需要 Apple 官方审核,把 公开链接发给朋友即可。 [图片] 在测试的同时,可以同步准备上架 App Store 的资料了。 4. 准备上架 Apple Store 审核资料 截屏 截屏是用来在 App Store 中显示你的 App 产品介绍页的,具体参照 截屏规范 [图片] 有 [代码]iPhone 15 Plus[代码] 和 [代码]iPhone 8 Plus[代码] 这两款机型就足够了,其他型号的手机能复用,分辨率应该是等比率缩放。 如果你像我一样,没有这两款手机,那用 iOS 模拟器。 Xcode -> 工具栏 Windows -> Devices and Simulators -> Create a new simulator -> Download more simulator runtimes [图片] 在微信开发者工具中运行这两款模拟器,利用模拟器自带截屏工具即可。 隐私政策 找一下常见 App 的隐私政策,在其产品介绍页中可以跳转过去。 如果你有网站就放在网站上,如果没有可以放在腾讯文档上。 [图片] 选择 App 供应的地区范围 哪些地区的用户可以下载你的 App。 [图片] 提交审核 一切准备好了后(包含备案),开启提交审核。 下午 5:35 提交审核,第二天早上 3:40 上架成功。✌🏻 [图片] 5. App Store 的数据 上架后刚好一周,看看最近一周的数据,还不错。 [图片] 这是评分数据 [图片] 6. 引流 二维码引流:草料二维码 通过草料二维码生成 App 的下载链接,放在网站上,引导用户跳转至 App。 Universal Links 参照 Apple 官网文章 Supporting associated domains 准备 Universal Links。 前面已经介绍了这个东东是干嘛的。 准备 [代码]apple-app-site-association[代码] 文件,放在网站的 [代码].well-known[代码] 目录下,完整路径为 [代码]/.well-known/apple-app-site-association[代码] 以下为示例,特别注意的是 [代码]appID[代码] 是由 [代码]团队 ID[代码] + [代码]Bundle ID[代码] 组成。 [代码]{ "applinks":{"apps":[], "details":[ { "appID":"<team_id>.<bundle_id>", "paths":["*"] } ] } } [代码] team_id 从 开发者账户 中获取 [图片] 顶部导航 当用户访问网站时,顶部引导用户跳转到 App 下载页。 等有空了搞搞。 7. 后记 小程序转 App,让个人或企业可以快速拥有 App,获取应用市场的流量,让开发者把精力放在业务逻辑上。 同时在开发小程序的过程中,发现开发者生态会散落在多个地方,比如 github,提供一些小程序模版、组件等能力,无法集中在一个地方比较方便的找到整个开发者生态的能力,和 VSCode 插件生态有点区别。 [图片] 先说 IDE 插件,比如我用 GPT4-Turbo 来写先代码或排查问题会在微信开发者工具和 Web 间跳转,操作流不太顺,如果能在微信开发者工具的插件入口中找到对应的 AI 代码助手,用起来应该很爽。 一旦平台的开放能力放出来,这些能力将源源不断的涌入到这个市场中,而不是作为平台方来集成这些能力,毕竟精力有限,同时还不一定做的最好,用插件可以让用户有更多的选择。 再说说 小程序组件,以大模型为例,目前市场有备案的大模型基座模型有好几家,在小程序开发过程中其实比较缺整体组件(UI + 背后的 API),有点像商场一样,平台方构建开放的能力,引导各个供应商提供开箱即用的能力,让用户可以快速上手,赶上这波大模型的技术趋势。 比如我自己在设计开放能力时的思考,平台专注骨架功能的开发,让开发者能参与到平台的建设中来,把生态盘活起来,最终提升大家研发运营的效率。 最后就是管理后端比较分散,比如 开放平台、donut、we 分析、云测、云托管,云开发,产品矩阵看不清,不容易知道整体的能力,缺少一个集中的控制台。 最后希望小程序越来越好 😄
01-30 - 春节营销活动之老虎机抽奖功能实践
老虎机抽奖功能实践 具体活动我在下面的文章中进行了基本的介绍的,但是每个功能细节没有细说,这里具体说下 供相关开发进行参考 本次介绍的功能为老虎机 ~ 春节营销活动小程序复盘? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/00048c0a7b0218870d11bf0096d813 ~ 老虎机这次的代码主要参考了,之前凹凸实验室开源的一个营销活动项目,这里面的大转盘亦如此, 所以呢在这里我最感谢的还是凹凸实验室,这个项目虽然已经不再维护了,但是基本的功能还是在的 具体开源地址如下所示: ~ 发现开源小程序之美五,营销组件库? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/000c4235c98740a1dc2a1a6045b013 ~ 在实现的时候遇到几个问题,就是这个开源项目里面的老虎机有一个回调函数,但是其实这个回调函数并未入预期返回转到的结果,而是没有任何返回,这个时候,具体的结果怎么拿到呢? 其实是在初始化的时候记忆已经确定了,跟我们的抽奖一样,其实进入页面的时候,结果 已经确定了 [图片] ~ [图片] ` [图片] ~
02-12 - "5.25秒变0.023秒:小程序图片优化全攻略"
“5.25秒变0.023秒:小程序图片优化全攻略” 最近在公司在写微信小程序,该小程序主要展示一些高清图片,所以图片数量非常多,而且图片尺寸也比较大,导致小程序的加载时间非常长。所以这里记录一下如何减少小程序图片的加载时间。 优化前 这里我挑选了一些项目里面比较大的图片,还有我之前保存的一些背景图,共计[代码]12[代码]张,上传到[代码]阿里云oss[代码],然后写了一个demo去渲染图片,这里我们先看看加载时间。 [图片] 可以看到,一旦文件大小到了[代码]1M[代码]以上,加载基本都在[代码]1秒[代码]以上了,而且加载最慢的一张图片大小为[代码]2.4M[代码];加载耗时[代码]5.27[代码]秒。然而在这个小程序中,有非常多的瀑布流图片展示,需要加载的图片也非常多,这对于这种图片展示类的小程序来说,简直是非常糟糕的体验。下面我们就分析一下,如何提升用户体验,缩短加载时间。 分析优化 这里我总结了一些常见图片优化策略,方法如下: [图片] 下面我们就根据总结的优化策略进行优化,具体如下: 优化1:使用[代码]webp[代码]格式的图片 首先我们知道,在小程序中是支持[代码]webp[代码]格式的图片的,所以我们可以将图片转换为[代码]webp[代码]格式,这样可以减少图片体积,提升加载速度。 公司使用的[代码]阿里云oss[代码]进行图片存储,阿里云oss是支持格式转换的,只需要在图片url后面加一定的参数即可,我们可以给图片后面加上[代码]?x-oss-process=image/format,webp[代码]即可。 [代码]// 原本图片路径: let url =' https://lonjin.oss-cn-beijing.aliyuncs.com/weixin-test/3%20%282%2917047050403470042.png' // 转化为webp格式的图片路径: let url =' https://lonjin.oss-cn-beijing.aliyuncs.com/weixin-test/3%20%282%2917047050403470042.png?x-oss-process=image/format,webp' [代码] 清除缓存,刷新页面,再来看加载时间: [图片] 可以看到,加载时间大幅缩短,图片格式转化后,图片大小已经非常小了,最大的一张图片仅仅有[代码]730kb[代码];随之加载时间也大幅缩短,最慢的一张图片从之前的5.27秒缩短为1.71秒,加载速度提升了[代码]3倍[代码]!其他的图片加载基本都在[代码]500ms[代码]左右,加载速度提升也是比较明显。 这里我们还需要注意一下[代码]webp[代码]图片格式的支持范围,我这里使用的[代码]uni-app[代码]做为demo,查了一下文档,支持范围如下: Android4以上(含)、iOS14以上(含)系统内置支持webp,此时,不管web、小程序、app,也不管vue/nvue/uvue都可以直接使用webp; iOS14以下,app-vue下,iOS不支持;app-nvue/uvue下,iOS支持;微信小程序2.9.0起,配置属性webp为true时iOS支持; 所以如果考虑一些特殊情况,我们可以进行一些特殊情况下的处理。 如果文件是直接放在服务器上的,我们可以借助一些第三方工具来把图片批量转化为[代码]webp[代码]格式;比如convertio.co 优化2:根据需求设置适当的分辨率 阿里云oss支持在图片后面加上参数来设置图片的分辨率,我这里写的demo中,[代码]image[代码]标签图片宽度均为小程序图片默认宽度;即为[代码]width: 320px;[代码],所以我们可以给图片url后面加上[代码]/resize,w_320[代码]即可,其中[代码]w_320[代码]表示图片宽度为[代码]320px[代码]。 [代码]// 原本图片路径: let url =' https://lonjin.oss-cn-beijing.aliyuncs.com/weixin-test/3%20%282%2917047050403470042.png?x-oss-process=image/format,webp' // 设置宽度后的图片路径: let url =' https://lonjin.oss-cn-beijing.aliyuncs.com/weixin-test/3%20%282%2917047050403470042.png?x-oss-process=image/format,webp/resize,w_320' [代码] 清除缓存,刷新页面,再来看加载时间: [图片] 图片大小大幅度减少,最大的一张图片仅仅有[代码]15.4 kB[代码];随之加载时间也大幅缩短,最慢的一张图片从之前的1.71秒缩短为316毫秒,加载速度提升了[代码]5倍[代码]!这时候所有图片的加载速度全部没有超过1秒,最大不超过[代码]350ms[代码];基本上算是质的飞跃了。不过需要注意的是,图片分辨率还是需要结合业务需求进行调整的,如果要求必须高清,不建议设置太小的分辨率。不过我们可以写一个检测用户网络状态的方法,在不同的网络环境下加载不同分辨率的图片;后面我会专门来写一篇文章来实现这个功能。 优化3:使用雪碧图 雪碧图,也叫[代码]Sprite[代码],是将多个小图片合并成一张大图,然后在页面中使用[代码]background-image[代码]和[代码]background-position[代码]属性来显示其中的某一张图片。这样可以减少图片的加载次数,减少图片的大小,同时减少图片的加载时间。在项目中难免会有很多小图标,我们就可以使用雪碧图的方式来使用,减少请求次数。这里我就不做展示了。 优化4:合理使用占位图片 通常情况下,为了内容的动态展示,需要通过网络请求从接口中获取图片的url。如果在网络慢的情况下,image加载图片的过程可能会非常慢,在请求完成之前页面都会因为没有数据而呈现一片空白,这是非常差的用户体验,这里我们可以借助小程序image标签上的[代码]@error[代码]和[代码]@load[代码]事件来实现占位图片的展示。我们可以根据需求去封装一个[代码]LoadImage[代码]组件统一处理,上面提到的[代码]优化1[代码]、[代码]优化2[代码]也可以通过定义参数实现批量添加,同时我们也可以给组件加上[代码]lazyLoad[代码],减轻小程序加载压力。 组件具体代码如下: [代码]<template> <view class="loadImage-wrapper"> <image v-if="isLoading" :src="defaultImage" :mode="mode" :lazy-load="lazyLoad" /> <image :class="[isLoading ? 'before-load' : '']" :src="imageUrl" :mode="mode" :lazy-load="lazyLoad" @load="imageLoad" /> </view> </template> <script> export default { props: { /** * 占位图 * @default /static/images/load-image.png */ defaultImage: { type: String, default: '/static/load-image.png', }, /** * 是否使用webp * @default false */ useWebp: { type: Boolean, default: false, }, /** * 图片的显示模式 * @default scaleToFill */ mode: { type: String, default: 'scaleToFill', }, /** * 图片加载分辨率-宽度 * @default */ width: { type: String, default: '', }, /** * 是否懒加载 * @default true */ lazyLoad: { type: Boolean, default: true, }, /** * 图片地址 * @default */ src: { type: String, default: '', }, }, data() { return { isLoading: true, } }, methods: { imageLoad() { this.isLoading = false }, }, computed: { imageUrl() { let url = this.src + '?' this.useWebp && (url += 'x-oss-process=image/format,webp') this.width && (url += '/resize,w_' + this.width) return url } }, } </script> <style lang="scss" scoped> .loadImage-wrapper { .before-load { width: 0; height: 0; opacity: 0; } } </style> [代码] 使用方式如下: [代码]<template> <view class="list"> <load-image v-for="(item, index) in list" :src="item" :lazyLoad="false" useWebp :width="320" :key="index" /> </view> </template> [代码] 优化后效果对比 我们再来看一下优化前后的加载时间对比: 优化前: [图片] 优化后: [图片] 我们用一张图片来做对比: 图片名称 大小 加载时间 优化前 wallhaven-we3z86.jpeg 2.4MB 5.27s 优化后 wallhaven-we3z86.jpeg 15.3KB 23ms 可以看到相对体积而言,缩小了[代码]99.4%[代码];相对加载时间,缩短了[代码]5.25[代码]秒,在几乎不影响图片质量的情况下,极大的提升了用户体验。 总结 关于小程序的图片优化,我们可以根据业务需求以及技术支持来选择不同的方案,这里我只列出了几个最常用的方案,如果业务需求比较复杂,可以多尝试一些方案。欢迎关注我的专栏🏷️前端进阶班 往期文章 5分钟get:Uni-App网络请求完美封装指南! uni-app开发小程序:项目架构以及经验分享 小程序TabBar创意动画(文末附完整源代码) 本文首发于lonjin个人博客,转载请注明出处!
01-12 - 零售行业小程序实践
零售行业如何通过小程序实现数字化。
2021-11-19 - 微信小程序CI流程搭建教学(2) -- jenkins一键发布
教学仓库: https://github.com/jinxuanzheng01/blog-xcx-ci-demo/tree/master,可以clone下来跟着教程一起尝试 CI流程搭建教学三部曲: 实现云构建如何接入CI(jenkins版)搭建一个版本发布管理后台 + 企业微信机器人群通知背景相信长期在做小程序的同学都有过以下经历: 只有固定的几台机器能发版,换台机器总担心出现点问题 (心力成本)多个小程序发版,切换多个仓库,甚至要用开发者工具去打开不同的项目(体力成本)频繁发版 + 发版流程琐碎 + 可能存在的多个小程序,一天的时间被机械劳动的事耗掉半天(时间成本)本文主要来分享一下我这边的一套解决,实现一键发布,统一管理,在读之前最好先看下上一篇文章,不然可能会有些阻碍 关于jenkins:https://www.jenkins.io/zh/ 案例 [图片] 这个是目前我们已经搭建好的一套服务,可以看到整个流程都做了那些事? 拉取代码询问当前需要变更的版本号构建(gulp, webpack,npm)存储当前版本镜像到静态资源服务器版本信息插入数据库更改后的版本文件提交到git仓库 其中红色为必须项,黑色看情况添加,如果你想也可以加入你想要添加的任何环节,下图是我们的版本发布管理后台的界面,存储的mysql的版本信息大概是这个样子: [图片] 关于管理后台第三章再讲,本文先着重描述怎么接入jenkins Let’go首先你需要一台云服务器用来搭建jenkins环境,推荐2核4g,不过1核2g也能跑 安装javajenkins依赖于java环境,所以先安装java yum install -y java-1.8.0-openjdk.x86_64 安装jenkins包官方教程: https://www.jenkins.io/zh/doc/book/installing/ 仓库地址: http://pkg.jenkins-ci.org/redhat-stable/ sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key yum install jenkins # 运行 service jenkins start 运行成功后访服务器端口号8080 [图片] 如果想修改端口号 vim /etc/sysconfig/jenkins, 找到JENKINS_PORT,修改后执行 [代码]service jenkins restart[代码] 即可 [图片] 关于插件安装个人比较懒,点推荐快速过,后面有用到的一些插件慢慢会介绍 [图片] 插件装完,中间需要创建管理员账号,一路确定最终安装成功 [图片] 创建流水线pipeline首先创建一个任务, 这里选择流水线(有兴趣可以了解下其他项), 点击确定 [图片] 点击流水线找到编辑script的地方 [图片] [图片] 这里可以选择在服务器上编写,还是从项目代码中读取jenkinsfile文件,我这里为了方便调试直接在jenkins机器上编写了 pipeline script流水线语法官方教程: https://www.jenkins.io/zh/doc/book/pipeline/,里面讲的比较详细,可以先按下面的代码运行,遇到问题再查教程 流水线结构写流水线之前,先想好我们整个发布流程需要做什么? 以当前例的话,大致分为5个环节: 拉取代码 询问需要更改的版本号安装npm包执行发布提交修改的版本号信息到仓库 每一个环节都是一个stage,先把stage列好,接下来我们一个一个补充 pipeline { agent any stages { // 拉取git代码 stage('git pull') { steps { } } // 询问当前版本信息 stage('inquirer version') { steps { } } // 构建 stage('build') { steps { } } // 推送版本信息到git仓库 stage('push version2git') { steps { } } } } 流水线语法 很多插件的语法使用还是比较复杂和琐碎的,这里提供了一个自动生成pipieline语法的功能 [图片] 以git为例,会自动生成相应的语法,避免很多查文档的时间 [图片] 拉取git代码根据上文所述,我们需要生成流水线脚本,但是想要让仓库和jenkins建立ssh链接,需要配置git仓库的公钥和jenkins的私钥凭据 配置git仓库公钥jenkins拉取git仓库代码,为了方便需要配下ssh秘钥 # 生成秘钥(没有要求一路回车即可) ssh-keygen -t rsa -C "616347058@qq.com" # 查看生成的ssh文件 cd ~/.ssh && ls # 一般会有id_rsa id_rsa.pub两个文件, 一个私钥一个公钥 点击个人头像点击setting找到ssh,粘贴 [代码]id_rsa.pub[代码] 里的内容到输入框,点击add添加完成 [图片] [图片] 配置jenkins私钥除了在git上配置公钥外,还需要在jenkins上配置下凭据,点击添加 [图片] 选择类型为ssh username,其他input的可以自己定一下的,这里需要一下,关于private项填的是私钥,需要复制[代码]id_rsa[代码]的内容,填写完成后点击添加,并在 Credentials 一栏选择刚才添加的证书 [图片] 当前页面没有显示红字即为成功 [图片] pipeline code// 拉取git代码 stage('git pull') { steps { git branch: 'test', credentialsId: '1', url: 'git@github.com:jinxuanzheng01/blog-xcx-ci-demo.git' } } 添加到指定位置后,点击保存,尝试构建一次查看是否成功,点左下角#5可以查看当前构建进程 [图片] 点击console output可以查看当前log,workspaces可以查看当前工作空间 [图片] 可以看到当前工作空间为仓库代码 [图片] 询问当前版本信息jenkins是不支持交互式命令的,所以需要换一种实现,查了下文档jenkins是有api支持的,方法名为 [代码]input[代码],不过在进行调用之前需要获取之前的版本信息,即读取version.config.json这个文件,整体代码如下: // 询问当前版本信息 stage('inquirer version') { steps { script { // 读取版本信息 def versionJson = readJSON file: './version.config.json', text: '' // 设置问题描述 def userInput = input( id: 'versionInput', message: '请设置版本信息', parameters: [ [defaultValue: versionJson.version, description: '设置版本号', name: 'VERSION', $class: 'TextParameterDefinition'], [defaultValue: 'jenkins CI is upload trial version as: ' + new Date().format('yyyy-MM-dd HH:mm:ss'), description: '设置版本描述(please use english)', name: 'VERSIONDESC', $class: 'TextParameterDefinition'] ]) // 设置全局变量 env.VERSION = userInput.VERSION; env.VERSIONDESC = userInput.VERSIONDESC; // 重写本地版本文件(为后续进行版本提交做准备) writeJSON file: './version.config.json', json: [version: env.VERSION, versionDesc: env.VERSIONDESC], pretty: 4; } } } 运行下, 会发现会提示等待,鼠标点击后弹出模态框,可以看到里面有从本地文件里提取出来的上个版本的信息,手动修改下即可 [图片] 注: readJSON和 writeJSON方法依赖 Pipeline Utility Steps 这个插件,没有的话会报错 执行构建这里实际都是构建流程,直接统一用一个“build stage” 即可, 这里步骤很简单,可以想一下从git clone下来一个项目应该干什么,这里实际就是在做这样一件事情 // 构建 stage('build') { steps { sh "npm install" sh "npm run build" } } 不过这里依赖node环境,需要先处理下,不然会报npm not found 安装nodejs环境这里的运行环境和本机没有关系,类似于一个沙盒,所以如果要安装node环境需要安装Config File Provider Plugin和NodeJS Plugin这两个插件 配置全局工具安装完成后添加全局工具,找到"系统管理 -> 全局工具配置 -> NodeJS" ,点击nodejs安装,默认项即可,点击保存,重启jenkins服务,回到服务器,输入命令 [代码]service jenkins restart[代码] [图片] pipeline code // 构建 stage('build') { steps { nodejs('NodeJS 14.3.0') { sh "npm install" sh "npm run build" } } } 上传成功这时候如果没问题的话代码应该已经发布到体验版了,这里如果报ip错误记得去微信公众后台添加一下上传的ip白名单 [图片] [图片] 提交新的版本号信息到仓库pipeline code基本就是将之前手动输入的命令写到脚本里 // 推送版本信息到git仓库 stage('push version2git') { steps { sh "git config --local user.name ${GIT_USER_NAME} && git config --local user.email ${GIT_USER_EMAIL}" sh "git add version.config.json" sh "git commit -m 'docs: 更改版本号为${VERSION}'" sh "git push origin ${BRANCH_NAME}" } } 关于上面代码中的"${}",其实是预设的环境变量,我们可以把一些固定的东西提出来,避免硬编码,例如: pipeline { agent any // 环境变量 environment { GIT_USER_NAME = 'jenkinsCI' GIT_USER_EMAIL = 'test@163.com' GIT_ADDRESS = 'git@github.com:jinxuanzheng01/blog-xcx-ci-demo.git' BRANCH_NAME = 'master' } } 更改jenkins账号权限直接进行push的话,git会报权限不足,这里倒不是因为ssh秘钥有问题,而是jenkins在执行的时候使用的不是linux机器的root权限,而是一个jenkins的账号 [图片] 所以最快速的做法是将jenkins运行环境修改为root权限 # 打开配置文件 vim /etc/sysconfig/jenkins # 修改jenkins_user为root, 默认为jenkins $JENKINS_USER="root" # 修改相关文件夹权限组 chown -R root:root /var/lib/jenkins chown -R root:root /var/cache/jenkins chown -R root:root /var/log/jenkins # 重启jenkins service jenkins restart 运行pipeline重新构建,jenkins无报错,查看git仓库,执行成功 [图片] Running success到此为止,整个pipeline已经work,你可以很轻松的尝试一键发布小程序 [图片] 尤其是有多个小程序项目的时候,可以单独建个分组,方便集中管理,再也不需要手动切入不同的仓库,甚至使用小程序开发者工具去打开各个小程序,大概如下 [图片] 项目集中管理不香么? Tips: jenkins流水线代码已经放到仓库里了,地址 https://github.com/jinxuanzheng01/blog-xcx-ci-demo/blob/master/Jenkinsfile 额外补充权限管理jenkins有很多的功能插件,比如权限管理,这个还是个比较刚需的功能,可以自行Google下并不难 发送http请求在pipeline中有时候需要去请求一些我们的其他服务,可以使用HTTP Request Plugin这个插件,文档地址:https://plugins.jenkins.io/http_request/里面有一些demo npm切换私有仓库方法一: npm可以直接用npm_token登录 方法二:项目内设置.npmrc,填写私有仓库 + 用户信息 jenkins使用docker报权限不足[图片]修改jenkins账号权限为root权限即可,上文有写,也可以直接修改docker.sock这个文件权限,如 [代码]chmod 777 /var/run/docker.sock[代码] 手动安装jenkins插件使用jenkins的插件管理安装插件很慢,有的时候还会失败,这里提供一个手动安装的方法 jenkins插件库: http://updates.jenkins-ci.org/download/plugins/config-file-provider/ 搜索需要的插件下载(一般是*.hpi格式的),打开jenkins "系统管理 -> 插件管理 -> 高级",选择下载的文件上传即可 [图片]
2020-05-28 - 小程序隐私保护指引设置最优的无感方案(需要有用户登录流程)
很简单,把 <button open-type="agreePrivacyAuthorization" /> 做成登录页中常见并且需要勾选的【已阅读并同意《用户协议》、《隐私协议》】的那个radio就行了 <template> <view> <view class="flex"><text>手机号</text><text>18612341234</text></view> <view class="flex"><text>短信验证码</text><text>1234</text></view> <button v-if="!isChecked" @click="handlegetPhonenumberFake">获取手机号(假)</button> <button v-else open-type="getPhoneNumber" @getphonenumber="handlegetPhonenumber"> 获取手机号(真)</button> <view class="flex"> <button class="radio" :class="{ current: isChecked }" open-type="agreePrivacyAuthorization" @click="handleClick" /> 已阅读并同意《隐私协议》、《用户协议》 </view> <button @click="handleLogin">登录</button> </view> </template> <script> export default { data() { return { isChecked: false }; }, mounted() {}, methods: { handlegetPhonenumberFake() { uni.showToast({ title: '请先阅读并勾选隐私协议、用户协议', icon: 'none', duration: 2000, }); }, handlegetPhonenumber(event) { if (this.isChecked) { const { iv, code } = event.detail; console.log('iv', iv); console.log('code', code); } else { uni.showToast({ title: '请先阅读并勾选隐私协议、用户协议', icon: 'none', duration: 2000, }); } }, handleClick() { this.isChecked = !this.isChecked; }, handleLogin() { if (this.isChecked) { // 登录业务代码... // 登录成功后返回页面 uni.navigateBack(); } else { uni.showToast({ title: '请先阅读并勾选隐私协议、用户协议', icon: 'none', duration: 2000, }); } }, }, };
2023-09-04 - 小程序实用npm包推荐
虽然都说不要重复造轮子, 但还是屡见不鲜, 下面给大家推荐几款实用的小程序npm包, 欢迎各位同志评论区继续补充. 1.mobx-miniprogram, mobx-miniprogram-bindings, 小程序状态管理, 类似于vuex; mobx-miniprogram用于创建状态数据, mobx-miniprogram-bindings用于对页面或组件绑定状态数据;使用文档: https://github.com/wechat-miniprogram/mobx; import { observable, action } from 'mobx-miniprogram'; export const store = observable({ // 数据字段 numA: 1, numB: 2, // 计算属性 get sum() { return this.numA + this.numB; }, // actions update: action(function () { this.numA++; }), }); // 页面使用 import{ createStoreBindings }from'mobx-miniprogram-bindings' import{ store }from'./store' Page({ data:{ someData:'...' }, onLoad(){ // 绑定 this.storeBindings = createStoreBindings(this,{ store, fields:['numA','numB','sum'], actions:['update'], }) }, onUnload(){ this.storeBindings.destroyStoreBindings() }, }) 2.dayjs, 时间处理工具, 包含时间解析, 时间格式化, 时间比较等常用功能, 最重要的尺寸较小, 非常适合移动端来使用, 使用文档: https://dayjs.fenxianglu.cn/category/; dayjs().format('YYYY-MM-DD HH:mm:ss'); // 2022-10-27 13:50:12 dayjs().add(7, 'day') // 7天后 dayjs().isBefore(dayjs('2011-01-01')) // 是否在2011-01-01之前 3.mp-html, 富文本解析利器, 小程序提供的rich-text组件虽然可以解析富文本, 但存在若干缺陷: 1. 文字无法选择; 2. 链接无法跳转; 3.图片无法预览和自适应尺寸等, 使用mp-html可以很好解决上述问题, 使用文档: https://www.npmjs.com/package/mp-html; 1.安装npm npm i mp-html 2.在需要使用页面的 json 文件中添加 { "usingComponents": { "mp-html": "mp-html" } } 在需要使用页面的 wxml 文件中添加
2022-10-27 - 微信小程序如何实现类似于chatGpt的打印机效果
背景:最近的chatGpt类的应用十分火爆。Gpt官网有个输入的效果。作为前端开发如何实现这种交互。 利用js实现。renderContent(item: MsgType, number) { const that = this; // showContent 是逐渐显示的内容。content是完整的显示内容。 function typing() { if (item.showContent && item?.showContent?.length < item.content.length) { // 每次需要截取的步长。步长加一。 let index = item.showContent.length + 1 item.showContent = item.content.substring(0, index); item.timer = setTimeout(typing, 100); } else { item.showContent = item.content; item.timer && clearTimeout(item.timer); } that.setData({ [`msgList[${number}].showContent`]: item.showContent, [`msgList[${number}].timer`]: item.timer, }); } typing(); } 2. 利用css实现。 wxml的代码 <view class="typing">简易中文打字效果</view> WXSS的效果 .typing { width: 15em; white-space: nowrap; border-right: 2px solid transparent; animation: typing 3.5s steps(15, end), blink-caret .75s step-end infinite; overflow: hidden; } /* 打印效果 */ @keyframes typing { from { width: 0; } to { width: 15em; } } /* 光标闪啊闪 */ @keyframes blink-caret { from, to { box-shadow: 1px 0 0 0 transparent; } 50% { box-shadow: 1px 0 0 0; } }
2023-04-03 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - vite —— 一种新的、更快地 web 开发工具【搬运】
vite 是什么 vite —— 一个由 vue 作者尤雨溪开发的 web 开发工具,它具有以下特点: 快速的冷启动即时的模块热更新真正的按需编译 从作者在微博上的发言: Vite,一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢。针对生产环境则可以把同一份代码用 rollup 打。虽然现在还比较粗糙,但这个方向我觉得是有潜力的,做得好可以彻底解决改一行代码等半天热更新的问题。 中可以看出 vite 主要特点是基于浏览器 native 的 ES module (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 来开发,省略打包这个步骤,因为需要什么资源直接在浏览器里引入即可。 基于浏览器 ES module 来开发 web 应用也不是什么新鲜事,snowpack 也基于此,不过目前此项目社区中并没有流行起来,vite 的出现也许会让这种开发方式再火一阵子。 有趣的是 vite 算是革了 webpack 的命了(生产环境用 rollup),所以 webpack 的开发者直接喊大哥了... [图片] 作者注:本文完成于 vite 早期时候,vite 部分功能和原理有更新。 NO.2 vite 的使用方式 同常见的开发工具一样,vite 提供了用 npm 或者 yarn 一建生成项目结构的方式,使用 yarn 在终端执行: $ yarn create vite-app $ cd $ yarn $ yarn dev 即可初始化一个 vite 项目(默认应用模板为 vue3.x),生成的项目结构十分简洁: |____node_modules |____App.vue // 应用入口 |____index.html // 页面入口 |____vite.config.js // 配置文件 |____package.json 执行 [代码]yarn dev[代码] 即可启动应用 。 NO.3 vite 启动链路 命令解析 这部分代码在 src/node/cli.ts 里,主要内容是借助 minimist —— 一个轻量级的命令解析工具解析 npm scripts,解析的函数是 [代码]resolveOptions[代码] ,精简后的代码片段如下。 function resolveOptions() { // command 可以是 dev/build/optimize if (argv._[0]) { argv.command = argv._[0]; } return argv; } 拿到 options 后,会根据 [代码]options.command[代码] 的值判断是执行在开发环境需要的 runServe 命令或生产环境需要的 runBuild 命令。 if (!options.command || options.command === 'serve') { runServe(options) } else if (options.command === 'build') { runBuild(options) } else if (options.command === 'optimize') { runOptimize(options) } 在 [代码]runServe[代码] 方法中,执行 server 模块的创建开发服务器方法,同样在 [代码]runBuild[代码] 中执行 build 模块的构建方法。 最新的版本中还增加了 optimize 命令的支持,关于 optimize 做了什么,我们下文再说。 server 这部分代码在 src/node/server/index.ts 里,主要暴露一个 [代码]createServer[代码] 方法。 vite 使用 koa 作 web server,使用 clmloader 创建了一个监听文件改动的 watcher,同时实现了一个插件机制,将 koa-app 和 watcher 以及其他必要工具组合成一个 context 对象注入到每个 plugin 中。 context 组成如下: [图片] plugin 依次从 context 里获取上面这些组成部分,有的 plugin 在 koa 实例添加了几个 middleware,有的借助 watcher 实现对文件的改动监听,这种插件机制带来的好处是整个应用结构清晰,同时每个插件处理不同的事情,职责更分明, plugin 上文我们说到 plugin,那么有哪些 plugin 呢?它们分别是: 用户注入的 plugins —— 自定义 pluginhmrPlugin —— 处理 hmrhtmlRewritePlugin —— 重写 html 内的 script 内容moduleRewritePlugin —— 重写模块中的 import 导入moduleResolvePlugin ——获取模块内容vuePlugin —— 处理 vue 单文件组件esbuildPlugin —— 使用 esbuild 处理资源assetPathPlugin —— 处理静态资源serveStaticPlugin —— 托管静态资源cssPlugin —— 处理 css/less/sass 等引用... 我们来看 plugin 的实现方式,开发一个用来拦截 json 文件 plugin 可以这么实现: interface ServerPluginContext { root: string app: Koa server: Server watcher: HMRWatcher resolver: InternalResolver config: ServerConfig } type ServerPlugin = (ctx:ServerPluginContext)=> void; const JsonInterceptPlugin:ServerPlugin = ({app})=>{ app.use(async (ctx, next) => { await next() if (ctx.path.endsWith('.json') && ctx.body) { ctx.type = 'js' ctx.body = `export default json` } }) } vite 背后的原理都在 plugin 里,这里不再一一解释每个 plugin 的作用,会放在下文背后的原理中一并讨论。 build 这部分代码在 node/build/index.ts 中,build 目录的结构虽然与 server 相似,同样导出一个 build 方法,同样也有许多 plugin,不过这些 plugin 与 server 中的用途不一样,因为 build 使用了 rollup ,所以这些 plugin 也是为 rollup 打包的 plugin ,本文就不再多提。 NO.4 vite 运行原理 ES module 要了解 vite 的运行原理,首先要知道什么是 ES module,目前流览器对其的支持如下: [图片] 主流的浏览器(IE11除外)均已经支持,其最大的特点是在浏览器端使用 [代码]export[代码] [代码]import[代码] 的方式导入和导出模块,在 script 标签里设置 [代码]type="module"[代码] ,然后使用模块内容。 import { bar } from './bar.js‘ 当 html 里嵌入上面的 script 标签时候,浏览器会发起 http 请求,请求 htttp server 托管的 bar.js ,在 bar.js 里,我们用 named export 导出 [代码]bar[代码] 变量,在上面的 script 中能获取到 bar 的定义。 // bar.js export const bar = 'bar'; 在 vite 中的作用 打开运行中的 vite 项目,访问 view-source 可以发现 html 里有段这样的代码: import { createApp } from '/@modules/vue' import App from '/App.vue' createApp(App).mount('#app') 从这段代码中,我们能 get 到以下几点信息: 从 [代码]http://localhost:3000/@modules/vue[代码] 中获取 [代码]createApp[代码] 这个方法从 [代码]http://localhost:3000/App.vue[代码] 中获取应用入口使用 [代码]createApp[代码] 创建应用并挂载节点 [代码]createApp[代码] 是 vue3.X 的 api,只需知道这是创建了 vue 应用即可,vite 利用 ES module,把 “构建 vue 应用” 这个本来需要通过 webpack 打包后才能执行的代码直接放在浏览器里执行,这么做是为了: 去掉打包步骤实现按需加载 去掉打包步骤 打包的概念是开发者利用打包工具将应用各个模块集合在一起形成 bundle,以一定规则读取模块的代码——以便在不支持模块化的浏览器里使用。 为了在浏览器里加载各模块,打包工具会借助胶水代码用来组装各模块,比如 webpack 使用 [代码]map[代码]存放模块 id 和路径,使用 [代码]__webpack_require__[代码] 方法获取模块导出。 vite 利用浏览器原生支持模块化导入这一特性,省略了对模块的组装,也就不需要生成 bundle,所以打包这一步就可以省略了。 实现按需打包 前面说到,webpack 之类的打包工具会将各模块提前打包进 bundle 里,但打包的过程是静态的——不管某个模块的代码是否执行到,这个模块都要打包到 bundle 里,这样的坏处就是随着项目越来越大打包后的 bundle 也越来越大。 开发者为了减少 bundle 大小,会使用动态引入 [代码]import()[代码] 的方式异步的加载模块( 被引入模块依然需要提前打包),又或者使用 tree shaking 等方式尽力的去掉未引用的模块,然而这些方式都不如 vite 的优雅,vite 可以只在需要某个模块的时候动态(借助 [代码]import()[代码] )的引入它,而不需要提前打包,虽然只能用在开发环境,不过这就够了。 vite 如何处理 ESM 既然 vite 使用 ESM 在浏览器里使用模块,那么这一步究竟是怎么做的? 上文提到过,在浏览器里使用 ES module 是使用 http 请求拿到模块,所以 vite 必须提供一个 web server 去代理这些模块,上文中提到的 [代码]koa[代码] 就是负责这个事情,vite 通过对请求路径的劫持获取资源的内容返回给浏览器,不过 vite 对于模块导入做了特殊处理。 @modules 是什么? 通过工程下的 index.html 和开发环境下的 html 源文件对比,发现 script 标签里的内容发生了改变,由 import { createApp } from 'vue' import App from '/App.vue' createApp(App).mount('#app') 变成了 import { createApp } from '/@modules/vue' import App from '/App.vue' createApp(App).mount('#app') vite 对 import 都做了一层处理,其过程如下: 在 koa 中间件里获取请求 body通过 es-module-lexer 解析资源 ast 拿到 import 的内容判断 import 的资源是否是绝对路径,绝对视为 npm 模块返回处理后的资源路径:[代码]"vue" => "/@modules/vue"[代码] 这部分代码在 serverPluginModuleRewrite 这个 plugin 中, 为什么需要 @modules? 如果我们在模块里写下以下代码的时候,浏览器中的 esm 是不可能获取到导入的模块内容的: import vue from 'vue' 因为 [代码]vue[代码] 这个模块安装在 node_modules 里,以往使用 webpack,webpack遇到上面的代码,会帮我们做以下几件事: 获取这段代码的内容解析成 AST遍历 AST 拿到 [代码]import[代码] 语句中的包的名称使用 enhanced-resolve 拿到包的实际地址进行打包, 但是浏览器中 ESM 无法直接访问项目下的 node_modules,所以 vite 对所有 import 都做了处理,用带有 @modules 的前缀重写它们。 从另外一个角度来看这是非常比较巧妙的做法,把文件路径的 rewrite 都写在同一个 plugin 里,这样后续如果加入更多逻辑,改动起来不会影响其他 plugin,其他 plugin 拿到资源路径都是 @modules ,比如说后续可能加入 alias 的配置:就像 webpack alias 一样:可以将项目里的本地文件配置成绝对路径的引用。 怎么返回模块内容 在下一个 koa middleware 中,用正则匹配到路径上带有 @modules 的资源,再通过 [代码]require('xxx')[代码] 拿到 包的导出返回给浏览器。 以往使用 webpack 之类的打包工具,它们除了将模块组装到一起形成 bundle,还可以让使用了不同模块规范的包互相引用,比如: ES module (esm) 导入 cjsCommonJS (cjs) 导入 esmdynamic import 导入 esmdynamic import 导入 cjs 关于 es module 的坑可以看这篇文章(https://zhuanlan.zhihu.com/p/40733281)。 起初在 vite 还只是为 vue3.x 设计的时候,对 vue esm 包是经过特殊处理的,比如:需要 [代码]@vue/runtime-dom[代码] 这个包的内容,不能直接通过 [代码]require('@vue/runtime-dom'[代码])得到,而需要通过 [代码]require('@vue/runtime-dom/dist/runtime-dom.esm-bundler.js'[代码] 的方式,这样可以使得 vite 拿到符合 esm 模块标准的 vue 包。 目前社区中大部分模块都没有设置默认导出 esm,而是导出了 cjs 的包,既然 vue3.0 需要额外处理才能拿到 esm 的包内容,那么其他日常使用的 npm 包是不是也同样需要支持?答案是肯定的,目前在 vite 项目里直接使用 lodash 还是会报错的。 [图片] 不过 vite 在最近的更新中,加入了 [代码]optimize[代码] 命令,这个命令专门为解决模块引用的坑而开发,例如我们要在 vite 中使用 lodash,只需要在 vite.config.js (vite 配置文件)中,配置 [代码]optimizeDeps[代码] 对象,在 [代码]include[代码] 数组中添加 lodash。 // vite.config.js module.exports = { optimizeDeps: { include: ["lodash"] } } 这样 vite 在执行 runOptimize 的时候中会使用 roolup 对 lodash 包重新编译,将编译成符合 esm 模块规范的新的包放入 node_modules 下的 .vite_opt_cache 中,然后配合 resolver 对 lodash 的导入进行处理:使用编译后的包内容代替原来 lodash 的包的内容,这样就解决了 vite 中不能使用 cjs 包的问题,这部分代码在 depOptimizer.ts 里。 不过这里还有个问题,由于在 depOptimizer.ts 中,vite 只会处理在项目下 package.json 里的 [代码]dependencies[代码] 里声明好的包进行处理,所以无法在项目里使用 import pick from 'lodash/pick' 的方式单使用 pick 方法,而要使用 import lodash from 'lodash' lodash.pick() 的方式,这可能在生产环境下使用某些包的时候对 bundle 的体积有影响。 返回模块的内容的代码在:serverPluginModuleResolve.ts 这个 plugin 中。 vite 如何编译模块 最初 vite 为 vue3.x 开发,所以这里的编译指的是编译 vue 单文件组件,其他 es 模块可以直接导入内容。 SFC vue 单文件组件(简称 SFC) 是 vue 的一个亮点,前端届对 SFC 褒贬不一,个人看来,SFC 是利大于弊的,虽然 SFC 带来了额外的开发工作量,比如为了解析 template 要写模板解析器,还要在 SFC 中解析出逻辑和样式,在 vscode 里要写 vscode 插件,在 webpack 里要写 vue-loader,但是对于使用方来说可以在一个文件里可以同时写 template、js、style,省了各文件互相跳转。 与 vue-loader 相似,vite 在解析 vue 文件的时候也要分别处理多次,我们打开浏览器的 network,可以看到: [图片] 1 个请求的 query 中什么都没有,另 2 个请求分别通过在 query 里指定了 type 为 style 和 template。 先来看看如何将一个 SFC 变成多个请求,我们从第一次请求开始分析,简化后的代码如下: function vuePlugin({app}){ app.use(async (ctx, next) => { if (!ctx.path.endsWith('.vue') && !ctx.vue) { return next() } const query = ctx.query // 获取文件名称 let filename = resolver.requestToFile(publicPath) // 解析器解析 SFC const descriptor = await parseSFC(root, filename, ctx.body) if (!descriptor) { ctx.status = 404 return } // 第一次请求 .vue if (!query.type) { if (descriptor.script && descriptor.script.src) { filename = await resolveSrcImport(descriptor.script, ctx, resolver) } ctx.type = 'js' // body 返回解析后的代码 ctx.body = await compileSFCMain(descriptor, filename, publicPath) } // ... } 在 compileSFCMain 中是一段长长的 generate 代码: function compileSFCMain(descriptor, filePath: string, publicPath: string) { let code = '' if (descriptor.script) { let content = descriptor.script.content code += content.replace(`export default`, 'const __script =') } else { code += `const __script = {}` } if (descriptor.styles) { code += `\nimport { updateStyle } from "${hmrClientId}"\n` descriptor.styles.forEach((s, i) => { const styleRequest = publicPath + `?type=style&index=${i}` code += `\nupdateStyle("${id}-${i}", ${JSON.stringify(styleRequest)})` }) if (hasScoped) { code += `\n__script.__scopeId = "data-v-${id}"` } } if (descriptor.template) { code += `\nimport { render as __render } from ${JSON.stringify( publicPath + `?type=template` )}` code += `\n__script.render = __render` } code += `\n__script.__hmrId = ${JSON.stringify(publicPath)}` code += `\n__script.__file = ${JSON.stringify(filePath)}` code += `\nexport default __script` return code } 直接看 generate 后的代码: import { updateStyle } from "/vite/hmr" updateStyle("c44b8200-0", "/App.vue?type=style&index=0") __script.__scopeId = "data-v-c44b8200" import { render as __render } from "/App.vue?type=template" __script.render = __render __script.__hmrId = "/App.vue" __script.__file = "/Users/muou/work/playground/vite-app/App.vue" export default __script 出现了 [代码]vite/hmr[代码] 的导入,[代码]vite/hmr[代码] 具体内容我们下文再分析,从这段代码中可以看到,对于 style vite 使用 [代码]updateStyle[代码] 这个方法处理,[代码]updateStyle[代码] 内容非常简单,这里就不贴代码了,就做了 1 件事:通过创建 style 元素,设置了它的 innerHtml 为 css 内容。 这两种方式都会使得浏览器发起 http 请求,这样就能被 koa 中间件捕获到了,所以就形成了上文我们看到的:对一个 .vue 文件处理三次的情景。 这部分代码在:serverPluginVue 这个 plugin 里。 css 如果在 vite 项目里引入一个 sass 文件会怎么样? 最初 vite 只是为 vue 项目开发,所以并没有对 css 预编译的支持,不过随着后续的几次大更新,在 vite 项目里使用 sass/less 等也可以跟使用 webpack 的时候一样优雅了,只需要安装对应的 css 预处理器即可。 在 cssPlugin 中,通过正则:[代码]/(.+).(less|sass|scss|styl|stylus)$/[代码] 判断路径是否需要 css 预编译,如果命中正则,就借助 cssUtils 里的方法借助 postcss 对要导入的 css 文件编译。 vite 热更新的实现 上文中出现了 vite/hmr ,这就是 vite 处理热更新的关键,在 serverPluginHmr plugin 中,对于 [代码]path[代码] 等于 [代码]vite/hmr[代码] 做了一次判断: app.use(async (ctx, next) => { if (ctx.path === '/vite/hmr') { ctx.type = 'js' ctx.status = 200 ctx.body = hmrClient } } hmrClient 是 vite 热更新的客户端代码,需要在浏览器里执行,这里先来说说通用的热更新实现,热更新一般需要四个部分: 首先需要 web 框架支持模块的 rerender/reload通过 watcher 监听文件改动通过 server 端编译资源,并推送新模块内容给 client 。client 收到新的模块内容,执行 rerender/reload vite 也不例外同样有这四个部分,其中客户端代码在:client.ts 里,服务端代码在 serverPluginHmr 里,对于 vue 组件的更新,通过 vue3.x 中的 [代码]HMRRuntime[代码] 处理的。 client 端 在 client 端, [代码]WebSocket[代码] 监听了一些更新的类型,然后分别处理,它们是: vue-reload —— vue 组件更新:通过 import 导入新的 vue 组件,然后执行 [代码]HMRRuntime.reload[代码]vue-rerender —— vue template 更新:通过 import 导入新的 template ,然后执行 [代码]HMRRuntime.rerender[代码]vue-style-update —— vue style 更新:直接插入新的 stylesheetstyle-update —— css 更新:document 插入新的 stylesheetstyle-remove —— css 移除:document 删除 stylesheetjs-update —— js 更新:直接执行full-reload —— 页面 roload:使用 [代码]window.reload[代码] 刷新页面 server 端 在 server 端,通过 watcher 监听页面改动,根据文件类型判断是 js Reload 还是 Vue Reload: watcher.on('change', async (file) => { const timestamp = Date.now() if (file.endsWith('.vue')) { handleVueReload(file, timestamp) } else if ( file.endsWith('.module.css') || !(file.endsWith('.css') || cssTransforms.some((t) => t.test(file, {}))) ) { // everything except plain .css are considered HMR dependencies. // plain css has its own HMR logic in ./serverPluginCss.ts. handleJSReload(file, timestamp) } }) 在 handleVueReload 方法里,会使用解析器拿到当前文件的 template/script/style ,并且与缓存里的上一次解析的结果进行比较,如果 template 发生改变就执行 vue-rerender,如果 style 发生改变就执行 vue-style-update,简化后的逻辑如下: async function handleVueReload( file timestamp, content ) { // 获取缓存 const cacheEntry = vueCache.get(file) // 解析 vue 文件 const descriptor = await parseSFC(root, file, content) if (!descriptor) { // read failed return } // 拿到上一次解析结果 const prevDescriptor = cacheEntry && cacheEntry.descriptor // 设置刷新变量 let needReload = false // script 改变标记 let needCssModuleReload = false // css 改变标记 let needRerender = false // template 改变标记 // 判断 script 是否相同 if (!isEqual(descriptor.script, prevDescriptor.script)) { needReload = true } // 判断 template 是否相同 if (!isEqual(descriptor.template, prevDescriptor.template)) { needRerender = true } // 通过 send 发送 socket if (needRerender){ send({ type: 'vue-rerender', path: publicPath, timestamp }) } } [代码]handleJSReload[代码] 方法则是根据文件路径引用,判断被哪个 vue 组件所依赖,如果未找到 vue 组件依赖,则判断页面需要刷新,否则走组件更新逻辑,这里就不贴代码了。 整体代码在 client.ts 和 serverPluginHmr.ts 里。 NO.5 结语 本文分析了 vite 的启动链路以及背后的部分原理,虽然在短时间内 vite 不会替代 webpack,但是能够看到社区中多了一种方案还是很兴奋的,这也是我写下这篇文章的原因。 vite 更新的实在太快了,佩服尤大的勤奋和开源精神,短短一个月就加入了诸如 css 预编译/react支持/通用 hmr 的支持,由于篇幅有限本文不再一一介绍这些新特性,这些新的特性等待读者朋友们自行去探讨了。 本文作者所属团队是淘系技术部创业部门-淘宝特价版,是独立于手淘的下一代超级APP,励志打造省钱省心高质量的极简购物心智,是产业带改革的C2M前台用户流量入口,去年DAU已经翻数倍,并仍在快速增长中,这里有数据化运营的增长体系,有快速组装玩法的沉浸式游戏化场景,有端内增强的极致体验,有多app联动小程序矩阵,紧贴业务共同前行。欢迎有志之士加入,简历投递 chongqiang.zcq@alibaba-inc.com全文转载自微信公众号 淘系前端团队 文章文章链接https://mp.weixin.qq.com/s/xdg1NIZYdNQgbunVUGh38w[图片]
2020-06-02 - [填坑手册]小程序PC版来了,如何做PC端的兼容?!
[图片] 微信宣布小程序将可以在PC端微信打开后,智库君就接到要求,需要兼容PC端小程序,一开始以为官方已经做了完美适配,不需要改什么,但当本人下载内测版开始测试的时候,才发现或许坑还挺多的~~~ 下面分享下本人“搬砖填坑”的全过程: (以下都是PC端小程序特有的问题,手机端正常) 先说下使用流程 [图片] 微信开发者工具菜单栏点击 设置->通用设置,在自动预览部分勾选“启动 PC 端自动预览”。 使用自动预览功能,点击 预览->自动预览->编译并预览,成功的话将在微信 PC 版上自动拉起小程序。 [图片] PC版打开后就横屏问题 [图片] [代码]{ "pages": [], "resizable":false, //在这里设置false,使得小程序默认手机尺寸 "pageOrientation":"portrait", //这里默认设置即可 ... } [代码] PC版微信默认打开小程序是ipad版,这样就会出现各种形变,布局错乱,这个可以在app.json进行配置,静止自动旋转,默认手机竖屏样子打开。 页面找不到问题 [图片] 这个问题本人也找了很久,一直很纳闷IDE工具和手机打开看都没什么问题,用PC打开小程序就出现页面找不到的情况,大致报错是: [代码]page[pages/XXX/XXX] not found.May be caused by :1. Forgot to add page route in app.json.2. Invoking Page() in async task. [代码] 一般这种情况以往是 app.json没配,或者页面里面缺少page(),但这次诡异的地方是只有“PC版小程序”报这个错!后来分析问题发现是:目前PC版小程序不能直接支持ES6,必须转换成ES5,同时由于一些语法转化不够完善,特别是ES7中的await 和 async 导致转化二次报错,这里就需要打开 “增强编译” 配置。 [图片] 打开有CSS报错 [图片] 因为目前PC版小程序估计内核的机制问题,还只支持低版本的选择器,如果你直接写小程序的标签,它无法识别,比如 [代码].popCont navigator{ //navigator 标签是小程序里的,PC端无法支持 width: 560rpx; height: 300rpx; } .popCont image{ //image 标签是小程序里的,PC端无法支持 width: 560rpx; height: 300rpx; } [代码] 但这些写法,其实在手机小程序和IDE工具里是完全正常的,PC版需要做兼容,改成class选择器。 布局结构混乱 如果遇到这种情况,会检查一下是否使用屏幕尺寸(rpx)来计算布局,PC 上屏幕尺寸比窗口尺寸大,应该使用窗口尺寸来计算。 小程序如何判断是 PC 平台? 通过 getSystemInfo 官方接口(platform 是 windows) 通过 UA(PC UA 包含 MiniProgramEnv/Windows) 微信官方PC版小程序内测地址: https://dldir1.qq.com/weixin/Windows/WeChat2.7.0_beta.exe 最新官方IDE调试工具 https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html 往期回顾: [打怪升级]小程序评论回复和发帖功能实战(二) [打怪升级]小程序评论回复和发贴组件实战(一) [填坑手册]小程序Canvas生成海报(一) [拆弹时刻]小程序Canvas生成海报(二) [填坑手册]小程序目录结构和component组件使用心得
2021-09-13 - 借助云开发实现婚礼小程序
原文链接(一) 原文链接(二) 最近时间充裕,索性对原来仓促做给自己结婚用的小程序来个整体优化,相关内容和功能请查看相关两篇文章介绍。 这次优化主要功能有: 1、访客分页; 2、留言分页及搜索; 3、留言审核机制; 4、云开发相关资源存储的转移; 5、插屏广告的使用。 欢迎大家体验: [图片]
2019-10-24