美文网首页
Vue前端框架-基础入门

Vue前端框架-基础入门

作者: cuicui_0829 | 来源:发表于2021-07-26 15:59 被阅读0次

Vue基础入门

1. Vue简介

1.1 Vue.js 是什么

Vue是一套用于构建用户界面的<b>渐进式框架</b>。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

1.2 Vue.js 特点

  • <b>简洁:</b> HTML 模板 + JSON 数据,再创建一个 Vue 实例,就这么简单。
  • <b>数据驱动:</b> 自动追踪依赖的模板表达式和计算属性。
  • <b>组件化:</b> 用解耦、可复用的组件来构造界面。
  • <b>轻量:</b> ~20kb min+gzip,无依赖。
  • <b>快速:</b> 精确有效的异步批量 DOM 更新。
  • <b>模块友好:</b> 通过 NPM 或 Bower 安装,无缝融入你的工作流。

2. 所需工具

  • 包管理工具:npm,基于node.js
  • 模块打包工具:webpack
  • 调试 开发者工具: vscode

3. 框架体系

  • MVVM框架:Vue.js
  • 项目脚手架:vue-cli
  • UI组件库:Element UI
  • 路由配置:vue-router
  • 前端请求接口:axios
  • 状态管理工具:Vuex
  • 脚本语言:ES6语法
  • css预处理器:scss样式预处理器

4. 搭建项目

4.1 简单安装过程:

4.1.1 安装node.js

这个直接推荐一篇菜鸟安装教程,包含windows和linux下安装,传送门如何安装node.js

4.1.2 安装vue及依赖包

安装好node.js后,就已经安装了npm。但是直接使用npm下载组件会很慢,我们首先安装淘宝镜像。
一下命令都是通过打开cmd控制台里面运行,除了特别提醒需要cd到某些目录下运行的意外,其他的都直接在cmd根目录运行即可。

npm install -g cnpm --registry=https://registry.npm.taobao.org

安装完淘宝镜像后就可以较快的安装如下模块了。
全局安装webpack(安装过程可能会出错,如果出错,可以关掉cmd,重新打开,并使用npm install webpack -g命令重新安装,即不使用淘宝镜像)

cnpm install webpack -g

全局安装vue-cli脚手架

npm install vue-cli -g

接下来测试下是否成功安装了vue,cmd控制台输入:

vue -V

可以查看到vue版本,如图:


image.png

4.1.3 创建一个vue项目

首先选择需要将创建的目录放到哪个位置,使用cd命令切换到目录下


image.png

该目录下输入命令创建项目

vue create vue-project

<b>4.1.3.1</b> 选择项目的配置,此处选Manually select features手动配置项目(上下键移动项,回车选择)

image.png

<b>4.1.3.2</b> 选择要使用的插件(上下键移动,空格选择,回车进入下一步),此处选babel,Router,Vuex,Css,Linter

image.png

<b>4.1.3.3</b> 选择启动项目的Vue.js版本

image.png

<b>4.1.3.4</b> 询问是够使用history模式,输入Y回车

image.png

<b>4.1.3.5</b> 选择less

image.png

<b>4.1.3.6</b> 选择eslint相关配置,此处我选第三项默认配置

image.png

<b>4.1.3.7</b> 选择何时进行lint校验,保存时校验还是fix时

image.png

<b>4.1.3.8</b> 询问单独配置还是一起在packpage中配置,选单独配置

image.png

<b>4.1.3.9</b> 最后是否存储以上配置以便下次新建项目时使用

image.png

<b>4.1.3.10</b> 至此新建成功,新项目默认没有vue.config.js,需要在根目录手动创建,然后进行自己需要的配置

<b>4.1.3.11</b> 启动项目


image.png image.png

<b>4.1.3.12</b> 安装element-ui组件库

npm i element-ui -S

引入elment
你可以引入整个 Element,或是根据需要仅引入部分组件。我们先介绍如何引入完整的 Element。
在 main.js 中写入以下内容:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});

以上代码便完成了 Element 的引入。需要注意的是,样式文件需要单独引入。

5. Vue常用指令

5.1 条件判断:v-if、v-else-if、v-else

判断条件是否成立,按条件渲染页面元素;相近的指令v-show

v-if和v-show的区别:

