小程序-商城项目搭建(配置tabbar、设计项目的公共样式、项目中引入iconfont图标库、网络请求封装等)

1,741 阅读10分钟

一、创建项目

项目的目录结构:

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?…

注册一个账号,登录到这个系统。

再主页中搜索你要的图标,加入到购物车。再购物车里面加入到指定的项目

image-20220328104545087

再我的项目中需要选中

image-20220328104618443

这个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、微博、邮箱等等。
  • 小程序我们可以用账户名密码的方式来登录,也可以直接微信授权登录

api-login.2fcc9f35

流程梳理:

  1. 前端需要调用wx.login获取code,得到这个code是一个登录凭证。code每个用户获取到都不一样。每个人的appid都不一样,微信官方生成的code也会不一致
  2. wx.request将code发送到你们自己的开发者服务器。后端可能是Java、Python、Node,不管是哪个后端,都可以接受传递code
  3. 后端会将code发送微信接口服务器(腾讯云服务器),服务器发送请求服务器。传递code、appid、appsecret给微信服务器。目的是为了进行身份验证。如果验证成功。返回sessionkey、openid
  4. 会将sessionkey、openid拿来生成token。
  5. 将token响应回本地小程序端。保存到本地存储
  6. 下次发送请求,在请求头里设置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  
    } 
}