模板
在项目过程中,常常会遇到某些相同的结构在不同的地方重复出现,这时可以将相同的布局代码片段放置到一个模块中,在不同的地方传入对应的数据进行渲染,这样能避免重复开发,提高开发效率
-
定义模板
在
<template/>
内定义代码片段,设置<template/>
的name属性,指定模板名称即可。<template name="myTemplate"> <view>内容</view> <view>{{content}}</view> </template>
-
使用模板
使用模板时,设置is属性指向需要使用的模板,设置data属性,将模板所需的变量传入。模板拥有自己的作用域,只能使用data属性传入的数据,而不是直接使用Page中的data数据,渲染时,
<template/>
标签将被模板中的代码块完全替代<template name="myTemplate"> <view>内容</view> <view>{{content}}</view> <view>{{name}}</view> <view>{{myObj.key1}}</view> <view>{{key2}}</view> </template> <template is="myTemplate" data="{{content:'内容', name, myObj, ...myObj2}}"/>
Page({ data:{ name: 'myTemplate', myObj:{ key1: 'value1' }, myObj2:{ key2:'value2' } } });
测试结果:模板只能使用自己的data数据,注意属性和值之间是冒号分开
-
嵌套使用模板:使用is进行引用模板
<template name="bTemplate"> <view>b template content</view> </template> <template name="aTemplate"> <view>a template content</view> <template is="bTemplate"/> </template> <template is="aTemplate"/>
事件
WXML中的事件系统和HTML中DOM事件系统极其相似,也是通过在组件上设置“bind(或 catch)+事件名”属性进行事件绑定,当触发事件时,框架会调用逻辑层中对应的事件处理函数,并将当前状态通过参数传递给事件处理函数,由于小程序中没有DOM节点概念,所以事件只能通过XML绑定,不能通过逻辑层动态绑定。官方对WXML事件的定义如下:
1. 事件是视图层到逻辑层的通讯方式。
2. 事件可以将用户的行为反馈到逻辑层进行处理。
3. 事件可以绑定在组件上,当触发事件时,就会执行逻辑层中对应的事件处理函数。
4. 事件对象可以携带额外信息,如id、 dataset、 touches
-
事件分类
- 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。例如:bindtap
- 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。例如:catchtap
WXML冒泡事件如下:
- touchstart:手指触摸动作开始
- touchmove:手指触摸后移动
- touchcancel:手指触摸动作被打断,如来电提示、弹窗
- touchend:手指触摸动作结束
- tap:手指触摸后马上离开
- longtap:手指触摸后,超过350ms在离开
对于冒泡事件每个组件都是默认支持的,除了上述事件之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如
<form/>
的submit事件,<scroll-view/>
的scroll事件。 -
事件绑定
事件绑定的写法和组件的属性一样,以key、value形式组织。
绑定时bind事件绑定不会阻止冒泡事件向上冒泡,catch事件会阻止向上冒泡。
- key:以bind或catch开头,然后跟上事件类型,字母均小写,如bindtap、catchtouchstart。
- value:时间函数名,对应Page中定义的同名函数。找不到同名函数会导致报错。
<view bindtap="tap1"> view1 <view catchtap="tap2"> view2 <view bindtap="tap3"> view3 </view> </view> </view>
点击view3会触发view2,而不会触发view1;点击view2也不会触发view1。
事件对象
如果没有特殊说明,当组件触发事件时,逻辑层绑定该事件的事件处理函数会收到一个事件对象。
<view bindtap="myEvent">view</view>
Page({
myEven:function(e){
console.log(e);
}
});
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MQCaXH1-1594622637164)(D:\文档\近期文件\常用文件\笔记\微信小程序\推文.assets\image-20200626120743563.png)]
事件对象属性基本上可分为三类:
- BaseEvent
- CustomEvent
- TouchEvent。
- BaseEvent为基础事件对象属性,包括:
- type:事件类型
- timeStamp:事件生成时的时间戳,页面打开到触发所经历过的毫秒数
- target:出发事件原组件(即冒泡开始的组件)的相关属性集合,如下:
- id:事件源组件的id
- tagName:事件源组件的类型
- dataset:事件源组件上由data-开头的自定义属性组成的集合
- currentTarget:事件绑定的当前组件的相关属性集合,属性如下:
- id:当前组件id
- tagName:当前组件S类型
- dataSet:当前组件上由data-开头的自定义属性组成的集合
画布组件 <canvas/>
中的触摸事件不可冒泡,所以没有currentTarget。dataSet是组件的自定义数据,以data- 开头,由”-“ 连接,大写会被转为小写,两个单词以上最终被转为驼峰命名形式
<view bindtap="myevent" data-my-name="weixin" data-my-Age="12">
dataset示例
</view>
Page({
myevent:function(e){
console.log(e.currentTarget.dataset);
}
});
测试结果输出自定义属性值
-
CustomEvnet为自定义事件对象(继承BaseEvent)
- detail:额外信息,通常传递组件特殊信息。
-
TouchEvent为触摸事件对象(继承BaseEvnet)
- touches:触摸事件,当前停留在屏幕中的触摸点信息的数组。
- changedTouches:触摸事件,当前变化的触摸点信息的数组,从无变有、从有变无、位置变化。
支持多点触摸,所以touches和changedTouches都是数组格式,每个元素为一个Touch对象(canvas触摸事件中为CanvasTouch对象)
Touch对象相关属性如下:
- identifier:触摸点的标识符
- pageX,pageY:距离文档左上角的距离,文档的左上角为原点,横向为X轴,纵向为Y轴
- clientX,clientY:距离页面可显示区域(屏幕除去导航条)左上角的距离,横向为X轴
CanvasTouch对象相关属性如下:
- identifier:触摸点的标识符
- x,y:距离Canvas左上角的距离,Canvas的左上角为原点
引用
一个WXML可以通过 import或include或引入其他WXML文件,两种方式都能引入WXML文件,区别在于
-
import引入WXML文件后只接受模板的定义,忽略模板定义之外的所有内容,而且使用过程中有作用域的概念。
-
include则是引入文件中除
<template/>
以外的代码直接拷贝到<include/>
位置,与 import相反。
整体来说,import是引入模板,include是引入组件。
-
import(只引用了模板)
src属性是需要被引入文件的相对地址
- a.wxml
<import src="b.wxml"/> <!-- 使用b.wxml中定义的模板 --> <template is="bTemplate" data=""/>
- b.wxml
<view>内容</view> <template name="bTemplate"> <view>b template content</view> </template> <template is="bTemplate"/>
-
include
include引入会将模板定义标签之外的内容直接赋值替换
<include/>
- a.wxml
<include src="b.wxml"/> <!-- 不能调用该模板 --> <template is="bTemplate"/> <!-- 但会显示出b.wxml中<view>的值 -->
- b.wxml
<!-- 会在a.wxml中显示 --> <view>内容</view> <template name="bTemplate"> <view>b template content</view> </template> <!-- 会在a.wxml中显示 --> <template is="bTemplate"/>
测试效果:可以显示出b.wxml中的所有内容。
初始化运行的事件
// 初始化运行
onLoad: function (options) {
this.setData({
times:this.data.times1
})
},
页面样式文件WXSS
WXSS( WeiXin Style Sheets)是基于CSS拓展的样式语言,用于描述WXML的组件样式,具有CSS的大部分特性,在CSS基础上WXSS拓展了尺寸单位、样式导入特性,对CSS选择器属性上做了部分兼容。
-
尺寸单位
-
rpx
在渲染过程中rpx会按比例转化为px,WXSS规定屏幕宽度为750rpx。如手机屏幕宽度为375px,即750rpx = 375px,那么在该手机中1rpx = 0.5px
-
rem
与rpx一样,WXSS规定屏幕宽度为20rem。
设备 rpx换算px (屏幕宽度/750) px换算rpx (750/屏幕宽度) iPhone5 1rpx = 0.42px 1px = 2.34rpx iPhone6 1rpx = 0.5px 1px = 2rpx iPhone6 Plus 1rpx = 0.552px 1px = 1.81rpx 在设计界面时,要实现尺寸自适应,可以用iPhone5作为视觉标准,进行设计。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。
-
-
选择器
选择器 样例 样例描述 .class .intro
选择所有拥有 class=“intro” 的组件 #id #firstname
选择拥有 id=“firstname” 的组件 element view
选择所有 view 组件 element, element view, checkbox
选择所有文档的 view 组件和所有的 checkbox 组件 ::after view::after
在 view 组件后边插入内容 ::before view::before
在 view 组件前边插入内容 示例
/*选择所有class含myClass有的组件,并设置边框*/ .myClass {border: solid 1px #000;} /*选择所有view组件且class含 myClass有的组件,并设置边框*/ view.myClass {border: solid 1px #000;} /*选择所有view组件中子节点class含有myclas子节点含有的组件,并设置边框*/ view .myclass{border: solid 1px #000;} /*选所有 class含有myContent组件中所有checkbox组件和radiobox组件,并设置它们的边框*/ .myContent checkbox, .myContent radiobox {boder: solid 1px #000;} /*选择所有view组件且class含有myClass且含有的组件,在其后面插入新内容,内容为new content*/ view. myclass: after {content: 'new content'}
-
内联样式
跟HTML一样,可以通过style、class属性控制样式。
<!-- 通过style动态设置样式 --> <view style="border:solid 1px #000; background-color:{{color}}"></view> <!-- 通过class选择器设置样式 --> <view class="myClassName1 myClassName"></view>
-
样式导入
为了便于管理,会将WXSS按职能拆分为多个文件,这是需要使用@import语句导入其他WXSS文件,用 " ; "表示语句结束
.common-view{border:solid 1px #000;} @import "common.wxss"; .page-container{padding:10px}
至此,小程序框架页面相关的4个文件已经介绍完成了
模块化
模块化主要解决JavaScript中命名冲突和文件依赖这两个问题。起初JavaScript将代码统一放在一个文件内,但随着代码量的增多出现了很多问题,为了避免全局冲突等问题,决定参考Java的方式,引入命名空间和闭包来解决变量冲突问题。
-
起初代码
var name = 'weixin', age = 12; function getName(){ // 实现代码 } function getAge(){ // 实现代码 }
-
优化后
(function(){ // 定义全局命名空间 myProject = myProject||{}; myProject.user = {}; myProject.user.name = 'weixin'; // 闭包内变量,外部不能访问 var age = 12; myProject.user.getName = function(){ // 实现代码 } myProject.user.getAge = function(){ // 实现代码 } })();
此时可以通过myProject.user获取name,和使用getName、getAge方法,通过命名空间可以缓解大部分冲突,而user这个命名空间被我使用之后,别人就不能使用该空间,并且每次都需要记住一串命名空间,所以该方法依旧存在弊端。
于是,模块化才正式诞生:
- 模块化是一段javas代码,具有统一的基本书写格式
- 模块之间通过基本交互规则,能彼此引用,协同工作
目前模块化大致可分为CommonJS 和 ES6两种规范。
文件作用域
小程序中一个JavaScript文件就是一个模块,在这个文件中声明的变量和函数只在该文件中有效,不同文件中的相同变量名和函数名是不会相互影响的。
模块可以调用一些全局的方法。
- a.js文件
App({
// 在App中定义全局属性
myGlobalData:{
name: 'weixin'
}
});
// 只能在该文件中使用的属性,并为其赋值
var myPrivateData = 'value1';
// 调用全局变量
var appData = getApp();
appData.myGlobalData.name += 'app';
- b.js文件
// 不会和a.js中同名变量冲突
var myPrivateData = 'value2';
// 同样可以调用全局变量,并为其赋值
var appData = getApp();
appData.myGlobalData.name += 'app';
模块的使用
JavaScript写完接口之后,需要将接口进行暴露。模块接口的暴露和引入十分简单:
- 通过exports暴露接口
- 通过require(path)引入依赖,path是需要引入的模块文件的相对路径
示例代码
- a.js
var privateData = 'weixin';
function run(who){
console.log(who + 'run');
}
// 暴露接口
module.exports.run = run;
/**
也可以写成
module.exports = {
run:run
};
*/
- b.js
// 获取暴露的接口
var otherMod = require('a.js');
Page({
onShow:function(){
// 调用接口
otherMod.run('somebody');
}
});
其他
JavaScript运行环境
微信小程序逻辑代码运行在三端:ios、Android和用于调试的开发者工具,这三端是各自不同的三个解析引擎:
- ios:小程序的JavaScript代码运行在JavaScriptCore中。
- Android:小程序的JavaScript代码是通过X5内核来解析。
- 开发者工具:运行在nwjs(chrome内核)中。
虽然环境相似,但一些语法、特性的支持还有一些区别。
小结
小程序框架是小程序开发的核心,小程序这种基于数据的编码方式和前端基于DOM的编码方式有很大不同,要着重了解熟悉框架,了解小程序运行原理之后,后续学习组件和API将会非常简单。