v-show:使用该指令的元素会始终渲染在dom中,通过改变该元素的display值来实现是否显示在页面上,不需要操作js,开销相比较小
v-if:真正的条件渲染,当满足条件时才将元素添加到dom中并渲染到页面,反之销毁元素。因此性能开销大。

<template>
  <div v-if="type == 'A'">
    A
  </div>
  <div v-else-if="type == 'B'">
    B
  </div>
  <div v-else>
    C
  </div>
</template>
<script>
export default {
  name: 'Home',
  data () {
    return {
      type: 'B'
    }
  }
}
</script>

5.2 列表遍历、渲染:v-for

使用v-for指令可以很方便地遍历数组或对象并将其数据展示到页面上

<template>
  <ul>
    <li v-for="(item, index) in list" :key="index">
      {{item.name}}
    </li>
  </ul>
</template>
<script>
export default {
  name: 'Home',
  data () {
    return {
      list: [
        {
          id: 1,
          name: '列表一'
        },
        {
          id: 2,
          name: '列表二'
        }
      ]
    }
  }
}
</script>

5.3 属性绑定: v-bind

使用v-bind用于响应地更新 HTML 特性,比如绑定某个class元素或元素的style样式。

<template>
  <ul>
    <li v-for="(item, index) in list" :key="index">
      <a :class="item.id == 1 ? 'red': ''">
        {{item.name}}
      </a>
    </li>
  </ul>
</template>
<script>
export default {
  name: 'Home',
  data () {
    return {
      list: [
        {
          id: 1,
          name: '列表一'
        },
        {
          id: 2,
          name: '列表二'
        }
      ]
    }
  }
}
</script>

5.4 表单输入绑定:v-model

使用v-model指令来实现表单元素和数据的双向绑定。监听用户的输入,然后更新数据

<template>
  <input v-model="message"/>
  <p>{{message}}</p>
</template>
<script>
export default {
  name: 'Home',
  data () {
    return {
      message: 'hello'
    }
  },
  methods: {
    changeMessage () {
      this.message = 'hello1'
    }
  }
}
</script>

5.5 事件处理:v-on (简写@)

使用v-on可以很简便地监听dom事件,如focus、click、mousedown等,同时vue允许在v-on指令中添加js代码并执行

<template>
  <input v-model="message"/>
  <button @click="changeMessage">点击</button>
</template>
<script>
export default {
  name: 'Home',
  data () {
    return {
      message: 'hello'
    }
  },
  methods: {
    changeMessage () {
      this.message = 'hello1'
    }
  }
}
</script>

6.V-router路由系统

6.1 路由配置&&路由嵌套

v-router通过在router文件夹中的index.js配置路由表,可很方便的切换组件展示内容,使用路由嵌套则可很方便地实现页面菜单切换,层次分明易维护。
index.js

