内置组件和基础语法

405 阅读8分钟

常见内置组件

text

Text组件用于显示文本, 类似于span标签, 是行内元素

基本使用

<!-- 当文本最后是以\n结尾的时候,后边的text组件会换行显示 -->
<text>Hello Wolrd\n</text>
<text>Hello Wolrd\n</text>

user-select

<!-- 
  user-select 文本是否可以长按选中
        --- 该效果只能在实体机测试,模拟器无法测试该效果
	--- 除了文本节点以外的其他节点都无法长按选中
        --- 默认值为false
        --- 该属性会使文本节点显示为 inline-block --- 即使以\n结尾也没有用(\n并不会被渲染出来)
        --- 该属性用来替换已废弃的属性selectable
-->
<!-- user-select的值是boolean类型的值,所以设置值的时候需要使用mustache语法 -->
<text user-select="{{ true }}">Hello Wolrd\n</text>
<!-- 上面的写法可以简写为如下形式 -->
<text user-select>Hello Wolrd\n</text>

space

<text>Hello World\n</text>
<!-- nbsp --- 默认值 --- 自动根据字体设置空格大小 -->
<text space="nbsp">Hello World\n</text>
<!-- ensp --- 半个中文字符的大小 --- 比nbsp要大一点点 -->
<text space="ensp">Hello World\n</text>
<!-- emsp --- 一个中文字符的大小 -->
<text space="emsp">Hello World\n</text>

decode

<!-- decode --- 对实体字符(&nbsp; &lt; &gt; &amp; &apos; &ensp; &emsp;)进行解析 -->
<text decode>2 &lt; 5</text>

button

具体配置选项可以查看文档

<!-- 基本使用 --- 默认是块级元素 -->
<button>click me</button>

<!-- 
  size属性 --- 按钮在块级元素和行内元素之间进行切换
      --- default --- 块级元素 --- 默认值
      ---- mini --- 行内元素 
-->
<button size="mini">click me</button>

<!-- 
  type --- 按钮默认提供的样式
    --- primary --- 绿色
    --- default --- 默认值 --- 灰色
    --- warn --- 红色
 -->
<button type="primary">click me</button>

<!-- plan --- 镂空效果 -->
<button plain>click me</button>

<!-- disabled --- 禁用 -->
<button disabled>click me</button>

<!-- loading --- 是否存在加载icon -->
<button loading>click me</button>

<!-- hover-class --- 按钮被按下的时候显示的样式 -->
<!-- 
	在home.wxss中设置的对应样式
	
	.hover {
    color: white;
    background-color: blue;
  }
-->
<button hover-class="hover">click me</button>

view

视图组件(块级元素,独占一行,通常用作容器组件) --- 相对于html中的div元素

具体配置选项可以查看文档

<!-- 基本使用 -->
<view>view组件</view>
<!-- 
  hover-class --- 按下去显示的样式
  hover-start-time --- 按住后多久出现点击态,单位毫秒
  hover-stay-time --- 手指松开后点击态保留时间,单位毫秒
  hover-stop-propagation --- 指定是否阻止本节点的祖先节点出现点击态
      --- hover-stop-propagation阻止的是点击态的传递,并不会阻止事件的冒泡
-->
<view 
  class="outer"
  hover-class="outer-hover"
  hover-start-time="{{ 100 }}"
  hover-stay-time="{{ 0 }}" 
>
 <view 
  class="inner"
  hover-class="inner-hover"
  hover-stop-propagation
>
  view组件
 </view>
</view>

image

Image组件用于显示图片

具体配置选项可以查看文档

<!-- 1.image的基本使用 -->
<!-- 
  重点:
    1.image组件可以写成单标签,也可以修成双标签 --- 单标签的时候必须有闭合符号
    2.image组件默认有自己的大小: 320x240
    3.image组件时一个行内块级元素(inline-block)
 -->
 <image />
<!-- 2.src: 本地路径(相对路径/绝对路径)/远程地址 -->
<image src='../../assets/detail/shop.png'/>
<!-- 对于绝对路径 --- 根目录对应的是项目的根目录 -->
<image src='/assets/detail/shop.png'/>
<!-- 网络图片 -->
<image src='https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg'/>
<!-- 
  webp --- 默认不支持webp图片 --- 如果需要支持 需要手动开启
  binderror --- 当错误发生时触发,event.detail = {errMsg} 
  bindload ---  当图片载入完毕时触发,event.detail = {height, width}
 -->
<image 
  src='https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg'
  bindload="handleLoad"
/>

