西瓜音乐小程序开发第一天

363 阅读8分钟
  • ✅初始化项目
  • ✅配置tabbar
  • ✅封装请求函数
  • ✅分层架构二次封装请求函数
  • ✅封装格式化方法 (utils/format.wxs)
  • ✅封装视频列表item组件
  • ✅完成视频列表页展示
  • 今日完成效果如下

\

初始化项目

\

删除无关项目文件

  • 删除pages目录下的index和logs,删除utils目录下的util.js
  • 初始化app.js为
// app.js
App({
  
})
  • 初始化app.json为
{
  "pages": [
    
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "Weixin",
    "navigationBarTextStyle": "black"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json",
  "lazyCodeLoading": "requiredComponents"
}
  • 初始化app.wxss为
/**app.wxss**/
  • 目前为止编译器/模拟器会报错

\

配置tabbar

新建两个页面

  1. 在pages右键创建文件夹home-musichome-video
  2. home-videohome-music上右键新建pageindex
  1. 小程序开发工具会自动在目录下创建四个文件,并自动在app.json中添加url

导入静态资源assets

  1. 在文件管理器中打开项目,复制已经准备好的assets资源到根目录

\

app.json中配置tabbar

"tabBar": {
    "list": [
      {
        "pagePath": "pages/home-music/index",
        "text": "音乐",
        "iconPath": "assets/images/tabbar/music_normal.png",
        "selectedIconPath": "assets/images/tabbar/music_active.png"
      },
      {
        "pagePath": "pages/home-video/index",
        "text": "视频",
        "iconPath": "assets/images/tabbar/video_normal.png",
        "selectedIconPath": "assets/images/tabbar/video_active.png"
      }
    ]
  },

  • 效果

\

封装请求函数

由于微信小程序给我们提供的wx.request请求许多参数都可以复用,所以可以封装一个请求函数

  1. 在根目录下新建service文件夹
  2. 新建service/index.js
const BASE_URL = "http://123.207.32.32:9001";
class HYRequest {
  request(url, method, params) {
    return new Promise((resolve, reject) => {
      wx.request({
        url: BASE_URL + url,
        method: method,
        data: params,
        success: function (res) {
          resolve(res.data);
        },
        fail: function (err) {
          reject(err);
        },
      });
    });
  }
  get(url, params) {
    return this.request(url, "GET", params);
  }
  post(url, data) {
    return this.request(url, "POST", data);
  }
}

const hyRequest = new HYRequest();
export default hyRequest;

二次封装请求函数

某些动作需要反复调用请求函数,但是有不变的参数,没必要每次传递,可以再封装一次

  1. 新建service/api_video.js
import hyRequest from './index'
export function getTopMV(offset,limit=10){
  return hyRequest.get('/top/mv',{
    offset,
    limit
  })
}
  1. 由于我们的api后台需要接收两个参数,一个是偏移量,一个是数量,我们默认每次截取十个即可

调用请求获得数据

  1. home-video/index.js中引入getTopMV方法
import { getTopMV } from "../../service/api_video";
  1. home-video/index.js的data中定义两个变量
 data: {
    topMVs: [],   //用以保存请求到的数据列表
    hasMore: true,  // 判断还有没有更多数据能够请求
  },
  1. 封装专属于home-video的网络请求方法,用以在不同操作下复用
/**
   * 封装网络请求的方法
   */
  async getTopMVData(offset) {
    // 判断是否可以请求
    if (!this.data.hasMore) return;

    // 展示加载动画
    wx.showNavigationBarLoading();
    // 真正请求数据
    const res = await getTopMV(offset);

    let newData = this.data.topMVs;
    if (offset === 0) {
      newData = res.data;
    } else {
      newData = newData.concat(res.data);
    }
    this.setData({
      topMVs: newData,
    });
    this.setData({
      hasMore: res.hasMore,
    });

    // 隐藏navigation刷新动画
    wx.hideNavigationBarLoading();
    // 当请求数据完成 关闭下拉刷新动画
    if (offset === 0) {
      wx.stopPullDownRefresh();
    }
  },
  1. 当生命周期onLoad执行时,调用请求方法
/**
   * 生命周期函数--监听页面加载
   * async await
   */
  onLoad: function (options) {
    this.getTopMVData(0);
  },
  1. 当下拉刷新时调用请求方法
 /**
   * 下拉刷新
   */
  onPullDownRefresh: function () {
    this.getTopMVData(0);
  },
  1. 当页面触底时调用请求方法
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
    this.getTopMVData(this.data.topMVs.length);
  },
  1. home-video/index.js完整代码
