momentjs的官网: 直通车.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.js">script>
<script>
// moment.js是日期处理类库
// 常用功能:
// 1.获取当前时间,按照你指定的格式输出, 后面大写的HH是24小时制的,如果是hh是12小时制的
let date1 = moment().format("YYYY年MM月DD日");//2020年05月14日
let date2 = moment().format("YYYY-MM-DD HH:mm:ss");//2020-05-14 11:51:20
console.log(date1,date2);
// 2.把指定的一个时间戳,按照你指定的格式输出
let date3 = moment(1430275262861).format("YYYY年MM月DD日");//2015年04月29日
let date4= moment(1430275262861).format("YYYY-MM-DD HH:mm:ss");//2015-04-29 10:41:02
console.log(date3,date4);
script>
body>
html>
效果说明: 基本使用就是moment(不给毫秒时间或者给一个毫秒时间).format(‘时间格式’)
Y年M月D日H小时,以24小时计,h以12小时计,m分钟,s秒
父组件通过 props 向下传递数据给子组件
<template>
<div>
<p>我是子组件p>
<p>{{msg}}p>
div>
template>
<script>
export default {
props:['msg'],
created() {
console.log(this.msg)//你好鸭
},
}
script>
<style >
style>
<template>
<div>
<p>我是主组件p>
<son msg="你好鸭">son>
div>
template>
<script>
/*
父组件传值给子组件
1.在子组件标签上定义一个属性,属性的值就是父组件要传给子组件的值
定义的属性名字不要用那些默认的名字比如什么class,src,url啊什么的
2. 在子组件中接受数据:
props:['定义在子组件标签上得属性']
3.使用值
直接用就行或者this.属性名
*/
//1.导入子组件
import son from "./components/son";
export default {
// 2.注册子组件
components: {
son
}
};
script>
<style>
style>
效果说明: 在父组件里面的子组件标签上定义一个属性 <子组件:‘自定义属性名=“值”’>子组件>
在子组件接收数据,在props中接收:props:[‘自定义属性名’],如果想使用传递过来的数据this.自定义属性名
注意点:props传递过来的数据是单向数据流,不可修改
下面这个你好鸭就是主组件传递给子组件的数据’你好鸭’
<template>
<div>
<div class="son" ref='son'>我是子组件div>
div>
template>
<script>
export default {
data() {
return {
msg:'我是子组件的msg',
timerId:""
}
},
created() {
//计时器
this.timerId = setInterval(() => {
console.log(111);
}, 1000);
},
//销毁前钩子函数
//销毁前,准备销毁, 他这里也是啥都可以访问,往往用来做销毁前的善后处理.
beforeDestroy() {
console.log('beforeDestroy',this.msg,this.$refs.son);
clearInterval(this.timerId);
},
//销毁后钩子函数
//中止了渲染(可以理解回到了created), 可以访问data和methods,不能访问渲染后的dom.
//可以访问data和methods,那他也可以做善后处理
//destroyed() {
// console.log('destroyed',this.msg,this.$refs.son);
// clearInterval(this.timerId);
// },
}
script>
<style scoped>
.son {
width: 300px;
height: 200px;
background-color: red;
}
style>
<template>
<div>
<button @click="isShow = !isShow">点我button>
<son v-if="isShow">son>
div>
template>
<script>
/*
生命周期钩子函数,都是到了某个时刻自动触发的
beforeCreate:
创建前,他还不能访问data和methods,实际开发基本不用.
created:
创建完成,他可以访问data和methods.他不能访问vue渲染的dom,实际开发的时候用他做页面一进来数据的获取.
brforeMount:
渲染前,他不能访问渲染后的dom,实际开发也基本不用.
mounted:
渲染后,可以访问vue渲染后的dom, 是第一个可以访问dom的生命周期函数.
beforeUpdate:
数据更新完毕了,但是还没有根据新的数据渲染dom.
updated:
根据新的数据 已经重新渲染dom完成了.
beforeDestroy:
销毁前,准备销毁, 他这里也是啥都可以访问,往往用来做销毁前的善后处理.
destroyed:
销毁后,中止了渲染(可以理解回到了created), 可以访问data和methods,不能访问渲染后的dom.
可以访问data和methods,那他也可以做善后处理
*/
//导入子组件
import son from './components/son'
export default {
data() {
return {
isShow:true
}
},
//注册
components:{
son
}
};
script>
<style>
style>
效果说明:
当红色盒子v-if是false时,不渲染时就是被销毁了,此时定时器也会一直有,此刻我不想让计时器一直打印
就可以在beforedestroy(){}里面写一些逻辑代码来阻止定时器继续打印,并且
beforedestroy是被销毁之前执行的函数,被销毁之前是什么不管是data还是methods里的数据,dom啊都是可以访问的,
当红色盒子v-if是false时,不渲染时就是被销毁了,此时定时器也会一直有,此刻我不想让计时器一直打印
就可以在destroyed(){}里面写一些逻辑代码来阻止定时器继续打印,并且
destroyed是被销毁后执行的函数,被销毁后是data还是methods里的数据是可以访问的,但是dom是不可以访问的,
// 解决相同路由跳转报错的问题
// 将vueRouter中的push保存起来
const VueRouterPush = VueRouter.prototype.push
// 重新给VueRouter中的push赋值
VueRouter.prototype.push = function push (to) {
// this的指向问题
// this就是vueRouter
// Router.push返回的promise对象(then&catch)
// 使用catch已经将错误信息捕获了
return VueRouterPush.call(this, to).catch(err => err)
}
<template>
<div id="player">
<h2 class="title">黑云音乐h2>
<div class="search">
<input type="text" v-model="songName" @keyup.enter="search" />
<button @click="search">
<span class="iconfont icon-search">span>
button>
div>
<div class="tab-wrapper">
<div class="tab-content">
<router-view>router-view>
div>
div>
div>
template>
<script>
export default {
data() {
return {
// 用户搜索的歌曲名
songName: ""
};
},
methods: {
search() {
// 输入框搜索歌曲不能为空
if (this.songName.trim() == "") {
alert("请输入正确的歌曲名");
return;
}
// 路由传值
this.$router.push({
path: "/songList",
query: {
songName: this.songName
}
});
}
}
};
script>
<style>
@import url("./assets/css/index.css");
@import url("./assets/css/iconfont.css");
style>
<template>
<div>
<el-carousel :interval="5000" arrow="always" height="370px">
<el-carousel-item v-for="(item, index) in imgUrl" :key="index">
<img :src="item.imageUrl" alt />
el-carousel-item>
el-carousel>
div>
template>
<script>
// 1.2.安装导入axios
// import axios from "axios";
export default {
data() {
return {
// 轮播图需要用到的图片存放数组
imgUrl: ""
};
},
created() {
// 3.使用axios:发送ajax请求得到轮播图图片
this.$axios({
url: "https://autumnfish.cn/banner",
params: {
xxx: Math.random()
}
}).then(res => {
// console.log(res);
this.imgUrl = res.data.banners;
});
}
};
script>
<style scoped>
img {
width: 100%;
}
style>
<template>
<div>
<div class="result-wrapper">
<div class="song" v-for="(item, index) in songList" :key="index">
<div class="name" @dblclick="goComments(item.id)">
<span class="iconfont icon-play" @click="playMusic(item.id)">span>
{{item.name}}
<span
class="iconfont icon-editmedia"
@click="playMV(item.mvid)"
v-if="item.mvid != 0"
>span>
div>
<div class="singer">{{item.artists | qjfilterSongArt}}div>
<div class="album">《{{item.album.name}}》div>
<div class="time">{{item.duration | filterTime}}div>
div>
div>
div>
template>
<script>
/*
过滤器定义在filters里面.
目前过滤器只能用在插值语法和v-bind中.
使用: {{ 参数 | 过滤器名 }}
过滤器本质是一个方法,会返回一个值.
*/
// 1.2.安装导入axios
// import axios from "axios";
export default {
data() {
return {
// 存放歌曲列表的数组
songList: []
};
},
methods: {
//发送请求得到歌曲列表封装成一个函数
getData() {
this.$axios({
url: "https://autumnfish.cn/search",
params: {
keywords: this.$route.query.songName,
xxx: Math.random() * 999
}
}).then(res => {
//console.log(res);
//把返回的歌曲列表存到songList数组中,然后渲染出来
this.songList = res.data.result.songs;
});
},
// 点击mv小图标时,去到/video路由对应的组件(把mvid参数带过去)
playMV(mvid) {
this.$router.push({
path: "/video",
query: {
mvid
}
});
},
//点击播放音乐小图标时,去到/player路由对应的组件(把id参数带过去)
playMusic(id) {
this.$router.push({
path: "/player",
query: {
id
}
});
},
//双击歌曲时,去到/comments路由对应的组件(把id参数带过去)
goComments(id) {
this.$router.push({
path: "/comments",
query: {
id
}
});
}
},
created() {
//接收路由传的值,搜索的歌曲名
//console.log(this.$route.query.songName);
// 3.使用axios,发送请求得到搜索的歌曲列表
this.getData();
},
//监听器们
watch: {
// 监听器监听传参的值
// (也就是搜索的歌曲名只要一发生变化就调用这个方法发送请求得到歌曲列表)
"$route.query.songName"() {
this.getData();
}
},
//过滤器们
filters: {
//过滤演唱者们的过滤器
// filterSongArt(arr) {
// let _arr = arr.map(item => {
// return item.name;
// });
// return _arr.join("&");
// },
// 过滤时间的过滤器(把时间戳形式的时间搞成分秒形式)
filterTime(time) {
let m = ("0000" + Math.floor(time / 1000 / 60)).slice(-2); //分
let s = ("0000" + Math.floor((time / 1000) % 60)).slice(-2); //秒
return m + ":" + s;
}
}
};
script>
<style scoped>
.name {
cursor: default;
user-select: none;
}
style>
<template>
<div class="video" v-if="mvInfo != ''">
<div class="title-wrapper">
<span class="tag">MVspan>
<span class="title">{{mvInfo.name}}span>
<span class="artist">{{mvInfo.artists | qjfilterSongArt}}span>
div>
<video :src="mvUrl" controls>video>
div>
template>
<script>
// 导入axios(用了axios全局调用,不需要每次导入了)
// import axios from "axios";
export default {
data() {
return {
// mv的信息
mvInfo: "",
// mv的播放地址
mvUrl: ""
};
},
created() {
//使用axios,发送请求得到mv的播放信息(标题跟演唱者)
this.$axios({
method: "get",
url: "https://autumnfish.cn/mv/detail",
params: {
mvid: this.$route.query.mvid,
xxx: Math.random() * 999
}
}).then(res => {
//console.log(res);
this.mvInfo = res.data.data;
});
//使用axios,发送请求得到mv的播放地址
this.$axios({
method: "get",
url: "https://autumnfish.cn/mv/url",
params: {
id: this.$route.query.mvid,
xxx: Math.random() * 999
}
}).then(res => {
//console.log(res);
this.mvUrl = res.data.data.url;
});
},
//过滤器们
// filters: {
// //过滤演唱者们的过滤器
// filterSongArt(arr) {
// let _arr = arr.map(item => {
// return item.name;
// });
// return _arr.join("&");
// }
// }
};
script>
<style>
style>
<template>
<div class="comment-wrapper">
<div class="items">
<div class="item" v-for="(item, index) in commentsList" :key="index">
<div class="left">
<img :src="item.user.avatarUrl" alt />
div>
<div class="right">
<div class="top">
<span class="user">{{item.user.nickname}}span>
<span class="content">{{item.content}}span>
div>
<div class="bottom">
<div class="time">{{item.time | filterTime}}div>
<div class="like-wrapper">
<span>span>
({{item.likedCount}})
div>
div>
div>
div>
div>
div>
template>
<script>
// 导入axios
// import axios from "axios";
import moment from 'moment'
export default {
data() {
return {
// 评论列表
commentsList: ""
};
},
created() {
// 使用axios发送请求得到评论的相关信息
this.$axios({
url: "https://autumnfish.cn/comment/music",
params: {
id: this.$route.query.id,
xxx: Math.random() * 999
}
}).then(res => {
//console.log(res);
//把返回的评论列表存到commentsList中然后渲染出来
this.commentsList = res.data.hotComments;
});
},
// 过滤器们
filters: {
// 处理时间的过滤器
filterTime(time) {
//后面大写的HH是24小时制的,如果是hh是12小时制的
return moment(time).format("YYYY年MM月DD日 HH:mm:ss")
}
}
};
script>
<style>
style>
<template>
<div class="player" v-if="songInfo!=''">
<div class="left">
<img src="../assets/img/player_bar.png" class="play_bar" :class="{playing:playing}" />
<img class="disc autoRotate" src="../assets/img/disc.png" :class="{playing:playing}" />
<img class="cover autoRotate" :src="songInfo.al.picUrl" :class="{playing:playing}" />
div>
<div class="right">
<div class="title">
<img src="../assets/img/tag.png" alt />
<span>{{songInfo.name}}span>
div>
<div class="singer">
歌手:
<span>{{songInfo.ar | qjfilterSongArt}}span>
div>
<div class="album">
所属专辑:
<span>{{songInfo.al.name}}span>
div>
<audio
class="audio"
controls
:src="musicUrl"
@timeupdate="timeChange"
@pause="pauseEvent"
@play="playEvent"
>audio>
<ul class="lyric-container">
<li
class="lyric"
:class="{big:currentIndex==index}"
v-for="(item, index) in songLrc"
:key="index"
>{{item}}li>
ul>
div>
div>
template>
<script>
// import axios from "axios";
export default {
data() {
return {
// 歌曲的播放地址
musicUrl: "",
// 歌曲信息
songInfo: "",
// 歌曲歌词
songLrc: [],
// audio进度条当前在的时间
currentTime: 0,
// 指的是歌词在songLrc的当前下标
currentIndex: 0,
// 碟片是否旋转
playing: false
};
},
methods: {
timeChange(e) {
this.currentTime = e.target.currentTime; //这里的单位是秒
},
playEvent() {
this.playing = true;
},
pauseEvent() {
this.playing = false;
}
},
//过滤器们
// filters: {
// //过滤演唱者们的过滤器
// filterSongArt(arr) {
// let _arr = arr.map(item => {
// return item.name;
// });
// return _arr.join("&");
// }
// },
//监听器audio播放条变动的时间
watch: {
currentTime() {
// 遍历歌词数组
for (let i = 0; i < this.songLrc.length; i++) {
if (this.songLrc[i]) {
let m = parseFloat(this.songLrc[i].split(":")[0].split("[")[1] * 60); //[xx:xx]左边的分算成秒
// 如果不是数字的话,就一般第一句是[by:茗雅呈]这种就没有时间不是数字
if (isNaN(m)) {
m = 0;
}
let s = parseFloat(this.songLrc[i].split(":")[1].split("]")[0]); //[xx:xx]
// 如果不是数字的话
if (isNaN(m)) {
s = 0;
}
let lrcTime = m + s;//歌词时间
// console.log(lrcTime);
// console.log(this.currentTime);
if (lrcTime > this.currentTime) {
// 如果歌词时间大于当前播放时间,说明应该还在上一个歌词,所以-1
this.currentIndex = i - 1;
return;
} else {
this.currentIndex = i;
}
}
}
}
},
created() {
//使用axios,发送请求得到音乐的播放地址
this.$axios({
method: "get",
url: "song/url",
params: {
id: this.$route.query.id,
xxx: Math.random() * 999
}
}).then(res => {
//console.log(res);
this.musicUrl = res.data.data[0].url;
});
//发送axios,发送请求得到音乐详情如图片,演唱者等
this.$axios({
method: "get",
url: "song/detail ",
params: {
ids: this.$route.query.id,
xxx: Math.random() * 999
}
}).then(res => {
// console.log(res);
this.songInfo = res.data.songs[0];
});
//发送axios,发送请求得到歌词
this.$axios({
method: "get",
url: "lyric",
params: {
id: this.$route.query.id,
xxx: Math.random() * 999
}
}).then(res => {
//console.log(res);
// 歌词变成数组了
if (res.data.nolyric != true) {
//要是有歌词的就显示歌词,如果没有歌词比如纯音乐他返回的res.data.nolyric:true
this.songLrc = res.data.lrc.lyric.split("\n");
}
});
}
};
script>
<style scoped>
/*这是我后面优化了一下让音乐的时候那个碟片可以旋转*/
/* 旋转的动画 */
@keyframes Rotate {
from {
transform: rotateZ(0);
}
to {
transform: rotateZ(360deg);
}
}
/* 旋转的类名 */
.autoRotate {
animation-name: Rotate;
animation-iteration-count: infinite;
animation-play-state: paused;
animation-timing-function: linear;
animation-duration: 5s;
}
/* 是否正在播放 */
.playing {
animation-play-state: running;
}
.play_bar {
position: absolute;
left: 100px;
/* top: -10px; */
z-index: 10;
transform: rotate(-30deg);
transform-origin: 12px 12px;
transition: 1s;
}
/* 播放杆 转回去 */
.play_bar.playing {
transform: rotate(0);
}
style>
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 轮播图
//1. 安装导入element插件以及他的css
// 2.注册
// 3.使用(复制官网代码到home.vue)
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
/*
1.回忆一下局部过滤器
写在某一个组件中,只对当前组件有用
写在filters里面
filters:{
filterArr(参数){
return 值;
}
}
使用:插值语法和v-ind中{{值 | filterArr}}
2.全局过滤器
写在main.js中,所有的组件都可以使用
Vue.filter('过滤器名字',(参数)=>{
return 值;
});
使用:插值语法和v-ind中{{值 | filterArr}}
*/
// 全局过滤器
Vue.filter('qjfilterSongArt', (arr) => {
let _arr = arr.map((item) => {
return item.name
});
return _arr.join('❤')
});
// 导入router路由index.js文件
import router from './router/index.js'
//导入axios
import axios from 'axios'
// axios设置基地址
// 后面用axios时就直接导入后面的 / 什么的就行
//会自动判断 axios的url前面是否有http地址,如果没有,它会把这个基地址加到前面,如果有了,它就不加了
axios.defaults.baseURL = 'https://autumnfish.cn'
// axios全局调用
//后面用axios时就不用在每个组件里面导入axios,用的时候直接this.axios({})
Vue.prototype.$axios=axios
new Vue({
render: h => h(App),
router
}).$mount('#app')
// 路由抽取全部到router文件夹下的index.js文件:
import Vue from 'vue'
// 导入子组件
import home from '../components/home.vue'
import songList from '../components/songList.vue'
import video from '../components/video.vue'
import player from '../components/player.vue'
import comments from '../components/comments.vue'
// 1.安装导入路由插件vue-router
import VueRouter from 'vue-router'
// 2.注册
Vue.use(VueRouter)
// 解决 相同路由相同参数跳转 请求组件,浏览器觉得是同一个没必要重新请求报错(百度找)
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
// 3.实例化配置
const router = new VueRouter({
routes: [
{
path: '/',
component: home
},
{
path: '/songList',
component: songList
},
{
path: '/video',
component: video
},
{
path: '/player',
component: player
},
{
path: '/comments',
component: comments
},
{
//路由重定向
path: '*',
redirect:'/'
}
]
});
// 4.挂载到new Vue
// 5.路由出口
// 导出路由待会去到main.js里面挂载
export default router
效果说明:
home.vue组件:展示轮播图
:
从App.vue中搜索歌曲到songList.vue组件:展示歌曲列表:
点击歌曲列表中的前面中的播放小图标进入player.vue组件,展示歌曲的相关信息,播放歌曲
双击歌曲列表的某一项会进入到comments.vue组件中,展示评论
点击歌曲列表中有mv标志的小图标进入到video.vue组件中播放mv
项目补充优化部分:
1.全局过滤
:上一篇文章的demo案例中写的都是局部过滤器,但是也简单提到了全局过滤器的使用方法
在该项目中,过滤处理 演唱者名字 就在多个子组件中都有用到,就可以把他写成全局过滤,
以下代码就是该项目中写在main.js的一个全局过滤器
2.路由抽取成单独js文件
本来该项目的index.js跟main.js中的内容我是写在一起的,但是实际开发中这样的话main.js中代码会非常多显得冗余,所以就把里面写的关于路由部分的部分单独抽取到index.js文件中(index.js文件是在router文件夹中的文件)
其中index.js中写完路由部分之后要把实例化的router导出给到main.js中挂载到main.js中的实例化new Vue
main.js就要导入刚刚index.js导出时router
export default 一个js里只能写一个
export default 它配套的import不用写import {}它是import 名字 from 路径
export 配套的import import {名字(export 中对应的名字)} from “路径”
但是export 可以写多个
使用exporrt default导出的值,在import 名字 from “路径”,这个名字可以随便定义
但是使用export 导出的值,在import {名字} from “路径” 这里的名字必须和export的名字对应上
3.路由重定向
当我们在浏览器随便输入一个东西时也不是下面那些路由时就会重定向,这里我们重定向是定在了/路由默认在home.vue组件下
4.axios基地址与全局配置
a. 基地址设置
axios.defaults.baseURL=‘http://183.237.67.218:3000’
会自动判断 axios的url前面是否有http地址,如果没有,它会加到前面,如果有了,它就不加了
b.全局配置
在vue原型上加入$axios,让Vue的所有实例都可以调用该axios, Vue.prototype. $axios=axios