import { createRouter, createWebHistory } from 'vue-router'
const Home = () => import('../views/Home.vue')
const pageA = () => import('../views/a.vue')
const pageB = () => import('../views/b.vue')
const pageC = () => import('../views/c.vue')

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    children: [
      {
        path: '/a',
        name: 'a',
        component: pageA
      },
      {
        path: '/b',
        name: 'b',
        component: pageB
      },
      {
        path: '/c',
        name: 'c',
        component: pageC
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

Home.vue

<div class="router">
  <router-link to="/a">A</router-link>
  <router-link :to="{path:'/b',query:{id:1}}">B</router-link>
  <router-link to="/c">C</router-link>
  <router-view></router-view>
</div>

a.vue

<template>
  <div class="a">
    This is page A
  </div>
</template>

b.vue

<template>
  <div class="b">
    This is page B
  </div>
</template>

c.vue

<template>
  <div class="c">
    This is page C
  </div>
</template>

6.2 路由跳转的几种方式

<b>6.2.1</b> router-link

<router-link to="/a">A</router-link>

<b>6.2.2</b> this.$router.push() (在函数里面调用)

this.$router.push({
  path: '/c',
  query: {
    id: 1,
    name: 'xc'
  }
})

<b>6.2.3</b> this.$router.replace() (用法同push)

<b>两者区别:</b>

this.$router.push 跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面

this.$router.replace 跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)

6.3 动态路由的使用

动态路由(可传递参数)

this.$router.push({
  path: '/c',
  query: {
    id: 1,
    name: 'xc'
  }
})

获取路由参数

<p v-if="$route.query.id">获取到的id:{{$route.query.id}}</p>

7. Vue生命周期

7.1 什么是生命周期?

简而言之:从生到死的过程,从Vue实例创建-运行-销毁的过程
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程
<b>生命周期方法?</b>
Vue从生到死的过程中伴随着各种各样的事件,这些事件会自动触发一些方法.这些方法我们统称为生命周期方法
生命周期钩子 = 生命周期函数 = 生命周期事件

创建期间生命周期方法
beforeCreate:
created:
beforeMount
mounted
运行期间生命周期方法
beforeUpdate
updated
销毁期间的生命周期方法
beforeDestroy
destroyed

7.2 代码解释

<script>
export default {
  name: 'c',
  data () {
    return {
      msg: 'IT程序员的日常'
    }
  },
  components: {
  },
  methods: {
    say () {
      console.log('say')
    }
  },
  beforeCreate () {
    // 执行beforeCreate的时候,表示Vue刚刚出生,还没有任何内容,data/methods都没有初始化
    console.log(this.msg, '----beforeCreate')
    // this.say()
  },
  created () {
    // 执行created的时候,表示Vue实例已经初始化好了部分内容,data/methods,想在Vue实例中最早访问到data/methods,只有在这个方法才能访问
    console.log(this.msg, '----created')
    this.say()
  },
  beforeMount () {
    // 执行beforeMount,表示已经根据数据编译好了模板,但是还没有渲染到界面上
    console.log('----beforeMount')
    console.log(document.querySelector('p').innerText)
    console.log(document.querySelector('p').innerHTML)
  },
  mounted () {
    // 执行mounted,表示已经根据数据编译好了模板,已经将模板有渲染到界面上,此时可以对界面进行其他操作了
    console.log('----mounted')
    console.log(document.querySelector('p').innerText)
    console.log(document.querySelector('p').innerHTML)
  },
  beforeUpdate () {
    // 主要data中的数据发生了变化就会执行,执行beforeUpdate时候,data的数据已经是最新的了,但是没有更新界面上的数据
    console.log(this.msg, '----beforeUpdate')
    console.log(document.querySelector('p').innerText)
    console.log(document.querySelector('p').innerHTML)
  },
  updated () {
    // 主要data中的数据发生了变化就会执行,执行updated时候,data的数据已经是最新的了,界面上的数据也已经更新
    console.log(this.msg, '----updated')
    console.log(document.querySelector('p').innerText)
    console.log(document.querySelector('p').innerHTML)
  },
  beforeDestroy () {
    // 执行beforeDestroy的时候,表示Vue实例即将销毁,但是还未销毁,实例中的数据等都可以使用,最后能使用Vue实例的地址
  },
  destroyed () {
    // 执行destroyed的时候,表示vue实例完全销毁,实例中的任何内容都不能被使用了
  }
}
</script>

8. Vue组件传值

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:

image.png

如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

针对不同的使用场景,如何选择行之有效的通信方式?本文总结了vue组件常用通信的几种方式,如props、emit/on、vuex,以通俗易懂的实例讲述这其中的差别及使用场景。

8.1 props/$emit

8.1.1 父组件向子组件传值

// Home.vue父组件
<template>
  <div>
    <User :userList="userList"></User>
  </div>
</template>
<script>
import User from '@/components/user.vue'
export default {
  name: 'Home',
  data () {
    return {
      userList: ['user1', 'user2']
    }
  },
  components: {
    User
  },
  methods: {
  }
}
</script>
<template>
  <div class="user">
    <ul>
      <li v-for="(item, index) in userList" :key="index">{{item}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'user',
  props: {
    userList: { // 这个就是父组件中子标签自定义名字
      type: Array,
      required: true
    }
  },
  data () {
    return {
    }
  },
  components: {
  },
  methods: {
  }
}
</script>

总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed

8.1.2 子组件向父组件传值(通过事件形式)

// 子组件
<template>
  <div class="color">
    <button @click="updateColor">改变颜色</button>
  </div>
</template>

<script>
export default {
  name: 'color',
  data () {
    return {
    }
  },
  components: {
  },
  methods: {
    updateColor () {
      this.$emit('changeColor', '子向父组件传值--绿色') // 自定义事件  传递值“子向父组件传值”
    }
  }
}
</script>
// 父组件
<template>
  <div>
    <color @changeColor="changeColor"></color>
    {{color}}
  </div>
</template>
<script>
import color from '@/components/color.vue'
export default {
  name: 'Home',
  data () {
    return {
      color: '红色'
    }
  },
  components: {
    color
  },
  methods: {
    changeColor (e) {
      this.color = e
    }
  }
}
</script>

总结:子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

8.2 emit/on

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
<b>8.2.1</b> 具体实现方式:
Vue 3.0写法

var Event=new Mitt();
Event.emit(事件名,数据);
Event.on(事件名,data => {});

Vue 2.0写法

var Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});

<b>举个例子</b>
假设兄弟组件有三个,分别是A、B、C组件,C组件如何获取A或者B组件的数据
<b>初始化</b>
首先你需要做的是创建事件总线并将其导出,以便其它模块可以使用或者监听它。新创建一个 .js 文件,比如 eventBus.js :

import Mitt from 'mitt'
export default new Mitt()
<template>
  <div>
    <my-a></my-a>
    <my-b></my-b>
    <my-c></my-c>
  </div>
</template>
<script>
import A from '@/components/a.vue'
import B from '@/components/b.vue'
import C from '@/components/c.vue'
export default {
  name: 'Home',
  data () {
    return {
    }
  },
  components: {
    'my-a': A,
    'my-b': B,
    'my-c': C
  },
  methods: {
  }
}
</script>
// A组件
<template>
  <div>
    <h4>A组件:{{name}}</h4>
    <button @click="send">将数据发送给C组件</button>
  </div>
</template>

<script>
import Event from '@/assets/js/eventBus.js'
export default {
  name: 'a1',
  data () {
    return {
      name: 'tom'
    }
  },
  components: {
  },
  methods: {
    send () {
      Event.emit('data-a', this.name)
    }
  }
}
</script>
// B组件
<template>
  <div>
    <h4>B组件:{{age}}</h4>
    <button @click="send">将数组发送给C组件</button>
  </div>
</template>

<script>
import Event from '@/assets/js/eventBus.js'
export default {
  name: 'b1',
  data () {
    return {
      age: 20
    }
  },
  components: {
  },
  methods: {
    send () {
      Event.emit('data-b', this.age)
    }
  }
}
</script>
// C组件
<template>
  <div>
    <h4>C组件:{{ name }},{{ age }}</h4>
  </div>
</template>

<script>
import Event from '@/assets/js/eventBus.js'
export default {
  name: 'c1',
  data () {
    return {
      name: '',
      age: ''
    }
  },
  components: {},
  methods: {},
  mounted () {
    // 在模板编译完成后执行
    Event.on('data-a', (name) => {
      this.name = name
    })
    Event.on('data-b', (age) => {
      this.age = age
    })
  }
}
</script>

on 监听了自定义事件 data-a和data-b,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

8.3 Vuex使用

8.3.1 Vuex五个核心概念

VueX 是一个专门为 Vue.js 应用设计的状态管理架构,统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。

Vue有五个核心概念,state, getters, mutations, actions, modules。

1、state:vuex的基本数据,用来存储变量(后四个属性都是用来操作state里面储存的变量的)。

2、getters:是对state里面的变量进行过滤的。

3、mutations:store中更改state数据状态的唯一方法。(mutations必须是同步函数)

4、actions:像一个装饰器,包含异步操作(请求API方法)、回调函数提交mutaions更改state数据状态,使之可以异步。

5、modules:项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

8.3.2 主要代码store.js

import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 0
  },
  mutations: {
    update_count (state, data) {
      state.count = data
    }
  },
  actions: {
  },
  modules: {
  }
})
// home.vue
<template>
  <div>
    <p>{{count}}</p>
    <button @click="add">增加</button>
    <button @click="reduce">减少</button>
  </div>
</template>
<script>
import { mapMutations, mapState } from 'vuex'
export default {
  name: 'Home',
  data () {
    return {
    }
  },
  computed: {
    ...mapState([
      'count'
    ])
  },
  components: {
  },
  methods: {
    ...mapMutations(['update_count']),
    add () {
      let num = this.count
      num++
      this.update_count(num)  // 映射 this.update_count() 为 this.$store.commit('update_count')
    },
    reduce () {
      let num = this.count
      num--
      this.update_count(num)
    }
  }
}
</script>

9. vue综合项目——仿网易云音乐播放器

9.1 项目需求

<b>主界面</b>

image.png

<b>mv界面</b>

image.png

从上到下,分别是搜索栏、主体部分、音频进度条,主体部分从左到右分别是音乐列表、磁盘动画、留言列表。

<b>主要功能有:</b>

1.歌曲搜索:搜索栏中输入关键字,按下回车或者鼠标点击输入框后面的放大镜图标,返回给左侧音乐列表所有相关的歌曲名称。

2.歌曲播放:点击音乐列表中每条音乐前面的红色播放图标实现播放功能,下方进度条显示当前播放进度。

3.查看热评:歌曲播放的同时,右侧评论列表展示所有热评。

4.歌曲播放时的动画:歌曲播放的同时,磁盘中央封面改成相对应的专辑封面,上面的磁头旋转到磁盘上,歌曲暂停后,磁头再旋转回去。

5.mv播放:点击音乐列表中右侧的小电视图标,展现出四周由遮罩层包含的mv界面。

6.mv暂停:点击四周遮罩层,mv暂停,回到主界面。

9.2 相关接口

1.歌曲搜索接口
    请求地址:https://autumnfish.cn/search
    请求方法:get
    请求参数:keywords(查询关键字)
    响应内容:歌曲搜索结果
2.歌曲url获取接口
    请求地址:https://autumnfish.cn/song/url
    请求方法:get
    请求参数:id(歌曲id)
    响应内容:歌曲url地址
3.歌曲详情获取
    请求地址:https://autumnfish.cn/song/detail
    请求方法:get
    请求参数:ids(歌曲id)
    响应内容:歌曲详情(包括封面信息)
4.热门评论获取
    请求地址:https://autumnfish.cn/comment/hot?type=0
    请求方法:get
    请求参数:id(歌曲id,地址中的type固定为0)
    响应内容:歌曲的热门评论
5.mv地址获取
    请求地址:https://autumnfish.cn/mv/url
    请求方法:get
    请求参数:id(mvid,为0表示没有mv)
    响应内容:mv的地址

9.3 代码实现

<b>html:</b>

<template>
  <div class="wrap">
    <div class="play_wrap" id="player">
      <div class="search_bar">
        <img src="../assets/img/player_title.png" alt="" />
        <!-- 搜索歌曲 -->
        <input
          type="text"
          autocomplete="off"
          v-model="query"
          @keyup.enter="searchMusic()"
        />
      </div>
      <div class="center_con">
        <!-- 搜索歌曲列表 -->
        <div class="song_wrapper" ref="song_wrapper">
          <ul class="song_list">
            <li v-for="(item, index) in musicList" :key="index">
              <!-- 点击放歌 -->
              <a href="javascript:;" @click="playMusic(item.id)"></a>
              <b>{{ item.name }}</b>
              <span>
                <i @click="playMv(item.mvid)" v-if="item.mvid != 0"></i>
              </span>
            </li>
          </ul>
          <img src="../assets/img/line.png" class="switch_btn" alt="" />
        </div>
        <!-- 歌曲信息容器 -->
        <div class="player_con" :class="{ playing: isPlay }">
          <img src="../assets/img/player_bar.png" class="play_bar" />
          <!-- 黑胶碟片 -->
          <img src="../assets/img/disc.png" class="disc autoRotate" />
          <img
            :src="coverUrl == '' ? require('../assets/img/cover.png') : coverUrl"
            class="cover autoRotate"
          />
        </div>
        <!-- 评论容器 -->
        <div class="comment_wrapper" ref="comment_wrapper">
          <h5 class="title">热门留言</h5>
          <div class="comment_list">
            <dl v-for="(item, index) in hotComments" :key="index">
              <dt>
                <img :src="item.user.avatarUrl" alt="" />
              </dt>
              <dd class="name">{{ item.user.nickname }}</dd>
              <dd class="detail">
                {{ item.content }}
              </dd>
            </dl>
          </div>
          <img src="../assets/img/line.png" class="right_line" />
        </div>
      </div>
      <!--播放-->
      <div class="audio_con">
        <audio
          ref="audio"
          @play="play"
          @pause="pause"
          :src="musicUrl"
          controls
          autoplay
          loop
          class="myaudio"
        ></audio>
      </div>
      <!-- 播放mv -->
      <div class="video_con" v-show="showVideo">
        <video ref="video" :src="mvUrl" controls="controls"></video>
        <div class="mask" @click="closeMv"></div>
      </div>
    </div>
  </div>