// pages/home-video/index.js
import { getTopMV } from "../../service/api_video";
Page({
  /**
   * 页面的初始数据
   */
  data: {
    topMVs: [],
    hasMore: true,
  },

  /**
   * 生命周期函数--监听页面加载
   * async await
   */
  onLoad: function (options) {
    this.getTopMVData(0);
  },

  /**
   * 封装网络请求的方法
   */
  async getTopMVData(offset) {
    // 判断是否可以请求
    if (!this.data.hasMore) return;

    // 展示加载动画
    wx.showNavigationBarLoading();
    // 真正请求数据
    const res = await getTopMV(offset);

    let newData = this.data.topMVs;
    if (offset === 0) {
      newData = res.data;
    } else {
      newData = newData.concat(res.data);
    }
    this.setData({
      topMVs: newData,
    });
    this.setData({
      hasMore: res.hasMore,
    });

    // 隐藏navigation刷新动画
    wx.hideNavigationBarLoading();
    // 当请求数据完成 关闭下拉刷新动画
    if (offset === 0) {
      wx.stopPullDownRefresh();
    }
  },

  /**
   * 封装事件处理的方法
   */
  handleVideoItemClick: function (event) {
    const id = event.currentTarget.dataset.item.id;

    // 页面跳转
    wx.navigateTo({
      url: `/pages/detail-video/index?id=${id}`,
    });
  },
  /**
   * 下拉刷新
   */
  onPullDownRefresh: function () {
    this.getTopMVData(0);
  },

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

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

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

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

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

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
    this.getTopMVData(this.data.topMVs.length);
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {},
});

遍历数据到页面

前面我们已经请求到数据了,可以直接在home-video/index.wxml中调用,由于列表的item也可以复用,我们将它封装成一个component

<!--pages/home-video/index.wxml-->
<view class="video">
  <view class="item" wx:for="{{topMVs}}" wx:key="id">
     <!-- <video-item-v1 item="{{item}}" 
      bindtap="handleVideoItemClick"
      data-item="{{item}}"></video-item-v1> -->
  </view>
</view>

\

封装item组件

  1. 在根目录下新建文件夹components/video-item-v1,右键video-item-v1新建component为index
  1. 可以看到component的index.js和普通组件不同,其中有一个专门接收父组件传递过来数据的函数
 /**
   * 组件的属性列表
   */
  properties: {
    
  },
  1. 可以在这儿定义接收的数据,就可以直接在wxml页面中使用了
/**
   * 组件的属性列表
   */
  properties: {
    item:{
      type:Object,
      default:{}
    }
  },
  1. 由于服务器返回的数据里面播放量时间都需要转换格式,我们可以在utils目录下定义一个format.wxs用于格式化数据,wxml中只能调用wxs定义的方法
function formatCount(count) {
  var counter = parseInt(count);
  if (counter > 100000000) {
    return (counter / 100000000).toFixed(1) + "亿";
  } else if (counter > 10000) {
    return (counter / 10000).toFixed(1) + "万";
  } else {
    return counter + "";
  }
}

function padLeftZero(time) {
  time = time + "";
  return ("00" + time).slice(time.length);
}
function formatDuration(duration) {
  duration = duration / 1000;

  var minute = Math.floor(duration / 60);
  // 计算秒钟
  var second = duration % 60;

  return padLeftZero(minute) + ":" + padLeftZero(second);
}
// commonjs
module.exports = {
  formatCount: formatCount,
  formatDuration: formatDuration,
};
  1. 现在我们可以在wxml中使用父组件传递过来的数据了
<!--components/video-item-v1/index.wxml-->
<wxs src="../../utils/format.wxs" module="format"></wxs>
<view class="item">
  <view class="album">
    <image class="images" src="{{item.cover}}" mode="widthFix" />
    <view class="info">
      <view class="count">{{format.formatCount(item.playCount)}}</view>
      <view class="duration">{{format.formatDuration(item.mv.videos[0].duration)}}</view>
    </view>
  </view>
  <view class="content">
    {{item.name}} - {{item.artisName}}
  </view>
</view>
  1. 为item组件配置样式
/* components/video-item-v1/index.wxss */
.item {
  width: 100%;
  margin-bottom: 30rpx;
}

.album {
  position: relative;
  border-radius: 12rpx;
  overflow: hidden;
  display: flex;
}

.album .image {
  width: 100%;
}

.info {
  position: absolute;
  padding: 0 10rpx;
  box-sizing: border-box;
  width: 100%;
  bottom: 8rpx;
  display: flex;
  justify-content: space-between;
  color: #fff;
  font-size: 24rpx;
}

.info .count {
  padding-left: 36rpx;
  position: relative;
}

.info .count::before {
  content: "";
  position: absolute;
  left: -2rpx;
  top: 4rpx;
  width: 30rpx;
  height: 24rpx;
  background-size: cover;
  background-image: url("data:image/jpeg;base64,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");
}

.content {
  margin-top: 10rpx;
  font-size: 28rpx;

  /* 显示两行 */
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  display: -moz-box;
  -moz-line-clamp: 2;
  -moz-box-orient: vertical;
  word-wrap: break-word;
  word-break: break-all;
  white-space: normal;
  overflow: hidden;
}

父组件引入子组件

  1. home-video/index.json中引入子组件
{
  "enablePullDownRefresh": true,
  "backgroundTextStyle": "dark",
  "usingComponents": {
    "video-item-v1": "/components/video-item-v1/index"
  }
}
  1. 改写home-video/index.wxml使用组件
<!--pages/home-video/index.wxml-->
<view class="video">
  <view class="item" wx:for="{{topMVs}}" wx:key="id">
     <video-item-v1 item="{{item}}" 
      bindtap="handleVideoItemClick"
      data-item="{{item}}"></video-item-v1>
  </view>
</view>

父组件传值就是通过data-item传递的

\

  1. 最后我们还需要修改home-video/index.wxss
/* pages/home-video/index.wxss */
.video {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
}

.item {
  width: 48%;
}

\

其他: