微信小程序(三)数据绑定&列表渲染&事件绑定&导航栏&常用API&生命周期&模板&组件

这里新建个页面log,然后用这个页面进行测试。 同时修改app.json,将log 页面设置为首页

  "pages": [
    "pages/index/index",
    "pages/log/log"
  ],
  "entryPagePath": "pages/log/log",

0. 数据绑定

0. 简单的绑定

wxml 用 {{val}} 取变量

<!--pages/log/log.wxml-->
<text>pages/log/log.wxml</text>
<view>{{data1}}</view>
<!--数据绑定-->
<view>
  <!--单项绑定-->
  <input type="text" placeholder="username1" value="{{username1}}"/>
  <!--双项绑定-->
  <input type="text" placeholder="username2" model:value="{{username2}}"/>
</view>

js 里面的data:

  data: {
    data1: '测试1',
    username1: '张三',
    username2: '张三'
  },

简单理解:

(1). 单项绑定: 数据只能从 data -> view。

(2). 双向绑定: 数据能从view -> data。 也就是页面修改元素值,对应data 的值,也能改变。这种一般用于表单元素。

例子:

补充:

1. <input model:value="{{ a.b }}" />  这样的表达式目前暂不支持。
2. 可以自己实现:
wxml: 
  <van-field
    model:value="{{ task.content }}"
    data-gater="task.content" 
    bindinput="inputFrame"
    label="任务: "
    type="textarea"
    placeholder="请输入任务"
    autosize
    border="{{ false }}"
  />
js: inputFrame
  inputFrame(e) {
    this.setData({[`${e.currentTarget.dataset.gater}`]: e.detail});
  }

1. 数据修改&js获取

Js 中修改:

this.setData({username1: '张三2', 'newKey': '1122'});

这个是字段覆盖,如果没有字段会新增字段。 不会影响其他的key。
js中获取:

this.data.key1...

2. 数据劫持原理

通过Object.defineproperty 进行劫持,get、set 方法。

<script type="text/javascript">
	let data = {
		"username": "张三",
		"age": 25
	}

	// 模拟组件对象
	let _this = {
		"sex": "男"
	}
	// 数据劫持
	for (let item in data) {
		Object.defineProperty(_this, item, {
			// 用来获取扩展属性值, 获取_this 的这个属性,一定需要调用get 方法
			get() {
				console.log('get', item)
				return data[item];
			},
			set(newValue) {
				console.log('set', item);
				data[item] = newValue;
			}
		})
	}
	console.log(_this)

	_this.username = "李四";
	_this.sex = "女";
	console.log("======")
	console.log(_this)
</script>

控制台打印:

​ 可以看到通过劫持的属性不会直接打印,劫持的属性点击后面的... 获取属性的时候才会调用到 get 方法。

​ 修改属性会调用到set,set 里面需要自己修改原data数据,这样才能和get 对应上。

3. 和vue 数据绑定区别

vue:

双向绑定:v-model    数据可以从data流向页面,也可以从页面流向data。一般应用在表单类元素上(input,select...)
单向绑定: v-bind    单向绑定只能从data修改页面中的值,不能从页面修改data的值。

双向绑定原理:
vue 通过数据劫持的方式实现双向绑定。当一个vue 实例被创建时,vue 会对其数据对象进行递归遍历,将每个属性转换为getter/setter,并且在内部维护一个依赖列表。当数据发生变化时,vue 会通知依赖列表的所有watcher 对象,让他们更新视图。

4. 绑定到this 对象上,和页面生命周期一样,单例

  onLoad(options) {
    this.customKey = {"username":"zs", "age": 25};
    console.log(this)
  },

结果: 和data 同级,使用的时候可以 this.customKey.age...

1. 列表渲染

if、for 语句简单使用。 对于for 循环,一般需要用wx:key="unique" 为多个相同的元素指定唯一的key。

wxml

<!--pages/log/log.wxml-->
<text>pages/log/log.wxml</text>
<!--if-->
<view wx-if="{{data1}}">data1: {{data1}}</view>
<!--if...else-->
<view wx-if="{{data2}}">data2: {{data2}}</view>
<view wx:else>data2:为空</view>

<!--
  for 循环,默认是下标是index、元素是item。 
  可以使用 wx:for-item 可以指定数组当前元素的变量名,使用 wx:for-index 可以指定数组当前下标的变量名:
