系列文章目录
前言
随着开发工具的不断发展,小程序开发框架,App跨平台框架,H5开发框架 uni-app的优势越来越明显。
一、uni-app是什么?
uni-app
是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序、快应用等多个平台。
uni-app
在手,做啥都不愁。即使不跨端,uni-app
也是更好的小程序开发框架(详见)、更好的App跨平台框架、更方便的H5开发框架。不管领导安排什么样的项目,你都可以快速交付,不需要转换开发思维、不需要更改开发习惯。
二、uni-app的优点
1. 跨平台开发:
使用uni-app可以一次开发,多端部署,支持编译到H5、小程序、App和桌面应用等多个平台,减少了开发成本和工作量。
2. 良好的性能:
uni-app采用原生渲染方式,性能优越,用户体验好。
3. 组件丰富:
uni-app内置了丰富的组件和模板,可以快速开发出高质量的应用。
4. 易于学习和使用:
uni-app采用Vue.js框架,对于熟悉Vue.js的开发者来说,很容易上手。
5. 多人协作便捷:
基于Git的版本控制系统可以使多人协作变得更加便捷。
6. 生态系统完善:
uni-app有丰富的社区和生态系统,可以轻松解决开发过程中的问题。
7. 适用范围广泛:
uni-app适用于开发各种类型的应用,包括商城、社交、新闻、游戏等。
三、实际项目经验
1. 提高开发效率和代码复用性:
由于 uni-app 支持多个平台的开发,因此可以大大减少开发人员的工作量,提高开发效率。
2. 优化性能和用户体验:
uni-app 应用可以兼容在多个平台上,可以通过混合原生应用和 Web 应用来提高应用的性能和用户体验。
3. 统一UI风格和设计语言:
由于uni-app开发可以同时适用于多个平台,因此对于 UI 风格和设计语言也有一定程度的统一性。
4. 快速迭代和发布:
由于 uni-app 可以同时适用于多个平台,因此可以快速迭代,提高应用的更新速度,同时也可以快速发布,在各个平台上进行推广。
四、近期开发的uni-app项目
1. UAMusic音乐项目
1.apk的封装
export const baseUrl ="https://flask-web-frak-shishn-kvmjsphrif.cn-shenzhen.fcapp.run";
export function topList() {
return new Promise(function(resolve,reject){
uni.request({
url: `${baseUrl}/toplist/detail`,
method: 'GET',
data: {},
success: res => {
let result = res.data.list;
resolve(result.splice(0,4));
},
fail: (err) => {
console.log(err);
},
complete: () => {}
});
});
}
export function list(listId) {
return uni.request({
url: `${baseUrl}/playlist/detail?id=${listId}`,
method: 'GET'
})
}
export function songDetail(id) { //获取歌曲详情,包括歌名、艺人、图片等
return uni.request({
url: `${baseUrl}/song/detail?ids=${id}`,
method: 'GET'
})
}
export function songUrl(id) { //获取歌曲音频的url
return uni.request({
url: `${baseUrl}/song/url?id=${id}`,
method: 'GET'
})
}
export function songLyric(id) { //获取歌词内容
return uni.request({
url: `${baseUrl}/lyric?id=${id}`,
method: 'GET'
})
}
export function searchHot() {
return uni.request({
url: `${baseUrl}/search/hot/detail`,
method: 'GET'
})
}
export function searchWord(word) {
return uni.request({
url: `${baseUrl}/search?keywords=${word}`,
method: 'GET'
})
}
export function searchSuggest(word) {
return uni.request({
url: `${baseUrl}/search/suggest?keywords=${word}&type=mobile`,
method: 'GET'
})
}
2.首页
<template>
<view class="content">
<uamhead :title="title"></uamhead>
<!-- <image class="logo" src="/static/logo2.png"></image> -->
<view class="text-area">
</view>
<view>
<input class="ss" @tap="search()" type="button" value="搜索音乐">
</view>
<view class="index-list">
<view class="index-list-ite" v-for="(item,index) in playlist" :key="index" @tap="handleToList(item.id)">
<view>
<image :src="item.coverImgUrl" mode=""></image>
<text class="listtext">{{item.updateFrequency}}</text>
</view>
<view class="listlist">
<text v-for="(i,index) in item.tracks">{{index+1}}.{{i.first}}-{{i.second}}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import {
topList
} from "../../common/api.js"
// const topListData = require('@/static/data/toplist.json')
import mForSkeleton from "@/components/m-for-skeleton/m-for-skeleton";
import uamhead from "../../components/uamhead/uamhead.vue";
export default {
components: {
mForSkeleton
},
data() {
return {
// playlist: topListData,
playlist: [],
title: 'UAMusic-lyh',
loading: true
}
},
onLoad() {
topList().then((res) => {
if (res.length) {
setTimeout(() => {
this.playlist = res;
this.logding = false
}, 100);
// console.log(res);
}
});
},
methods: {
handleToList(id) {
uni.navigateTo({
url: '/pages/list/list?id=' + id
});
},
search(){
uni.navigateTo({
url: '/pages/search/search?'
});
},
hide() {
this.loading = false;
}
}
}
</script>
3. 歌单
<template>
<view>
<!-- <uamhead :title="title"></uamhead> -->
<view class="toubu">
<view class="gdtext">歌单</view>
<view class="gd">
<view class="bd">
<text>{{playlist.playCount}}</text>
<img :src="playlist.coverImgUrl" alt="">
</view>
<view class="bd2">
<text>{{title}}</text>
<view>{{playlist.description}}</view>
</view>
</view>
</view>
<view class="zb">
<view class="zb1">
<img class="bfq" src="../../static/image/播放 (2).jpg" alt="">播放全部(共{{playlist.trackCount}}首)
</view>
<view class="gq" @tap="navList(item.id)" v-for="(item,index) in playlist.tracks" :key="index">
<text class="gqxh">{{index+1}}</text>
<view>{{item.name}}
<view class="ym">
<img class="sq" src="../../static/image/sq.png" alt="">
<text v-for="(i,index) in item.ar" :key="index">{{i.name}}/</text>
</view>
</view>
<view class="gqtp" @tap="navList(item.id)" ><img class="bfq" src="../../static/image/播放 (1).png" alt=""></view>
</view>
</view>
</view>
</template>
<script>
import {
list
} from "../../common/api.js"
export default {
data() {
return {
// id:"",
title: "榜单列表",
playlist: [],
privileges: [],
isShow: [],
title: [],
gm: []
}
},
onLoad(options) {
let listId = options.id;
console.log(listId);
list(listId).then((res) => {
this.playlist = res.data.playlist;
this.title = res.data.playlist.name;
console.log(res.data.playlist.tracks[0].id);
this.gm = res.data.playlist.tracks;
// this.privileges = res.data.playlist.tracks[0].ar[0].name;
// console.log(res.data.playlist.tracks);
});
},
methods: {
navList(listId){
uni.navigateTo({
url:'/pages/player/player?id='+listId
});
}
}
}
</script>
3.播放器
<template>
<view>
<view class="content">
<view class="player">
<image :class="{ 'run': isplayrotate }" :src="song.picUrl" mode=""></image>
<svg v-if="!isplayrotate" @tap="playing" t="1680513087859" class="icon" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7556" width="50" height="50">
<path
d="M512 37.888C251.2384 37.888 37.888 251.2896 37.888 512s213.3504 474.112 474.112 474.112 474.112-213.3504 474.112-474.112S772.7104 37.888 512 37.888z m0 908.6976c-239.0016 0-434.5856-195.584-434.5856-434.5856 0-239.0016 195.584-434.5856 434.5856-434.5856 239.0016 0 434.5856 195.584 434.5856 434.5856 0 239.0016-195.584 434.5856-434.5856 434.5856z"
p-id="7557" fill="#ffffff"></path>
<path
d="M660.736 444.416L499.2 338.944c-54.528-35.84-100.9664-11.9296-100.9664 53.7088v238.592c0 65.6384 44.3904 89.4976 100.9664 53.7088l161.536-105.3696c54.528-39.7824 54.528-99.4304 0-135.2704z"
p-id="7558" fill="#ffffff"></path>
</svg>
<svg v-else @tap="paused" t="1680509815568" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="6297" width="50" height="50">
<path
d="M512 1024C228.266667 1024 0 795.733333 0 512S228.266667 0 512 0s512 228.266667 512 512-228.266667 512-512 512z m0-42.666667c260.266667 0 469.333333-209.066667 469.333333-469.333333S772.266667 42.666667 512 42.666667 42.666667 251.733333 42.666667 512s209.066667 469.333333 469.333333 469.333333z m-106.666667-682.666666c12.8 0 21.333333 8.533333 21.333334 21.333333v384c0 12.8-8.533333 21.333333-21.333334 21.333333s-21.333333-8.533333-21.333333-21.333333V320c0-12.8 8.533333-21.333333 21.333333-21.333333z m213.333334 0c12.8 0 21.333333 8.533333 21.333333 21.333333v384c0 12.8-8.533333 21.333333-21.333333 21.333333s-21.333333-8.533333-21.333334-21.333333V320c0-12.8 8.533333-21.333333 21.333334-21.333333z"
fill="#ffffff" fill-opacity=".9" p-id="6298"></path>
</svg>
<view></view>
</view>
<scroll-view class="lyric" scroll-y="true">
<view class="wrap" :style="{ transform: 'translateY(' + - (lyricIndex - 1) * 82 + 'rpx)' }">
<view class="item" :class="{ active: lyricIndex == index }" v-for="(item, index) in song.lyric"
:key="index">
{{ item.lyric }}
</view>
</view>
</scroll-view>
<view class="songtitle">{{ song.author }} : {{ song.name }}</view>
</view>
</view>
</template>
<script>
//
import {
songDetail,
songUrl,
songLyric
} from "../../common/api.js";
const innerAudioContext = uni.createInnerAudioContext();
export default {
data() {
return {
title: '黑胶唱片',
song: {
id: '',
author: '',
name: '',
picUrl: '',
url: '',
lyric: ''
},
isplayrotate: false,
lyricIndex: 0,
innerAudioContext: null,
}
},
onLoad(options) {
let sId = options.id;
console.log(sId);
songDetail(sId).then((res) => { //获取歌曲详情
let s = res.data.songs[0]
this.song.name = s.name;
this.song.id = s.id;
this.song.picUrl = s.al.picUrl;
this.song.author = s.ar[0].name;
// console.log(res);
console.log(s);
// console.log(this.song);
})
songUrl(sId).then((res) => { //获取歌曲音频的url
this.song.songUrl = res.data.data[0].url;
// console.log(res);
})
songLyric(sId).then((res) => { //获取歌词内容
// this.song.lyric = res.data.lrc.lyric;
let lyric = res.data.lrc.lyric;
let result = [];
let re = /\[([^\]]+)\]([^[]+)/g;
lyric.replace(re, ($0, $1, $2) => {
result.push({
time: this.formatTimeToSec($1),
lyric: $2
})
}),
this.song.lyric = result;
// console.log(this.song.lyric);
})
},
methods: {
playing() {
innerAudioContext.autoplay = true;
innerAudioContext.src = this.song.songUrl;
innerAudioContext.onPlay(() => {
this.isplayrotate = true;
this.listenLyricIndex();
});
innerAudioContext.onError((res) => {
console.log(res.errMsg);
console.log(res.errCode);
});
this.innerAudioContext = innerAudioContext
},
paused() {
innerAudioContext.pause();
this.isplayrotate = false;
console.log("停止播放");
innerAudioContext.onPause(() => { //暂停
innerAudioContext.startTime = innerAudioContext.currentTime;
});
},
formatTimeToSec(time) {
var arr = time.split(':');
return (parseFloat(arr[0]) * 60 + parseFloat(arr[1])).toFixed(2);
},
listenLyricIndex() {
clearInterval(this.timer);
this.timer = setInterval(() => {
for (var i = 0; i < this.song.lyric.length; i++) {
if (this.song.lyric[this.song.lyric.length - 1].time < this.innerAudioContext
.currentTime) {
this.lyricIndex = this.song.lyric.length - 1;
break;
}
if (this.song.lyric[i].time < this.innerAudioContext.currentTime && this.song.lyric[i + 1]
.time > this.innerAudioContext.currentTime) {
this.lyricIndex = i;
}
}
})
}
}
}
</script>
4.搜索页
<template>
<view>
<!-- <input type="button" class="ss" value="" v-model="ss"> -->
<text>搜索内容:{{ss}}</text>
<!-- <uniSearchBar></uniSearchBar> -->
<uni-search-bar placeholder="搜索音乐" @focus="gg" v-on:input="dd" @confirm="ff" @cancel="ff" v-model="ss" @blur="ff"
cancel-text="取消">
<template v-slot:searchIcon>
<uni-icons color="#999999" size="18" type="home" />
</template>
</uni-search-bar>
<div class="sy">
搜索历史
<view v-for="(item,index) of key" >{{item}}</view>
</div>
<!-- 一字 -->
<div v-if="showChildDialog2">
<view>搜索</view>
<view>
<h6 v-for="(item,index) of cc">{{item.keyword}}</h6>
</view>
</div>
<div v-if="showChildDialog">
<view> <text>热搜榜</text></view>
<h6 v-for="(item,index) of aa" @tap="hh(item.searchWord)">
<img :src="item.iconUrl" alt="">
{{item.searchWord}}--{{item.content}}--{{item.score}}
</h6>
</div>
<view v-if="showChildDialog3">
<view><text>播放列表</text></view>
<h6 v-for="(item,index) of bb" :key="index" @tap="navList(item.id)">
<text>{{index+1}}</text>
{{item.album.name}}
<img src="../../static/image/sq.png" alt="">
<text v-for="(i,index) of item.artists" :key="index">{{i.name}}/</text>
<img src="../../static/播放.png" alt="">
</h6>
</view>
</view>
</template>
<script>
import {
searchHot,
searchWord,
searchSuggest
} from "../../common/api.js";
import
unisearchbar
from "../../uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue";
import gjcss from "../../components/gjcss/gjcss.vue"
export default {
data() {
return {
ss: '',
aa: [],
bb: [],
cc: [],
a: 1,
key: [],
showChildDialog: true,
showChildDialog2: true,
showChildDialog3: false
}
},
onLoad() {
searchHot().then((res) => {
this.aa = res.data.data;
});
uni.getStorage({
key: 'key',
success: function (res) {
this.key=res.data;
console.log(6666)
}
});
},
methods: {
gg() {
if (this.ss != '') {
this.showChildDialog2 = true;
}
console.log(this.showChildDialog3);
if (this.showChildDialog3 == true) {
}
},
dd() {
if (this.ss != '') {
this.showChildDialog = false;
this.showChildDialog2 = true;
searchSuggest(this.ss).then((res) => {
this.cc = res.data.result.allMatch;
});
} else {
this.showChildDialog = true;
this.showChildDialog2 = false;
}
},
ff() {
if (this.ss != '') {
this.showChildDialog2 = false;
this.showChildDialog3 = true;
searchWord(this.ss).then((res) => {
this.bb = res.data.result.songs;
});
this.key.unshift(this.ss);
this.key=[...new Set(this.key)];
uni.setStorage({
key: 'key',
data: this.key,
success: function (res) {
// this.key=res.data;
console.log(this.key)
}
});
}
},
hh(t) {
this.ss=t;
},
navList(listId) {
uni.navigateTo({
url: '/pages/player/player?id=' + listId
});
}
},
}
</script>
2. 本项目涉及的知识点一览
uni-app项目的创建
h5和安卓app发布
使用git和gitee进行代码的版本控制
使用gimp测量图片大小与距离以编写css
pages.json页面路由的配置
使用阿里云智能logo设计网站设计应用logo
安装与使用微信小程序开发工具
在uni-app中使用静态资源
iconfont的下载与使用
认识uniapp应用生命周期和页面生命周期
uni-app基础组件的使用view、scrollview、text、list
、rich-text等
安装、使用第三方组件-循环骨架m-for-skeleton
自定义组件开发-uamhead及使用向组件props传值
使用apifox调用、测试webapi
使用uni.request发起对api的网络请求并处理响应结果
在模板中声明事件及在代码中定义响应方法
uni-app中进行数据绑定、使用v-属性
使用uni.navigate进行页面间的跳转及传递参数
使用uni.createInnerAudioContext()创建音频对象并控制音频的播放
使用css3的animation实现音乐唱盘的动画效果
使用css的动态绑定技术实现动画启动-停止的控制
使用正则表达式对字符串进行搜索、替换
使用css的tranform配合js的setInterval同步歌曲的播放进度显示相应歌词
安装和使用uni-app的扩展组件uni-ui优化应用开发