<!-- 选取相册中图片 -->
<button bindtap='handleChooseAlbum'>选中图片</button>
<image src="{{imagePath}}"/>
Page({
  data: {
    imagePath: ''
  },

  handleLoad(e) {
    console.log(e.detail)
  },

  handleChooseAlbum() {
    // 选择相册中的图片 或 拍照选取图片(真机)
    wx.chooseImage({
      // 最多可以选取9张
      count: 9,
      // 使用箭头函数,以保证内部使用的this指向正确
      success: res => {
        this.setData({
          imagePath: res.tempFilePaths[0]
        })
      }
    })
  }
})
<!-- 
  lazy-load: 图片的懒加载 
    --- 在即将进入一定范围(上下三屏)时才开始加载
    --- 也就是在可视窗口的上边和下边都有一个一样宽度和高度的窗口
    --- 当图片进入到这个范围的时候,图片会自动开始加载操作
-->
<image 
  wx:for="{{10}}"
  wx:key="item"
  src="https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg"
  bindload='handleImageLoad'
  lazy-load
/>
<!-- 
  4.show-menu-by-longpress: 长按图片显示发送给朋友、收藏、保存图片、识别小程序码的菜单
  识别小程序码的菜单 --- 模拟器上会根据图片中有没有小程序码自动识别是否需要显示
  在模拟器中点击是没有任何作用的,测试需要在真机上执行
-->
<image 
  show-menu-by-longpress
  src="https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg"
/>

mode

mode选项可以设置,图片在image组件中的图片裁剪、缩放的模式,具体可以设置的值和示例可以查看文档

input

具体配置选项可以查看文档

<!-- 
	1.基本使用 
			--- 默认背景色是透明的,而且没有边框,所以可能没有显示效果,但它是真实存在的 
			--- 其也可以设置单标签 或者 设置为 双标签
-->
<input />

<!-- 2.value: input中的默认值 --- 虽然文档说是必传的,但是不设置也是可以的,默认值就是空字符串 -->
<input value='哈哈哈'/>

<!-- 
	3.type: 决定键盘类型(文本输入键盘/数字/身份证/带小数点的数字键盘/昵称输入键盘)
		--- 默认值 text --- 文本输入键盘
		--- 需要在真机进行测试
    --- 如果类型是safe-password的时候,需要根据官方文档教程对秘钥进行配置,在传输内容的时候其会自动将内容进行加密操作
-->
<input type='number'/>

<!-- 4.password: 是否使用暗文显示 -->
<input password/>

<!-- 5.placeholder: 占位文字 -->
<input placeholder='请输入内容'/>

<!-- 	
  confirm-type --- 设置键盘右下角按钮的文字,仅在type='text'时生效
  可选值为 --- send/search/next/go/done  --- 默认值为done
 -->
<input type="text" confirm-type="search" />

事件绑定

LHncVQ.png

<!-- 6.input绑定事件 -->
<input 
  bindinput='handleInput'
  bindfocus='handleFocus'
  bindblur='handleBlur'
  bindconfirm="handleConfirm"
 />
Page({
  handleInput(e) {
    console.log('input', e.detail)
  },

  handleFocus(e) {
    console.log('focue', e.detail)
  },

  handleBlur(e) {
    console.log('blur', e.detail)
  },

  handleConfirm(e) {
    console.log('confirm', e.detail)
  }
})

scroll-view

<!-- 
  默认情况下,scroll-view是不可以滑动的
  如果需要滑动,需要加上scroll-x 或 scroll-y属性
	
  如果不设置scroll-x 或 scroll-y的时候
  scroll-view的特性和view是一致的
	所以如果需要垂直滚动,最好为scroll-view设置一个高度
-->
<scroll-view class="container" scroll-y>
  <view class="card" />
  <view class="card" />
  <view class="card" />
  <view class="card" />
  <view class="card" />
  <view class="card" />
  <view class="card" />
  <view class="card" />
</scroll-view>
<scroll-view 
  class="container" 
  scroll-x
  bindscrolltolower="scrolltolower"
  bindscrolltoupper="scrolltoUpper"
  bindscroll="handleScroll"
>
  <view wx:for="{{ 10 }}" wx:key="item" class="card" />
</scroll-view>
Page({
  scrolltolower(e) {
    console.log('lower', e.detail)
  },

  scrolltoUpper(e) {
    console.log('upper', e.detail)
  },

  handleScroll(e) {
    console.log('scroll', e)
  }
})

所有组件共有的属性

所有wxml标签(组件)都支持的属性称之为共同属性,有如下共同属性