-->
<view wx:for="{{messages}}">
  {{index}}: {{item.message}}
</view>

Data:

  /**
   * 页面的初始数据
   */
  data: {
    data1: '测试1',
    data2: '',
    messages: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  },

结果:

hidden 用于隐藏元素。hidden 使用, 只能对块级元素生效,对 flex 布局的元素不生效。 如果想生效,可以display 属性自己隐藏。

<view hidden="{{hiddenView}}">动态显示与隐藏</view>

2. 事件绑定

下面有几个注意点:

1. bind  的事件会冒泡, catch会阻止冒泡
2. 当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。
属性	类型	说明
type	String	事件类型	
timeStamp	Integer	事件生成时的时间戳	
target	Object	触发事件的组件的一些属性值集合	
currentTarget	Object	当前组件的一些属性值集合	
mark	Object	事件标记数据
3. Event.target 和 event.currentTarget 的区别
a) Event.target 是触发事件的对象,但不一样是绑定事件的对象,如: 事件委托,冒泡
b) currentTarget 触发时间的对象一定是绑定事件的对象, 没有事件委托
4. 参数绑定
需要在wxml 对应标签用 data-key="value" 的形式进行绑定, 获取的时候用 event.target.dataset.key || event.currentTarget.dataset.key

xml:

<!--pages/log/log.wxml-->
<text>pages/log/log.wxml</text>

<button bindtap="handleTap">
    Click here!
</button>

<!--绑定事件为动态的函数名称-->
<button catchtap="{{handlerName}}">
    Click here2!
</button>

<!--绑定事件为动态的函数名称,且传递参数-->
<button catchtap="{{handlerName}}" data-name="张三" data-age="20">
    Click here3!
</button>

Js

// pages/log/log.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    handlerName: 'handleTap'
  },

  /** S 自定义函数 */ 
  handleTap: (e) => {
    console.log(e);
    wx.showModal({
      title: '提示',
      content: '这是一个模态弹窗',
      success (res) {
        if (res.confirm) {
          console.log('用户点击确定')
        } else if (res.cancel) {
          console.log('用户点击取消')
        }
      }
    })  
  }
  /** E 自定义函数 */ 
})

依次点击,查看1、2、3,查看输出:

3. 导航栏

修改app.json 文件,设置导航栏。 例如:
entryPagePath 是默认的首页,如果不设置entryPagePath ,默认是pages 数组的第一个。

{
  "pages": [
    "pages/index/index",
    "pages/log/log",
    "pages/log2/log2"
  ],
  "entryPagePath": "pages/log/log",
  "window": {
    "navigationBarBackgroundColor": "#ddd",
    "navigationBarTextStyle": "white",
    "navigationBarTitleText": "Geogre"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    },{
      "pagePath": "pages/log/log",
      "text": "日志"
    },{
      "pagePath": "pages/log2/log2",
      "text": "日志2"
    }]
}
}

另外:iconPath、selectedIconPath 可以设置未激活和激活状态的图标。

效果:

4. 常用API

小程序提供了很多实用的方法,都存在全局对象wx 对象中。

1. 路由跳转

在小程序中所有页面的路由全部由框架进行管理。框架以栈的形式维护了当前的所有页面。 当发生路由切换的时候,页面栈的表现如下:

开发者可以使用 getCurrentPages() 函数获取当前页面栈。

路由跳转:

-- 页面切换. (url 不能是app.json的tabBar 配置的导航页面)
wx.navigateTo(Object object):保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。小程序中页面栈最多十层。 也就是上面菜单上有后退按钮,可以返回上个页面。
wx.redirectTo(Object object):关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。不支持后退,只能回到主页面。
-- 切换tab (url 必须是app.json的tabBar 配置的导航页面)
wx.switchTab(Object object):跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
-- 任意切换
reLaunch 关闭所有页面,打开到应用内的某个页面

总结:
navigateTo, redirectTo 只能打开非 tabBar 页面。
switchTab 只能打开 tabBar 页面。
reLaunch 可以打开任意页面。
页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
调用页面路由带的参数可以在目标页面的onLoad中获取。

比如:

 handleTap: (e) => {
    wx.navigateTo({
      url: '/pages/log2/log2',
    }) 
  },
  handleTap2: (e) => {
    wx.switchTab({
      // 切换导航,需要在app.json的tabBar 配置导航页面
      url: '/pages/index/index',
    }) 
  }

也可以用navigator 进行跳转

