一、创建项目
项目的目录结构:
components:存放项目中组件。
lib:存放第三方的一些插件,比如腾讯地图。
pages:存放页面。
request:请求封装的文件
assets:存放静态资源
utils:工具包。开发过程中常用的一些公共工具
二、配置tabbar
"tabBar": {
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "assets/images/home.png",
"selectedIconPath": "assets/images/home-o.png"
},
{
"pagePath": "pages/category/category",
"text": "分类",
"iconPath": "assets/images/category.png",
"selectedIconPath": "assets/images/category-o.png"
},
{
"pagePath": "pages/cart/cart",
"text": "购物车",
"iconPath": "assets/images/cart.png",
"selectedIconPath": "assets/images/cart-o.png"
},
{
"pagePath": "pages/center/center",
"text": "个人",
"iconPath": "assets/images/my.png",
"selectedIconPath": "assets/images/my-o.png"
}
]
}
你需要将图表引入到项目中,注意图表的大小。
三、设计项目的公共样式
再app.wxss文件中设置如下的代码
/* 将常用的标签,样式设置初始化 */
page,view,text,navigator,swipper,swipper-item{
margin: 0px;
padding: 0px;
/* 移动端开发我们一般都使用怪异盒子 */
box-sizing: border-box;
}
/* 给页面设计统一的样式 */
page{
/* 设置全局的字体为28rpx */
font-size: 28rpx;
/* 设置全局主题色 */
--themeColor:#ff7159
}
其中page代表每个页面,你还可以给他设置背景颜色
四、项目中引入iconfont图标库
再小程序中我们可以引入iconfont图表库来加载你们想要的图表。
访问:www.iconfont.cn/home/index?…
注册一个账号,登录到这个系统。
再主页中搜索你要的图标,加入到购物车。再购物车里面加入到指定的项目
再我的项目中需要选中
这个css文件里面的代码,你放到浏览器运行以下就可以复制到项目中。
你可以项目中创建styles文件夹,iconfont.wxss,将样式复制进去。
再你的页面中,less文件中引入这个wxss文件
@import "../../assets/styles/iconfont.wxss"
再页面使用图标的时候
<text class="iconfont icon-sousuo">
五、发送网络请求
微信api地址:developers.weixin.qq.com/miniprogram…
var reqTask = wx.request({
url: 'http://127.0.0.1:4000/home/swiperdata',
method: 'GET',
dataType: 'json',
success: (result)=>{
console.log(result);
this.setData({
swiperData:result.data.message
})
},
fail: (error)=>{
console.log(error);
},
complete: ()=>{
console.log("资源清理工作");
}
});
六、请求封装
再每个页面中如果都需要发送请求,需要将请求封装到公共文件中
再项目request文件夹下面创建一个请求工具api.js
// 封装请求代码
let baseURL = "http://127.0.0.1:4000"
const fecthData = (url,data={},method="GET")=>{
//写一个加载动画
return new Promise((resolve,reject)=>{
wx.request({
url: baseURL+url,
data: data,
method: method,
dataType: 'json',
success: (result)=>{
resolve(result)
},
fail: (error)=>{
reject(error)
},
complete: ()=>{
//关闭加载动画
}
});
}
)}
export default fecthData
暴露了一个默认的函数,外面用这个函数的时候
import fecthData from "../api";
再request文件夹下面创建modules文件夹,存放各个页面的请求对象。
再modules文件夹里面home.js里设计代码
import fecthData from "../api";
// 获取到轮播图
export const bannerRequest = ()=>fecthData("/home/swiperdata");
// 获取小图标的请求
export const iconRequest = ()=>fecthData("/home/catitems");
// 获取滑动模块数据
export const recommendRequest = ()=>fecthData("/home/floordata")
再页面中要使用请求
import {bannerRequest} from "../../request/modules/home.js"
data:{ swiperData:[]}
async onLoad(){
const res = await bannerRequest()
this.setData({
swiperData:res.data.message
})
}
获取到数据后,如果出现异常情况,再小程序开发工具,调试数据。AppData这个模块浏览器安装Vue插件
七、页面跳转
再小程序中我们可以使用navigator来进行跳转
<navigator url="/pages/logs/logs" open-type="navigate">
这种跳转方式就相当于a标签来跳转。
如果是按钮跳转,需要用到官方的api。地址为:developers.weixin.qq.com/miniprogram…
// 跳转tabbar页面
wx.switchTab({
url: '/pages/center/center'
});
// 普通页面跳转,可以返回
wx.navigateTo({
url: '/pages/logs/logs”,
success: (result)=>{
console.log(result);
}
});
//重定向跳转
wx.redirectTo({
url: '/pages/logs/logs',
});
如果跳转过程中你需要传递参数
// 普通页面跳转,可以返回
const id = event.currentTarget.dataset.id;
wx.navigateTo({
url: '/pages/logs/logs?id='+id+"&name=123",
success: (result)=>{
console.log(result);
}
});
再地址栏?拼接参数
在新的页面中获取参数的方式
onLoad:function(options){
console.log(options)//{id:2434343,name:"123"}
}
每一个页面都会有一个onLoad钩子函数,里面可以默认接受一个对象作为参数来获取传递过来的值
八、小程序插槽
小程序中我们使用插槽和Vue比较类似。slot标签来完成插槽功能
子组件定义插槽
<view class="content">
<!-- slot标签也是插槽占位符 -->
<slot name="listslot"></slot>
</view>
name:代表就是插槽的名字,找到这个插槽外部必须指定对应名字
默认在小程序中插槽是无法使用的。需要开启插槽功能,子组件js文件中配置如下的代码
Component({
options:{
// 开启插槽功能
multipleSlots:true
},
/** * 组件的属性列表 */
properties: { },
/** * 组件的初始数据 */
data: { },
/** * 组件的方法列表 */
methods: { }
})
options:里面就可以设置开启插槽功能。官方文档里要求配置的信息
父组件
<tabs tabs="{{tabs}}" bindchangeTabsIndex="changeTabsIndex">
<view wx:if="{{tabs[0].isActive}}" class="" slot="listslot">
<text class="" >这是热门商品</text>
</view>
<view wx:else class="" slot="listslot">
<button>提交按钮</button>
</view>
</tabs>
tabs:就是定义的子组件,你需要在子组件中间加入你们想要传递的数据给插槽。view标签上面slot属性,这个属性值就等于slot标签上面name值
九、下拉刷新上拉加载
下拉刷新:完成功能在页面下拉的时候,动态加载新一页的数据。
上拉加载:小程序中分页功能,当检测到触底的时候,动态发送异步请求获取数据
上拉加载
/** * 页面上拉触底事件的处理函数 */
onReachBottom: function () {
if(this.data.currentPage>=this.data.totalPage){
wx.showToast({
title: '没有更多',
duration: 1500
});
}else{
this.setData({
currentPage:++this.data.currentPage
})
this.fetchGoodsData()
}
},
onReachBottom:小程序页面生命周期提供了这个钩子函数用于检测是否触底。
判断当前你的页码是否超过了最大页,没有超过情况页码进行累加发异步请求获取最新的数据。
showToast:微信提供的弹框功能。移动端的弹框和pc不一样。
async fetchGoodsData(id){
const res = await getGoods(
{
currentPage:this.data.currentPage,
pageSize:this.data.pageSize
}
)
console.log(res);
this.setData({
// 将分页的数据和上次的数据合并起来显示
goodsList:[...this.data.goodsList,...res.data.message],
total:res.data.total
})
// 计算出一共多少页
this.setData({
totalPage:this.data.total / this.data.pageSize
})
},
每次调用fetchGoodsData这个函数,我们要获取最新页码的数据。拿到新的数据需要和原来的数据进行合并
goodsList:[...this.data.goodsList,...res.data.message],
下拉刷新
动态获取最新一页数据
/** * 页面相关事件处理函数--监听用户下拉动作 * 下拉刷新 */
onPullDownRefresh: function () {
this.setData({
currentPage:1,
goodsList:[]
})
this.fetchGoodsData()
},
默认页面是无法使用下拉刷新的,需要在页面json配置文件中开启刷新功能
{
"usingComponents": {
"tabs":"../../components/tabs/index"
},
"enablePullDownRefresh":true,
"backgroundTextStyle":"dark"
}
enablePullDownRefresh:开启下拉刷新
backgroundTextStyle:配置刷新动画颜色
十、本地存储
地址:developers.weixin.qq.com/miniprogram…
从本地存储获取数据
wx.getStorageSync({
key: 'key',
success (res) {
console.log(res.data)
}
})
也可以写为
const value = wx.getStorageSync("key")
上面的这个方法属于同步方法。
小程序还提供了异步操作的方法
wx.getStorage({
key: 'key',
success (res) {
console.log(res.data)
}
})
方法名字上面没有Sync代表异步获取数据。
存储数据到本地
wx.setStorageSync({
key:"key",
data:"value"
})
//或者
wx.setStorageSync("key","value")
保存数据到本地,需要提供key-value的语法
当然你可以用异步方法
wx.setStorage({ key:"key", data:"value"})
十一、小程序登录授权
移动端要实现登录功能。
- App端可以用账户名和密码的方式来进行登录
- 第三方登录。用github、微博、邮箱等等。
- 小程序我们可以用账户名密码的方式来登录,也可以直接微信授权登录
流程梳理:
- 前端需要调用wx.login获取code,得到这个code是一个登录凭证。code每个用户获取到都不一样。每个人的appid都不一样,微信官方生成的code也会不一致
- wx.request将code发送到你们自己的开发者服务器。后端可能是Java、Python、Node,不管是哪个后端,都可以接受传递code
- 后端会将code发送微信接口服务器(腾讯云服务器),服务器发送请求服务器。传递code、appid、appsecret给微信服务器。目的是为了进行身份验证。如果验证成功。返回sessionkey、openid
- 会将sessionkey、openid拿来生成token。
- 将token响应回本地小程序端。保存到本地存储
- 下次发送请求,在请求头里设置token,得到token验证后的接口数据,比如订单、浏览记录、收藏
授权代码
1、调用getUserProfile获取用户授权
wx.getUserProfile({
desc:"获取身份用户登录",
success(res){
//可以获取userInfo信息
const {userInfo} = res
}
})
2、授权成功后调用登录
wx.getUserProfile({
desc:"获取身份用户登录",
success(res){
console.log(res);
const {userInfo} = res
// (2)发送请求获取到code
wx.login({
// 这个code是微信生成的登录凭证
success(res){
const code = res.code
}
})
}
})
授权成功的回调函数success里面调用wx.login函数获取code值.
3、调用wx.request发送请求传递code参数
login(){
// 将this的值赋值给变量
let _this = this
// 这个api是目前微信推荐我们使用来获取用户信息的api
//(1) 在老版本里getUserInfo
wx.getUserProfile({
desc:"获取身份用户登录",
success(res){
console.log(res);
const {userInfo} = res
// (2)发送请求获取到code
wx.login({
// 这个code是微信生成的登录凭证
success(res){
wx.request({
url: 'http://47.98.128.191:3001/users/wxLogin', //自己服务器地址
data: {
code:res.code,
appId:"wx36e047cbd8d6766d",
appSecret:"c5afb12e7ab702332a04fa25c0658b84",
userInfo
},
method: 'POST',
success: (result)=>{
//请求成功获取到result,里面包含token
const token = result.data.token
}
});
}
})
}
})
}
4、将token保存到本都存储,将userInfo保存到data
wx.setStorageSync("token", result.data.token);
// 保存用户信息,修改用户登录状态
_this.setData({
hasUserInfo:true,
userInfo
})
需要注意的是,代码涉及到回调地狱,所有this的执行不再是page页面,所以你需要将this保存为_this
let _this = this
这段代码在最开始就声明清楚
5、页面中显示登录后的头像
<view class="container">
<view class="userInfo">
<block wx:if="{{!hasUserInfo}}">
<image bindtap="login" src="https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/xuchaobo/20210801185911.png" mode="widthFix"></image>
<text>点击头像登录</text>
</block>
<block wx:else>
<image class="" src="{{userInfo.avatarUrl}}" mode="widthFix"></image>
<text>{{userInfo.nickName}}</text>
</block>
</view>
</view>
当hasUserInfo为true的时候,代表已经登录。我们就可以显示登录成功的用户头像
自动登录
授权登录完成后,下次用户在进入小程序我们的数据被初始化,无法检测用户是否登录。所以需要优化代码。
1、在app.js文件中进行token的验证
// app.js
App({
onLaunch() {
//获取token
const token = wx.getStorageSync("token");
if (token) {
wx.request({
url: 'http://47.98.128.191:3001/users/getUserInfo',
header: { 'Authorization': token },
method: 'GET',
success: (result) => {
console.log(result);
// 将数据保存到全局对象
this.globalData.userInfo = result.data.userInfo
},
fail: (error) => {
console.log(error);
}
});
}
},
show(){ },
// 微信提供的一个全局对象,任何一个页面都可以从globalData获取数据
globalData: { userInfo: null }
})
当启动项目的时候就发送异步请求到后端验证token是否过期,如果没有过期那我们就将得到的userInfo信息保存到本地globalData对象中。
globalData这个对象是全局对象,任何一个页面都可以获取到并使用
this.globalData.userInfo = result.data.userInfo
2、在页面中获取到globalData的数据进行判断是否登录
// getApp这个函数就是用于获取app.js里设置的数据和函数
var app = getApp();
onLoad: function (options) {
const {userInfo} = app.globalData
console.log(userInfo);
this.setData({
hasUserInfo:true,
userInfo
})
},
页面onLoad的生命周期函数可以获取globalData数据并保存到这个页面的data中。页面就能自动完成登录
十二、页面中的watch
小程序中组件可以使用observer来进行数据监控,一旦数据发生变化,启动监控代码。
在小程序页面默认无法使用observer来实现监控。在页面watch的方式来监控。
小程序页面默认没有提供watch功能,需要自己构造一个watch功能出现。
一、在app.js文件中引入如下代码
/** * 设置监听器 */
setWatcher(page) {
console.log(page);
let data = page.data;
let watch = page.watch;
Object.keys(watch).forEach(v => {
let key = v.split('.'); // 将watch中的属性以'.'切分成数组
let nowData = data;// 将data赋值给nowData
for (let i = 0; i < key.length - 1; i++) { // 遍历key数组的元素,除了最后一个!
nowData = nowData[key[i]];// 将nowData指向它的key属性对象
}
let lastKey = key[key.length - 1];
// 假设key==='my.name',此时nowData===data['my']===data.my,lastKey==='name'
let watchFun = watch[v].handler || watch[v]; // 兼容带handler和不带handler的两种写法 let deep = watch[v].deep; // 若未设置deep,则为undefine
this.observe(nowData, lastKey, watchFun, deep, page); // 监听nowData对象的lastKey
})
},
/** * 监听属性 并执行监听函数 */
observe(obj, key, watchFun, deep, page) {
var val = obj[key];
// 判断deep是true 且 val不能为空 且 typeof val==='object'(数组内数值变化也需要深度监听)
if (deep && val != null && typeof val === 'object') {
Object.keys(val).forEach(childKey => { // 遍历val对象下的每一个key
this.observe(val, childKey, watchFun, deep, page); // 递归调用监听函数
})
}
var that = this;
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
set: function (value) { // 用page对象调用,改变函数内this指向,以便this.data访问data内的属性值
watchFun.call(page, value, val); // value是新值,val是旧值
val = value;
if (deep) { // 若是深度监听,重新监听该对象,以便监听其属性。
that.observe(obj, key, watchFun, deep, page);
}
},
get: function () {
return val;
}
})
},
二、在指定的页面中引入下面代码
/** * 生命周期函数--监听页面加载 */
onLoad: function (options) {
getApp().setWatcher({
data:this.data,
watch:this.watch
}); // 设置监听器
},
watch: {
number: function (newValue) {
console.log("watch");
console.log(newValue); // name改变时,调用该方法输出新值。
}
},
三、也可以在watch中用handler的写法
watch: {
number: function (newValue) {
console.log("watch");
console.log(newValue); // name改变时,调用该方法输出新值。
},
user:{
handler(val){
console.log(val);
},
deep:true
}
}