====== 2.小程序的代码构成 ====== 一个小程序主体部分三个文件组成,必须放在项目的根目录 \\ - app.js 小程序逻辑 - app.json 小程序公共配置 - app.wxss 小程序公共样式表 :!: 没有app.wxml文件,小程序的首页是由app.json文件的"pages"字段配置。小程序所有的页面必须在app.json声明才可以。 文件类型说明: \\ - .json 后缀的 JSON 配置文件 \\ - .wxml 后缀的 WXML 模板文件 \\ - .wxss 后缀的 WXSS 样式文件 \\ - .js 后缀的 JS 脚本逻辑文件 \\ ===== -#1 JSON 配置 ===== JSON 是一种数据格式,在小程序中,JSON扮演的静态配置的角色。小程序是无法在运行过程中去动态更新JSON 配置文件从而发生对应的变化的。 \\ JSON文件都是被包裹在一个大括号中 {},通过key-value的方式来表达数据。JSON的Key必须包裹在一个双引号中。:!: 无法使用注释,试图添加注释将会引发报错。\\ 值只能是以下几种数据格式 \\ * 数字,包含浮点数和整数 * 字符串,需要包裹在双引号中 * Bool值,true 或者 false * 数组,需要包裹在方括号中 [] * 对象,需要包裹在大括号中 {} * Null {{ :微信小程序:json.png?400 |}} ==== - app.json ==== 全局配置:包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。[[https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/code.html#JSON-配置|[官方文档]JSON-配置]] \\ { "pages":[ "pages/index/index", "pages/logs/logs" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "WeChat", "navigationBarTextStyle":"black" } } pages 接受一个数组(使用中括号[]),每一项都是字符串(双引号),来指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。:!: 小程序中新增/减少页面,都需要对 pages 数组进行修改。\\ window用于设置小程序的状态栏、导航条、标题、窗口背景色。[[https://www.w3cschool.cn/weixinapp/hw9k1q8m.html|[w3cschool]小程序配置]]\\ ==== - project.config.json ==== 工具配置 :工具上做的任何配置都会写入到这个文件,当你重新安装工具或者换电脑工作时,你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项。[[https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html|开发者工具的配置]] \\ ==== - page.json ==== 页面配置:用来表示 pages/logs 目录下的 logs.json 这类和小程序页面相关的配置。 \\ 可能你小程序里边的每个页面都有不一样的色调来区分不同功能模块,因此提供了 page.json,让开发者可以独立定义每个页面的一些属性。 [[https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#页面配置|页面配置]]\\ :!: page.json中配置项会覆盖 app.json 的 window 中相同的配置项 \\ page.json只能设置window相关的配置项,以决定本页面的窗口表现,所以无需写window这个键。[[https://www.w3cschool.cn/weixinapp/hw9k1q8m.html|小程序配置]] \\ { "navigationBarBackgroundColor": "#ffffff", "navigationBarTextStyle": "black", "navigationBarTitleText": "微信接口功能演示", "backgroundColor": "#eeeeee", "backgroundTextStyle": "light" } ===== -#2 WXML 模板 ===== WXML由标签、属性等等构成,充当的就是类似 HTML 的角色。不同之处 \\ - 小程序的 WXML 用的标签是 view, button, text 等等[[https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/framework.html|小程序的能力]] - 多了一些'' wx:if'' 这样的属性以及'' { { } }'' 这样的表达式 {{userInfo.nickName}} {{motto}} 小程序采用MVVM的开发模式,提倡把渲染逻辑分离。简单来说就是不要再让JS直接操控DOM,JS只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系即可。 \\ wxml这么写 \\ {{ msg }} JS 只需要管理状态即可: \\ this.setData({ msg: "Hello World" }) ==== - 数据绑定 ==== 通过''{{}}''的语法把一个变量绑定到界面上,我们称为数据绑定。 \\ 更多绑定参考[[https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/data.html|微信小程序 数据绑定]] \\ === - 内容绑定 === {{message}} // page.js Page({ data: { message: 'Hello MINA!' } }) === - 组件属性 === (需要在双引号之内) Page({ data: { id: 0 } }) === - 控制属性 === (需要在双引号之内) Page({ data: { condition: true } }) === - 数据路径运算 === {{object.key}} {{array[0]}} Page({ data: { object: { key: 'Hello ' }, array: ['MINA'] } }) 也可以用扩展运算符...来将一个对象展开 \\ Page({ data: { obj1: { a: 1, b: 2 }, obj2: { c: 3, d: 4 } } }) {a: 1, b: 2, c: 3, d: 4, e: 5} 仅仅通过数据绑定还不够完整的描述状态和界面的关系,还需要 if/else, for等控制能力,在小程序里边,这些控制能力都用 wx: 开头的属性来表达。[[https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/|WXML]] \\ === - 双向绑定语法 === 如果使用 this.setData({ value: 'leaf' }) 来更新 value ,this.data.value 和输入框的中显示的值都会被更新为 leaf ;但如果用户修改了输入框里的值,却不会同时改变 this.data.value 。 如果需要在用户输入的同时改变 this.data.value ,需要借助简易双向绑定机制。此时,可以在对应项目之前加入 model: 前缀 双向绑定的表达式有如下限制: 只能是一个单一字段的绑定 ==== - 列表渲染 ==== === - wx:for === 在组件 view 上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。[[https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/list.html|[官方文档 列表渲染]]] \\ :!: 默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item \\ {{index}}: {{item.message}} Page({ data: { array: [{ message: 'foo', }, { message: 'bar' }] } }) 输出: \\ 0:foo 1:bar 使用 wx:for-item 可以指定数组当前元素的变量名, \\ 使用 wx:for-index 可以指定数组当前下标的变量名:\\ {{idx}}: {{itemName.message}} 输出: \\ 0:foo 1:bar :!: 当 wx:for 的值为字符串时,会将字符串解析成字符串数组 \\ {{item}} 等同于 \\ {{item}} === - wx:key === 如果列表中项目的位置动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 中的输入内容, 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。 \\ wx:key 的值以两种形式提供 \\ * 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。 * 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个 :!: 唯一的字符串或者数字,如: 当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。 {{item.id}} {{item}} Page({ data: { objectArray: [ {id: 5, unique: 'unique_5'}, {id: 4, unique: 'unique_4'}, {id: 3, unique: 'unique_3'}, {id: 2, unique: 'unique_2'}, {id: 1, unique: 'unique_1'}, {id: 0, unique: 'unique_0'}, ], numberArray: [1, 2, 3, 4] }, switch: function(e) { const length = this.data.objectArray.length for (let i = 0; i < length; ++i) { const x = Math.floor(Math.random() * length) const y = Math.floor(Math.random() * length) const temp = this.data.objectArray[x] this.data.objectArray[x] = this.data.objectArray[y] this.data.objectArray[y] = temp } this.setData({ objectArray: this.data.objectArray }) }, addToFront: function(e) { const length = this.data.objectArray.length this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray) this.setData({ objectArray: this.data.objectArray }) }, addNumberToFront: function(e){ this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray) this.setData({ numberArray: this.data.numberArray }) } }) {{ :微信小程序:wxkey.png?400 |}} [[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/random|Math.random()]] 函数返回一个浮点, 伪随机数在范围从0到小于1 \\ [[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/floor|Math.floor()]] 返回小于或等于一个给定数字的最大整数。 \\ [[https://www.w3school.com.cn/js/jsref_concat_array.asp|concat()]] 方法用于连接两个或多个数组,该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。 \\ ==== - 条件渲染 ==== === - wx:if === 在框架中,我们用 '' wx:if="{ {condition} }" ''来判断是否需要渲染该代码块:[[https://www.w3cschool.cn/weixinapp/weixinapp-conditional.html|微信小程序 条件渲染 wx:if]] \\ True 也可以用'' wx:elif ''和'' wx:else''来添加一个else块: \\ 1 2 3 === - block wx:if === 因为''wx:if''是一个控制属性,需要将它添加到一个标签上。 \\ 但是如果我们想一次性判断多个组件标签,我们可以使用一个'' block/ '' 标签将多个组件包装起来,并在上边使用''wx:if''控制属性。 \\ view1 view2 ===== - WXSS 样式 ===== WXSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改。[[https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html|WXSS]] \\ - app.wxss 作为全局样式,会作用于当前小程序的所有页面 \\ - 局部页面样式 page.wxss 仅对当前页面生效。 \\ ==== - 样式导入 ==== /** common.wxss 局部样式 **/ .small-p { padding:5px; } :!:使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。 /** app.wxss 全局样式**/ @import "common.wxss"; .middle-p { padding:15px; } ==== - 内联样式 ==== *style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。 *class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔。 ===== - JS 逻辑交互(事件) ===== 用户做交互:响应用户的点击、获取用户的位置等等。在小程序里边,我们就通过编写 JS 脚本文件来处理用户的操作。 ==== - 代码案例 ==== 编写 JS 脚本文件来处理用户的操作。[[https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html|WXML - 事件]] \\ { { msg } } 点击 button 按钮的时候,我们希望把界面上 msg 显示成 "Hello World",于是我们在 button 上声明一个属性: bindtap ,在 JS 文件里边声明了 clickMe 方法来响应这次点击操作: \\ //app.js Page({ clickMe: function() { this.setData({ msg: "Hello World" }) } }) ==== - 什么是事件 ==== [[https://www.w3cschool.cn/weixinapp/weixinapp-event.html|微信小程序 事件]] \\ * 事件是视图层到逻辑层的通讯方式。 * 事件可以将用户的行为反馈到逻辑层进行处理。 * 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。 * 事件对象可以携带额外信息,如id, dataset, touches。 在组件中绑定一个事件处理函数。\\ 如''bindtap'',当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数。 \\ Click me! //与index.wxml文件同目录下index.js,参数是event。 Page({ tapName: function(event) { console.log(event) } }) ''console.log(event)''打印数据 { "type": "tap", "timeStamp":895, "target": { "id": "tapTest", "dataset": { "hi": "WeChat" } }, "currentTarget": { "id": "tapTest", "dataset": { "hi": "WeChat" } }, "detail": { "x":53, "y":14 }, "touches": [{ "identifier":0, "pageX":53, "pageY":14, "clientX":53, "clientY":14, }], "changedTouches": [{ "identifier":0, "pageX":53, "pageY":14, "clientX":53, "clientY":14, }], } ==== - 事件分类 ==== 事件分为冒泡事件和非冒泡事件 \\ * 冒泡事件:当一个组件上的事件被触发后,该事件向父节点传递。 * 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。 WXML的冒泡事件列表:\\ ^ 类型 ^ 触发条件 ^ | touchstart | 手指触摸动作开始 | | touchmove | 手指触摸后移动 | | touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | | touchend | 手指触摸动作结束 | | tap | 手指触摸后马上离开 | | longtap | 手指触摸后,超过350ms再离开 | 注:除上表之外的其他组件自定义事件都是非冒泡事件,如[[https://www.w3cschool.cn/weixinapp/m8x71q8z.html|
]]的submit事件,[[https://www.w3cschool.cn/weixinapp/3glu1q92.html|]]的input事件,的scroll事件,(详见各个[[https://www.w3cschool.cn/weixinapp/itz51q8o.html|组件]]) ==== - 事件绑定 ==== 事件绑定的写法同组件的属性,以key、value的形式。\\ * key以bindcatch开头,然后跟上事件的类型,如bindtap, catchtouchstart * value是一个字符串,需要在对应的Page中定义同名的函数。不然当触发事件的时候会报错。 :!: ''bind''事件绑定不会阻止冒泡事件向上冒泡,''catch''事件绑定可以阻止冒泡事件向上冒泡。 \\ outer view middle view inner view 如在上边这个例子中,点击inner view会先后触发handleTap3和handleTap2(因为tap事件会冒泡到middle view,而middle view阻止了tap事件冒泡,不再向父节点传递),点击middle view会触发handleTap2,点击outter view会触发handleTap1。 :!: mut-bind 来绑定事件。一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发。 ==== - 数据绑定 ==== Click here! * 此时,页面的 this.data.handlerName 必须是一个字符串,指定事件处理函数名; * 如果它是个空字符串,则这个绑定会失效。 ==== - 事件的捕获阶段 ==== 捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反。 可以采用''capture-bind''、''capture-catch''关键字,后者将中断捕获阶段和取消冒泡阶段。 在下面的代码中,点击 inner view 会先后调用handleTap2、handleTap4、handleTap3、handleTap1。 outer view inner view ==== - 事件对象 ==== 如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。 \\ === BaseEvent基础事件对象属性列表 === ^ 属性 | 类型 ^ 说明 ^ | type | String | 事件类型 | | timeStamp | Integer | 事件生成时的**时间戳** | | target | Object | 触发事件的组件的一些**属性值集合** | | currentTarget | Object | **当前组件**的一些**属性值集合** | target 触发事件的源组件。 \\ ^ 属性 | 类型 ^ 说明 ^ | id | String | 事件源组件的id | | tagName | String | 当前组件的类型 | | dataset | Object | 事件源组件上由data-开头的自定义属性组成的集合 | currentTarget 事件绑定的当前组件。 \\ ^ 属性 | 类型 ^ 说明 ^ | id | String | 当前组件的id | | tagName | String | 当前组件的类型 | | dataset | Object | 事件源组件上由data-开头的自定义属性组成的集合 | 说明: target 和 currentTarget 可以参考上例中,点击 inner view 时,handleTap3 收到的事件对象 target 和 currentTarget 都是 inner,而 handleTap2 收到的事件对象 target 就是 inner,currentTarget 就是 middle。 dataset \\ 在组件中可以定义数据,这些数据将会通过事件传递给 SERVICE。书写方式:以data-开头,多个单词由连字符-链接,不能有大写(大写会自动转成小写)如data-element-type,最终在 event.target.dataset 中会将连字符转成驼峰elementType。 \\ DataSet Test Page({ bindViewTap:function(event){ event.target.dataset.alphaBeta === 1 // - 会转为驼峰写法 event.target.dataset.alphabeta === 2 // 大写会转为小写 } }) === CustomEvent 自定义事件对象属性列表(继承 BaseEvent) === ^ 属性 | 类型 ^ 说明 ^ | detail | Object | 额外的信息 | detail \\ 自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,详见[[https://www.w3cschool.cn/weixinapp/itz51q8o.html|组件]]定义中各个事件的定义。\\ 点击事件的detail 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离。\\ === TouchEvent 触摸事件对象属性列表(继承 BaseEvent) === ^ 属性 | 类型 ^ 说明 ^ | touches | Array | 触摸事件,当前停留在屏幕中的触摸点信息的数组 | | changedTouches | Object | 触摸事件,当前变化的触摸点信息的数组 | ===== - sitemap 配置 ===== 小程序根目录下的 sitemap.json 文件用来配置小程序及其页面是否允许被微信索引。[[https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html|[官方文档]sitemap 配置]] //所有页面都会被微信索引(默认情况) { "rules":[{ "action": "allow", //允许 "page": "*" }] } //配置 path/to/page 页面被索引,其余页面不被索引 { { "rules":[{ "action": "allow", //允许 "page": "path/to/page" }, { "action": "disallow",//不允许 "page": "*" }] } }