<navigator url="/pages/index/index" open-type="reLaunch" class="navigator">
  <button>跳转到首页</button>
</navigator>

携带参数: 在路径上加参数,但是注意参数超过一定长度会被截断,所以一般传ID参数。 还可以用本地存储共享变量的方式。
    wx.navigateTo({
      url: '/pages/log3/log3?key1=value1&key2=value2',
    })

log3 的onLoad 方法的options 就是参数(是一个对象)

  onLoad(options) {
    console.log(options)
  },
    
// 结果
{key1: "value1", key2: "value2"}

通过appData 选项卡,也可以看到当前存活的页面,可以知道原来页面是销毁还是隐藏:

2. 界面交互

a) 显示消息提示框: wx.showToast() 
b) 显示消息加载框: wx.showLoading(), 必须显示的调用hideLoading() 才能隐藏
c) 关闭消息提示框: wx.hideToast()
d) 关闭消息加载框: wx.hideLoading()
e) 模态框:wx.showModal(Object object)

例如:

 /** S 自定义函数 */ 
  handleTap: (e) => {
    wx.showToast({
      title: '成功',
      icon: 'success',
      duration: 2000
    })
  },
  handleTap2: (e) => {
    wx.showLoading({
      title: '加载中',
    })
    
    setTimeout(function () {
      wx.hideLoading()
    }, 2000)
  },
  handleTap3: (e) => {
    wx.showModal({
      title: '提示',
      content: '这是一个模态弹窗',
      success (res) {
        if (res.confirm) {
          console.log('用户点击确定')
        } else if (res.cancel) {
          console.log('用户点击取消')
        }
      }
    })
  }
  /** E 自定义函数 */ 

3. 网络请求

  1. 语法: wx.request()
  2. 注意点:
    1. 协议必须是https协议
    2. 一个接口最多配置20个域名
    3. 并发限制上限是10个
    4. 开发过程中设置不校验合法域名: 开发工具 ---> 右上角详情 ----> 本地设置 ---> 不校验

例如:

 handleTap3: (e) => {
    wx.request({
      url: 'http://localhost:8090/template/list',
      data: {
        x: '',
        y: ''
      },
      method: 'GET', // 默认值
      dataType: 'json', // 对返回值调用JSON.parse(), 默认值
      header: {
        'content-type': 'application/json' // 默认值
      },
      success (res) {
        console.log(res.data)
      }
    })
  }
  1. 下载文件
wx.downloadFile(Object object)
  1. 上传文件
wx.uploadFile(Object object): 将本地资源上传到服务器。客户端发起一个 HTTPS POST 请求,其中 content-type 为 multipart/form-data。
  1. 其他

也有websocket,tcp,udp 相关接口。

4. 本地存储

  1. 语法:
带sync 的是同步版本,不带的是异步获取,需要在成功回调方法编写相关逻辑
--- 设置
wx.setStorageSync(): 将数据存储在本地缓存中指定的 key 中。会覆盖掉原来该 key 对应的内容。除非用户主动删除或因存储空间原因被系统清理,否则数据都一直可用。单个 key 允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB。
wx.setStorage(): 同上面,只不过是异步操作的。

-- 获取
wx.getStorageSync()
wx.getStorage()

-- 获取所有的
getStorageInfo | getStorageInfoSync。 返回值:
属性	类型	说明
keys	Array.<string>	当前 storage 中所有的 key
currentSize	number	当前占用的空间大小, 单位 KB
limitSize	number	限制的空间大小,单位 KB

-- 删除
a) wx.removeStorage() 异步
b) wx.removeStroageSync() 同步
-- 清空
c) wx.clearStorage() 异步
d) wx.clearStorageSync() 同步
  1. 注意点:
1. 建议存储的数据为json数据
2. 单个 key 允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB
3. 属于永久存储,同H5的localStorage一样
  1. 例如:
// pages/log/log.js
Page({
  /** S 自定义函数 */ 
  handleTap: (e) => {
    wx.getStorageInfo({
      success (res) {
        console.log(res.keys)
        console.log(res.currentSize)
        console.log(res.limitSize)
      }
    })

    const res = wx.getStorageInfoSync()
    console.log("getStorageInfoSync", res.keys)
    console.log("getStorageInfoSync", res.currentSize)
    console.log("getStorageInfoSync", res.limitSize)
  },
  handleTap2: (e) => {
    wx.setStorageSync('key1', 'value1')

    // 开启加密存储
    wx.setStorage({
      key: "key2",
      data: "value2",
      encrypt: true, // 若开启加密存储,setStorage 和 getStorage 需要同时声明 encrypt 的值为 true
    })
  },
  handleTap3: (e) => {
    wx.getStorage({
      key: "key2",
      encrypt: true, // 若开启加密存储,setStorage 和 getStorage 需要同时声明 encrypt 的值为 true
      success(res) {
        console.log("getStorage", res.data)
      }
    })

    var value = wx.getStorageSync('key1')
    console.log("getStorageSync", value)
  }
  /** E 自定义函数 */ 
})

查看getStorageInfo 和 getStorageInfoSync返回控制台:

也可以在调试器的storage 板块查看本地存储:

5. 生命周期

下图说明了页面 Page 实例的生命周期。引用官网的图:

我们新建一个页面也可以看到默认生成的JS函数,如下:

// pages/log3/log3.js
Page({

  /**
   * 页面的初始数据
   */
  data: {

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  }
})

这里需要注意:

​ 一般数据的初始化写在:onLoad 方法内部;onShow、onHide 可能会调用多次(在路由使用navigateTo 跳转时会保留当前页面,后退会继续回到当前页面调用onShow 方法), onLoad和onUnLoad、onRead 只会调用一次。

6. 模板使用

  1. 模板wxml

pages目录新建template/template1.1xml

<template name="msgItem">
  <view>
    <view>{{index}}: {{msg}}</view>
    <view>Time: {{time}}</view>
  </view>
</template>

<template name="messages">
  <view wx:for="{{messages}}">
  {{index}}: {{item.message}}
</view>
</template>
  1. 页面引入
<import src="../template/template1"></import>
<!--pages/log/log.wxml-->
<text>pages/log/log.wxml</text>

<button bindtap="handleTap">
    Click here!
</button>

<button bindtap="handleTap2">
    Click here2!
</button>

<button bindtap="handleTap3">
    Click here3!
</button>


<template is="msgItem" data="{{...item}}"></template>
<view>=====</view>
<template is="messages" data="{{messages}}"></template>

Js 数据:

  data: {
    item: {
      index: 0,
      msg: "测试消息",
      time: "0709"
    },
    messages: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  },

结果:

7. 自定义组件

​ 开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。

​ 注意先修改project.config.json里面setting 模块:

"ignoreDevUnusedFiles": false

1. 创建自定义组件

​ 类似于页面,一个自定义组件由 json wxml wxss js 4个文件组成。要编写一个自定义组件,首先需要在 json 文件中进行自定义组件声明(将 component 字段设为 true 可将这一组文件设为自定义组件):

{
  "component": true
}

wxml

<!--pages/component/cus-table/cus-table.wxml-->
<text>pages/component/cus-table/cus-table.wxml</text>
<!-- 这是自定义组件的内部WXML结构 -->
<view class="inner">
  {{innerText}}
</view>

<view class="inner">
  {{innerLabel}}
</view>
<slot></slot>

wxss

/* pages/component/cus-table/cus-table.wxss */
/* 这里的样式只应用于这个自定义组件 */
.inner {
  color: red;
}

js

// pages/component/cus-table/cus-table.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 这里定义了innerText属性,属性值可以在组件使用时指定
    innerText: {
      type: String,
      value: 'default value',
    }
  },
  data: {
    // 这里是一些组件内部数据
    someData: {},
    innerLabel: "内部数据"
  },
  methods: {
    // 这里是一个自定义方法
    customMethod: function(){
      
    }
  }
})

2. 使用组件

  1. json
{
  "usingComponents": {
    "cus-table": "/pages/cus-component/cus-table/cus-table"
  }
}
  1. wxml
<view>
  <!-- 以下是对一个自定义组件的引用 -->
  <cus-table inner-text="Some text"></cus-table>
</view>

自定义组件的 wxml 节点结构在与数据结合之后,将被插入到引用位置内。这里注意标签名称和传递的参数必须和组件声明时候对应上。

8. 参考:

小程序相关文档:https://developers.weixin.qq.com/miniprogram/dev/framework/

双向绑定:https://developers.weixin.qq.com/miniprogram/dev/framework/view/two-way-bindings.html

事件:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html

模板:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/template.html

posted @ 2023-07-10 21:35  QiaoZhi  阅读(198)  评论(0编辑  收藏  举报