</template>

<b>css:</b>

<style scoped>
body,
ul,
dl,
dd {
  margin: 0px;
  padding: 0px;
}

.wrap {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: url("../assets/img/bg.jpg") no-repeat;
  background-size: 100% 100%;
}

.play_wrap {
  width: 800px;
  height: 544px;
  position: fixed;
  left: 50%;
  top: 50%;
  margin-left: -400px;
  margin-top: -272px;
}

.search_bar {
  height: 60px;
  background-color: #1eacda;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  z-index: 11;
}

.search_bar img {
  margin-left: 23px;
}

.search_bar input {
  margin-right: 23px;
  width: 296px;
  height: 34px;
  border-radius: 17px;
  border: 0px;
  background: url("../assets/img/zoom.png") 265px center no-repeat
    rgba(255, 255, 255, 0.45);
  text-indent: 15px;
  outline: none;
}

.center_con {
  height: 435px;
  background-color: rgba(255, 255, 255, 0.5);
  display: flex;
  position: relative;
}

.song_wrapper {
  width: 200px;
  height: 435px;
  box-sizing: border-box;
  padding: 10px;
  list-style: none;
  position: absolute;
  left: 0px;
  top: 0px;
  z-index: 1;
}

.song_stretch {
  width: 600px;
}

.song_list {
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  height: 100%;
}
.song_list::-webkit-scrollbar {
  display: none;
}

.song_list li {
  font-size: 12px;
  color: #333;
  height: 40px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  width: 580px;
  padding-left: 10px;
}

.song_list li:nth-child(odd) {
  background-color: rgba(240, 240, 240, 0.3);
}

.song_list li a {
  display: block;
  width: 17px;
  height: 17px;
  background-image: url("../assets/img/play.png");
  background-size: 100%;
  margin-right: 5px;
  box-sizing: border-box;
}

.song_list li b {
  font-weight: normal;
  width: 122px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.song_stretch .song_list li b {
  width: 200px;
}

.song_stretch .song_list li em {
  width: 150px;
}

.song_list li span {
  width: 23px;
  height: 17px;
  margin-right: 50px;
}
.song_list li span i {
  display: block;
  width: 100%;
  height: 100%;
  cursor: pointer;
  background: url("../assets/img/table.png") left -48px no-repeat;
}

.song_list li em,
.song_list li i {
  font-style: normal;
  width: 100px;
}

.player_con {
  width: 400px;
  height: 435px;
  position: absolute;
  left: 200px;
  top: 0px;
}

.player_con2 {
  width: 400px;
  height: 435px;
  position: absolute;
  left: 200px;
  top: 0px;
}

.player_con2 video {
  position: absolute;
  left: 20px;
  top: 30px;
  width: 355px;
  height: 265px;
}

.disc {
  position: absolute;
  left: 73px;
  top: 60px;
  z-index: 9;
}
.cover {
  position: absolute;
  left: 125px;
  top: 112px;
  width: 150px;
  height: 150px;
  border-radius: 75px;
  z-index: 8;
}
.comment_wrapper {
  width: 180px;
  height: 435px;
  list-style: none;
  position: absolute;
  left: 600px;
  top: 0px;
  padding: 25px 10px;
}
.comment_wrapper .title {
  position: absolute;
  top: 0;
  margin-top: 10px;
}
.comment_wrapper .comment_list {
  overflow: auto;
  height: 410px;
}
.comment_wrapper .comment_list::-webkit-scrollbar {
  display: none;
}
.comment_wrapper dl {
  padding-top: 10px;
  padding-left: 55px;
  position: relative;
  margin-bottom: 20px;
}

.comment_wrapper dt {
  position: absolute;
  left: 4px;
  top: 10px;
}

.comment_wrapper dt img {
  width: 40px;
  height: 40px;
  border-radius: 20px;
}

.comment_wrapper dd {
  font-size: 12px;
}

.comment_wrapper .name {
  font-weight: bold;
  color: #333;
  padding-top: 5px;
}

.comment_wrapper .detail {
  color: #666;
  margin-top: 5px;
  line-height: 18px;
}
.audio_con {
  height: 50px;
  background-color: #f1f3f4;
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}
.myaudio {
  width: 800px;
  height: 40px;
  margin-top: 5px;
  outline: none;
  background-color: #f1f3f4;
}
/* 旋转的动画 */
@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;
}
/* 是否正在播放 */
.player_con.playing .disc,
.player_con.playing .cover {
  animation-play-state: running;
}

