前言
最近在学习vue写vue项目的时候,突然想起自己很久以前写的小程序,拿出来再看看才发现当时写的都是个啥啊哈哈哈😹。小程序相比于vue来说真的要简单很多,之前我在写小程序的时候很多编程重要的思想和编程规范都没有用上,比方说:数据驱动思想、组件化思想以及路由接口管理等都没有用上,可以说是非常不严格的一个项目了,这样的项目即使能跑起来其实也没有什么意义,对自己能力的段落并没有多少。所以在这里,以我之前的这个刚入门小程序开始写的项目为错误案例,和大家分享一下在刚入手写小程序时应该要注意的一些东西以及我的心体会。
页面功能展示
介绍
本项目前端部分采用小程序所支持的 wxml + wxss + js + json
开发模式;
后端部分采用github上已经开源的网易云音乐NodeJS版api接口NeteaseCloudMusicApi,提供音乐数据。通过在本地部署网易云音乐api接口(在终端中打开这个文档或者cmd,直接 node app.js
,默认就在3000端口部署完成了,详情可看我之前的文章使用node在本地部署网易云音乐API接口
然后在onLaunc
中调用wx.request()
获取接口数据,再把数据存入globalData中
,其他页面就可以调用拿到数据,然后再渲染到页面就可以了,拿轮播图举例:
轮播图
songList.js
// 获取banner
getBanner: function () {
wx.request({
url: 'http://localhost:3000/banner',
dataType: "json",
console.log(result);
success: (result) => {
// 设置给bg
this.setData({
background: result.data.banners
})
},
})
},
先定义一个数据变量:background
,通过wx.request
获取接口数据,再将数据存入data
中
data: {
background: {},
},
之后就可以通过这个变量,将所拿到对应的数据渲染到页面中去
songList.wxml
<view class="title">音乐资讯</view>
<swiper indicator-dots="true" indicator-color="rgba(255,255,255,0.5)" indicator-active-color="rgba(194,12,12,1)" autoplay="true" interval="3000" duration="1000" circular="true">
<block wx:for="{{background}}" wx:key="this">
<swiper-item>
<image src="{{item.imageUrl}}" />
</swiper-item>
</block>
</swiper>
同样其它功能的实现也是这样的流程
songList
页面
热门歌手
<view class="title">热门歌手</view>
<view class="hotlist">
<block wx:for="{{singer}}" wx:key="this" wx:for-index="in">
<view class="item" data-index="{{in}}" bindtap="hotlink">
<image src="{{item.picUrl}}"></image>
<text>{{item.name}}</text>
</view>
</block>
</view>
// 获取歌手
getSinger: function () {
wx.request({
url: 'http://localhost:3000/top/artists',
dataType: "json",
success: (result) => {
// console.log(result.data.artists)
// 设置给bg
this.setData({
singer: result.data.artists
})
},
})
},
新歌速递
<view class="title">新歌速递</view>
<view class="musicbox">
<block wx:for="{{newMusiclist}}" wx:key="this" wx:for-index="in">
<view class="item">
<view class="txtbox">
<text class="mname">{{item.name}}</text>
<text class="name">{{item.song.artists[0].name}}</text>
</view>
<view class="playimg" bindtap="playlink" data-index="{{in}}">
<image src="/images/play.png"></image>
</view>
</view>
</block>
</view>
// 获取新音乐
getnewMusic: function () {
wx.request({
url: 'http://localhost:3000/personalized/newsong',
dataType: "json",
success: (result) => {
// console.log(result.data.result)
//
this.setData({
newMusiclist: result.data.result
})
},
})
},
singerDetail
页面
点击歌手详情
歌手简介
歌手热门单曲
singerDetail.wxml
<!--pages/singerDetail/singerDetail.wxml-->
<view class="imgshow">
<image src="{{singerdetail.data.data.artist.cover}}" mode="widthFix"/>
<view class="txtbox">
<view class="name">{{singerdetail.data.data.artist.name}}</view>
<view class="txt">网易云音乐-热门歌手</view>
</view>
</view>
<view class="title">
歌手简介
</view>
<view class="content">{{singerdetail.data.data.artist.briefDesc}}
</view>
<view class="title">热门单曲</view>
<view class="musicbox">
<block wx:for="{{hotMusicList.data.songs}}" wx:key="this" wx:for-index="in">
<view class="item">
<view class="txtbox">
<text class="mname">{{item.name}}</text>
<text class="name">{{singerdetail.data.data.artist.name}}-{{item.al.name}}</text>
</view>
<view class="playimg" bindtap="playlink" data-index="{{in}}">
<image src="/images/play.png"></image>
</view>
</view>
</block>
</view>
singerDetail.js
Page({
/**
* 页面的初始数据
*/
data: {
// 当前歌手数据
singerdata: {},
// 歌手详情
singerdetail: {},
// 歌手热门歌曲
hotMusicList: {}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取页面传输过来的歌手基本数据,并进行存储
const eventChannel = this.getOpenerEventChannel()
eventChannel.on('acceptDataFromOpenerPage', data => {
// console.log(data.data.id)
this.setData({
singerdata: data
})
})
// 调用渲染页面的方法
this.getdetail()
// 调用热门歌曲
this.gethotMusic()
},
//页面详情数据获取
getdetail: function () {
// 获取id
const id = this.data.singerdata.data.id
// console.log(id);
// 通过id来做数据请求
wx.request({
url: 'http://localhost:3000/artist/detail?id=' + id,
success: (result) => {
// console.log(result);
this.setData({
singerdetail: result
})
},
})
},
// 热门歌曲
gethotMusic: function () {
// 获取id
const id = this.data.singerdata.data.id
// console.log(id);
// 通过id来做数据请求
wx.request({
url: 'http://localhost:3000/artist/top/song?id=' + id,
success: (result) => {
// console.log(result);
this.setData({
hotMusicList: result
})
},
})
},
// 点击跳转play页面
playlink: function (e) {
// 拿到当前下标
const index = e.currentTarget.dataset.index
// 拿到播放列表数据
const musicdata = this.data.hotMusicList.data.songs
// console.log(musicdata);
// 获取歌曲id
const mid = musicdata[index].id
wx.request({
url: 'http://localhost:3000/check/music?id=' + mid,
success: (res) => {
// console.log(res.data);
if (res.data.message === "ok") {
// console.log("可以播放");
// 定义数据对象
const objdata = {}
// 存储列表数据
objdata.musiclist = musicdata
// 存储当前播放的歌曲下标
objdata.nowIndex = index
// console.log(objdata);
wx.navigateTo({
url: '/pages/play/play',
// events: events,
success: (result) => {
result.eventChannel.emit('acceptDataFromOpenerPage', {
data: objdata
})
},
})
} else {
console.log("不能播放");
// 弹窗提示
wx.showModal({
title: '提示',
content: '歌曲没有版权,请选择其他歌曲播放',
showCancel: true
})
}
}
})
},
play
页面
歌曲播放功能的实现
歌词渲染
play.wxml
<image class="bg" src="{{music.al.picUrl}}"></image>
<!-- 头部 -->
<view class="musicName">{{music.name}}</view>
<!-- 轮播组件 -->
<swiper>
<swiper-item class="imgbox">
<image src="{{music.al.picUrl}}" mode="" />
</swiper-item>
<swiper-item>
<!-- // 歌词滑动 -->
<scroll-view scroll-y="true">
<block wx:for="{{lrcdata}}" wx:key="this">
<view>{{item[1]}}</view>
</block>
</scroll-view>
</swiper-item>
</swiper>
<!-- 进度条 -->
<view class="timebox">
<view>00:00</view>
<slider block-size="13" />
<view>03:45</view>
</view>
<!-- 底部按钮 -->
<view class="footer">
<image class="mode" src="/images/随机.png" alt="" />
<image class="min1" src="/images/上一首.png" />
<image bindtap="playDate" class="big" src="/images/{{ action.method === 'play' ? 'pause' : 'play' }}.png" />
<image class="min2" src="/images/下一首.png" />
<image class="list" src="/images/列表.png" />
</view>
<audio src="http://music.163.com/song/media/outer/url?id={{musicid}}.mp3" action="{{action}}"></audio>
play.js
const myaudio = wx.createInnerAudioContext();
Page({
/**
* 页面的初始数据
*/
data: {
//歌曲列表
musiclist: [],
// 当前歌曲下标
nowIndex: "",
// 当前歌曲数据
music: {},
// 歌曲id
musicid: "",
// 控制播放方法
action: {
"method": "play"
},
// 定义歌词数据
lrcdata: []
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取传过来的歌曲数据
const eventChannel = this.getOpenerEventChannel()
eventChannel.on('acceptDataFromOpenerPage', data => {
// console.log(data.data)
const nowIndex = data.data.nowIndex
const musiclist = data.data.musiclist
// 当前播放歌曲
const music = musiclist[nowIndex]
// console.log(music);
// 赋值
this.setData({
nowIndex: nowIndex,
musiclist: musiclist,
musicid: music.id
})
})
// 调用获取歌曲详情方法
this.getMusicDetail()
// 调用获取歌词
this.getLrc()
},
getMusicDetail: function () {
wx.request({
url: 'http://localhost:3000/song/detail?ids=' + this.data.musicid,
success: (result) => {
// console.log(result.data.songs[0]);
this.setData({
music: result.data.songs[0]
})
}
})
},
// 播放与暂停
playDate: function () {
// 获取当前状态
let date = this.data.action.method
// 判断当前状态
if (date === "play") {
this.setData({
action: {
"method": "pause"
}
})
} else {
this.setData({
action: {
"method": "play"
}
})
}
},
// 获取歌词
getLrc: function () {
wx.request({
url: 'http://localhost:3000/lyric?id=' + this.data.musicid,
success: (result) => {
const lrcstr = result.data.lrc.lyric
// 整理歌词
this.setLrcShow(lrcstr)
}
})
},
// 调整歌词布局
setLrcShow: function (lrc) {
// 定义空列表
let lrcdata = []
// 歌词拆分段落
const lrcList = lrc.split("\n")
// console.log(lecList);
// 分离出来歌词和相应的时间
const e = /\[\d{2}:\d{2}\.\d{2,3}\]/
// 遍历列表
lrcList.forEach(item => {
if (item) {
// console.log(item);
// 获取时间
let itemDate = item.match(e)
// console.log(itemDate);
// 去掉空时间
if (itemDate) {
itemDate = itemDate[0]
// 整理时间,拆分出中括号
itemDate = itemDate.slice(1, -1)
const timeList = itemDate.split(":")
const tiem0 = timeList[0]
const tiem1 = timeList[1]
// 拿到最终的时间值
const time = parseFloat(tiem0) * 60 + parseFloat(tiem1)
// 拿到歌词
const lrcstr = item.replace(e, "")
// console.log(lrcstr);
// 歌词和时间匹配整合
lrcdata.push([time, lrcstr])
}
}
})
// 存储歌词数据
this.setData({
lrcdata: lrcdata
})
},
总结
作为一个刚刚入门的小程序新手,当时我对软件开发的认识是非常浅薄,很多重要的思想和思维都没有,当时想的只是一味地想要把小程序写出来把相应的功能弄出来就可以了,这是非常错误片面的!
编程看上去像是一件很难的事情,很多代码冗杂、看不懂、难以设计啊这样的。实际上,我们可以一步一步拆分开来,自己定义专门的东西去做专门的东西,就像雇人分别去管理不同的部门,各司其职,通过对自己之前刚入门的项目的审视,我想向刚入门的新手说:
- 要有组件化、模块化思想
- 不要把所有的东西都写在一个页面,这样代码可读性太低了,而且很不方便维护!正确的应该是要自定义组件,创建一个
components
来专门存放组件,然后在相应的地方调用组件渲染到页面上,使代码更容易复用。 - 一个页面不要是由一个个独立的HTML、CSS和JavaScript文件组成,而是按照组件的思想将页面划分成一个个组件,如轮播图组件、歌手简介组件、热门歌手组件等。将这些组件拼装在一起。
- 组件化的目的,是为了让页面中的各个部分可以被复用,以减少重复的代码,提升了代码的可读性,也使得页面更容易维护。
- 不要把所有的东西都写在一个页面,这样代码可读性太低了,而且很不方便维护!正确的应该是要自定义组件,创建一个
- 模块化思想
- 就像组件化一样,不能像我以前那样每次都来拿
src
获取数据,通过wx.request
来请求,但是这种方法只能请求一次数据,如果首页用wx.request
来请求的话,代码看起来会很冗长和杂乱。不仅自己容易搞糊涂,其他人看代码时也会很累。 - 因此为了代码的可读性与可维护性, 我们要专门新建一个
service
文件专门存放API
,新建一个api.js
。 - 使用es6的
export
import
语法,例如使用export default
将封装的request
函数向外暴露,将每个需要请求的页面都在service
文件夹下配置相应的js文件统一管理。
- 就像组件化一样,不能像我以前那样每次都来拿
// api.js
const API_BASE_URL = '...'; //相应的接口
const request = (url, data) => {
let _url = API_BASE_URL + url;
return new Promise((resolve, reject) => {
wx.request({
url: _url,
method: "get",
data: data,
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success(request) {
resolve(request.data)
},
fail(error) {
reject(error)
}
})
});
}
module.exports ={
getBanner:(data)=>{
return request('/banner',data)//个性推荐轮播
}
}
以上,就是我个人的一些思考与心得体会,写得不好还请多多见谅,后续我还会继续分享我写的VUE项目。