页面的基本结构
入口文件初始化
app.json
{
"pages": [
"pages/welcome/welcome" // 引导指向文件
// 这个地方也具备新建页面功能: 起个名字保存一下,即可新建页面
//注:这里的注释仅做博客记录,真正的json文件中不可以使用注释
],
"window":{ //配置窗口样式
"navigationBarBackgroundColor":"#405f80"
},
"tabBar": { //tabar
"borderStyle": "white",
"color": "#949494",
"selectedColor": "#1F4BA5",
"list": [
{
"pagePath": "pages/posts/post",
"text": "文章",
"iconPath":"images/tab/yuedu.png",
"selectedIconPath":"images/tab/yuedu_hl.png"
},{
"pagePath": "pages/movies/movies",
"text": "电影",
"iconPath": "images/tab/dianying.png",
"selectedIconPath": "images/tab/dianying_hl.png"
}]
}
}
js脚本文件
Page({
/* 页面的初始数据 */
data: {
},
/*生命周期函数--监听页面加载*/
onLoad: function (options) {},
/*生命周期函数--监听页面初次渲染完成*/
onReady: function () {},
/*生命周期函数--监听页面显示*/
onShow: function () {},
/*生命周期函数--监听页面隐藏*/
onHide: function () {},
/*生命周期函数--监听页面关闭*/
onUnload: function () {},
/*页面相关事件处理函数--监听用户下拉动作*/
onPullDownRefresh: function () {},
/*页面上拉触底事件的处理函数*/
onReachBottom: function () {},
/*用户点击右上角分享*/
onShareAppMessage: function () {}
//还可以自定义方法/属性
//例子:
imgPath:"/images/...",
process:function(){
}
})
执行顺序:onload onShow onready
首先执行页面初始化 再执行页面显示函数 再执行页面渲染完成
wx.redirectTo 时会执行 onUnload
wx.navigateTo 时会执行 onHide
轮播的实现
最简单版本
<view>
<swiper>
<swiper-item>content1</swiper-item>
<swiper-item>content2</swiper-item>
<swiper-item>content3</swiper-item>
</swiper>
</view>
这样文字就可以滑动了。swiper-item代表滑动的容器
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
换成图片
<view>
<swiper>
<swiper-item><image src="xxx.png" /></swiper-item>
<swiper-item><image src="xxx.png" /></swiper-item>
<swiper-item><image src="xxx.png" /></swiper-item>
</swiper>
</view>
注:对<swiper-item>设置样式是没有用的。
对swiper整个组件设置高宽,必须设置到<swiper>根节点上。
<swiper-item>默认的高宽取的是<swiper>的高宽。
但是同时也需要对图片进行宽度100%设置。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
可以在swiper根节点上设置轮播效果。
如:indicator-dots="{{true}}" 设置显示分页器
<swiper indicator-dots="{{true}}">
<swiper-item><image src="xxx.png" /></swiper-item>
<swiper-item><image src="xxx.png" /></swiper-item>
<swiper-item><image src="xxx.png" /></swiper-item>
</swiper>
更多参数参考下图
轮播图绑定跳转页面:
//post.wxml:
<swiper indicator-dots="{{true}}">
<swiper-item>
<image catchtap="onSwiperItemTap" src="xxx.png" data-postId="3"/>
//查看数据相应页面的id
</swiper-item>
<swiper-item>
<image catchtap="onSwiperItemTap" src="xxx.png" data-postId="4"/>
</swiper-item>
<swiper-item>
<image catchtap="onSwiperItemTap" src="xxx.png" data-postId="5"/>
</swiper-item>
</swiper>
//post.js:
onSwiperItemTap:function(event){
var postId = event.currentTarget.dataset.postid;
wx.navigateTo({
url: "post-detail/post-detail?id=" + postId;
})
}
>>>>>>>>>>>>>
优化catchtap="onSwiperItemTap"过多问题
使用事件冒泡的机制
<swiper catchtap="onSwiperTap" indicator-dots="{{true}}">
<swiper-item>
<image src="xxx.png" data-postId="3"/>
//查看数据相应页面的id
</swiper-item>
<swiper-item>
<image src="xxx.png" data-postId="4"/>
</swiper-item>
<swiper-item>
<image src="xxx.png" data-postId="5"/>
</swiper-item>
</swiper>
事件冒泡机制:
点击图片,事件会由image传递到swiper-item上,再由swiper-item传递到swiper上。
swiper上使用的是catchtap,事件不会再往上传递了。
js:
onSwiperTap:function(event){
var postId = event.target.dataset.postid; //注:target
//target这里指的是image,而currentTarget指的是swiper
wx.navigateTo({
url:"post-detail/post-detail?id="+postId
})
}
单独页面中的配置
小程序的样式以就近原则
比如:
pages中的welcome页面
app.json和welcome.json设置了相同配置,welcome.json优先级高。
全局json和页面json的区别:
//全局:对整个应用程序大的配置
可以配置:
pages、window、tabBar、networkTimeout、debug、
functionalPages、subpackages、workers、
requiredBackgroundModes、plugins、preloadRule、resizable
{
"pages": [
"pages/welcome/welcome"
],
"window":{
"navigationBarBackgroundColor":"#405f80"
},
}
//单独页面
在单独页面下,只能配置window,既然只能配置window,所以小程序就规定,"window"不要了
{
"navigationBarBackgroundColor":"#405f80"
},
绑定数据
简单例子:
//js
Page({
data:{
date:"Oct 22 2018"
}
})
//wxml
<text>date</text> //会显示Oct 22 2018
假如说文档数据是从服务器请求来的。
一般数据放到onLoad函数中,在页面初始化的同时向服务器请求数据。
//傻瓜版数据平铺渲染,下面会介绍for循环方式绑定。
//js:
Page({
data:{
},
onLoad:function(){
//假如说这样一段数据是从服务器获取来的
var local_database = [
{
date: "Sep 18 2016",
title: "文章标题",
imgSrc: "/images/post/crab.png",
avatar: "/images/avatar/1.png",
content: "文章简短文字介绍",
reading: "112",
collection: "96",
headImgSrc: "/images/post/crab.png",
author: "吴彦祖",
dateTime: "24小时前",
detail: "文章详细内容介绍",
postId: 0,
music: {
url: "https://xxx/c957/ddf46174d597d368111db3ff9fbdaa7a.mp3",
title: "当你老了 (Live)-李健",
coverImg: "http://p1.music.126.net/93vWfsA7gvpBK20Ify82bw==/2908208256687521.jpg"
}
},
要把数据全都绑定到wxml文件中
所以要把local_database放到上面的data:{}结构体中,作为data的一个属性
使用小程序提供的setData语法:
this.setData(local_database) //相当于把local_database 放到data:{}
})
//wxml:
<view class='post-container'>
<view class='post-author-date'>
<image class='post-author' src='{{avatar}}'></image>
<text class='post-date'>{{date}}</text>
</view>
<text class='post-title'>{{title}}</text>
<image class='post-image' src='{{imgSrc}}'></image>
<text class='post-content'>{{content}}</text>
<view class='post-like'>
<image class='post-like-image' src='/images/icon/chat.png'></image>
<text class='post-like-font'>{{collection}}</text>
<image class='post-like-image' src='/images/icon/view.png'></image>
<text class='post-like-font'>{{reading}}</text>
</view>
</view>
判断&&渲染
<text wx:if="{{true}}"></text>
wx:for 渲染
//js:
Page({
data:{
},
onLoad:function(options){
var posts_content = [
{
date: "Oct 23 2018",
title: "战争与松脂",
imgSrc: "/images/post/crab.png",
avatar: "/images/avatar/1.png",
content: "这是一部关于会议、艺术、爱与战争的杰出著作。",
reading: "112",
collection: "96",
},
{
date: "Oct 23 2018",
title: "第二十二条军规",
imgSrc: "/images/post/xx.png",
avatar: "/images/avatar/2.png",
content: "《第二十二条军规》是一部严肃的、讽刺性极强的小说。",
reading: "162",
collection: "126",
},
]
//错误示范
this.setData(posts_content) //传递到data中
//在wxml中绑定posts_content时,他找不到这个。
//因为它是把posts_content里面的数据拿出来平铺到data中,自然没有posts_content
正确思路,给他起个名字
this.setData({
posts_key: posts_content
})
//相当于在data:{
// posts_key:[ ] //数据绑定到这里
//},
})
<block wx:for="{{posts_key}}" wx:for-item="item">
// wx:for="{{posts_content}}" 绑定上方变量
// wx:for-item="item" 定义子元素代称,子元素叫做item
// wx:for-item="item" 其实也可以不用指定,不写也可以使用,for循环默认子元素就是item
//wx:for-index="idx" 想知道一个子元素序号是多少;{{idx}}输出
//wx:for-index="idx" 其实也可以不用指定,默认是 {{index}}
<view class="container">
<view>
<image src="{{item.avatar}}" /> // 绑定头像
<text>{{item.date}}</text> // 绑定日期
</view>
<text>{{item.title}}</text> //绑定标题
<image src="{{item.imgSrc}}" /> //绑定封面
<text>{{item.content}}</text> //绑定文本描述
<view>
<image src="xxx/chat.png" />
<text>{{item.collection}}</text> //绑定收藏人数
<image src="xxx/view.png" />
<text>{{item.reading}}</text> //绑定预览人数
</view>
</view>
</block>
页面跳转
用到的两个知识点
- 响应的点击事件
- 微信提供的导航API
类型 | 触发条件 | 最低版本 |
---|---|---|
touchstart | 手指触摸动作开始 | |
touchmove | 手指触摸后移动 | |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | |
touchend | 手指触摸动作结束 | |
tap | 手指触摸后马上离开 | |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | 1.5.0 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) | |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | |
animationstart | 会在一个 WXSS animation 动画开始时触发 | |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | |
animationend | 会在一个 WXSS animation 动画完成时触发 | |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 | 1.9.90 |
小程序有个默认的规定
比如使用tap事件
,做一个tap事件绑定
,在前面加bind
。
上面的事件,当做事件绑定
的时候,都需要在前面加上bind
。
<view bindtap="onTap"></view>
点击的时候立刻执行这个函数
js:
onTap:function(){
wx.navigateTo({
url:"../posts/post" //跳转到post页面
})
}
>>>>>>>>>>>>>>>>>>>
模板跳转:
<template catchtap="onTap"></template >
// 把跳转绑定到template上是没有效果的
// 原因:template就是一个占位符,当页面文件编译之后,template就消失了。
// 代替的是一些template里面的一些元素
解决办法:
在template上面绑定一个view
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
<view catchtap="onPostTap" > // ☜
<template is="postItem" data="{{...item}}" />
</view> // ☜
</block>
冒泡与非冒泡
<view bindtap="onTap">
<text bindtap="onTextTap"></text>
</view>
//JS:
Page({
onTap:function(event){
console.log("execute onTap")
},
onTextTap:function(event){
console.log("execute onTextTap")
},
})
//输出:
点击<text bindtap="onTextTap"></text>时:
execute onTextTap //先输出文字
execute onTap //再输出外面边框
点击父级<view bindtap="onTap"></view>时:
execute onTap //只输出外面边框
点击子元素,如果父级有监听函数,父级函数也会执行,这就是冒泡。
阻止事件冒泡:
<view bindtap="onTap">
<text catchtap="onTextTap">文本</text> //注意:catchtap
</view>
//JS:
Page({
onTap:function(event){
console.log("execute onTap")
},
onTextTap:function(event){
console.log("execute onTextTap")
},
})
点击<text catchtap="onTextTap">文本</text>时:
execute onTextTap //只输出文字
常见的touch、tap等大部分都是属于冒泡事件。
只有一些为数极少的如<form/>的submit事件
,<input />的input事件
,<scroll-view/>的scroll事件
等是非冒泡事件。
将业务数据分离到单独的数据文件夹
// posts-data.js
var local_database = [
{
date: "Oct 23 2018",
title: "红鞋子",
imgSrc: "/images/post/1.png",
avatar: "/images/avatar/1.png",
content: "一只红鞋子总是想念着另一只红鞋子,想啊,想啊,想有一个家,一个暖暖的、亮亮的家。",
reading: "112",
collection: "96",
headImgSrc: "/images/post/crab.png",
author: "明天吧",
dateTime: "1小时前",
detail: "红鞋子,我想做一片绿叶,桥那边,月亮是块大烙饼,开满蒲公英的地方...",
postId: 0,
music: {
url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
title: "当你老了 (Live)-李健",
coverImg: "http://xx/2908208256687521.jpg"
}
}, {
date: "Oct 22 2018",
title: "月亮和六便士",
imgSrc: "/images/post/2.png",
avatar: "/images/avatar/2.png",
content: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家",
reading: "112",
collection: "96",
headImgSrc: "/images/post/crab.png",
author: "特暖暖的",
dateTime: "2小时前",
detail: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家,偶然间认识了一位证券经纪人,
对方在人届中年后突然响应内心的呼唤,离经叛道舍弃一切,先是奔赴巴黎,后又
到南太平洋的塔希提岛与土著人一起生活,全身心投入绘画,并在死后声名大噪。",
postId: 1,
music: {
url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
title: "水流众生 - 李健",
coverImg: "http://xx/2908208256687521.jpg"
}
},
]
》》》》》》该位置定义出口
如何引入数据
// 使用require方法加载js模块儿文件
首先要在 posts-data.js 上面的位置定义一个出口
module.exports = {
postList: local_database //给对象定义一个名字
}
一定要定义,实际上和node.js中的模块输出是一样的
注意:上面不是只能指定一个postList属性变量,
如果还有其他的,可以在后面继续添加,如下:
var a = "2"
module.exports = {
postList: local_database,
a_key: a
}
>>>>>>>>>>>>
在其他页面引入
比如在pages/posts/post.js引入变量
//post.js 页头加入
var postsData = require('../../data/posts-data.js')
//必须使用相对路径
Page({
data:{
},
onLoad:function(options){
this.setData({
posts_key:postsData.postList
});
/小程序总是会读取data对象来做数据绑定
/而读取数据是在onLoad事件执行之后发生的。
}
})
template模板的使用
模板文件只需要
wxml
和wxss
页面
编写模板
<template name="postItem">
<view>
<image src="{{item.avatar}}" /> // 绑定头像
<text>{{item.date}}</text> // 绑定日期
</view>
<text>{{item.title}}</text> //绑定标题
<image src="{{item.imgSrc}}" /> //绑定封面
<text>{{item.content}}</text> //绑定文本描述
<view>
<image src="xxx/chat.png" />
<text>{{item.collection}}</text> //绑定收藏人数
<image src="xxx/view.png" />
<text>{{item.reading}}</text> //绑定预览人数
</view>
</template>
使用template模板:
引入结构:
-----------------------------------------------------
在pages/posts/post.wxml下引入template模板
<import src="post-item/post-item-template.wxml" />
<template is="postItem" />
如果template在for循环中,需要传递data数据:
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
<template is="postItem" data="{{item}}" />
//data="{{item}}" 使用数据子元素填充
</block>
引入样式:
-----------------------------------------------------
还需要在pages/posts/post.wxss下引入template模板的样式
@import "post-item/post-item-template.wxss";
模板引用数据不使用item做前缀的方法:
注意:上面的template 模板中的数据添加了item作为前缀。{{item.date}}
如果不添加item前缀直接{{date}}绑定数据:
<template name="postItem">
<view>
<image src="{{avatar}}" /> // 绑定头像
<text>{{date}}</text> // 绑定日期
</view>
<text>{{title}}</text> //绑定标题
<image src="{{imgSrc}}" /> //绑定封面
<text>{{content}}</text> //绑定文本描述
<view>
<image src="xxx/chat.png" />
<text>{{collection}}</text> //绑定收藏人数
<image src="xxx/view.png" />
<text>{{reading}}</text> //绑定预览人数
</view>
</template>
在引用template在for循环中,需要传递data数据:
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
<template is="postItem" data="{{...item}}" /> 注意data="{{...item}}"
</block>
{{item}}与{{...item}}的区别:
{{item}} 指的是☞:数据数组上面的一个子元素,这个子元素是一个JavaScript对象。
{{...item}} 指的是☞:把对象展开平铺了,都是直接的属性名,所以绑定的时候不需要指定item了。
根据ID跳转不同页面
绑定页面ID:
// posts-data.js
var local_database = [
{
date: "Oct 23 2018",
title: "红鞋子",
imgSrc: "/images/post/1.png",
avatar: "/images/avatar/1.png",
content: "一只红鞋子总是想念着另一只红鞋子,想啊,想啊,想有一个家,一个暖暖的、亮亮的家。",
reading: "112",
collection: "96",
headImgSrc: "/images/post/crab.png",
author: "明天吧",
dateTime: "1小时前",
detail: "红鞋子,我想做一片绿叶,桥那边,月亮是块大烙饼,开满蒲公英的地方...",
postId: 0,
music: {
url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
title: "当你老了 (Live)-李健",
coverImg: "http://xx/2908208256687521.jpg"
}
}, {
date: "Oct 22 2018",
title: "月亮和六便士",
imgSrc: "/images/post/2.png",
avatar: "/images/avatar/2.png",
content: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家",
reading: "112",
collection: "96",
headImgSrc: "/images/post/crab.png",
author: "特暖暖的",
dateTime: "2小时前",
detail: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家,偶然间认识了一位证券经纪人,
对方在人届中年后突然响应内心的呼唤,离经叛道舍弃一切,先是奔赴巴黎,后又
到南太平洋的塔希提岛与土著人一起生活,全身心投入绘画,并在死后声名大噪。",
postId: 1,
music: {
url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
title: "水流众生 - 李健",
coverImg: "http://xx/2908208256687521.jpg"
}
},
]
module.exports = {
postList: local_database
}
以上是获取的数据,注意数组每个子对象都有一个`postId`来记录id,以示区分
页面如何取到该ID?
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
<view catchtap="onPostTap" data-postId="{{item.postId}}">
// ☝ data-postId="{{item.postId}}">
// data-xxx data-前缀是必须 ,后面的可以自己起名
//{{item}} 就是数据单个对象,可以通过item.postId取到postId值
//当然不一定非要叫data-postId,随意起名data-postName也可以
<template is="postItem" data="{{...item}}" />
</view>
</block>
获取页面ID:
绑定ID之后,在当前脚本文件获取点击元素的postId
js:
Page({
data:{
},
onLoad:function(){
},
onPostTap:function(event){
var postId = event.currentTarget.dataset.postid;
event 事件给的框架对象
currentTarget 当前鼠标点击的事件,对应上面的绑定onPostTap的view
dataset 所有自定义数据的集合
上面只定义了一个<view catchtap="onPostTap" data-postId="{{item.postId}}">
所有他只有一个postId
}
})
举例多个自定义数据
<view catchtap="onPostTap" data-postId="{{item.postId}}" data-post-name="name">
<template is="postItem" data="{{...item}}" />
</view>
上面自定义了两个数据 data-postId="{{item.postId}}" data-post-name="name"
输出时,只有`-`后面的字母大写。其他全部转为小写。
根据页面ID跳转:
<view catchtap="onPostTap" data-postId="{{item.postId}}">
<template is="postItem" data="{{...item}}" />
</view>
post.js:(传递)
var postsData = require('../../data/posts-data.js');
Page({
data:{
},
onLoad:function(){
},
onPostTap:function(event){
var postId = event.currentTarget.dataset.postid; //取到ID
wx.navigateTo({ //使用它的原因,是详情页需要返回按钮
url:"post-detail/post-detail?id="+postId
})
}
})
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
post-detail.js:(接收)
Page({
onLoad:function(option){
var postId = option.id //这里的id和上面?id对应
}
})
post-detail.js可以拿到id后,需要从posts-data.js找到对应的数据,绑定到post-detail.wxml中
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
post-detail.js:
先引入数据
var postsData = require('../../../data/posts-data.js')
Page({
onLoad:function(option){
var postId = option.id
var postData = postsData.postList[postId] // ☜ 新增该行
// 获取对应数据 从引入数据文件postsData.postList获取对应数据
this.setData({
postData: postData
})
}
})
post-detail.wxml:
<view>
<image src="{{postData.headImgSrc}}" />
<view>
<image src="{{postData.avatar}}" />
<text>{{postData.author}}</text>
<text>{{postData.dateTime}}</text>
</view>
<text>{{postData.title}}</text>
<text>{{postData.detail}}</text>
</view>
绑定数据时:需要加前缀postData 因为数据导出时,绑定到postData上
缓存
收藏功能:
当用户点击收藏按钮时,小程序应该向服务器发送一个收藏或取消收藏的状态指令,
然后由服务器把用户的收藏状态指令记录下来,在下次初始化页面的时候由服务器
把数据传递到客户端这里来,并且表现出收藏的按钮是收藏的还是未收藏的状态。
但是目前没有服务器,所以需要一个服务器替代品,可以永久记录文章收藏状态的一个地方。
小程序缓存就具备这样的功能。
如何设置一个缓存:
Page({
onLoad:function(){
},
// wx.setStorage('key','盖伦') //☜新建一个异步缓存
wx.setStorageSync('key','盖伦') //☜新建一个同步缓存
可以使用开发者工具的Storage查看缓存。(第一次需要进入页面创建缓存)
})
如何修改一个缓存:
Page({
onLoad:function(){
},
wx.setStorageSync('key','盖伦') //☜新建一个同步缓存
wx.setStorageSync('key',{ //☜修改一个缓存 key需要指定哪个缓存
game:"英雄联盟",
developer:"拳头"
})
})
如何获取一个缓存:
<image catchtap="onCollectionTap" src="xx/xxx.png" />
onCollectionTap:function(event){
var game = wx.getStorageSync('key') //☜获取键值为key的同步缓存
console.log(game)
}
//点击输出 {game:"英雄联盟",developer:"拳头"}
如何删除一个指定key缓存:
<image catchtap="onCollectionTap" src="xx/xxx.png" />
<image catchtap="onRemoveTap" src="xx/xxy.png" />
onRemoveTap:function(event){
wx.removeStorageSync('key') //☜删除键值为key的同步缓存
wx.clearStorageSync(); //☜删除所有缓存
}
图片两种状态的切换
jQ: 拿到image的dom,通过脚本文件动态控制图片的src,实现动态的轮转。
小程序:通过数据绑定的方法或者使用小程序提供的组件if else,实现图片的显示隐藏和动态切换。
小程序提倡数据优先的思想,必须通过动态绑定来实现。这种思想比较接近angular.js
使用if else的方法:
//post-detail.wxml:
<image wx:if="{{collected}}" src="xxx/collection.png" /> /收藏的图片
<image wx:else src="xxx/collection-anti.png" /> /未收藏的图片
在脚本中控制{{collected}}的真假,从而决定到底显示哪一张图片。
变量{{collected}}为真的话
显示collection.png图片,否则显示collection-anti.png
使用数据绑定三元运算方法:
<image catchtap = "onMusicTap" class="audio"
src ="{{isPlayingMusic? '/images/music-stop.png' : 'images/music-start.png'}}"
使用缓存控制图片显隐实现收藏效果:
//post-detail.wxml:
<image wx:if="{{collected}}" catchtap="onColletionTap" src="xxx/collection.png" />
<image wx:else catchtap="onColletionTap" src="xxx/collection-anti.png" />
//两张图片绑定catchtap="onColletionTap"
//post-detail.js
Page({
data:{
},
onLoad:function(option){
var postId = option.id; //当前文章的id
为了供下面的函数onColletionTap可以使用postId
需要把值传递给data
this.setData({
currentPostId: postId
})
//思路,存放记录文章收藏状态
/*var postsCollected={
1: "true",
2: "false",
3: "true",
...
}*/
var postsCollected = wx.getStorageSync('posts_collected')
//读取所有文章缓存状态
if(postsCollected){ //缓存是存在的,才去读取。
var postcollected = postsCollected[postId]
//获取当前页面的收藏状态
if(postsCollected){ //缓存是存在的
this.setData({
collected: postcollected;
// 把detail文章是不是收藏,绑定到了collected上
//从而在页面可以读取到它,控制显隐状态
})
}
}else{ //如果缓存不存在,需要一个创建缓存的流程
var postsCollected = {}; //创建空对象
postsCollected[postId] = false; //把当前的状态设置为未收藏
wx.setStorageSync('posts_collected',postsCollected) //设置该缓存
}
},
onColletionTap:function(event){ //当用户点击收藏和取消收藏
//首先获取缓存
var postsCollected = wx.getStorageSync('posts_collected')
//查看当前页面收藏状态
var postCollected = postsCollected[this.data.currentPostId]
//currentPostId可以使用因为上面函数把值传递到了data中
postCollected = !postCollected; //获取到状态后取反。
// 收藏的变成未收藏的,未收藏的变为收藏
postsCollected[this.data.currentPostId] = postCollected;
//更新当前的状态值
wx.setStorageSync('posts_collected',postsCollected);
//对当前的缓存进行更新(更新文章是否的缓存值)
this.setData({
collected: postCollected //更新数据绑定
})
}
})
小程序弹框(交互反馈)
为了使用时候更方便知道编写位置就在上面代码基础上编写
Page({
data:{
},
onColletionTap:function(event){ //当用户点击收藏和取消收藏
这段代码可忽视
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//首先获取缓存
var postsCollected = wx.getStorageSync('posts_collected')
//查看当前页面收藏状态
var postCollected = postsCollected[this.data.currentPostId]
//currentPostId可以使用因为上面函数把值传递到了data中
postCollected = !postCollected; //获取到状态后取反。
// 收藏的变成未收藏的,未收藏的变为收藏
postsCollected[this.data.currentPostId] = postCollected;
//更新当前的状态值
wx.setStorageSync('posts_collected',postsCollected);
//对当前的缓存进行更新(更新文章是否的缓存值)
this.setData({
collected: postCollected //更新数据绑定
})
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
已上代码可忽视
//交互
wx.showToast({ //不需要用户操作,会自动消失
//title:"收藏成功" 这种写法不是动态的
title:postCollected?"收藏成功":"取消成功",
duration:"1000", //消失时间 1s 默认1500
icon:"success" //success loading
})
相反的办法 隐藏
wx.hideToast()
setTimeout(function(){
wx.hideToast()
},2000)
}
})
小程序弹框(操作反馈)
模态弹框,不主动关闭就会一直存在。
wx.showModal({
title:"收藏",
content:"是否收藏该文章",
showCancel:"true", //显示取消按钮
cancelText:"不收藏", //取消按钮文字
cancelColor:"#333", //取消按钮文字颜色
confirmText:"收藏", //确定按钮
confirmColor:"#405f80" //确定按钮文字颜色
})
>>>>>>>>>>>>>>>>>>>>
符合项目的弹框
小程序弹框(交互反馈[底部])
出现在底部的弹框
onShareTap:function(event){
var itemList = [
"分享给微信好友",
"分享到朋友圈",
"分享到QQ",
"分享到微博"
]
wx.showActionSheet({
itemList:itemList,
itemColor: "#405f80", //设置颜色
success:function(res){
/ res.cancel 用户是不是点击了取消按钮
/ res.tapIndex 数组元素的序号,从0开始
wx.showModal({
title: "用户分享到了"+itemList[res.tapIndex],
content: "现在无法实现分享功能,小程序不支持"
})
}
})
}
音乐播放
歌曲和封面图片等,都必须使用在线链接,因为小程序有大小限制。
小程序有两种实现音乐播放的方法:一种是组件,一种API
API:
-
wx.playBackgroundAudio()
播放 -
wx.pauseBackgroundAudio()
暂停 -
wx.stopBackgroundAudio()
停止 -
wx.onBackgroundAudioPlay(callback)
监听音乐播放 -
wx.onBackgroundAudioPause(callback)
监听音乐暂停 -
wx.onBackgroundAudioStop(callback)
监听音乐停止
<image catchtap = "onMusicTap" class="audio"
src ="{{isPlayingMusic? '/images/music-stop.png' : 'images/music-start.png'}}"
var postsData = require('../../data/posts-data.js') //引入数据
Page({
data:{
isPlayingMusic:false //记录音乐播放状态
定不定义都行,就是强迫症
}
onMusicTap:function(event){
var currentPostId = this.data.currentPostId; //获取当前页面ID
记录播放状态和文章收藏一样
var isPlayingMusic = this.data.isPlayingMusic; //拿到播放状态
要根据当前的音乐播放状态来考虑是不是暂停,正在播放才能考虑是不是暂停
if(isPlayingMusic){ //如果是真
wx.pauseBackgroundAudio(); 音乐暂停
this.setData({ //暂停音乐之后
isPlayingMusic: false;
})
}else{
wx.playBackgroundAudio({
//常用的 地址 标题 封面图片
dataUrl:postsData.postList[currentPostId].music.url,
title:postsData.postList[currentPostId].music.title,
coverImgUrl:postsData.postList[currentPostId].music.coverImg
})
this.setData({ //播放音乐之后
isPlayingMusic: true;
})
}
})
解决音乐总控开关和音乐不同步问题
最好写在onload里面:
//上面的js文件:
Page({
data:{
},
onLoad:function(){
...
var that = this;
wx.onBackgroundAudioPlay(function(){ //监听到音乐播放
that.setData({
isPlayingMusic: true; //设置状态
})
}),
wx.onBackgroundAudioPause(function(){ //监听到音乐暂停
that.setData({
isPlayingMusic: false; //设置状态
})
})
}
})
解决BUG:播放音乐之后,再进入还是未播放状态
把变量存到全局
页面的js实现页面生命周期控制
全局app.js设置的是关于应用程序生命周期的一些方法和属性
app.js
App({
globalData:{ //专门保存全局的一些数据
g_isPlayingMusic: false,
}
>>>>>>>>
APP的生命周期函数只有三个
onLaunch:function(){ //应用程序启动
console.log("onLaunch")
},
onShow:function(){ //应用程序显示
console.log("onShow")
},
onHide:function(){ //隐藏到后台,页面不会销毁
console.log("onHide")
}
>>>>>>>>
执行顺序: onLaunch onShow
onHide没有执行的原因
在ios上可以按Home键,把应用程序放到后台去,这个时候会执行onHide方法
当隐藏之后onLaunch是不会执行的,只会执行onShow
关闭了应用程序之后再启动才会执行onLaunch
})
在post-detail.js中尝试拿到变量 `g_isPlayingMusic`
//post-detail.js
var app = getApp();
Page({
data:{
isPlayingMusic:false,
},
onLoad:function(option){
// var globalData = app.globalData; //拿到全局globalData
...
if(app.globalData.g_isPlayingMusic){
this.setData({
isPlayingMusic: true;
//如果全局记录播放,当前改成播放状态
})
}
...
wx.onBackgroundAudioPlay(function(){ //监听到音乐播放
that.setData({
isPlayingMusic: true; //设置状态
})
app.globalData.g_isPlayingMusic = true;
//改变全局播放状态
});
wx.onBackgroundAudioPause(function(){ //监听到音乐暂停
that.setData({
isPlayingMusic: false; //设置状态
})
app.globalData.g_isPlayingMusic = false;
//同步全局播放状态
})
}
})
解决BUG:播放音乐之后,切换另外页面歌曲是正在播放
造成这个问题的原因:
在页面中播放歌曲,把全局歌曲播放状态设置了true
换另外页面的时候,又执行onload函数
获取的是全局播放状态
if(app.globalData.g_isPlayingMusic){
this.setData({
isPlayingMusic: true;
//如果全局记录播放,当前改成播放状态
})
}
全局状态为true,从而导致了图标状态为true
实际上,全局的g_isPlayingMusic指的不是当前页面的播放状态
而是上一篇播放页面的状态,g_isPlayingMusic指代不对。
解决办法:
现在需要另外的一个变量,来记录当前的音乐播放指代是哪个页面;
而且这样的属性也应该是全局的。
在APP中再设置一个变量:
// app.js:
App({
globalData:{
g_isPlayingMusic: false,
g_currentMusicPostId:null //新增
}
})
//g_isPlayingMusic 音乐是不是被播放
//g_currentMusicPostId 哪一个音乐正在被播放
//post-detail.js:
wx.onBackgroundAudioPlay(function(){
that.setData({
isPlayingMusic: true;
})
app.globalData.g_isPlayingMusic = true;
app.globalData.g_currentMusicPostId = this.data.currentPostId
//记录ID
});
wx.onBackgroundAudioPause(function(){
that.setData({
isPlayingMusic: false;
})
app.globalData.g_isPlayingMusic = false;
app.globalData.g_currentMusicPostId = null;
//音乐暂停的时候把当前播放的音乐给清空
})
还要判断一下:
if(app.globalData.g_isPlayingMusic
&& app.globalData.g_currentMusicPostId === postId){
//判断是不是当前ID
this.setData({
isPlayingMusic: true;
})
}
Tab选项卡
//app.json:
{
"page":[],
"window":{},
"tabBar":{
"list":[
{
"pagePath":"pages/index/index",
"text":"首页"
},{
"pagePath":"pages/logs/logs",
"text":"日志"
}
]
}
}
没有写的必要,在app.json中输入tabBar回车,会自动补全。
图标、字体、颜色等参考API就搞定
要跳转的页面(想要出现tabbar的页面),必须放到list下面。
template的使用
**组件编写
//demo-template.wxml:
<template name="demoTemplate">
...
</template>
**引入template
<import src="../demo-template/demo-template.wxml" />
<template is="demoTemplate" />
**引入template样式
@import "../demo-template/demo-template.wxss";
获取在线接口数据
movies.js:
Page({
onLoad:function(){
wx.request({
url:'http://t.yushu.im/v2/movie/top250',
data:{}, //用来向服务器提交数据
method:'GET' //OPTIONS, GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT
// header:{} 设置请求的header
success:function(res){
//success
console.log(res)
},
fail:function(){
//fail //请求超时,网断了,走这个(网络建立没有成功)
console.log("failed")
},
complete:function(){
//complete
}
})
}
})
优化获取数据:
// app.js
App({
globalData:{
...
doubanBase: "http://t.yushu.im"
}
})
// movie.js:
var app = getApp();
Page({
onLoad:function(event){
var inTheatersUrl = app.globalData.doubanBase + "/v2/movie/in_theaters";
var comingSoonUrl = app.globalData.doubanBase + "/v2/movie/coming_soon";
var top250Url = app.globalData.doubanBase + "/v2/movie/top250";
//发送请求,获取数据
this.getMovieListData(inTheatersUrl);
this.getMovieListData(comingSoonUrl);
this.getMovieListData(top250Url);
},
getMovieListData:function(url){
wx.request({
url: url,
method:'GET'
header:{ //新版本更新后可以忽略
"Content-Type":""
},
success:function(res){
//success
console.log(res)
},
fail:function(){
console.log(error)
}
}
})
//设置只获取三条数据
var inTheatersUrl = app.globalData.doubanBase
+"/v2/movie/in_theaters"+"?start=0&count=3";
//从第0个开始,只取三条数据(根据文档请求属性来的)
wx.request是异步方法
var data = this.getMovieListData(inTheatersUrl)是获取不到数据的
数据绑定
// movie.js:
Page({
data:{
inTheaters:{},
comingSoon:{},
top250:{}
//如果不先定义,下面的方法都是异步的
//初始化的时候,找不到变量,绑定的就会出错
//如果是对象的话,一定要给他空值
},
onLoad:function(event){
var inTheatersUrl = app.globalData.doubanBase + "/v2/movie/in_theaters"+"?start=0&count=3";
var comingSoonUrl = app.globalData.doubanBase + "/v2/movie/coming_soon"+"?start=0&count=3";
var top250Url = app.globalData.doubanBase + "/v2/movie/top250"+"?start=0&count=3";
this.getMovieListData(inTheatersUrl,"inTheaters","正在热映"); //传递一个key识别
this.getMovieListData(comingSoonUrl,"comingSoon","即将上映");
this.getMovieListData(top250Url,"top250","豆瓣Top250");
},
getMovieListData:function(url,settedKey,categoryTitle){
var that = this;
wx.request({
url: url,
method:'GET'
header:{ //新版本更新后可以忽略
"Content-Type":""
},
success:function(res){
//success
console.log(res)
that.processDoubanData(res.data,settedKey,categoryTitle) //处理数据
},
fail:function(){
console.log(error)
}
},
processDoubanData:function(moviesDouban,settedKey,categoryTitle){
var movies = [];
for(var idx in moviesDouban.subjects){
var subject = movieDouban.subjects[idx];
var title = subject.title;
if(title.length>=6){ //如果字数超过6个 截取一下
title = title.substring(0,6)+"...";
}
var temp = {
stars:util.convertToStarsArray(subject.rating.stars),
title:title;
average:subject.rating.average, //综合评分
coverageUrl: subject.images.large, //大图
movieId:subject.id //方便跳转电影详情
}
movies.push(temp)
}
//生成key的对象
var readyData = {};
readyData[settedKey] = {
categoryTitle:categoryTitle, //标题
movies:movies
}
this.setData(readyData)
}
})
// 绑定数据
<view class="container">
<view>
<template is="movieListTemplate" data="{{...inTheaters}}"/>
</view>
<view>
<template is="movieListTemplate" data="{{...comingSoon}}"/>
</view>
<view>
<template is="movieListTemplate" data="{{...top250}}"/>
</view>
</view>
movieListTemplate:
<template name="movieListTemplate">
<view>
<text>{{categoryTitle}}</text> //标题数据
<view>
<text>更多</text>
<image src="/images/icon/arrow-right.png" />
</view>
</view>
<view class="movies-container">
<block wx:for="{{movies}}" wx:for-item="movie">
<template is="movieTemplate" data="{{...movie}}" />
</block>
</view>
</template>
movieTemplate:
<template name="movieTemplate">
<view class="movie-container">
<image class="movie-img" src="{{coverageUrl}}" />
<text class="movie-title">{{title}}</text>
<template is="starsTemplate" data="{{stars:stars,score:average}}"/>
</view>
</template>
starsTemplate:
<template name="starsTemplate">
<view class="stars-container">
<view class="stars">
//循环生成星星
<block wx:for="{{stars}}" wx:for-item="i">
<image wx:if="{{i}}" src="/images/icon/star.png" />
<image wx:else src="/images/icon/none-star.png" />
</block>
<!--<image src="/images/icon/star.png" />
<image src="/images/icon/star.png" />
<image src="/images/icon/star.png" />
<image src="/images/icon/star.png" />
<image src="/images/icon/star.png" />-->
</view>
<text class="star-score">{{average}}</text>
</view>
</template>
评分功能实现
豆瓣的评分 50=5颗星 35=3星半 45=4星半
简化版,不考虑0.5星的
把评分转换成数组:
5星: [1,1,1,1,1]
3星: [1,1,1,0,0]
utils/util.js 存放公共方法:
//util.js:
function convertToStarsArray(stars){
var num = stars.toString().substring(0,1);
var array = [];
for(var i =1;i<=5;i++){
if(i<=num){
array.push(1);
}else{
array.push(0);
}
}
return array;
}
module.exports = {
convertToStarsArray:convertToStarsArray //输出
}
//movie.js
var util = require('../../utils/util.js') //引用
如果要写半星:
3.5星 = [1,1,1,2,0] 用2代替半星
<image wx:if="{{i==1}}" />
<image wx:elif="{{i==2}}" />
<image wx:else />
更多电影列表
跳转页:
// movie-list-template:
<view class="movie-head">
<text class="slogan">{{categoryTitle}}</text>
<view catchtap="onMoreTap" class="more" data-category="{{categoryTitle}}">
//绑定,在onMoreTap就能获取到
<text class="more-text">更多</text>
<image class="more-img" src="/images/icon/arrow-right.png">
</view>
</view>
onMoreTap:function(event){
var category = event.currentTarget.dataset.category;
wx.navigateTo({
url:"more-movie/more-movie?category="+category
//跳转url
})
}
//more-movie.js: 获取category
Page({
data:{},
onLoad:function(options){
var category = options.category;
}
})
动态设置导航栏标题方法
Page({
data:{
navigateTitle:"",
},
onLoad:function(options){
var category = options.category;
this.setData({
navigateTitle:category
})
},
onReady:function(event){
wx.setNavigationBarTitle({
title:this.data.navigateTitle,
success:function(res){
//success
}
})
}
})
//设置导航标题不能写在onload
onload指的是页面初始化,页面初始化过程中是不应该操作和UI相关的东西的,因为页面还没有正式生成
onReady当页面已经准备完毕了,才执行onReady函数
onShow 也不可以使用,他们之间的执行顺序onloady onShow onReady,
onReady表示页面已经渲染完成了,写在onShow里面会闪一下,然后被覆盖。
Q/A整理
Q:1. 小程序单位rpx是什么,如何使用?
A:小程序使用rpx当做单位。他的功能类似于动态rem
。
rpx指的不是物理像素点,指的是逻辑像素点。
比如iPhone6的宽度是750,但在浏览器预览中宽度是375。
你根据设计稿上面的尺寸写400px;就会超过屏幕,因为它指的是物理像素。
如果使用400rpx。当屏幕375时,他会自动按照750的宽度改变比例。功能类似于移动端中的动态REM。会适应所有手机屏幕尺寸。
Q:2.小程序有哪些文件类型?
- wxml 是编写小程序
骨架
的文件。 - json 是编写小程序
配置
的文件。 - js 是编写小程序
行为
的文件。 - wxss 是编写小程序
样式
的文件。
Q:3.<text>标签有啥特质
- 支持嵌套
text
- 支持
\n
- 文字可以在手机上
长按选中
Q:4.为什么做for循环渲染推荐使用<block>标签把内容包括起来?
答:<block>标签
没有一个具体功能性作用,不像swiper
、image
等标签有实际作用,可以把他理解成一个括号
,凡是在<block>标签
里面代码都可以识别成一个整体。
Q:5.for循环有哪些指定参数?
-
wx:for="{{data}}"
必须 指定循环数据 -
wx:for-item="item"
非必须 不写默认为{{item}} -
wx:for-index="idx"
非必须 不写默认为{{index}}
Q:6.bindtap和catchtap的区别?
- bindtap(事件冒泡)
点击子元素,如果父级有监听函数,父级函数也会执行 - catchtap(非冒泡)
点击子元素,不管父级有没有监听函数,父级函数都不会执行
Q:7.微信小程序提供了哪些常用跳转API,各有什么功能?
A:区别:
- wx.navigateTo 页面父子级跳转:
当前页面没有关闭,后台运行,跳转的页面返回按钮
注:小程序限制,层级最多五层;保证了小程序的简洁性。
- wx.redirectTo 页面平行跳转:
当前页面关闭,跳转的页面没有返回按钮
- wx.switchTab
只能跳转含有tabbar的页面
,上面两种不能跳转
有tabbar的页面。
A:相同点,参数相同:
- url 存放
跳转地址
- success 当跳转
成功
之后会执行这个函数 - fail 当跳转
失败
之后会执行这个函数 - complete
无论成功还是失败
都会执行这个函数
Q:8.数据绑定时{{item}}与{{...item}}的区别?
{{item}}
指数组的单个对象数据
,绑定数据时需要指定{{item.xxx}}
{{...item}}
是把该单个对象数据平铺
,不需要指定就可以直接使用对象属性,调用时{{xxx}}
Q:9.自定义属性是什么?
在组件的属性列表里面,凡是data-
+"自定义单词"
构成的属性,我们都称之为自定义属性。
自定义属性有一些规则:
- 必须以
data
固定形式开头 - 可以有若干个
-
连接若干个单词
, 例:data-postId-name-dd
Q:10.缓存有哪些特性?
在小程序里,如果设置了一些缓存之后,不向用户主动提供wx.removeStorageSync('key')
(单一删除)、wx.clearStorageSync()
(全部删除)的方法,这个缓存是永久存在
的。并没有失效期
这个概念。
缓存上限不是以缓存个数来计算的,是以体积来计算的,缓存的上限最大不能超过10MB
。
小程序的缓存总共有
四类操作
、八种方法
,
每个操作同时具有同步和异步
的方法
Q:11.wx.showToast和wx.showModal在逻辑上的区别?
wx.showToast
不需要
用户确认
,会自动消失
wx.showModal
是必须需要用户确认
的
Q:12.全局app.js和页面xx.js区别?
- 全局app.js:
App({})
开头,控制的是关于应用程序生命周期的一些方法和属性
- 页面.js:
Page({})
开头,实现页面生命周期控制
Q:13.如何引入template模板
-
引入template:
<import src="../demo-template/demo-template.wxml" />
<template is="demoTemplate" />
-
引入template样式:
@import "../demo-template/demo-template.wxss";
Q:14.如何引入js文件
- 输出
module.exports = { xxx:xxx }
- 引用
var util = require('../../utils/util.js')
Q:15.如何引入全局App.js文件
var app = getApp();
Q:16.template模板中推荐使用什么路径?
推荐使用绝对路径
,引入时可能嵌套层级不同,使用绝对路径可以解决路径错误问题。
Q:17.event的target和currentTarget有什么区别?
target
指的是当前点击的组件;
currentTarget
指的是事件捕获的组件;
Q:18.在线数据如何获得?
Q:19.多个数据绑定?
{{stars:stars,score:average}}
含义:stars和average在这里又重新组装成JavaScript对象,传递给内部。
Q:20.如何设置自定义属性,并且拿到它?
设置自定义属性:data-category="{{categoryTitle}}"
拿到当前自定义属性值:event.currentTarget.dataset.category;