微信小程序开发文档 第58页 小程序更新机制 未启动时更新 开发者在管理后台发布新版本的小程序之后,如果某个用户本地有小程序的历史版本,此时打开的可能还是旧版本。微信客户端会有若干个时机去检查本地缓存的小程序有没有更新版本,如果有则会静默更新到新版本。总的来说,开发者在后台发布新版本之后,无法立刻影响到所有现网用户,但最差情况下,也在发布之后 24 小时之内下发新版本信息到用户。用户下次打开时会先更新最新版本再打开。 启动时更新 小程序每次冷启动时,都会检查是否有更新版本,如果发现有新版本,将会异步下载新版本的代码包,并同时用客户端本地的包进行启动,即新版本的小程序需要等下一次冷启动才会应用上。 如果需要马上应用最新版本,可以使用 wx.getUpdateManager API 进行处理。 const updateManager = wx.getUpdateManager() updateManager.onCheckForUpdate(function (res) { // 请求完新版本信息的回调 console.log(res.hasUpdate) }) updateManager.onUpdateReady(function () { wx.showModal({ title: '更新提示', content: '新版本已经准备好,是否重启应用?', success(res) { if (res.confirm) { // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 updateManager.applyUpdate() } } }) }) updateManager.onUpdateFailed(function () { // 新版本下载失败 })
2024-04-02
初始渲染缓存 基础库 2.11.1 开始支持,低版本需做兼容处理。 初始渲染缓存工作原理 小程序页面的初始化分为两个部分。 逻辑层初始化:载入必需的小程序代码、初始化页面 this 对象(也包括它涉及到的所有自定义组件的 this 对象)、将相关数据发送给视图层。 视图层初始化:载入必需的小程序代码,然后等待逻辑层初始化完毕并接收逻辑层发送的数据,最后渲染页面。 在启动页面时,尤其是小程序冷启动、进入第一个页面时,逻辑层初始化的时间较长。在页面初始化过程中,用户将看到小程序的标准载入画面(冷启动时)或可能看到轻微的白屏现象(页面跳转过程中)。 启用初始渲染缓存,可以使视图层不需要等待逻辑层初始化完毕,而直接提前将页面初始 data 的渲染结果展示给用户,这可以使得页面对用户可见的时间大大提前。它的工作原理如下: 在小程序页面第一次被打开后,将页面初始数据渲染结果记录下来,写入一个持久化的缓存区域(缓存可长时间保留,但可能因为小程序更新、基础库更新、储存空间回收等原因被清除); 在这个页面被第二次打开时,检查缓存中是否还存有这个页面上一次初始数据的渲染结果,如果有,就直接将渲染结果展示出来; 如果展示了缓存中的渲染结果,这个页面暂时还不能响应用户事件,等到逻辑层初始化完毕后才能响应用户事件。 利用初始渲染缓存,可以: 快速展示出页面中永远不会变的部分,如导航栏; 预先展示一个骨架页,提升用户体验; 展示自定义的加载提示; 提前展示广告,等等。 支持的组件 在初始渲染缓存阶段中,复杂组件不能被展示或不能响应交互。 目前支持的内置组件: <view /> <text /> <button /> <image /> <scroll-view /> <rich-text /> 自定义组件本身可以被展示(但它们里面用到的内置组件也遵循上述限制)。 静态初始渲染缓存 若想启用初始渲染缓存,最简单的方法是在页面的 json 文件中添加配置项 “initialRenderingCache”: “static” : { "initialRenderingCache": "static" } 如果想要对所有页面启用,可以在 app.json 的 window 配置段中添加这个配置: { "window": { "initialRenderingCache": "static" } } 添加这个配置项之后,在手机中预览小程序首页,然后杀死小程序再次进入,就会通过初始渲染缓存来渲染首页。 注意:这种情况下,初始渲染缓存记录的是页面 data 应用在页面 WXML 上的结果,不包含任何 setData 的结果。 例如,如果想要在页面中展示出“正在加载”几个字,这几个字受到 loading 数据字段控制: <view wx:if="{{loading}}">正在加载</view> 这种情况下, loading 应当在 data 中指定为 true ,如: // 正确的做法 Page({ data: { loading: true } }) 而不能通过 setData 将 loading 置为 true : // 错误的做法!不要这么做! Page({ data: {}, onLoad: function() { this.setData({ loading: true }) } }) 换而言之,这种做法只包含页面 data 的渲染结果,即页面的纯静态成分。 在初始渲染缓存中添加动态内容 有些场景中,只是页面 data 的渲染结果会比较局限。有时会想要额外展示一些可变的内容,如展示的广告图片 URL 等。 这种情况下可以使用“动态”初始渲染缓存的方式。首先,配置 “initialRenderingCache”: “dynamic” : { "initialRenderingCache": "dynamic" } 此时,初始渲染缓存不会被自动启用,还需要在页面中调用 this.setInitialRenderingCache(dynamicData) 才能启用。其中, dynamicData 是一组数据,与 data 一起参与页面 WXML 渲染。 Page({ data: { loading: true }, onReady: function() { this.setInitialRenderingCache({ loadingHint: '正在加载' // 这一部分数据将被应用于界面上,相当于在初始 data...
2024-04-02
获取界面上的节点信息 WXML节点信息 节点信息查询 API 可以用于获取节点属性、样式、在界面上的位置等信息。 最常见的用法是使用这个接口来查询某个节点的当前位置,以及界面的滚动位置。 示例代码: const query = wx.createSelectorQuery() query.select('#the-id').boundingClientRect(function(res){ res.top // #the-id 节点的上边界坐标(相对于显示区域) }) query.selectViewport().scrollOffset(function(res){ res.scrollTop // 显示区域的竖直滚动位置 }) query.exec() 上述示例中, #the-id 是一个节点选择器,与 CSS 的选择器相近但略有区别,请参见 SelectorQuery.select 的相关说明。 在自定义组件或包含自定义组件的页面中,推荐使用 this.createSelectorQuery 来代替 wx.createSelectorQuery ,这样可以确保在正确的范围内选择节点。 WXML节点布局相交状态 节点布局相交状态 API 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。 这一组API涉及的主要概念如下。 参照节点:监听的参照节点,取它的布局区域作为参照区域。如果有多个参照节点,则会取它们布局区域的 交集 作为参照区域。页面显示区域也可作为参照区域之一。 目标节点:监听的目标,默认只能是一个节点(使用 selectAll 选项时,可以同时监听多个节点)。 相交区域:目标节点的布局区域与参照区域的相交区域。 相交比例:相交区域占参照区域的比例。 阈值:相交比例如果达到阈值,则会触发监听器的回调函数。阈值可以有多个。 以下示例代码可以在目标节点(用选择器 .target-class 指定)每次进入或离开页面显示区域时,触发回调函数。 示例代码: Page({ onLoad: function(){ wx.createIntersectionObserver().relativeToViewport().observe('.target-class', (res) => { res.id // 目标节点 id res.dataset // 目标节点 dataset res.intersectionRatio // 相交区域占目标节点的布局区域的比例 res.intersectionRect // 相交区域 res.intersectionRect.left // 相交区域的左边界坐标 res.intersectionRect.top // 相交区域的上边界坐标 res.intersectionRect.width // 相交区域的宽度 res.intersectionRect.height // 相交区域的高度 }) } }) 以下示例代码可以在目标节点(用选择器 .target-class 指定)与参照节点(用选择器 .relative-class 指定)在页面显示区域内相交或相离,且相交或相离程度达到目标节点布局区域的20%和50%时,触发回调函数。 示例代码: Page({ onLoad: function(){ wx.createIntersectionObserver(this, { thresholds: [0.2, 0.5] }).relativeTo('.relative-class').relativeToViewport().observe('.target-class', (res) => { res.intersectionRatio // 相交区域占目标节点的布局区域的比例 res.intersectionRect // 相交区域 res.intersectionRect.left // 相交区域的左边界坐标 res.intersectionRect.top // 相交区域的上边界坐标 res.intersectionRect.width // 相交区域的宽度 res.intersectionRect.height // 相交区域的高度 }) } }) 注意:与页面显示区域的相交区域并不准确代表用户可见的区域,因为参与计算的区域是“布局区域”,布局区域可能会在绘制时被其他节点裁剪隐藏(如遇祖先节点中 overflow 样式为 hidden 的节点)或遮盖(如遇 fixed 定位的节点)。 在自定义组件或包含自定义组件的页面中,推荐使用 this.createIntersectionObserver 来代替 wx.createIntersectionObserver ,这样可以确保在正确的范围内选择节点。
2024-04-02
简易双向绑定 基础库 2.9.3 开始支持,低版本需做兼容处理。 双向绑定语法 在 WXML 中,普通的属性的绑定是单向的。例如: <input value="{{value}}" /> 如果使用 this.setData({ value: 'leaf' }) 来更新 value ,this.data.value 和输入框的中显示的值都会被更新为 leaf ;但如果用户修改了输入框里的值,却不会同时改变 this.data.value 。 如果需要在用户输入的同时改变 this.data.value ,需要借助简易双向绑定机制。此时,可以在对应项目之前加入 model: 前缀: <input model:value="{{value}}" /> 这样,如果输入框的值被改变了, this.data.value 也会同时改变。同时, WXML 中所有绑定了 value 的位置也会被一同更新, 数据监听器 也会被正常触发。 用于双向绑定的表达式有如下限制: 1.只能是一个单一字段的绑定,如 <input model:value="值为 {{value}}" /> <input model:value="{{ a + b }}" /> 都是非法的; 2.目前,尚不能 data 路径,如 <input model:value="{{ a.b }}" /> 这样的表达式目前暂不支持。 在自定义组件中传递双向绑定 双向绑定同样可以使用在自定义组件上。如下的自定义组件: // custom-component.js Component({ properties: { myValue: String } }) <!-- custom-component.wxml --> <input model:value="{{myValue}}" /> 这个自定义组件将自身的 myValue 属性双向绑定到了组件内输入框的 value 属性上。这样,如果页面这样使用这个组件: <custom-component model:my-value="{{pageValue}}" /> 当输入框的值变更时,自定义组件的 myValue 属性会同时变更,这样,页面的 this.data.pageValue 也会同时变更,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新。 在自定义组件中触发双向绑定更新 自定义组件还可以自己触发双向绑定更新,做法就是:使用 setData 设置自身的属性。例如: // custom-component.js Component({ properties: { myValue: String }, methods: { update: function() { // 更新 myValue this.setData({ myValue: 'leaf' }) } } }) 如果页面这样使用这个组件: <custom-component model:my-value="{{pageValue}}" /> 当组件使用 setData 更新 myValue 时,页面的 this.data.pageValue 也会同时变更,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新。
2024-04-02
WXML WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。 要完整了解 WXML 语法,请参考WXML 语法参考。 用以下一些简单的例子来看看 WXML 具有什么能力: 数据绑定 <!--wxml--> <view> {{message}} </view> // page.js Page({ data: { message: 'Hello MINA!' } }) 列表渲染 <!--wxml--> <view wx:for="{{array}}"> {{item}} </view> // page.js Page({ data: { array: [1, 2, 3, 4, 5] } }) 条件渲染 <!--wxml--> <view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view> <view wx:elif="{{view == 'APP'}}"> APP </view> <view wx:else="{{view == 'MINA'}}"> MINA </view> // page.js Page({ data: { view: 'MINA' } }) 模板 <!--wxml--> <template name="staffName"> <view> FirstName: {{firstName}}, LastName: {{lastName}} </view> </template> <template is="staffName" data="{{...staffA}}"></template> <template is="staffName" data="{{...staffB}}"></template> <template is="staffName" data="{{...staffC}}"></template> // page.js Page({ data: { staffA: {firstName: 'Hulk', lastName: 'Hu'}, staffB: {firstName: 'Shang', lastName: 'You'}, staffC: {firstName: 'Gideon', lastName: 'Lin'} } }) 引用 <!-- item.wxml -->...
2024-04-02
模块化 可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。 注意: exports 是 module.exports 的一个引用,因此在模块里边随意更改 exports 的指向会造成未知的错误。所以更推荐开发者采用 module.exports 来暴露模块接口,除非你已经清晰知道这两者的关系。 小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候建议拷贝出相关的代码到小程序的目录中,或者使用小程序支持的 npm 功能。 // common.js function sayHello(name) { console.log(`Hello ${name} !`) } function sayGoodbye(name) { console.log(`Goodbye ${name} !`) } module.exports.sayHello = sayHello exports.sayGoodbye = sayGoodbye 在需要使用这些模块的文件中,使用 require 将公共代码引入 var common = require('common.js') Page({ helloMINA: function() { common.sayHello('MINA') }, goodbyeMINA: function() { common.sayGoodbye('MINA') } }) 文件作用域 在 JavaScript 文件中声明的变量和函数只在该文件中有效;不同的文件中可以声明相同名字的变量和函数,不会互相影响。 通过全局函数 getApp 可以获取全局的应用实例,如果需要全局的数据可以在 App() 中设置,如: // app.js App({ globalData: 1 }) // a.js // The localValue can only be used in file a.js. var localValue = 'a' // Get the app instance. var app = getApp() // Get the global data and change it. app.globalData++ // b.js // You can redefine localValue in file b.js, without interference with the localValue in a.js. var localValue = 'b' // If a.js it run before b.js,...
2024-04-02
注册页面 对于小程序中的每个页面,都需要在页面对应的 js 文件中进行注册,指定页面的初始数据、生命周期回调、事件处理函数等。 使用 Page 构造器注册页面 简单的页面可以使用 Page() 进行构造。 代码示例: //index.js Page({ data: { text: "This is page data." }, onLoad: function(options) { // 页面创建时执行 }, onShow: function() { // 页面出现在前台时执行 }, onReady: function() { // 页面首次渲染完毕时执行 }, onHide: function() { // 页面从前台变为后台时执行 }, onUnload: function() { // 页面销毁时执行 }, onPullDownRefresh: function() { // 触发下拉刷新时执行 }, onReachBottom: function() { // 页面触底时执行 }, onShareAppMessage: function () { // 页面被用户分享时执行 }, onPageScroll: function() { // 页面滚动时执行 }, onResize: function() { // 页面尺寸变化时执行 }, onTabItemTap(item) { // tab 点击时执行 console.log(item.index) console.log(item.pagePath) console.log(item.text) }, // 事件响应函数 viewTap: function() { this.setData({ text: 'Set some data for updating view.' }, function() { // this is setData callback }) }, // 自由数据 customData: { hi: 'MINA' } }) 详细的参数含义和使用请参考 Page 参考文档 。...
2024-04-02
注册小程序 每个小程序都需要在 app.js 中调用 App 方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。 详细的参数含义和使用请参考 App 参考文档 。 // app.js App({ onLaunch (options) { // Do something initial when launch. }, onShow (options) { // Do something when show. }, onHide () { // Do something when hide. }, onError (msg) { console.log(msg) }, globalData: 'I am global data' }) 整个小程序只有一个 App 实例,是全部页面共享的。开发者可以通过 getApp 方法获取到全局唯一的 App 实例,获取App上的数据或调用开发者注册在 App 上的函数。 // xxx.js const appInstance = getApp() console.log(appInstance.globalData) // I am global data
2024-04-02
小程序宿主环境 我们称微信客户端给小程序所提供的环境为宿主环境。小程序借助宿主环境提供的能力,可以完成许多普通网页无法完成的功能。 上一章中我们把小程序涉及到的文件类型阐述了一遍,我们结合 QuickStart 这个项目来讲一下这些文件是怎么配合工作的。 渲染层和逻辑层 首先,我们来简单了解下小程序的运行环境。小程序的运行环境分成渲染层和逻辑层,其中 WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。 小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发,小程序的通信模型下图所示。 有关渲染层和逻辑层的详细文档参考 小程序框架 。 程序与页面 微信客户端在打开小程序之前,会把整个小程序的代码包下载到本地。 紧接着通过 app.json 的 pages 字段就可以知道你当前小程序的所有页面路径: { "pages":[ "pages/index/index", "pages/logs/logs" ] } 这个配置说明在 QuickStart 项目定义了两个页面,分别位于 pages/index/index 和 pages/logs/logs。而写在 pages 字段的第一个页面就是这个小程序的首页(打开小程序看到的第一个页面)。 于是微信客户端就把首页的代码装载进来,通过小程序底层的一些机制,就可以渲染出这个首页。 小程序启动之后,在 app.js 定义的 App 实例的 onLaunch 回调会被执行: App({ onLaunch: function () { // 小程序启动之后 触发 } }) 整个小程序只有一个 App 实例,是全部页面共享的,更多的事件回调参考文档 注册程序 App 。 接下来我们简单看看小程序的一个页面是怎么写的。 你可以观察到 pages/logs/logs 下其实是包括了4种文件的,微信客户端会先根据 logs.json 配置生成一个界面,顶部的颜色和文字你都可以在这个 json 文件里边定义好。紧接着客户端就会装载这个页面的 WXML 结构和 WXSS 样式。最后客户端会装载 logs.js,你可以看到 logs.js 的大体内容就是: Page({ data: { // 参与页面渲染的数据 logs: [] }, onLoad: function () { // 页面渲染后 执行 } }) Page 是一个页面构造器,这个构造器就生成了一个页面。在生成页面的时候,小程序框架会把 data 数据和 index.wxml 一起渲染出最终的结构,于是就得到了你看到的小程序的样子。 在渲染完界面之后,页面实例就会收到一个 onLoad 的回调,你可以在这个回调处理你的逻辑。 有关于 Page 构造器更多详细的文档参考 注册页面 Page 。 组件 小程序提供了丰富的基础组件给开发者,开发者可以像搭积木一样,组合各种组件拼合成自己的小程序。 就像 HTML 的 div, p 等标签一样,在小程序里边,你只需要在 WXML 写上对应的组件标签名字就可以把该组件显示在界面上,例如,你需要在界面上显示地图,你只需要这样写即可: <map></map> 使用组件的时候,还可以通过属性传递值给组件,让组件可以以不同的状态去展现,例如,我们希望地图一开始的中心的经纬度是广州,那么你需要声明地图的 longitude(中心经度) 和 latitude(中心纬度)两个属性: <map longitude="广州经度" latitude="广州纬度"></map> 组件的内部行为也会通过事件的形式让开发者可以感知,例如用户点击了地图上的某个标记,你可以在 js 编写 markertap 函数来处理: <map bindmarkertap="markertap" longitude="广州经度" latitude="广州纬度"></map> 当然你也可以通过 style 或者 class 来控制组件的外层样式,以便适应你的界面宽度高度等等。 API 为了让开发者可以很方便的调起微信提供的能力,例如获取用户信息、微信支付等等,小程序提供了很多 API 给开发者去使用。 要获取用户的地理位置时,只需要: wx.getLocation({ type: 'wgs84', success: (res) => { var latitude = res.latitude // 纬度 var longitude = res.longitude // 经度 } }) 调用微信扫一扫能力,只需要: wx.scanCode({ success: (res) => { console.log(res) } }) 需要注意的是:多数 API...
2024-04-02
小程序简介 小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。 小程序技术发展史 小程序并非凭空冒出来的一个概念。当微信中的 WebView 逐渐成为移动 Web 的一个重要入口时,微信就有相关的 JS API 了。 代码清单1-1 使用 WeixinJSBridge 预览图片 WeixinJSBridge.invoke('imagePreview', { current: 'http://inews.gtimg.com/newsapp_bt/0/1693121381/641', urls: [ // 所有图片的URL列表,数组格式 'https://img1.gtimg.com/10/1048/104857/10485731_980x1200_0.jpg', 'https://img1.gtimg.com/10/1048/104857/10485726_980x1200_0.jpg', 'https://img1.gtimg.com/10/1048/104857/10485729_980x1200_0.jpg' ] }, function(res) { console.log(res.err_msg) }) 代码1-1是一个调用微信原生组件浏览图片的JS API,相比于额外引入一个JS图片预览组件库,这种调用方式显得非常简洁和高效。 实际上,微信官方是没有对外暴露过如此调用的,此类 API 最初是提供给腾讯内部一些业务使用,很多外部开发者发现了之后,依葫芦画瓢地使用了,逐渐成为微信中网页的事实标准。2015年初,微信发布了一整套网页开发工具包,称之为 JS-SDK,开放了拍摄、录音、语音识别、二维码、地图、支付、分享、卡券等几十个API。给所有的 Web 开发者打开了一扇全新的窗户,让所有开发者都可以使用到微信的原生能力,去完成一些之前做不到或者难以做到的事情。 同样是调用原生的浏览图片,调用方式如代码清单1-2所示。 代码清单1-2 使用 JS-SDK 调用图片预览组件 wx.previewImage({ current: 'https://img1.gtimg.com/10/1048/104857/10485726_980x1200_0.jpg', urls: [ // 所有图片的URL列表,数组格式 'https://img1.gtimg.com/10/1048/104857/10485731_980x1200_0.jpg', 'https://img1.gtimg.com/10/1048/104857/10485726_980x1200_0.jpg', 'https://img1.gtimg.com/10/1048/104857/10485729_980x1200_0.jpg' ], success: function(res) { console.log(res) } }) JS-SDK是对之前的 WeixinJSBridge 的一个包装,以及新能力的释放,并且由对内开放转为了对所有开发者开放,在很短的时间内获得了极大的关注。从数据监控来看,绝大部分在微信内传播的移动网页都使用到了相关的接口。 JS-SDK 解决了移动网页能力不足的问题,通过暴露微信的接口使得 Web 开发者能够拥有更多的能力,然而在更多的能力之外,JS-SDK 的模式并没有解决使用移动网页遇到的体验不良的问题。用户在访问网页的时候,在浏览器开始显示之前都会有一个的白屏过程,在移动端,受限于设备性能和网络速度,白屏会更加明显。我们团队把很多技术精力放置在如何帮助平台上的 Web 开发者解决这个问题。因此我们设计了一个 JS-SDK 的增强版本,其中有一个重要的功能,称之为“微信 Web 资源离线存储”。 以下文字引用自内部的文档(没有最终对外开放): 微信 Web 资源离线存储是面向 Web 开发者提供的基于微信内的 Web 加速方案。通过使用微信离线存储,Web 开发者可借助微信提供的资源存储能力,直接从微信本地加载 Web 资源而不需要再从服务端拉取,从而减少网页加载时间,为微信用户提供更优质的网页浏览体验。每个公众号下所有 Web App 累计最多可缓存 5M 的资源。 这个设计有点类似 HTML5 的 Application Cache,但在设计上规避了一些 Application Cache的不足。 在内部测试中,我们发现 离线存储 能够解决一些问题,但对于一些复杂的页面依然会有白屏问题,例如页面加载了大量的 CSS 或者是 JavaScript 文件。除了白屏,影响 Web 体验的问题还有缺少操作的反馈,主要表现在两个方面:页面切换的生硬和点击的迟滞感。 微信面临的问题是如何设计一个比较好的系统,使得所有开发者在微信中都能获得比较好的体验。这个问题是之前的 JS-SDK 所处理不了的,需要一个全新的系统来完成,它需要使得所有的开发者都能做到: – 快速的加载 – 更强大的能力 – 原生的体验 – 易用且安全的微信数据开放 –...
2024-04-02