LJGR7L.png

<!-- 
  hidden属性 只是为元素添加上了display为none
  组件依旧会被解析和渲染
  如果要组件完全不解析和渲染,推荐使用wx;if
-->
<view hidden>hidden</view>

WXML + WXSS + WXS

WXSS

页面样式的三种写法: 行内样式、页面样式、全局样式

三种样式都可以作用于页面的组件

如果在一个组件上同时拥有者三种样式,他们的优先级是: 行内样式 > 页面样式 > 全局样式

home.wxml

<!-- 行内样式 -->
<view style="color: red;" class="fontColor">样式</view>

home.wxss

/* 页面样式 */
.fontColor {
  color: blue;
}

app.wxss

/* 全局样式 */
.fontColor {
  color: gray;
}

wxss中的样式选择器的优先级和css中的样式选择器的优先级是一致的

LJGW2U.png

样式引入

在某些情况下,我们可能会将样式分在多个wxss文件中,方便对样式的管理。

这个时候,我们就可以使用样式导入,来让单独的wxss生效

/*
	使用@import进行导入
  @import后跟需要导入的外联样式表的相对路径(或者绝对路 径也可以),用;表示语句结束。
*/
@import '/style/foo.wxss';

rpx

rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx

在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

所以建议在开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准

.font {
  /*
  	在iPhone6上 40rpx === 20px
  */
  font-size: 40rpx;
}

总结: 假设页面的宽度为page px ---> page px = 750rpx ---> 1px=750 rpx/page

官方样式库

为了减少开发者样式开发的工作量,小程序官方提供了WeUI-WXSS基本样式库,具体使用方式可以查阅文档

WXML

mustache

WXML: 基本格式

  • 类似于HTML代码: 比如可以写成单标签,也可以写成双标签
  • 必须有严格的闭合: 没有闭合会导致编译错误
  • 大小写敏感: class和Class是不同的属性
<button size="mini" bindtap="handleClick">click me</button>
<view class="foo {{ isActive ? 'active' : '' }}">foo</view>
Page({
  data: {
    isActive: false
  },

  handleClick() {
    this.setData({
      isActive: !this.data.isActive
    })
  }
})
.active {
  color: red;
}

逻辑判断 wx:if wx:elif wx:else

<input value="{{ score }}" type="text" bindinput="handleInput" />
<view wx:if="{{ score > 80 }}">优秀</view>
<view wx:elif="{{ score > 60 }}">及格</view>
<view wx:else>不及格</view>
Page({
  data: {
    score: 0
  },

  handleInput(e) {
    this.setData({
      score: e.detail.value
    })
  }
})
hidden 和 wx:if 的区别:

hidden --- 为组件添加一个名为hidden的属性,并使其display的值为none

wx:if --- 如果值为false,那么组件对应的值压根就不会被渲染

如果一个组件的切换是非常频繁的时候,推荐使用hidden来隐藏元素。否则推荐使用wx:if来隐藏对应的元素

列表渲染 – wx:for

在组件中,我们可以使用wx:for来遍历一个数组 (字符串 - 数字)

  • 默认情况下,遍历后在wxml中可以使用一个变量index,保存的是当前遍历数据的下标值
  • 数组中对应某项的数据,使用变量名item获取
<!-- 遍历数组 --- item 和 index是默认提供的变量 -->
<view wx:for="{{ ['Klaus', 'Alex', 'Steven'] }}" wx:key="item">{{ index }} --- {{ item }}</view>

<!-- 字符串会被作为字符数组进行遍历 -->
<view wx:for="Klaus"  wx:key="item">{{ index }} --- {{ item }}</view>

<!-- 数字会被作为从0开始依次递增的数组 如3会被识别为[0, 1, 2] -->
<view wx:for="{{ 10 }}"  wx:key="item">{{ index }} --- {{ item }}</view>

<!-- *this表示的就是就是item,也就是循环遍历的每一个元素 -->
<view wx:for="users"  wx:key="*this">{{ index }} --- {{ item }}</view>

<!-- 
   如果遍历的是一个对象,且key是字符串的时候,那么这个字符串可以被认为是对象的属性
   例如这里的id 对应的就是item.id
-->
<view wx:for="users"  wx:key="id">{{ index }} --- {{ item }}</view>
block

某些情况下,我们需要使用 wx:if 或 wx:for时,可能需要包裹一组组件标签,这个使用我们就可以使用block标签来替代view等标签

block的功能类似于vue中的template或react中的fragment

<block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性( 如wx:if 或 wx: for )

