本地生活项目
项目效果图
外观和标签栏的配置
window
用于设置小程序的状态栏、导航条、标题、窗口背景色。
tabBar
如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
其中 list 接受一个数组,只能配置最少 2 个、最多 5 个 tab。tab 按数组的顺序排序,每个项都是一个对象,其属性值如下:
目标
建3个tab的tabBar
具体代码:
核心代码:
app.json
{ "pages":[ "pages/index/index", "pages/message/message", "pages/profile/profile" ], "window":{ "navigationBarBackgroundColor": "#3a4891", "navigationBarTextStyle":"white", "navigationBarTitleText": "本地生活", "backgroundColor": "#bcc0c9", "backgroundTextStyle":"light", "enablePullDownRefresh": false }, "tabBar":{ "list":[ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "assets/home.png", "selectedIconPath":"assets/home-active.png" }, { "pagePath": "pages/message/message", "text": "消息", "iconPath": "assets/message.png", "selectedIconPath":"assets/message-active.png" }, { "pagePath": "pages/profile/profile", "text": "我的", "iconPath": "assets/profile.png", "selectedIconPath":"assets/profile-active.png" } ] }, "style": "v2", "sitemapLocation": "sitemap.json" }
<!--index.wxml--> <view class="container"> 首页 </view>
<!--pages/message/message.wxml--> <text>pages/message/message.wxml</text>
<!--pages/profile/profile.wxml--> <text>pages/profile/profile.wxml</text>
公共样式
设置tabBar的样式:
app.json全部代码
{ "pages":[ "pages/index/index", "pages/message/message", "pages/profile/profile" ], "window":{ "navigationBarBackgroundColor": "#3a4891", "navigationBarTextStyle":"white", "navigationBarTitleText": "本地生活", "backgroundColor": "#bcc0c9", "backgroundTextStyle":"light", "enablePullDownRefresh": false }, "tabBar":{ "color": "#999", "selectedColor": "#444", "backgroundColor": "#fff", "borderStyle": "black", "list":[ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "assets/home.png", "selectedIconPath":"assets/home-active.png" }, { "pagePath": "pages/message/message", "text": "消息", "iconPath": "assets/message.png", "selectedIconPath":"assets/message-active.png" }, { "pagePath": "pages/profile/profile", "text": "我的", "iconPath": "assets/profile.png", "selectedIconPath":"assets/profile-active.png" } ] }, "style": "v2", "sitemapLocation": "sitemap.json" }
首页布局
知识点
image
基础库 1.0.0 开始支持,低版本需做兼容处理。
mode 的合法值
目标
代码
<!--index.wxml--> <swiper class="slides"> <swiper-item> <image src="/assets/banner/banner-01.png" mode="aspectFill"></image> </swiper-item> <swiper-item> <image src="/assets/banner/banner-02.png" mode="aspectFill"></image> </swiper-item> </swiper> <view class="grids"> <view class="item"> <image src="/assets/grid/grid-01.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-02.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-03.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-04.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-05.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-06.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-07.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-08.png" /> <text>美食</text> </view> <view class="item"> <image src="/assets/grid/grid-09.png" /> <text>美食</text> </view> </view> <view class="links"> <image src="/assets/link-01.png"></image> <image src="/assets/link-02.png"></image> </view>
/**index.wxss**/ .slides { height: 380rpx; } .slides image{ width: 100%; height: 100%; } .grids{ display: flex; flex-wrap: wrap; background-color: #fff; border-left: 1rpx solid #eee; border-bottom: 1rpx solid #eee; color: #888; } .grids .item{ display: flex; flex-direction: column; justify-content: center; align-items: center; width: 33.333333%; height: 250rpx; border-right: 1rpx solid #eee; border-top: 1rpx solid #eee; box-sizing: border-box; } .grids .item image{ width: 70rpx; height: 70rpx; } .grids .item text{ margin-top: 20rpx; color: #888; font-size: 28rpx; } .links{ display: flex; } .links image{ height: 200rpx; }
效果:
小程序中发送HTTP请求
如果是测试环境:
在详情中设置
如果是正式环境
1.请求的地址必须在管理后台添加白名单
2.域名必须备案,服务端必须采用HTTPS
//index.js //获取应用实例 const app = getApp() Page({ /** * 页面的初始数据 */ data: { }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { wx.request({ //这里的地址没有跨域的概念 url: 'https://api.douban.com/v2/movie/coming_soon', header: { 'Content-Type': 'json' }, success: function(res){ console.log(res) } //发送异步请求 不再是 WEB 那套 ajax //2.没有跨域 //3.请求的地址必须在管理后台添加白名单 //4.域名必须备案,服务端必须采用HTTPS }) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })
首页数据加载及数据绑定
封装http请求:
在utils的文件夹下:新建fetch.js
module.exports = (url, data) => { return new Promise((resolve, reject) => { wx.request({ url: `https://locally.uieee.com/${url}`, success: resolve, fail: reject }) }) }
index.js导入fetch,并使用
//index.js //获取应用实例 const app = getApp() const fetch = require('../../utils/fetch') Page({ /** * 页面的初始数据 */ data: { slides: [], categories: [] }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { wx.request({ //这里的地址没有跨域的概念 url: 'https://api.douban.com/v2/movie/coming_soon', header: { 'Content-Type': 'json' }, success: function(res){ console.log(res) } //发送异步请求 不再是 WEB 那套 ajax //2.没有跨域 //3.请求的地址必须在管理后台添加白名单 //4.域名必须备案,服务端必须采用HTTPS }) // wx.request({ // url: 'https://locally.uieee.com/slides', // success: res => { // this.setData({slides: res.data}); // } // }) fetch('slides').then(res => { this.setData({ slides: res.data}) }) // wx.request({ // url: 'https://locally.uieee.com/categories', // success: res => { // this.setData({ categories : res.data }) // } // }) fetch('slides').then(res => { this.setData({ categories: res.data}) }) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })
页面间跳转
注意点:
tab页跳转到另一个tab页,需要使用navigator的open-type=“switchTab”
例如:
<view class="grids"> <navigator class="item" url="/pages/message/message" open-type="switchTab"> <image src="/assets/grid/grid-02.png" /> <text>美食</text> </navigator> </view>
普通跳转 ,注意修改navigator的高宽百分比,不然会空白
添加了navigator
列表页分类信息加载
注意点, 设置分类导航栏的名字的顺序,一定在onReady函数执行完之后。
list.js
/** * 生命周期函数--监听页面加载 */ onLoad: function (options) { console.log(2) console.log(options) //这里不能确定一定是在 onReady过后执行 fetch(`categories/${options.cat}`).then(res => { console.log(res.data) //设置导航条 // wx.setNavigationBarTitle({ // title: res.data.name // }) this.setData({ category: res.data}) }) }, onReady: function () { console.log(1) if(this.data.category.name){ wx.setNavigationBarTitle({ title: res.data.name }) } },
第一页的商铺信息的加载
关键代码:
优化封装的fetch.js,让其可以携带参数
module.exports = (url, data) => { return new Promise((resolve, reject) => { wx.request({ url: `https://locally.uieee.com/${url}`, data: data, success: resolve, fail: reject }) }) }
正确使用promise.2个请求,有前后顺序的正确写法:
/** * 生命周期函数--监听页面加载 */ onLoad: function (options) { console.log(2) console.log(options) //这里不能确定一定是在 onReady过后执行 fetch(`categories/${options.cat}`).then(res => { console.log(res.data) //设置导航条 // wx.setNavigationBarTitle({ // title: res.data.name // }) this.setData({ category: res.data}) //加载完分类信息过后再去加载商铺信息 return fetch(`categories/${this.data.category.id}shops`,{_page;1,_limit: 10}) }) .then(res => { this.setData({ shops: res.data }) }) },
上拉加载更多onReachBottom
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
list.json
值越小,越迟钝,看具体需求
{ "onReachBottomDistance": 20 }
list.js
// pages/list/list.js const fetch = require('../../utils/fetch') Page({ /** * 页面的初始数据 */ data: { //当前加载的分类 category:{}, //子分类下的全部店铺 shops: [], pageIndex:0, pageSize:20, hasMore: true }, loadMore(){ if(!this.data.hasMore) return; //从data中取出 pageIndex 和 pageSize let { pageIndex, pageSize } = this.data const params = {_page: ++pageIndex, _limit: pageSize} fetch(`categories/${this.data.category.id}/shops`, params) .then(res => { const totalCount = parseInt(res.header['X-Total-Count']) const hasMore = pageIndex * pageSize < totalCount const shops = this.data.shops.concat(res.data) this.setData({ shops, pageIndex, hasMore}) }) } /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { console.log(2) console.log(options) //这里不能确定一定是在 onReady过后执行 fetch(`categories/${options.cat}`).then(res => { console.log(res.data) //设置导航条 // wx.setNavigationBarTitle({ // title: res.data.name // }) this.setData({ category: res.data}) //加载完分类信息过后再去加载商铺信息 this.loadMore(); }) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { console.log(1) if(this.data.category.name){ wx.setNavigationBarTitle({ title: res.data.name }) } }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { //需要判断是否正在加载,否则会有多次触发问题 console.log('到底了,别啦了'); this.loadMore(); }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })
下拉刷新
启动下拉刷新
list.json { "enablePullDownRefresh": true, "onReachBottomDistance": 20 }
停止下拉刷新
wx.stopPullDownRefresh(Object object) // pages/list/list.js const fetch = require('../../utils/fetch') Page({ /** * 页面的初始数据 */ data: { //当前加载的分类 category:{}, //子分类下的全部店铺 shops: [], pageIndex:0, pageSize:20, hasMore: true }, loadMore(){ if(!this.data.hasMore) return; //从data中取出 pageIndex 和 pageSize let { pageIndex, pageSize } = this.data const params = {_page: ++pageIndex, _limit: pageSize} return fetch(`categories/${this.data.category.id}/shops`, params) .then(res => { const totalCount = parseInt(res.header['X-Total-Count']) const hasMore = pageIndex * pageSize < totalCount const shops = this.data.shops.concat(res.data) this.setData({ shops, pageIndex, hasMore}) }) } /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { console.log(2) console.log(options) //这里不能确定一定是在 onReady过后执行 fetch(`categories/${options.cat}`).then(res => { console.log(res.data) //设置导航条 // wx.setNavigationBarTitle({ // title: res.data.name // }) this.setData({ category: res.data}) //加载完分类信息过后再去加载商铺信息 this.loadMore(); }) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { console.log(1) if(this.data.category.name){ wx.setNavigationBarTitle({ title: res.data.name }) } }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { //重新加载 this.setData({shops:[],pageIndex:0, hasMore:false}); this.loadMore() }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { //需要判断是否正在加载,否则会有多次触发问题 console.log('到底了,别啦了'); this.loadMore().then(() => wx.stopPullDownRefresh()); }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })
详细页
注意点:
使用wxs行内脚本设置过滤器。但是不支持es6
预览图片
效果: