- miniprogram-ci 代码上传 如何设置体验版?
miniprogram-ci上传代码(ci.upload),能否提供一个配置参数,设置成为体验版? 目前只能通过后台去手动选择,这个功能后期能提供下吗? 感谢!!!
2021-08-05 - 利用 GitHub Actions 实现小程序的持续集成
众所周知,微信小程序的开发需要依赖微信官方提供的开发工具(IDE),在 IDE 中完成小程序的真机预览和上传,每次都要手动操作还是比较麻烦的。对于托管在 GitHub 的项目,我们可以利用 GitHub 提供的 GitHub Actions 这一能力,实现小程序的持续集成(CI/CD)。 我们主要利用了 GitHub Action for WeChat MiniProgram 这个 Action,实现了以下自动化流程: 当 Commit 推送到 [代码]master[代码] 分支时,触发小程序预览并显示预览二维码 当有人创建了 Pull Request 时,触发小程序预览并显示预览二维码 当创建版本 Tag 并推送到 GitHub 时,触发小程序的构建和上传 更多有关小程序 GitHub Actions 的玩法,也欢迎探索和交流~ 准备工作 在使用 GitHub Action for WeChat MiniProgram 前,需要先准备好小程序代码上传密钥,这是用于小程序开发者身份鉴权的凭证。获取步骤如下: 登录微信小程序管理后台 https://mp.weixin.qq.com 登录后在左侧菜单依次找到「开发」—「开发管理」,在右侧界面找到「开发设置」Tab。向下滚动找到「小程序代码上传」模块,点击「生成」按钮。管理员使用微信扫码后,获得密钥文件并下载 关闭「IP 白名单」功能。因为执行 GitHub Actions 的机器 IP 范围我们无法获得,这里需要关闭此功能 [图片] 打开 GitHub 小程序项目的「Settings」,在左侧菜单依次找到「Secrets」—「Actions」,点击右上角按钮「New repository secret」,「Name」输入 [代码]PRIVATE_KEY[代码],「Secret」粘贴第 2 步中密钥文件的内容,点击「Add secret」保存 [图片] 至此,准备工作已完成,我们已经给项目配置了 GitHub Actions 所需的密钥。 在项目中使用 示例项目:crazyurus/recruit-miniprogram 首先在项目根目录下创建 [代码].github/workflows[代码] 文件夹,再根据我们的需求编写 YAML 文件 实现小程序预览 在 [代码]workflows[代码] 目录下新建 [代码]preview.yaml[代码]。我们需要当 Commit 推送到 [代码]master[代码] 分支时触发小程序预览。因此在 [代码]preview.yaml[代码] 中添加: [代码]name: 提交 Commit 预览小程序 on: push: branches: - master [代码] 命中该条件时,这个 YAML 就会被 GitHub Actions 执行,我们可以在项目的「Actions」中看到执行进度和结果: [图片] 接下来需要给 [代码]preview.yaml[代码] 定义具体的行为,在其中添加: [代码]jobs: preview: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - id: preview name: Compile uses: crazyurus/miniprogram-action@1.0.0 with: action_type: preview env: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} - name: QR Code uses: peter-evans/commit-comment@v2 with: body: | Copy the following content to the address bar of the browser to open the preview QR code ``` ${{ steps.preview.outputs.preview_qrcode }} ``` [代码] 其中,[代码]jobs.preview[代码] 是 Job 的名称,[代码]runs-on[代码] 是 Job 运行环境,[代码]steps[代码] 是运行步骤。这个 Job 主要有 3 步: Checkout 项目代码 运行 [代码]crazyurus/miniprogram-action[代码] 这个 Action(即前面提到的 GitHub Action for WeChat MiniProgram),[代码]id[代码] 是 Step 的唯一标识(用于后面取 [代码]outputs[代码]),[代码]with[代码] 传递参数 [代码]action_type[代码],[代码]env[代码] 传递密钥内容。这个 Action 会对小程序进行编译,并生成预览二维码,二维码图片的 Base64 内容会通过 [代码]outputs.preview_qrcode[代码] 返回 运行 [代码]peter-evans/commit-comment[代码] 这个 Action,用来给 Commit 增加评论。我们把上一步获取到的二维码图片 Base64 评论到这个 Commit 上,在 Commit 左边即可看到一个评论图标: [图片] 点击后即可看到这条评论和二维码内容。由于 GitHub Markdown 的限制,我们暂时无法使用图片格式语法展示 Base64 图片,需要将内容复制后粘贴到浏览器地址栏即可预览。 [图片] 如果我们要实现有人创建了 Pull Request 时,触发小程序预览并显示预览二维码。只需要: 新建 [代码]pull-request.yaml[代码] 更换 [代码]on[代码] 中的触发条件,改为 [代码]pull_request[代码] 修改第 3 步中的 Action,替换为 [代码]dannyskoog/pull-request-comment[代码],用来给 Pull Request 增加评论 完整配置如下: [代码]name: 创建 Pull Request 预览小程序 on: pull_request: branches: master types: [opened, synchronize, reopened] jobs: preview: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - id: preview name: Compile uses: crazyurus/miniprogram-action@1.0.0 with: action_type: preview env: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} - name: QR Code uses: dannyskoog/pull-request-comment@v1 with: message: | Copy the following content to the address bar of the browser to open the preview QR code ``` ${{ steps.preview.outputs.preview_qrcode }} ``` [代码] 如果需要预览时指定场景值、页面路径、页面参数等,GitHub Action for WeChat MiniProgram 也同样支持,在 [代码]with[代码] 增加参数即可。支持的参数如下: 参数 是否必填 描述 默认值 action_type [代码]false[代码] Action 类型, [代码]preview[代码] 或 [代码]upload[代码] [代码]upload[代码] project_path [代码]false[代码] 项目路径,需包含 [代码]project.config.json[代码] 文件 [代码].[代码] page_path [代码]false[代码] 预览的页面路径,例如 [代码]pages/index/index[代码] [代码]app.json[代码] 中第一个页面 page_query [代码]false[代码] 预览的页面参数,例如 [代码]a=b&c=1[代码] scene [代码]false[代码] 场景值,详细见 微信官方文档 [代码]1011[代码] version [代码]false[代码] 上传的版本号 [代码]1.0.0[代码] description [代码]false[代码] 上传的版本描述 需要注意的是,如果你的小程序代码不在项目的根目录下,则需要配置 [代码]project_path[代码] 参数指定小程序的目录([代码]project.config.json[代码] 文件所在的目录)。例如使用了 [代码]Taro[代码] 等框架,需要由框架编译后再运行,则需要指定 [代码]project_path[代码] 为编译后的目录 [代码]dist[代码],并且在 YAML 文件中的 Compile Job 前需要增加框架依赖安装和编译的步骤。YAML 如下: [代码]jobs: preview: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Install dependencies run: npm ci - name: Taro compile run: npm run build:weapp - name: Compile uses: crazyurus/miniprogram-action@1.0.0 with: action_type: preview project_path: dist env: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} [代码] 实现小程序上传 我们使用 Git Tag 触发小程序的发布,例如执行以下命令: [代码]$ git tag 2.3.4 $ git push --tags [代码] 此时会在最新的 Commit 创建一个 [代码]2.3.4[代码] 的 Tag。我们基于这个 Commit 发布小程序的 [代码]2.3.4[代码] 版本,因此需要 GitHub Actions 触发小程序上传。 在 [代码]workflows[代码] 目录下新建 [代码]upload.yaml[代码],并添加: [代码]name: 上传小程序 on: push: tags: - "*.*.*" jobs: upload: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Upload uses: crazyurus/miniprogram-action@1.0.0 with: action_type: upload version: ${{ github.ref_name }} env: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} [代码] 其中,[代码]github.ref_name[代码] 这里代表标签名称,我们将标签名称作为版本号传递给 Action,再由 Action 上传到微信服务端。上传后效果如下: [图片] 接下来在微信小程序管理后台提交审核即可。 给项目增加 Badge 至此小程序项目就成功接入了 GitHub Actions,我们还可以给项目 [代码]README.md[代码] 增加徽章(Badge),用于显示项目的持续集成状态。将以下图片添加到 [代码]README.md[代码] 中: [代码] [代码] 其中,[代码]crazyurus/recruit-miniprogram[代码] 需要替换为你的项目名称,[代码]preview.yaml[代码] 需要替换为你的 YAML 文件名。 效果如下: [图片] 更多方式可参见 GitHub 官方文档 Adding a workflow status badge 实现原理 Action 实现:crazyurus/miniprogram-action GitHub Action for WeChat MiniProgram 基于微信官方提供的 [代码]miniprogram-ci[代码] 实现了小程序的预览和上传。主要逻辑如下: 输入参数读取,使用 [代码]@actions/core[代码] 提供的 [代码]getInput[代码] 方法 使用环境变量 [代码]process.env.PRIVATE_KEY[代码] 获取密钥,[代码]process.env.GITHUB_WORKSPACE[代码] 获取小程序项目路径 读取小程序 [代码]project.config.json[代码] 文件,获取 [代码]AppID[代码]、[代码]compileType[代码]、[代码]miniprogramRoot[代码] 以及 [代码]es6[代码]、[代码]minified[代码] 等编译参数,转换后传递给 [代码]miniprogram-ci[代码] 若小程序项目存在 [代码]package.json[代码],则在预览或上传前调用 [代码]ci.packNpm[代码] 对小程序中的 NPM 依赖进行构建 根据参数 [代码]action_type[代码] 决定调用 [代码]ci.preview[代码] 还是 [代码]ci.upload[代码] 小程序预览执行完成,通过 [代码]@actions/core[代码] 提供的 [代码]setOutput[代码] 方法将二维码输出 总结 我们使用 GitHub Action for WeChat MiniProgram 实现了小程序项目的持续集成,在 Commit 或 Pull Request 时触发预览并生成二维码,在添加 Tag 时上传小程序到管理后台。 总而言之,GitHub Actions 的功能十分强大,接下来你还可以探索: 给项目增加更多的自动化流程,例如 Pull Request 被合并后也触发上传。推荐阅读阮一峰的 GitHub Actions 入门教程 了解更多用法 给项目引入更多的 GitHub Actions 让流程更强大,例如将生成的二维码通过飞书机器人发送给自己或群、基于 [代码]miniprogram-automator[代码] 实现小程序的自动化测试等。更多的 GitHub Actions 可以到 GitHub Actions 市场 获取 当现有的 GitHub Actions 无法满足项目的需求时,可以参考 GitHub Actions 开发文档 和 GitHub Action for WeChat MiniProgram 的代码自己实现一个
2022-12-07 - skyline 是不是不支持 IntersectionObserver API?
如题 👆 附: 想实现列表项 懒加载+进入可视区过渡动画 的效果, 如果不能使用 IntersectionObserver API , 有什么比较好的其他方案吗? 计算 scrollTop 的方法有点担心性能问题
2024-05-15 - [基础库3.7.4] skyline 性能好像有点问题
skyline模式下的scroll-view,在滚动时有明显卡顿skyline的相册,打开大图返回后,不停的request相同的图片,直到内存爆掉[图片]
01-01 - 你不知道的小程序系列之生命周期执行顺序
再次开始之前先问几个问题: 你是否知道[代码]Page[代码]生命周期 与 [代码]pagelifetimes[代码] 生命周期执行顺序? 你是否知道[代码]behaviors[代码]中的生命周期与组件生命周期执行顺序? 你是否知道[代码]Page[代码]生命周期 与 组件[代码]pagelifetimes[代码]生命周期执行顺序? 要回答上面的问题,首先我们看看小程序生命周期有哪些: App onLaunch onShow onHide Page onLoad onShow onReady onHide onUnload Component created attached ready moved detached 想一下加载一个页面(包含组件)的加载顺序,按照直觉小程序加载顺序应该是这样的加载顺序(以下列子中[代码]Component[代码]都是同步组件): App(onLaunch) -> Page(onLoad) -> Component(created) 但其实并不然,小程序的加载顺序是这样的: 首先执行 [代码]App.onLaunch[代码] -> [代码]App.onShow[代码] 其次执行 [代码]Component.created[代码] -> [代码]Component.attached[代码] 再执行 [代码]Page.onLoad[代码] -> [代码]Page.onShow[代码] 最后 执行 [代码]Component.ready[代码] -> [代码]Page.onReady[代码] 其实也不难理解微信这么设计背后的逻辑,我们先看下官方的的生命周期: [图片] 可以看到,在页面[代码]onLoad[代码]之前会有页面[代码]create[代码]阶段,这其中就包含了组件的初始化,等组件初始化完成之后,才会执行页面的[代码]onLoad[代码], 之后页面[代码]ready[代码]事件也是在组件[代码]ready[代码]之后才触发的。 下面我们来看看 [代码]Behavior[代码], [代码]Behavior[代码] 与 [代码]Vue[代码]中的 [代码]mixin[代码] 类似,猜想下其中的执行顺序: Behavior.created => Component.created 测试下来和预期相符,其实在[代码]Vue[代码]的文档中有一段这样的描述: 另外,混入对象的钩子将在组件自身钩子之前调用。 这样的设计和主流设计保持一致。接下来我们看看 [代码]pageLifetimes[代码],有[代码]show[代码]和[代码]hide[代码]生命周期对应页面的展示与隐藏,预期的执行顺序: pageLifetime.show => Page.onShow 测试下来也和预期相符,那么我们可以推断出如下的结论: 当页面中包含组件时,组件的生命周期(包括pageLifetimes)总是优先于页面,[代码]Behaviors[代码]生命周期优先于组件的生命周期。但其实有个例外:页面退出堆栈,当页面[代码]unload[代码]时会执行如下顺序: Page.onUnload => Component.detached 看了以上的分析你应该知道了答案,最后做个总结(demo): [图片] 最后的最后布置个作业 异步组件(异步渲染的组件,通常是通过if条件判断是否渲染)的生命周期执行顺序是怎样的,pagelifetimes会不会执行?
2020-01-10 - Webview、Skyline 混用切换耗时吗?
在学习 Skyline 的过程中,许多开发者会有一个疑问:是否可以将小程序的部分页面迁移到 Skyline? 对 Skyline 感兴趣但还没有完全决定是否要使用的开发者来说,可能只想先尝试一下 Skyline 的功能。 实际上,Skyline 支持最小粒度的页面配置,意味着我们可以为某个页面单独开启 Skyline,而不必将整个小程序迁移到 Skyline 上。 开发者可以更加灵活地使用 Skyline,并逐步将小程序迁移到 Skyline 上,从而获得更好的性能和用户体验。 我们知道 Webview 和 Skyline 是两个渲染引擎,对于 Webview 和 Skyline 混用,大家又有新的疑问:当进行页面切换的时,混用是否会增加耗时? 这里需要分三种情况: 1、Skyline -> Webview:这种情况取决于 app.json 里配置的全局 renderer,即小程序设置的默认渲染引擎 如果全局 renderer 是 Skyline,那么 Webview 不会被预加载,此时 Skyline 跳转 Webview 耗时会增加,开发者需要手动调用 wx.preloadWebview 做预加载。如果全局 renderer 是 Webview,由于 Webview 默认会预加载,所以 Skyline -> Webview 和 Webview -> Webview 耗时一样,不会增加耗时。2、Webview -> Skyline:Skyline 默认都不会被预加载,开发者需要手动调用 wx.preloadSkylineView 做预加载。 3、Skyline -> Skyline:速度变快,因为多个页面复用同一个 Skyline 实例。 根据上述三种情况的分析,为了保证混用渲染引擎的页面切换耗时最短,我们需要在以下时机进行预加载。 wx.preloadWebview 当 Skyline 页面跳转到 Webview 页面时并且全局 renderer 是 Skyline 由于 Skyline 不影响渲染线程,所以预加载 Webview 的时机只需要在主要逻辑完成后即可 // Skyline page.js Page({ onShow() { // 等待执行完主要逻辑后进行预加载 wx.preloadWebview() } }) wx.preloadSkylineView 当 Webview 页面跳转 Skyline 页面时,因为 Skyline 默认不预加载,所以我们需要手动预加载。 建议大家在 Skyline 页面的 onShow 生命周期里延迟一段时间后调用,这样可以保证在 Skyline 页面被返回时也能够重新预加载。 注意:预加载会影响当前页面的渲染,建议异步延迟去执行预加载操作 // Webview page.js Page({ onShow() { // 延迟 200ms 预加载 Skyline // 建议这个延迟时机在页面渲染完成之后 setTimeout(() => { wx.preloadSkylineView() }, 200) } }) 做好预加载是提高 Webview 和 Skyline 混用体验的有效方式,需要根据实际情况进行调整和优化,以达到最佳的预加载效果。
2024-03-07 - Skyline引擎渲染情况下,自定义tabBar显示异常?
[图片] windows rc版 [图片]
2023-10-19 - Skyline模式下scroll-view组件scroll方法不执行?
<scroll-view type="list" scroll-y bindscrolltolower="{{h}}" bindscroll="{{i}}" > 如题,Skyline模式下, bindscrolltolower,会正常执行, 但是 bindscroll 一直不执行,是咋回事?
2023-11-03 - 基于 GitLab CI 的 小程序自动化部署
小程序发布了 miniprogram-ci 已经一段时间了。 不建议采用通过命令行来触发上传动作, 通过 miniprogram-ci 更为方便 小程序中的 体验版 和 开发板 开发版: 开发者通过 [代码]微信开发者工具[代码] 上传代码的版本。不同开发者上传后,对应着不同的 [代码]开发版[代码],[代码]开发版[代码] 不能直接被访问到。通过 微信开发者工具 上的 [代码]预览[代码] 功能,生成二维码预览(二维码有效时间较短 20~30 min)。 体验版: 在 微信公众平台 后台,指定某个 [代码]开发版[代码] 指定为 [代码]体验版[代码]。[代码]体验版[代码] 长期有效,只能指定一个。 所以如果我们可以将 [代码]体验版[代码] 始终指定某一个开发者 的 [代码]开发版[代码] 上,然后每次通过这个开发者上传小程序代码。这样就只需要访问 [代码]体验版[代码] 即可。 小程序自动部署 常用的 CI/CD 有很多,由于小程序上传代码只能通过 [代码]微信开发者工具[代码]。 而 [代码]微信开发者工具[代码] 只能支持 Windows/Mac 而不支持 Linux,所以最后决定采用 GitLab CI。 因为 [代码]GitLab CI[代码] 中的 GitLab Runner 可以执行 GitLab CI 中的任务。我们使用 [代码]GitLab Runner[代码] 在自己电脑上注册个 Runner 来执行上传小程序代码的任务。 GitLab CI 配置流程 下载 GitLab Runner Install,注册一个 Runner 运行 gitlab-runner register 根据命令提示操作 [代码]# 输入 GitLab 服务地址 Please enter the gitlab-ci coordinator URL (e.g. <https://gitlab.com/>): https://gitlab.com # token 在对应项目中的 Setting => CI/CD => Runners 中 如下 图1 Please enter the gitlab-ci token for this runner: hVGe8TvU5KL-ZQqsv48z # 输入描述信息 Please enter the gitlab-ci description for this runner: this is description for the runner # 注意: 这里输入的 tags 会在后面 .gitlab-ci.yml 配置的时候用到。 Please enter the gitlab-ci tags for this runner (comma separated): test # 是否作执行没有指定 tags 的 builds Whether to run untagged builds [true/false]: false # 是将 Runner 锁定至当前项目 Whether to lock the Runner to current project [true/false]: true # 注册成功 Registering runner... succeeded runner=hVGe8TvU # 输入 Runner 执行环境 Please enter the executor: docker, docker-ssh, shell, parallels, ssh, virtualbox, docker+machine, docker-ssh+machine, kubernetes: shell Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! [代码] [图片] 这个时候就注册号好了。我们去到项目的 Setting => CI/CD => Runners 下会发这个 Runner [图片] 注意: 此时这个 Runner 前面是个 感叹号,我们运行 [代码]gitlab-runner start [代码] 配置 .gitlab.yml 文件,在项目根目录下创建 .gitlab-ci.yml 文件 [代码]image: node:9 # 制定任务执行时的 docker 镜像 stages: # 定义阶段用于执行任务(定义了 build 和 deploy 两个阶段) - build - deploy build: # 定义 build 任务 stage: build # 该任务属于 build 阶段 script: # 执行下面脚本 - yarn install - yarn build artifacts: # 制定任务成功后保存的文件(可以在 GitLab 上下载) name: $CI_JOB_NAME untracked: false # 是否添加所有 git untracked 的文件 paths: # 需要添加文件的位置 - dist tags: # tags 制定运行在哪个 Runner 上 - gitlab-org deploy: # 定义 deploy 任务 stage: deploy # 该任务属于 deploy 阶段 dependencies: # 该任务依赖 build 阶段的所有任务 - build variables: # 定义变量 VERSION: 1.0.0 DESCRIPTION: 描述内容 CLI: C:\Program Files (x86)\Tencent\微信web开发者工具\cli.bat script: # 执行下面脚本 # 下面是 小程序 上传代码的脚本,以 Windows 为例 # https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html#4-%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%B8%8A%E4%BC%A0%E4%BB%A3%E7%A0%81 - %CLI% -u %VERSION%@%cd% --upload-desc %DESCRIPTION% tags: # 这里需要在我们刚注册的 Runner 运行,和注册时的 tags 匹配 - test [代码] 注意:小程序上传代码通过开发工具的 命令行调用 GitLab Pipelines 当我们提交将代码 push GitLab 后, 当 GitLab 检测到 [代码].gitlab-ci.yml[代码] 文件后, 会自动触发 Pipeline 任务,如图: [图片] 我们在 GitLab 上看 这个 Pipeline 下的 Jobs 的运行状态 当 [代码]deploy[代码] Job 成功后,就表示我们自动化的上传代码流程成功。 修改开发者工具源代码。 由于在执行cli的时候,错误并不会抛出。(比如开发者工具没有登录,但是 cli 执行 upload 命令的时候错误没有抛出,导致就pipeline 显示成功) 所以我们需要在修改开发者工具目录下的 cli.bat 文件,添加一行内容 [代码]@echo off set CALLING_DIR=%CD% cd /d %~dp0 .\node.exe .\cli.js %* + if %errorlevel% neq 0 exit /b %errorlevel% ... [代码]
2021-05-28 - 小程序测试环境和生产环境如何区分?
小程序测试环境和生产环境如何区分
2021-01-05 - css 超出两行文本省略号在某些iphone机型上展示效果为一行省略号显示
如代码片段中设置的样式,文本设置的是超出两行省略号显示,标签中包裹了显示为‘自营’的另外一个标签,在开发工具和安卓机测试都可以正常显示,但在iphone机型上有些展示为一行省略号 下图为代码片段在开发工具中的正常显示效果: [图片] 下图是iphone13的不正常显示效果,本应该显示两行省略号,结果显示为一行省略号,机型:iphone13 iOS系统:17.2.1 微信版本:8.0.42 [图片] 下图是华为mate60 pro的显示效果, 两行省略号显示正常,微信版本:8.0.44 [图片] 用多台iphone真机测试结果如下: iphone12pro ios16.0 微信:8.0.45 显示正常 Iphone8plus iOS13.3.1 微信:8.0.45 显示正常 iphone14pm ios17.2 微信:8.0.45 显示不正常 iphone13 ios17.2.1 微信:8.0.42 显示不正常 测试发现: 如果外层标签中不包裹‘自营’的标签,用iPhone13真机测试显示也可以正常 [图片] 望官方尽快回复
2024-01-05 - "renderer": "skyline",模式下如何开启多行省略?
view{ overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; } [图片]
2024-08-13 - 微信小程序中安全区域计算和适配
前言 自从iphoneX问世之后,因为iphoneX、iphoneXR和后续全面屏手机设备,因为物理Home键被底部小黑条代替了,这时候很多前端小伙伴在开发的过程都会遇到 “全面屏”和“非全面屏”的兼容性问题,普遍问题就是底部按钮或者选项卡与底部黑线重叠 解释 根据官方解释: 安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)的影响。 具体区域如图展示 [图片] 适配方案 当前有效的解决方式有几种 使用已知底部小黑条高度34px/68rpx来适配 使用苹果官方推出的css函数env()、constant()适配 使用微信官方API,getSystemInfo()中的safeArea对象进行适配 使用已知底部小黑条高度34px/68rpx来适配 这种方式是根据实践得出,通过物理方式测出iPhone底部的小黑条(Home Indicator)高度是34px,实际在开发者工具选中真机获取到高度也是34px,所以直接根据该值,设置margin-bottom、padding-bottom、height也能实现。同时这样做要有一个前提,需要判断当前机型是需要适配安全区域的机型。 但是这种方案相对来说是不推荐使用的。比较是一个比较古老原始的方案 使用苹果官方推出的css函数env()、constant()适配 这种方案是苹果官方推荐使用env(),constant()来适配,开发者不需要管数值具体是多少。 env和constant是IOS11新增特性,有4个预定义变量: safe-area-inset-left:安全区域距离左边边界的距离 safe-area-inset-right:安全区域距离右边边界的距离 safe-area-inset-top:安全区域距离顶部边界的距离 safe-area-inset-bottom :安全距离底部边界的距离 具体用法如下: Tips: constant和env不能调换位置 [代码] padding-bottom: constant(safe-area-inset-bottom); /*兼容 IOS<11.2*/ padding-bottom: env(safe-area-inset-bottom); /*兼容 IOS>11.2*/ [代码] 其实利用这个能解决大部分的适配场景了,但是有时候开发需要自定义头部信息,这时候就没办法使用css来解决了 使用微信官方API,getSystemInfo()中的safeArea对象进行适配 通过 wx.getSystemInfo获取到各种安全区域信息,解析出具体的设备类型,通过设备类型做宽高自适应,话不多说,直接上代码 代码实现 [代码] const res = wx.getSystemInfoSync() const result = { ...res, bottomSafeHeight: 0, isIphoneX: false, isMi: false, isIphone: false, isIpad: false, isIOS: false, isHeightPhone: false, } const modelmes = result.model const system = result.system // 判断设备型号 if (modelmes.search('iPhone X') != -1 || modelmes.search('iPhone 11') != -1) { result.isIphoneX = true; } if (modelmes.search('MI') != -1) { result.isMi = true; } if (modelmes.search('iPhone') != -1) { result.isIphone = true; } if (modelmes.search('iPad') > -1) { result.isIpad = true; } let screenWidth = result.screenWidth let screenHeight = result.screenHeight // 宽高比自适应 screenWidth = Math.min(screenWidth, screenHeight) screenHeight = Math.max(screenWidth, screenHeight) const ipadDiff = Math.abs(screenHeight / screenWidth - 1.33333) if (ipadDiff < 0.01) { result.isIpad = true } if (result.isIphone || system.indexOf('iOS') > -1) { result.isIOS = true } const myCanvasWidth = (640 / 375) * result.screenWidth const myCanvasHeight = (1000 / 667) * result.screenHeight const scale = myCanvasWidth / myCanvasHeight if (scale < 0.64) { result.isHeightPhone = true } result.navHeight = result.statusBarHeight + 46 result.pageWidth = result.windowWidth result.pageHeight = result.windowHeight - result.navHeight if (!result.isIOS) { result.bottomSafeHeight = 0 } const capsuleInfo = wx.getMenuButtonBoundingClientRect() // 胶囊热区 = 胶囊和状态栏之间的留白 * 2 (保持胶囊和状态栏上下留白一致) * 2(设计上为了更好看) + 胶囊高度 const navbarHeight = (capsuleInfo.top - result.statusBarHeight) * 4 + capsuleInfo.height // 写入胶囊数据 result.capsuleInfo = capsuleInfo; // 安全区域 const safeArea = result.safeArea // 可视区域高度 - 适配横竖屏场景 const screenHeight = Math.max(result.screenHeight, result.screenWidth) const height = Math.max(safeArea.height, safeArea.width) // 状态栏高度 const statusBarHeight = result.statusBarHeight // 获取底部安全区域高度(全面屏手机) if (safeArea && height && screenHeight) { result.bottomSafeHeight = screenHeight - height - statusBarHeight if (result.bottomSafeHeight < 0) { result.bottomSafeHeight = 0 } } // 设置header高度 result.headerHeight = statusBarHeight + navbarHeight // 导航栏高度 result.navbarHeight = navbarHeight [代码]
2022-11-04 - skyline如何解决z-index层级问题?设置的层级无效,切换会webview模式是正常
[图片]
2024-05-07 - skyline 渲染引擎 iconfont怎么使用?
<view class="iconfont icon-gongzuotai"></view> 在IDE里面90%的时候不生效,但是偶尔能生效,真机上不生效。有的帖子说是放到 data 里面然后通过变量访问,试过也不行。哪位大佬能帮忙解答一下 skyline 引擎 iconfont 应该怎么使用?
2024-04-16 - 小程序开启skyline后,无法使用插件组件?
app.json配置skyline后,无法使用插件组件, [图片] 删除skyline配置后就正常了 [图片] 代码片段:https://developers.weixin.qq.com/s/mO6yxpmh7iSa
2024-07-03 - 基于Proxy的小程序状态管理
作者:wwayne 原文:基于Proxy的小程序状态管理 Fundebug经授权转载,版权归原作者所有。 微信小程序的市场在进一步的扩大,而背后的技术社区仍在摸索着最好的实践方案。我在帮助Nike,沃尔玛以及一些创业公司开发小程序后,依旧认为使用小程序原生框架是一个更高效,稳定的选择,而使用原生框架唯独缺少一个好的状态管理库,如果不引入状态管理则会让我们在模块化,项目结构以及单元测试上都有些捉襟见肘。 目前相对比较稳健的做法是针对redux或者mobx做一个adaptor应用到小程序中,但这样需要自己想办法打包引入外部库,还要想怎么去写这个adaptor,总显得有些麻烦。于是我迸发出一个想法去写一个专用于小程序的状态管理库,它使用起来足够简单并且可以通过小程序自己的npm机制安装。 目前我已经用这个开源库开发了两个电商小程序,在提高我开发效率的同时亦保证了程序的性能,所以接下来我想谈谈这背后的理念以启发更多开发者尝试新的解决方案。 基于Proxy的状态管理实现 Proxy在小程序中已经得到了足够好的支持,目前并没有发现在任何iPhone或者Android上不能使用Proxy的情况。而基于Proxy的状态管理其实也就是订阅监听的模式,一方面监听数据的变化,另一方面将这些变化传达给订阅的小程序页面。 举一个比较常见的例子,当一个用户从自己的主页进入用户编辑页面,然后更改了自己的用户名点击保存后,用户主页和用户编辑页上的用户名这时候都应该被更新。这背后的程序逻辑则是:更新这个行为将触发Proxy去通知状态管理库,然后状态管理库负责检查此时还在页面栈中的所有页面,更新订阅了用户名这个数据的页面,如下图: [图片] Part1: 监听数据变化 监听数据变化其实就是监听各个Store的属性变化,实现上就是在各个Store前面加了一层Proxy,用更直观的图片来表示就是这样: [图片] 当一个Store被观察以后,它的属性就都变成了Proxy实例,当这个属性值是Object或者Array的时候,它内部的值也会被包装成Proxy实例,这样无论多深层的数据变动都能被监听到。 而在Proxy的后面,Store的属性其实是被另一套数据(紫色部分)所维护,这套数据不负责监听,它就是纯数据,针对属性的任何变动最后都会应用到这套数据上来,它的作用是维护和返回最新的数据。 实现细节: https://github.com/wwayne/min… Part2: 页面数据绑定 因为小程序每个页面的js都是向Page中传递一个对象,这就让我们有机会包装这个对象,从而实现: 进入页面后,将页面保存在页面栈中 将来自状态管理库的数据映射到这个页面的data上来 页面退出时,将页面从页面栈中移除 实现细节: https://github.com/wwayne/min… Part3: 页面订阅更新 当数据被监听到变化后,我们需要依次做两件事,先是找到所有存储在页面栈里的页面,然后根据各个页面订阅的数据来检查变化,如果有变化就通知这些页面,从而让它们去触发setData更新页面。 实现细节:https://github.com/wwayne/min… 使用状态管理的例子 有了状态管理库,现在我们就来实现一开始举例的更新用户信息的操作,我们的文件路径如下: [代码]stores/ user.js pages/ userEdit/ index.js index.wxml [代码] 1. 首先我们创建一个Store保存用户的信息,并且监听它的变化: [代码]// stores/user.js import { observe } from 'minii' Class UserStore { constructor () { this.name = 'bob' } changeName (name) { this.name = name } } export default observe(new UserStore(), 'user') [代码] 2. 接着在我们的小程序页面订阅Store的信息 [代码]// pages/userEdit/index.js import { mapToData } from 'minii' import userStore from '../../stores/user' const connect = mapToData(state => (({ myName: state.user.name })) Page(connect({ updateNameToJames () { userStore. changeName('james') } })) [代码] 3. 完成,现在可以在页面中使用和更新数据了 [代码]// pages/userEdit/index.wxml <text>{{ myName }}</text> <button bindtap="updateNameToJames">update name to James</button> [代码] 最后 小程序因为有体积的限制,所以我希望在代码量上也尽量做到轻量和便捷,所以目前这个状态管理库并没有太多很复杂的功能,在小程序打包后所占用的体积也不到1kb,颇有点够用就好的意思。 我也已经用它开发了两款小程序,在经历了一段时间的用户使用后,我也更有信心说这个方案在小程序中是可行的。如果你有任何想法和建议,都欢迎告诉我。 项目Github: https://github.com/wwayne/minii 关于作者 Hi, 我是wwayne,是一名居住在上海的独立软件工程师,我正在开发我的新产品 talk-to-kim, 你可以在Github 或者专栏 一个人写代码找到我
2019-06-20 - 如何在自定义组件中使用virtualHost后通过selectComponent获取组件实例?
component-a.js [图片] component-b.wxml [图片] component-b.js [图片] 在组件A中设置virtualHost: true属性,将节点虚拟化 在组件B中使用组件A,并设置ID 并且在JS中通过selectComponent获取组件实例,此时获取不到A组件实例 期望 能够获取到组件A的实例。
2020-07-06 - 小程序app.onLaunch与page.onLoad异步问题的最佳实践
场景: 在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。 直接上终极解决方案,公司内部已接入两年很稳定: 1.可完美解决异步问题 2.不污染原生生命周期,与onLoad等钩子共存 3.使用方便 4.可灵活定制异步钩子 5.采用监听模式实现,接入无需修改以前相关逻辑 6.支持各种小程序和vue架构 。。。 //为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档 //app.js //globalData提出来声明 let globalData = { // 是否已拿到token token: '', // 用户信息 userInfo: { userId: '', head: '' } } //注册自定义钩子 import CustomHook from 'spa-custom-hooks'; CustomHook.install({ 'Login':{ name:'Login', watchKey: 'token', onUpdate(token){ //有token则触发此钩子 return !!token; } }, 'User':{ name:'User', watchKey: 'userInfo', onUpdate(user){ //获取到userinfo里的userId则触发此钩子 return !!user.userId; } } }, globalData) // 正常走初始化逻辑 App({ globalData, onLaunch() { //发起异步登录拿token login((token)=>{ this.globalData.token = token //使用token拿用户信息 getUser((user)=>{ this.globalData.user = user }) }) } }) //关键点来了 //Page.js,业务页面使用 Page({ onLoadLogin() { //拿到token啦,可以使用token发起请求了 const token = getApp().globalData.token }, onLoadUser() { //拿到用户信息啦 const userInfo = getApp().globalData.userInfo }, onReadyUser() { //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦 const userInfo = getApp().globalData.userInfo // 获取canvas上下文 const ctx = getCanvasContext2d() ctx.drawImage(userInfo.head,0,0,100,100) }, onShowUser() { //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑 const userInfo = getApp().globalData.userInfo switch(userInfo.sex){ case 0: // 走女生逻辑 break case 1: // 走男生逻辑 break } } }) 具体文档和Demo见↓ Github:https://github.com/1977474741/spa-custom-hooks 祝大家用的愉快,记得star哦
2023-04-23 - 小程序中如何加载使用第三方字体
简述:使用开启CORS的woff字体格式(或ttf)的https资源地址,使用[代码]wx.loadFontFace[代码] API(css原生[代码]@font-face[代码]语法)加载使用。 ⚠️ 注意:字体商用需授权许可,目前免费可商用的大部分字体,可见参考索引5。以下示例使用的是开源字体:得意黑和钉钉进步体。 1. wx.loadFontFace使用wx.loadFontFace 加载字体,配合wxss和wxml使用,参见文档 & 官方示例 // -- wxss -- // .DingTalk-JinBuTi { // font-family: 'DingTalk-JinBuTi'; // } wx.loadFontFace({ family: 'DingTalk-JinBuTi', source: 'url("https://xxx/font/DingTalk-JinBuTi.woff")', //此处需替换为真实字体地址 success(res) { console.log(res.status) }, fail: function (res) { console.log(res.status) }, complete: function (res) { console.log(res.status) } }); 注意: 格式支持常见的基本所有类型(ttf、woff、woff2(web主流)、otf、sfnt),建议格式为 TTF 和 WOFF(推荐),WOFF2 在低版本的 iOS 上会不兼容。字体链接仅支持https链接字体链接访问需满足浏览器同源策略,字体文件资源设置CORS的[代码]Access-Control-Allow-Origin[代码]为小程序域名:[代码]servicewechat.com[代码]或者*才可以。经过测试,ios和小米手机对于未设置CORS的字体文件仍然可以正常加载,荣耀和vivo无法正常加载,需要设置正确的CORS即可正常加载在加载成功之后,会自动刷新字体显示不需要设置downloadFile合法域名及业务域名。扩展:一次加载,全局使用如下在app.js中加载添加全局参数(global: true),即可在任意页面中的font-family中使用该字体。 onLaunch() { wx.loadFontFace({ family: 'DingTalk-JinBuTi', global: true, source: 'url("https://xxx/font/DingTalk-JinBuTi.woff")', //此处需替换为真实字体地址 success(res) { console.log(res.status) }, fail: function (res) { console.log(res.status) }, complete: function (res) { console.log(res.status) } }); }, 2. @font-face同css加载字体的规则,见MDN文档, 注意:字体链接访问需满足浏览器同源策略。支持常见格式。 @font-face { font-family: "SmileySans-Oblique"; src: url("https://xxx/font/SmileySans-Oblique.woff"); //此处需替换为真实字体地址 } .SmileySans-Oblique { font-family: 'SmileySans-Oblique'; } 扩展:全局使用在app.wxss中引入字体即可全局使用。 @font-face { font-family: "SmileySans-Oblique"; src: url("https://xxx/font/SmileySans-Oblique.woff"); //此处需替换为真实字体地址 } .SmileySans-Oblique { font-family: 'SmileySans-Oblique'; } 3. face-font base64 因源码过大致小程序增加包体积,暂不考虑。英文字体可以考虑。参见此。 扩展:在canvas中使用小程序canvas渲染时需要字体加载完成,css方式字体加载完成不容易监听,小程序文档提到可使用wx.loadFontFaced的回调中渲染canvas。参考文档及代码示例。 另外wxml-to-canvas中不支持font-family属性,可参考链接4修改源码实现。 参考wx.loadFontFace(Object object) | 微信开放文档微信小程序使用自定义字体的三种方法 - 掘金loadFontFace 支持全局生效 | 微信开放社区wxml-to-canvas没有fontWeight的相关支持? | 微信开放社区光明正大用字体——2023年“不要钱”的字体集合!哔哩哔哩bilibili[图片]
2023-03-21 - 小程序性能优化最佳实践
[视频] 本视频主要介绍小程序的启动流程和关键步骤,以及全新的引擎与框架能力。
2024-10-14 - 使用分包异步化组件实现可变Tab页面
背景和需求 众所周知,在微信小程序内,TabBar 页面必须放主包内,这固然是为了用户体验做出的限制,但是也限制了开发者,如果想要实现不同的客户可以定制不同的TabBar页面,而很多页面又是分散到不同分包内的,那我们能选择的方案也就是在所有可作为TabBar页面上放置自定义TabBar组件,而后根据客户的不同配置,展示不同的TabBar 选项,当客户点击Tab时,使用[代码]navigateTo[代码]或[代码]redirectTo[代码]进行切换页面。 但这个方案存在明显的问题,首先如果使用[代码]navigateTo[代码]进行切换,会有很明显的页面切换动画,很容易到达10层页面栈限制(当然这个可以使用无限路由方案进行缓解,但是无限路由是一种万不得已且体验很差的路由方案),且由于页面未进行销毁,内存占用会比较大,容易造成卡顿;如果使用[代码]redirectTo[代码]进行切换,页面节点状态无法保存(如滚动位置),页面数据倒是可以使用全局状态管理库进行保存,但是每次在切换 Tab 都会有明显的数据重新加载的动画效果。 曙光 在微信小程序支持分包异步化之前,对于上面的问题一直没有好的解决方案,支持分包异步化之后,我们可以将一些组件放入分包内异步加载,这一定程度上解决了主包过大的问题。同时也让我们看到了希望,我们可以将很多组件放入分包内进行异步加载,主包空间空了出来,可以放更多的页面,但不是所有页面都能放入主包,那还有其他方案吗? 我们想,既然组件能从分包异步加载,那页面可以吗? 我们知道,在微信小程序内,通常都会使用Page进行声明页面,但也可以用Component声明页面,也就是说 Component 声明的组件可以当成页面用,那反过来,Page 声明的页面可以当成组件用吗? 答案是可以,但是当这样使用的时候,页面的生命周期方法不会被执行,且实例对象上不存在options(页面路由参数),route(当前页面路由地址)等数据,那我们就不能愉快地玩耍了吗? No! 没有页面该有的属性?那我们就拿到实例对象给他补上去! 生命周期方法不执行?那我们就拿到实例对象后自己去调用! 解决思路 要将现有页面作为组件加载,那我们必须要有一个容器页面,去承载真实页面,在容器页面中去补上已经作为组件的真实页面缺失的属性,在对应的生命周期方法中调用真实页面的生命周期钩子。 我们第一步就需要创建一个容器页面出来,我们可以选择手动创建,也可以自动化创建, 但是已有项目来说,手动创建太费时,且每增加新页面都要修改容器页面代码,故此不考虑。 自动化构建容器页面包含如下步骤: 读取 app.json,获取所有分包页面路径 读取分包页面对应的json文件,将其中内容记录到 [代码]tab-bar-page-config.js[代码] 中,因为我们需要在运行时读取真实页面的标题,背景色等信息,而微信小程序不支持从js中读取json文件,故需要将json内容提前读取出来,为了减少数据量,记录时可以将[代码]usingComponents[代码]等无需运行时使用的数据去掉。效果如下: [代码]// tab-bar-page-config.js module.exports = { "/pack_a/page_1": { "navigationBarTitleText": "页面标题", "navigationBarBackgroundColor": "#ffffff" }, "/pack_b/page_2": { "navigationBarTitleText": "页面标题", "navigationBarBackgroundColor": "#ffffff" } /* 其他页面信息 */ } [代码] 生成 wxml 文件,效果如下: [代码]<pack_a_page_1 id="pack_a_page_1" wx:if="{{ pagePath === '/pack_a/page_1' }}" /> <pack_b_page_2 id="pack_b_page_2" wx:elif="{{ pagePath === '/pack_b/page_2' }}" /> <!-- 其他页面节点 --> [代码] 生成容器页面 json 文件,效果如下: [代码]{ "usingComponents": { "pack_a_page_1": "/pack_a/page_1", "pack_b_page_2": "/pack_b/page_2", /* 其他页面 */ }, "componentPlaceholder": { "pack_a_page_1": "view", "pack_b_page_2": "view", /* 其他页面 */ } } [代码] 编写容器页面 js 逻辑,大体如下: [代码]Page({ data: { // 当前真实页面的路径 pagePath: '', }, // 真实页面的实例 pageInstance: null, onLoad() { // 根据网络接口返回数据,得到当前容器页面应当显示的真实页面路径 this.setData({ pagePath: someDataFromNet.pagePath, }); }, onShow() { this.pageInstance?.onShow?.(); }, onReady() { this.pageInstance?.onReady?.(); }, /* 其他生命周期 */ }) [代码] 我们现在面临一个问题,那就是我们是使用分包异步化组件进行加载真实页面,那真实页面是什么时候加载成功的呢?我们知道当组件加载成功后,会执行组件的 [代码]lifetimes.attached[代码] 生命周期, 那既然页面可以当成组件用,那页面是否也有这个生命周期呢?通过查阅文档,我们知道了可以在页面中使用[代码]Behavior[代码], 我们可以通过[代码]Behavior[代码]中定义 [代码]lifetimes.attached[代码],在其中通过 [代码]this.triggerEvent('pageattached')[代码] 去通知容器页面,现在我们的 wxml 需要做一些修改,如下: [代码]<pack_a_page_1 wx:if="{{ pagePath === '/pack_a/page_1' }}" bind:pageattached="onPageAttached" /> <pack_b_page_2 wx:elif="{{ pagePath === '/pack_b/page_2' }}" bind:pageattached="onPageAttached" /> <!-- 其他页面节点 --> [代码] js 中也要增加 [代码]onPageAttached[代码] 方法,如下: [代码]Page({ /* 其他生命周期方法 */ onPageAttached() { const route = this.data.pagePath.slice(1); const id = route.replace(/\//g, '_'); const page = this.selectComponent(`#${route}`); page.route = route; page.options = {}; // 补全其他信息, // 调用对应生命周期方法, page.onLoad?.(page.options); // 由于组件可能加载得比较晚,容器页面的 onShow 和 onReady 已经执行过了,这里需要手动执行一遍真实页面的 onShow 和 onReady // 还需要额外做一些判断,避免 onShow 连续执行多遍 page.onShow?.(page.options); page.onReady?.(page.options); }, }) [代码] 好了,准备工作基本上做完,现在就差给所有页面加上我们之前写的[代码]Behavior[代码]了,如果项目一开始就封装了[代码]BasePage[代码]之类的方法,我们只需要在 BasePage 将这个[代码]Behavior[代码]加到[代码]BasePage[代码]中就行,如果没有的话,可以通过改写[代码]Page[代码]去实现,这里就不举例了。 现在我们按照上面的步骤生成5个容器页面并且加入到 [代码]app.json[代码] 中了,然后开始下一步了, 等等。。。5个容器页面?生成的代码有5份!不行,这样会平白占用很多主包空间的,我们需要做一些优化:将生成5个容器页面优化成生成一个容器组件,然后在5个容器页面内去引用该组件,并修改上面的一些逻辑,这样生成的代码就基本上少了1/5,还是很可观的。 现在还差封装[代码]switchTab[代码]方法了,在其中将[代码]url[代码]替换成容器页面的地址,然后记录该容器页面需要展示的真实页面地址,在容器页面中加载对应的真实页面即可。亦可改写 [代码]wx.switchTab[代码] 去调用我们封装的 [代码]switchTab[代码] 方法,在此就不举例了。 好了,现在基础步骤已完成,就差看效果了。 咦,好像还差某些东西,页面标题呢?怎么不能下拉刷新了?这个页面好像是没有顶部导航栏的呀。 我们一个一个来。 标题及背景颜色等 还记得之前生成的 [代码]tab-bar-page-config.js[代码] 吗?我们在其中记录了页面的一些信息,现在,我们需要在运行时去调用微信API设置标题,颜色等信息。解决。 下拉刷新 微信没有提供是否启用下拉刷新的API,所以我们只能给所有容器页面都加上下拉刷新,然后 [代码]onPullDownRefresh[代码] 中判断如果当前真实页面没有启用下拉刷新,就调用[代码]wx.stopPullDownRefresh[代码]停止下拉刷新,否则就调用真实页面的[代码]onPullDownRefresh[代码]钩子。额。。。勉强算解决吧。 顶部导航栏 微信同样没提供是否启用顶部导航栏的API,故只能将5个容器页面分成2类,2个是不带顶部导航的,剩下3个是带顶部导航的,在我们封装的 [代码]switchTab[代码] 中增加判断要跳转的页面是否是包含顶部导航的,分别落到不同的容器页面上即可。解决。 至此,动态Tab页面基本上实现了,还有些样式上的兼容问题,如:某个页面的wxss声明了 [代码]page { backgroud: 'red'; } [代码] 那容器页面内的所有页面都会被影响,对此我们只能在页面的[代码]wxss[代码]中不使用[代码]标签选择器[代码],实际上在微信开发者工具中,使用[代码]标签选择器[代码]是会报警告的,但是口头约束是没有用的,还是会有人会写,故我们引入了[代码]postcss[代码],编写插件使在构建时将标签选择器去掉,并且报出警告。 至此,功能基本完成,需要做的就是验证哪些功能出现了问题,做出相应的修改。 结尾 分包异步化作为一个新出现的特性,还存在一些不稳定,如在开发者工具中,经常出现加载失败的问题,ios 真机调试报错等问题,且要求的最低SDK版本为[代码]2.17.3[代码],要在生产环境中使用还需要做很多的验证工作,也希望微信官方能尽早修改开发者工具中的问题。
2021-11-29 - 小程序图片懒加载终极方案
效果图 既然来了,把妹子都给你。 [图片] 定义懒加载,前端人都知道的一种性能优化方式,简单的来说,只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。这就是图片懒加载。 实现原理监听页面的[代码]scroll[代码]事件,判读元素距离页面的[代码]top[代码]值是否是小于等于页面的可视高度 判断逻辑代码如下 [代码]element.getBoundingClientRect().top <= document.documentElement.clientHeight ? 显示 : 默认[代码] 我们知道小程序页面的脚本逻辑是在JsCore中运行,JsCore是一个没有窗口对象的环境,所以不能在脚本中使用window,也无法在脚本中操作组件。 所以关于图片懒加载就需要在数据上面做文章了。 页面页面上面只需要根据数据的某一个字段来判断是否显示图片就可以了,字段为Boolean类型,当为false的时候显示默认图片就行了。 代码大概长成这样 <view wx:for="{{list}}" class='item item-{{index}}' wx:key="{{index}}"> <image class="{{item.show ? 'active': ''}}" src="{{item.show ? item.src : item.def}}"></image> </view> 布局跟简单,[代码]view[代码]组件里面有个图片,并循环[代码]list[代码],有多少就展示多少 [代码]image[代码]组件的[代码]src[代码]字段通过每一项的[代码]show[代码]来进行绑定,[代码]active[代码]是加了个透明的过渡 样式 image{ transition: all .3s ease; opacity: 0; } .active{ opacity: 1; } 逻辑 本位主要讲解懒加载,所以把数据写死在页面上了 数据结构如下: [图片] 我们使用两种方式来实现懒加载,准备好没有,一起来快乐的撸码吧。 WXML节点信息 小程序支持调用createSelectQuery创建一个[代码]SelectorQuery[代码]实例,并使用[代码]select[代码]方法来选择节点,并通过[代码]boundingClientRect[代码]来获取节点信息。 wx.createSelectorQuery().select('.item').boundingClientRect((ret)=>{ console.log(ret) }).exec() 显示结果如下 [图片] 悄悄告诉你,小程序里面有个[代码]onPageScroll[代码]函数,是用来监听页面的滚动的。 还有个[代码]getSystemInfo[代码]函数,可以获取获取系统信息,里面包含屏幕的高度。 接下来,思路就透彻了吧。还是上面的逻辑, 扒拉扒拉直接写代码就行了,这里只写下主要的逻辑,完整代码请戳文末github showImg(){ let group = this.data.group let height = this.data.height // 页面的可视高度 wx.createSelectorQuery().selectAll('.item').boundingClientRect((ret) => { ret.forEach((item, index) => { if (item.top <= height) { 判断是否在显示范围内 group[index].show = true // 根据下标改变状态 } }) this.setData({ group }) }).exec() } onPageScroll(){ // 滚动事件 this.showImg() } 至此,我们完成了一个小程序版的图片懒加载,只是思维转变了下,其实并没有改变实现方式。我们来学些新的东西吧。 节点布局相交状态 节点相交状态是啥?它是一个新的API,叫做[代码]IntersectionObserver[代码], 本文只讲解简单的使用,了解更多请猛戳没错,就是点我 小程序里面给它的定义是节点布局交叉状态API可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。 里面设计的概念主要有五个,分别为 参照节点:以某参照节点的布局区域作为参照区域,参照节点可以有多个,多个话参照区域取它们的布局区域的交集目标节点:监听的目标,只能是一个节点相交区域:目标节点与参照节点的相交区域相交比例:目标节点与参照节点的相交比例阈值:可以有多个,默认为[0], 可以理解为交叉比例,例如[0.2, 0.5]关于它的API有五个,依次如下 1、[代码]createIntersectionObserver([this], [options])[代码],见名知意,创建一个IntersectionObserver实例 2、[代码]intersectionObserver.relativeTo(selector, [margins])[代码], 指定节点作为参照区域,margins参数可以放大缩小参照区域,可以包含top、left、bottom、right四项 3、[代码]intersectionObserver.relativeToViewport([margin])[代码],指定页面显示区域为参照区域 4、[代码]intersectionObserver.observer(targetSelector, callback)[代码],参数为指定监听的节点和一个回调函数,目标元素的相交状态发生变化时就会触发此函数,callback函数包含一个result,下面再讲 5、[代码]intersectionObserver.disconnect()[代码] 停止监听,回调函数不会再触发 然后说下callback函数中的result,它包含的字段为 [图片] 我们主要使用[代码]intersectionRatio[代码]进行判断,当它大于0时说明是相交的也就是可见的。 先来波测试题,请说出下面的函数做了什么,并且log函数会执行几次 1、 wx.createIntersectionObserver().relativeToViewport().observer('.box', (result) => { console.log('监听box组件触发的函数') }) 2、 wx.createIntersectionObserver().relativeTo('.box').observer('.item', (result) => { console.log('监听item组件触发的函数') }) 3、 wx.createIntersectionObserver().relativeToViewport().observer('.box', (result) => { if(result.intersectionRatio > 0){ console.log('.box组件是可见的') } }) duang,揭晓答案。 第一个以当前页面的视窗监听了[代码].box[代码]组件,log会触发两次,一次是进入页面一次是离开页面 第二个以[代码].box[代码]节点的布局区域监听了[代码].item[代码]组件,log会触发两次,一次是进入页面一次是离开页面 第三个以当前页面的视窗监听了[代码].box[代码]组件,log只会在节点可见的时候触发 好了,题也做了,API你也掌握了,相信你已经可以使用[代码]IntersectionObserver[代码]来实现图片懒加载了吧,主要逻辑如下 let group = this.data.group // 获取图片数组数据 for (let i in this.data.group){ wx.createIntersectionObserver().relativeToViewport().observe('.item-'+ i, (ret) => { if (ret.intersectionRatio > 0){ group[i].show = true } this.setData({ group }) }) } 最后 至此,我们使用两种方式实现了小程序版本的图片懒加载,可以发现,使用[代码]IntersectionObserver[代码]来实现不要太酸爽
2020-05-12