block标签的好处

  • 将需要进行遍历或者判断的内容进行包裹
  • 将遍历和判断的属性放在block便签中,不影响普通属性的阅读,提高代码的可读性
  • 相比view,使用block进行包裹,可以提升性能

虽然wx:for在遍历的时候,为我们提供了默认的变量itemindex,但是某些情况下,我们可能想使用其他名称,尤其是在出现多层遍历时,名字重复的时候

这个时候,我们可以为item和index其别名

<view 
  wx:for="{{ ['Klaus', 'Alex', 'Steven'] }}" 
  wx:key="item"
  wx:for-item="user"
  wx:for-index="i"
>
  {{ i }} --- {{ user }}
</view>

key

小程序在渲染的过程中,也使用了虚拟DOM,所以在小程序中进行遍历的时候,依旧需要加上key属性,其功能和vue和react中添加key属性的作用是一致的

当存在key属性后,小程序可以更好的识别对应的VDOM(key一致且节点类型一致的时候),此时在进行DIFF算法的时候,可以更好的识别新旧VNode

所以一句话,key的作用主要是为了高效的更新虚拟DOM

LS7yFf.png

template

WXML提供**模板(template),**可以在模板中定义代码片段,在不同的地方调用

template标签主要用在小程序的早期版本中,因为小程序的早期版本并不支持组件化

<!-- 
  如果一个template没有进行任何的使用,其本身是不会进行任何的渲染的
  我们可以为其提供name属性 --- 为template设置一个名字
 -->
<template name="userInfo">
  <view>{{ userName }}</view>
  <view>{{ userAge }}</view>
</template>

<!-- 使用 
       通过is属性进行模板的关联 
       通过data属性进行props传递 --- 注意: data是一个没有大括号的对象(★★★)
-->
<template is="userInfo"  data="{{ userName: 'Klaus', userAge: 23 }}" />
引入

小程序wxml中提供了两种引入文件的方式: import和include

  • Import引入: import 可以在该文件中使用目标文件定义的 template ---- 先引入后使用

  • include: 可以将目标文件中除了 <template/> <wxs/> 外的整个代码引入 --- 引入即使用

import

<template name="userInfo">
  <view>{{ userName }}</view>
  <view>{{ userAge }}</view>
</template>
<!-- 
  可以使用import引入template模板
  src --- 模板路径 --- 可以是相对路径或绝对路径 --- 后缀名wxml可以省略
-->
<import src="/wxml/template" />

<template is="userInfo"  data="{{ userName: 'Klaus', userAge: 23 }}" />

include

<view>Klaus</view>
<view>23</view>
<!-- 
  可以使用include来引入非wxs和template的代码片段 --- 功能类似于mixin
  src --- 模板路径 --- 可以是相对路径或绝对路径 --- 后缀名wxml可以省略
-->
<include src="/wxml/foo" />

WXS

**WXS(WeiXin Script)**是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构

WXS 代码可以编写在 wxml 文件中的 <wxs> 标签内,或以 .wxs 为后缀名的文件内 --- WXS的两种使用方式

为什么要设计WXS语言呢?

  • 在WXML中是不能直接调用Page/Component中定义的函数的
  • 但是某些情况, 我们可以希望使用函数来处理WXML中的数据,这个时候就使用WXS

WXS使用的限制和特点

  • WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API

    ​ --- 不可以直接调用JavaScript代码

  • WXS 函数不能作为组件的事件回调

​ ----- 不可以作为事件响应函数使用

  • 由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备 上二者运行效率无差异
<!--
	在WXML的mustache语法中,不可以直接使用JavaScript语法,如果要使用必须结合WXS
-->
<view>{{ 12.6667.toFixed(3) }}</view>

页面内直接定义

<!-- module属性用来为wxs模块起名称 -->
<wxs module="format">
 // 在WXS中不支持ES6的语法 
 var priceFormat = function(price, num) {
    num = num || 2
    price = parseFloat(price || 0)
  
    return price.toFixed(num)
 }

 // WXS使用node环境作为沙盒执行环境 --- 所以导出方式必须使用CJS的模块导出方式  
 module.exports = {
   priceFormat: priceFormat
 }
</wxs>

<view>{{ format.priceFormat(16.66667, 2) }}</view>

外部引入

format.wxs

var priceFormat = function(price, num) {
  num = num || 2
  price = parseFloat(price || 0)
  
  return price.toFixed(num)
}

module.exports = {
 priceFormat: priceFormat
}

home.wxml