.play_bar {
  position: absolute;
  left: 200px;
  top: -10px;
  z-index: 10;
  transform: rotate(-25deg);
  transform-origin: 12px 12px;
  transition: 1s;
}
/* 播放杆 转回去 */
.player_con.playing .play_bar {
  transform: rotate(0);
}
/* 搜索历史列表 */
.search_history {
  position: absolute;
  width: 296px;
  overflow: hidden;
  background-color: rgba(255, 255, 255, 0.3);
  list-style: none;
  right: 23px;
  top: 50px;
  box-sizing: border-box;
  padding: 10px 20px;
  border-radius: 17px;
}
.search_history li {
  line-height: 24px;
  font-size: 12px;
  cursor: pointer;
}
.switch_btn {
  position: absolute;
  right: 0;
  top: 0;
  cursor: pointer;
}
.right_line {
  position: absolute;
  left: 0;
  top: 0;
}
.video_con video {
  position: fixed;
  width: 800px;
  height: 546px;
  left: 50%;
  top: 50%;
  margin-top: -273px;
  transform: translateX(-50%);
  z-index: 990;
}
.video_con .mask {
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  z-index: 980;
  background-color: rgba(0, 0, 0, 0.8);
}
.video_con .shutoff {
  position: fixed;
  width: 40px;
  height: 40px;
  background: url("../assets/img/shutoff.png") no-repeat;
  left: 50%;
  margin-left: 400px;
  margin-top: -273px;
  top: 50%;
  z-index: 995;
}
</style>

<b>js:</b>

<script>
import axios from 'axios'
export default {
  name: 'music',
  data () {
    return {
      // 搜索关键字
      query: '',
      // 歌曲列表
      musicList: [],
      // 歌曲url
      musicUrl: '',
      // 是否正在播放
      isPlay: false,
      // 歌曲热门评论
      hotComments: [],
      // 歌曲封面地址
      coverUrl: '',
      // 显示视频播放
      showVideo: false,
      // mv地址
      mvUrl: ''
    }
  },
  components: {},
  // 方法
  methods: {
    // 搜索歌曲
    searchMusic () {
      if (this.query == 0) {
        return false
      }
      axios.get('/api/search?keywords=' + this.query).then(response => {
        // 保存内容
        this.musicList = response.data.result.songs
      })
      // 清空搜索
      this.query = ''
    },
    // 播放歌曲
    playMusic (musicId) {
      // 获取歌曲url
      axios.get('/api/song/url?id=' + musicId).then(response => {
        // 保存歌曲url地址
        this.musicUrl = response.data.data[0].url
      })
      // 获取歌曲热门评论
      axios.get('/api/comment/hot?type=0&id=' + musicId).then(response => {
        // 保存热门评论
        this.hotComments = response.data.hotComments
      })
      // 获取歌曲封面
      axios.get('/api/song/detail?ids=' + musicId).then(response => {
        // 设置封面
        this.coverUrl = response.data.songs[0].al.picUrl
      })
    },
    // audio的play事件
    play () {
      this.isPlay = true
      // 清空mv信息
      this.mvUrl = ''
    },
    // audio的pause事件
    pause () {
      this.isPlay = false
    },
    // 播放mv
    playMv (vid) {
      if (vid) {
        this.showVideo = true
        // 获取mv信息
        axios.get('/api/mv/url?id=' + vid).then(response => {
          // 暂停歌曲播放
          this.$refs.audio.pause()
          // 获取mv地址
          this.mvUrl = response.data.data.url
        })
      }
    },
    // 关闭mv界面
    closeMv () {
      this.showVideo = false
      this.$refs.video.pause()
    }
  }
}
</script>

相关文章

网友评论

      本文标题:Vue前端框架-基础入门

      本文链接:https://www.haomeiwen.com/subject/bhlgmltx.html