<!-- module属性用来为wxs模块起名称 -->
<wxs src="/wxs/format.wxs" module="format" />

<view>{{ format.priceFormat(16.66667, 2) }}</view>

事件

小程序需要经常和用户进行某种交互,比如点击界面上的某个按钮或者区域,比如滑动了某个区域, 这些交互都会产生各种各样的事件

<!-- 事件绑定的四个基本方式 -->
<view bindtap="handleTap">click me</view>
<view bind:tap="handleTap">click me</view>
<view catchtap="handleTap">click me</view>
<view catch:tap="handleTap">click me</view>

某些组件会有自己特性的**事件类型,**大家可以在使用组件时具体查看对应的文档

  • 比如input有bindinput/bindblur/bindfocus等
  • 比如scroll-view有bindscrolltowpper/bindscrolltolower等

但有些事件是所有组件都有的, 并且也比较常见的事件类型

LS28vf.png

  • touchcancel: 在某些特定场景下才会触发(比如来电打断等)

  • tap事件和longpress事件通常只会触发其中一个

<view 
  bind:touchstart="handleTouchStart"
  bind:touchmove="handleTouchMove"
  bind:touchend="handleTouchEnd"
  bind:tap="handleTap"
  bind:longpress="handleLongpress"
>
  click me  
</view>
Page({
  handleTouchStart() {
    console.log('handleTouchStart')
  },
  handleTouchMove() {
    console.log('handleTouchMove')
  },
  handleTouchEnd() {
    console.log('handleTouchEnd')
  },
  handleTap() {
    console.log('handleTap')
  },
  handleLongpress() {
    console.log('handleLongpress')
  }
})

对于轻击事件,事件触发顺序为: touchstart -> touchend -> tap

对于长按事件,事件触发顺序为: touchstart -> longpress -> touchend

事件对象

当某个事件触发时, 会产生一个事件对象, 并且这个对象会被传入到对应事件的回调函数中

LS2ceO.png

touches和changedTouches

touches --- 触发事件时候手指的个数组成的数组

changedTouches --- 触发事件的时候,相比之前,变化的手指个数

在绝大多数情况下,touches和changedTouches的值是一致的

但在以下情况下,这两个值有所差异:

  1. 在touchend中
  2. 在多手指触摸时

LS2Usw.png

currentTarget和target

target --- 产生事件的那个dom元素

currentTarget --- 实际触发事件的那个dom元素

LS2bh8.png

事件参数的传递

当视图层发生事件时,某些情况需要事件携带一些参数到执行的函数中, 这个时候就可以通过data-属性(HTML原生功能)来完成

  • 格式: data-属性的名称
  • 获取: e.currentTarget.dataset.属性的名称
<view bind:tap="handleTap" data-name="Klaus">
  click me  
</view>
Page({
  handleTap(e) {
    console.log(e.target.dataset.name)
  }
})

事件冒泡和事件捕获

当界面产生一个事件时,事件分为了捕获阶段和冒泡阶段

LS2Jze.png

在事件执行的时候,会先从外向内进行事件捕获,在从内往外进行事件冒泡

<!--
	bind事件名 --- 对应事件在冒泡的时候被触发
  capture-bind:事件名 --- 对应事件在捕获阶段被触发
	注意: bind事件名和bind:事件名是等价的
			 但是capture-bind:事件名 必须存在冒号,也就是说capture-bin事件名 对应的事件回调是不会被正确触发的
-->
<view class="outer" bindtap="outerTap" capture-bind:tap="captureOuterTap">
  <view class="inner" bindtap="innerTap" capture-bind:tap="captureInnerTap" />
</view>
Page({
  outerTap() {
   console.log('outerTap')
  },
  
  innerTap() {
    console.log('innerTap')
  },
  
  captureOuterTap() {
    console.log('captureOuterTap')
  },

  captureInnerTap() {
    console.log('captureInnerTap')
  }
})

阻止冒泡

在WXML中可以使用catch事件名来阻止事件传递

catch事件名catch:事件名 是等价的

<view class="outer" bindtap="outerTap" capture-bind:tap="captureOuterTap">
  <!-- 使用catchtap来在冒泡阶段阻止事件冒泡 -->
  <view class="inner" catchtap="innerTap" capture-bind:tap="captureInnerTap" />
</view>
<view class="outer" bindtap="outerTap" capture-bind:tap="captureOuterTap">
  <!-- 使用capture-catch:tap来在捕获阶段阻止事件冒泡 -->
  <view class="inner" bindtap="innerTap" capture-catch:tap="captureInnerTap" />
</view>