前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记。

项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star。


歌曲列表 歌曲播放
一、子路由配置以及转场动画实现
  • components->singer-detail目录下:创建singer-detai.vue
  • route->index.js中:引入并配置Singer子路由SingerDetail
    1. import SingerDetail from '@/components/singer-detail/singer-detail'
    2.  
    3. {
    4. path: '/singer',
    5. component: Singer,
    6. children: [
    7. {
    8. path: ':id',
    9. component: SingerDetail
    10. }
    11. ]
    12. }
  • singer.vue中:添加<router-view></router-view>
  • listview.vue中:
  1. 给<li class="list-group-item">添加点击事件:

    1. @click="selectItem(item)"
  2. methods中定义selectItem方法,将item作为事件参数,派发出去:
    1. selectItem(item){
    2. this.$emit('select', item)
    3. }
  • singer.vue中的<listview>监听select事件,触发selectSinger,执行业务逻辑:

    1. @select="selectSinger"
    1. selectSinger(singer){
    2. this.$router.push({ //动态添加路由地址
    3. path: `/singer/${singer.id}`
    4. })
    5. }
  1. 注意:子路由并不是一个页面,只是一个层,使用z-index将之前的层全部盖住

  2. CSS样式:
    1. singer-detail
    2. position: fixed
    3. z-index: 100
    4. top: 0
    5. bottom: 0
    6. left: 0
    7. right: 0
    8. background: $color-background 
  • 转场动画:从右向左滑动
  1. 给singer-detail添加transition:

    1. <transition name="slide">
    2. <div class="singer-detail"></div>
    3. </transition>
  2. CSS样式:
    1. .slide-enter-active, .slide-leave-active
    2. transition: all 0.3s
    3. .slide-enter, .slide-leave-to
    4. transform: translate3d(100%, 0, 0) //100% 完全移动到屏幕右侧 动画开始后向左滑入
二、Vuex
  • 问题:子路由SingerDetail需要从父路由页面Singer获取很多数据,都用参数获取内容太多
  • 解决:使用Vuex实现路由之间参数数据的获取
  • Vuex GitBook地址:https://vuex.vuejs.org/zh/
  • 什么是Vuex:Vuex 是一个专为 Vue.js 应用程序开发的【状态管理模式】。
  1. 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
  2. 适用情况:构建一个中大型单页应用,考虑如何更好地在组件外部管理状态时,使用Vuex
三、Vuex初始化及歌手数据的配置

Vuex安装及文件

  • 安装

    1. npm install vuex --save
  • src->store目录下新建:
  1. index.js:入口文件
  2. state.js:管理所有状态 state
  3. mutations.js:管理所有mutation —— 更改 Vuex 的 store 中状态state的唯一方法
  4. mutation-types.js:管理所有mutation 事件类型(type)--字符串常量
  5. actions.js:处理异步操作和修改、以及对mutation的封装
  6. getters.js:对获取的state 做一些映射
  • Vuex 中的 mutation 非常类似于事件:
  1. 每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
  2. 这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

歌手数据配置

  • state.js中:定义singer数据

    1. const state = {
    2. singer: {}
    3. }
    4.  
    5. export default state
  • mutation-types.js中:定义设置singer数据的字符串常量
    1. export const SET_SINGER = 'SET_SINGER'
  • mutations.js中:对state进行修改,引入mutation-types作关联
    1. import * as types from './mutation-types'
    2.  
    3. const mutations = {
    4. [types.SET_SINGER](state, singer){
    5. state.singer = singer
    6. }
    7. }
    8. export default mutations
  • getter.js中:对state进行包装和输出,获得state.singer
    1. export const singer = state => state.singer
    2. //state => state.singer 箭头函数的简写,state是一个function,return返回一个state.singer
  • 同步修改,只需要通过mutation修改,不需要action进行异步操作
  • 初始化 index.js入口文件:
    1. import Vue from 'vue'
    2. import Vuex from 'vuex'
    3.  
    4. // * as 是es6的新import语法
    5. import * as actions from './actions'
    6. import * as getters from './getters'
    7. import state from './state'
    8. import mutations from './mutations'
    9.  
    10. //Vuex 内置日志插件用于一般的调试
    11. import createLogger from 'vuex/dist/logger'
    12.  
    13. Vue.use(Vuex)
    14.  
    15. //只在开发环境时启动严格模式
    16. const debug = process.env.NODE_ENV !== 'production'
    17.  
    18. //工厂方法输出一个单例Vuex.Store模式
    19. export default new Vuex.Store({
    20. actions,
    21. getters,
    22. state,
    23. mutations,
    24. strict: debug,
    25. plugins: debug ? [createLogger()] : []
    26. })
  1. 在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
  2. 不要在发布环境下启用严格模式:严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。
  • main.js中:引入Store,并在new Vue实例中注入

    1. import store from './store'
  • singer.vue中:
  1. 引用vuex提供的【写入数据】语法糖

    1. import {mapMutations} from 'vuex'
  2. 在methods属性中调用mapMutations作对象映射:把mutation的修改映射为一个方法名setSinger
    1. ...mapMutations({
    2. setSinger: 'SET_SINGER' //对应mutation-types中定义的常量
    3. })
  3. 在selectSinger(singer)方法中将singer传入this.setSinger()
    1. selectSinger(singer){
    2. this.$router.push({
    3. path: `/singer/${singer.id}`
    4. })
    5. this.setSinger(singer)//实现对mutation的提交,向state【写入数据】
    6. }
  • singer-detail.vue中:
  1. 引用vuex提供的【取出数据】语法糖

    1. import {mapGetters} from 'vuex'
  2. 在computed中通过mapGetters挂载singer属性:
    1. computed: {
    2. ...mapGetters([
    3. 'singer' //拿到getters.js中的singer
    4. ])
    5. }
  3. 在created()中打印出this.singer,查看vuex中数据的传递是否成功
    1. created() {
    2. console.log(this.singer)
    3. }
四、歌手详情数据抓取
  • api->singer.js中:

    1. export function gerSingerDetail(singerId) {
    2. const url = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg'
    3.  
    4. const data = Object.assign({}, commonParams, {
    5. hostUin: 0,
    6. needNewCode: 0,
    7. platform: 'yqq',
    8. order: 'listen',
    9. begin: 0,
    10. num: 100,
    11. songstatus: 1,
    12. singermid: singerId
    13. })
    14.  
    15. return jsonp(url, data, options)
    16. }
  • singer-detail.vue中:
  1. 引入getSingerDetail方法和ERR_OK常量

    1. import {getSingerDetail} from '@/api/singer'
    2. import {ERR_OK} from '@/api/config'
  2. 在methods中定义_getDetail()私有方法,通过调用getSingerDetail()返回promise对象,获取singer数据
    1. _getDetail() {
    2. getSingerDetail(this.singer.id).then((res) => {
    3. if(res.code === ERR_OK){
    4. console.log(res.data.list)
    5. }
    6. })
    7. }
  • 坑:只有从singer页面选择歌手跳转到对应singer-detail路由中,才能得到singer数据;在singer-detail路由页面刷新时不会得到数据,这样也是没有意义的
  • 解决: 在_getDetail()中添加判断,当获取不到singer.id时,调用this.$route.push,使页面回退到singer路由
    1. if(!this.singer.id){
    2. this.$router.push('/singer')
    3. return
    4. }
    5. }
五、歌手详情数据处理和Song类的封装
  • api目录下创建song.js:使用JavaScript constructor 属性构造一个Song类

    1. export default class Song {
    2. constructor({id, mid, singer, name, album, duration, image, url}){
    3. //将参数全部拷贝到当前实例中
    4. this.id = id
    5. this.mid = mid
    6. this.singer = singer
    7. this.name = name
    8. this.album = album
    9. this.duration = duration
    10. this.image = image
    11. this.url = url
    12. }
    13. }

    这样就可通过遍历res.data.list数据,得到经过Song类封装的对象

      设计为类而不是对象的好处

  • 可以把代码集中的一个地方维护
  • 类的扩展器比对象的扩展器强很多,而且它是一种面向对象的编程方式
  • 歌手详情数据处理: singer-detail.vue
  1. data中维护一个数据 songs:[ ]
  2. 在song.js中:处理musicData数据抽象出工厂方法,返回song实例
    1. //抽象出一个工厂方法:传入musicData对象参数,实例化一个Song
    2. export function createSong(musicData){
    3. return new Song({
    4. id: musicData.songid,
    5. mid: musicData.songmid,
    6. singer: filterSinger(musicData.singer),
    7. name: musicData.songname,
    8. album: musicData.albumname,
    9. duration: musicData.interval, //歌曲时长s
    10. image: `https://y.gtimg.cn/music/photo_new/T002R300x300M000${musicData.albummid}.jpg?max_age=2592000`,
    11. //url: `http://ws.stream.qqmusic.qq.com/C100${musicData.songmid}.m4a?fromtag=0&guid=126548448`
    12. //注意guid以实时数据为主
    13. url: `http://dl.stream.qqmusic.qq.com/C400${musicData.songmid}.m4a?vkey=${songVkey}&guid=6319873028&uin=0&fromtag=66`
    14. })
    15. }
    16.  
    17. //格式化处理singer数据
    18. function filterSinger(singer){
    19. let ret = []
    20. if(!singer){
    21. return ''
    22. }
    23. singer.forEach((s) => {
    24. ret.push(s.name)
    25. })
    26. return ret.join('/')
    27. }

vue.js最新版获取QQ音乐播放源

  • 播放源地址:http://dl.stream.qqmusic.qq.com/C400001apXAh2mHRub.m4a?guid=6319873028&vkey=6DAE080C291DECFDC9A3C532879658439F66EBA6C588813C8A1C12917030F
    A050C2352C15343CCCAC8FDE731383C2489026145978797D513&uin=0&fromtag=66
  • 拼接的url: http://dl.stream.qqmusic.qq.com/C400${musicData.songmid}.m4a?vkey=${songVkey}&guid=6319873028&uin=0&fromtag=66
  • 注意:guid是会变化的,以自己抓取的实际值为准,需改动的有两处:①song.js中拼接的url ②singer.js中参数guid

——参考【蚂蚁农场博客

  1. 获取正确url需要反向代理的方式请求vkey: webpack.dev.config.js中配置

    1. app.get('/api/music', function(req, res){
    2. var url="https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg"
    3.  
    4. axios.get(url, {
    5. headers: { //通过node请求QQ接口,发送http请求时,修改referer和host
    6. referer: 'https://y.qq.com/',
    7. host: 'c.y.qq.com'
    8. },
    9. params: req.query //把前端传过来的params,全部给QQ的url
    10. }).then((response) => {
    11. res.json(response.data)
    12. }).catch((e) => {
    13. console.log(e)
    14. })
    15. })
    16. }

    注意:配置完之后必须重新启动!!!

  2. 在api->singer.js中:定义getMusic方法获取vkey
    1. export function getMusic(songmid) {
    2. const url = '/api/music'
    3. const data = Object.assign({}, commonParams, {
    4. songmid: songmid,
    5. filename: 'C400' + songmid + '.m4a',
    6. guid: 6319873028, //会变,以实时抓取的数据为准
    7. platform: 'yqq',
    8. loginUin: 0,
    9. hostUin: 0,
    10. needNewCode: 0,
    11. cid:205361747,
    12. uin: 0,
    13. format: 'json'
    14. })
    15. return axios.get(url, {
    16. params: data
    17. }).then((res) => {
    18. return Promise.resolve(res.data)
    19. })
    20. }
  3. methods中:定义方法_normallizeSongs(list)按需求重新处理数据
    1. _normallizeSongs(list){
    2. let ret = [] //返回值
    3. list.forEach((item) => {
    4. let {musicData} = item //得到music对象
    5. // console.log(musicData)
    6. //createSong必传两个参数
    7. if(musicData.songid && musicData.albummid){
    8. // console.log(getMusic(musicData.songmid))
    9. getMusic(musicData.songmid).then((res) => {
    10. // console.log(res)
    11. if(res.code === ERR_OK){
    12. // console.log(res.data)
    13. const svkey = res.data.items
    14. const songVkey = svkey[0].vkey
    15. const newSong = createSong(musicData, songVkey)
    16. ret.push(newSong)
    17. }
    18. })
    19. }
    20. })
    21. // console.log(ret)
    22. return ret
    23. }
  • _getDetail()中:将处理好的数据赋给songs

    1. this.songs = this._normallizeSongs(res.data.list)

六、music-list组件开发
  • 在components->music-lict目录下:创建music-list.vue
  1. 布局DOM:

    1. <div class="music-list">
    2. <div class="back">
    3. <i class="icon-back"></i>
    4. </div>
    5. <h1 class="title" v-html="title"></h1>
    6. <div class="bg-image" :style="bgStyle">
    7. <div class="filter"></div>
    8. </div>
    9. </div>
  2. 需要从父组件接收的props参数:
    1. props: {
    2. bgImage: {
    3. type: String,
    4. default: ''
    5. },
    6. songs: {
    7. type: Array,
    8. default: []
    9. },
    10. title: {
    11. type: String,
    12. default: ''
    13. }
    14. }
  • singer-detail.vue中:应用music-list组件
  1. 将<div class="singer-detail">及其样式删掉,替换为<music-list>

    1. <music-list :songs="songs" :title="title" :bg-image="bgImage"></music-list>
  2. title和bgImage数据通过computed计算得到:
    1. title() {
    2. return this.singer.name
    3. },
    4. bgImage() {
    5. return this.singer.avatar
    6. }
  • music-list.vue中:将获得的数据填入DOM,bgStyle样式属性通过computed计算得到

    1. bgStyle() {
    2. return `background-image: url(${this.bgImage})`
    3. }
  • 【歌曲列表】抽象为song-list组件
  1. base->song-list目录下:创建song-list.vue
  2. 布局DOM
    1. <div class="song-list">
    2. <ul>
    3. <li v-for="(song, index) in songs" :key="index" class="item">
    4. <div class="content">
    5. <h2 class="name">{{song.name}}</h2>
    6. <p class="desc">{{getDesc(song)}}</p>
    7. </div>
    8. </li>
    9. </ul>
    10. </div>
  3. CSS样式
    1. .song-list
    2. .item
    3. display: flex
    4. align-items: center
    5. box-sizing: border-box
    6. height: 64px
    7. font-size: $font-size-medium
    8. .content
    9. flex: 1
    10. line-height: 20px
    11. overflow: hidden
    12. .name
    13. no-wrap()
    14. color: $color-text
    15. .desc
    16. no-wrap()
    17. margin-top: 4px
    18. color: $color-text-d
  4. 需要从父组件接收props参数songs
    1. props: {
    2. songs: {
    3. type: Array,
    4. default: []
    5. }
    6. }
  5. 将得到的数据填入DOM,其中desc通过methods定义getDesc(song)得到
    1. methods: {
    2. getDesc(song){
    3. return `${song.singer} 。${song.album}`
    4. }
    5. }
  • 在music-list.vue中应用song-list组件
  1. 引用并注册scroll和song-list组件

    1. import Scroll from '@/base/scroll/scroll'
    2. import SongList from '@/base/song-list/song-list'
  2. 布局DOM
    1. <scroll :data="songs" class="list" ref="list">
    2. <div class="song-list-wrapper">
    3. <song-list :songs="songs"></song-list>
    4. </div>
    5. </scroll>
  3. CSS样式:
    1. .list
    2. position: fixed
    3. top: 0
    4. bottom: 0
    5. width: 100%
    6. overflow: hidden
    7. background: $color-background
    8. .song-list-wrapper
    9. padding: 20px 30px
  • 坑:<scroll class="list">的top值不能写死,因为不同浏览器不同视口中bgImage的高度是不同的
  • 解决:给bgImage和list都添加ref引用,在mounted中得到当前加载好的bgImage的高度,动态赋值给top

    1. <div class="bg-image" :style="bgStyle" ref="bgImage">
    2. <scroll :data="songs" class="list" ref="list">
    1. mounted() {
    2. this.$refs.list.$el.style.top = `${this.$refs.bgImage.clientHeight}px`
    3. }
七、歌手详情页交互效果

需求

  1. 允许列表可以往上滚动,music-list.vue中去掉list的样式:overflow: hidden
  2. 需要一个在列表文字下面的层,随着列表的滚动实现往上推
  • 实现:
  1. <scroll>前添加布局DOM

    1. <div class="bg-layer" ref="layer"></div>
  2. CSS样式:
    1. .bg-layer
    2. position: relative
    3. height: 100% //屏幕高度的100%
    4. background: $color-background
  3. create()中添加属性,监听滚动:
    1. created() {
    2. this.probeType = 3
    3. this.listenScroll = true
    4. }

    将属性传入<scroll>中,并监听scroll事件,实时监听scroll位置:

    1. <scroll :data="songs"
    2. class="list"
    3. ref="list"
    4. :probe-type="probeType"
    5. :listen-scroll="listenScroll"
    6. @scroll="scroll">
  4. 同歌手列表: data中维护一个scrollY数据
    1. data() {
    2. return{
    3. scrollY: 0
    4. }
    5. }
  5. 在methods中定义scroll(),实时给scrollY赋值:
    1. scroll(pos) {
    2. this.scrollY = pos.y
    3. }
  6. watch:{ }中 监测scrollY,为layer添加引用,设置layer的transform
    1. watch: {
    2. scrollY(newY) {
    3. this.$refs.layer.style['transform'] = `translate3d(0, ${newY}px, 0)`
    4. this.$refs.layer.style['webkitTransform'] = `translate3d(0, ${newY}px, 0)`
    5. }
    6. }
  • 坑:bg-layer的高度只有屏幕高度的100%,并不能无限滚动,当超出屏幕高度后下面的内容会露出来
  • 解决:限制bg-layer的滚动位置,最远只能滚动到标题以下,再往上滚动列表时,bg-layer固定不再滚动
  • 实现:
  1. mounted中记录imageHeight,计算得到最小滚动Y

    1. this.imageHeight = this.$refs.bgImage.clientHeight
    2. this.minTranslateY = -this.imageHeight + RESERVED_HEIGHT //最远滚动位置,不超过minTranslateY
  2. 定义顶部以下偏移常量:
    1. const RESERVED_HEIGHT = 40 //滚动偏移距离
  3. scrollY(newY)中得到最大滚动量,修改transform替换newY:
    1. watch: {
    2. scrollY(newY) {
    3. let translateY = Math.max(this.minTranslateY, newY) //最大滚动量
    4. this.$refs.layer.style['transform'] = `translate3d(0, ${translateY}px, 0)`
    5. this.$refs.layer.style['webkitTransform'] = `translate3d(0, ${translateY}px, 0)`
    6. }
    7. }
  • 坑:当滚动到顶部时,列表文字会遮住图片,需要图片遮住文字
  • 解决:scrollY(newY)中添加判断,当滚到顶部时,改变图片的z-index和高度,否则,重置回初始位置
    1. //滚动到顶部时,图片遮住文字
    2. let zIndex = 0
    3. if(newY < this.minTranslateY) {
    4. zIndex = 10
    5. this.$refs.bgImage.style.paddingTop = 0
    6. this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px`
    7. }else{
    8. this.$refs.bgImage.style.paddingTop = '70%'
    9. this.$refs.bgImage.style.height = 0
    10. }
    11. this.$refs.bgImage.style.zIndex = zIndex

需求:列表从初始位置向下滚动时,图片随着滚动实现缩小放大

  • 图片从顶部放大缩小,关键样式:transform-origin: top

    1. let scale = 1
    2. const percent = Math.abs(newY / this.imageHeight)
    3. if(newY > 0) {
    4. scale = 1 + percent
    5. zIndex = 10
    6. }
    7. this.$refs.bgImage.style['transform'] = `scale(${scale})`
    8. this.$refs.bgImage.style['webkitTransform'] = `scale(${scale})`
    9. this.$refs.bgImage.style.zIndex = zIndex

需求:列表滚动到顶部时,(iphone手机中)图片有一个高斯模糊的变化

  1. <div class="bg-image" :style="bgStyle" ref="bgImage">
  2. <div class="filter" ref="filter"></div>
  3. </div>
  1. let blur = 0
  2. if(newY <= 0){
  3. blur = Math.min(20 * percent, 20)
  4. }
  5. this.$refs.filter.style['backdrop-filter'] = `blur(${blur}px)`
  6. this.$refs.filter.style['webkitBackdrop-filter'] = `blur(${blur}px)`

优化:封装JS的prefixStyle

  • CSS中不用写prefix是因为vue-loader用到了autoprefix插件自动添加
  • JS中没有,需要自己封装:利用浏览器的能力检测特性
  1. 在dom.js中扩展一个方法:

    1. //能力检测: 查看elementStyle支持哪些特性
    2. let elementStyle = document.createElement('div').style
    3.  
    4. //供应商: 遍历查找浏览器的前缀名称,返回对应的当前浏览器
    5. let vendor = (() => {
    6. let transformNames = {
    7. webkit: 'webkitTransform',
    8. Moz: 'MozTransform',
    9. O: 'OTransform',
    10. ms: 'msTransform',
    11. standard: 'transform'
    12. }
    13.  
    14. for (let key in transformNames) {
    15. if(elementStyle[transformNames[key]] !== undefined) {
    16. return key
    17. }
    18. }
    19.  
    20. return false
    21. })()
    22.  
    23. export function prefixStyle(style) {
    24. if(vendor === false){
    25. return false
    26. }
    27.  
    28. if(vendor === 'standard'){
    29. return style
    30. }
    31.  
    32. return vendor + style.charAt(0).toUpperCase() + style.substr(1)
    33. }
  2. music-list.vue中引用prefixStyle,并定义常量代替原始属性,删掉手动添加prefix的语句
    1. import {prefixStyle} from '@/common/js/dom'
    2.  
    3. const transform = prefixStyle('transform')
    4. const backdrop = prefixStyle('backdrop-filter')
    5.  
    6. this.$refs.layer.style[transform] = `translate3d(0, ${translateY}px, 0)`
    7. this.$refs.bgImage.style[transform] = `scale(${scale})`
    8. this.$refs.filter.style[backdrop] = `blur(${blur}px)`

其它功能

  • 返回按钮:@click="back"

    1. back(){
    2. this.$router.back() //回退到上一级路由
    3. }
  • 播放按钮:
    1. <div class="play-wrapper">
    2. <div class="play">
    3. <i class="icon-play"></i>
    4. <span class="text">随机播放全部</span>
    5. </div>
    6. </div>
  1. 坑:只有当列表数据都加载完成后,播放按钮才会显示
  2. 解决:设置按钮显示时机 v-show="songs.length>0"
  1. 坑:当列表滚动到顶部时,播放按钮因为绝对定位还在,体验不好,应该消失
  2. 解决:给按钮添加引用ref="playBtn",在scrollY(newY)中判断滚动到顶部时修改display为none,正常显示时重置为空
    1. if(newY < this.minTranslateY) {
    2. this.$refs.playBtn.style.display = 'none'
    3. }else{
    4. this.$refs.playBtn.style.display = ''
    5. }

优化:异步获取的歌曲数据显示之前,添加loading

  1. <div class="loading-container" v-show="!songs.length">
  2. <loading></loading>
  3. </div>
  1. .loading-container
  2. position: absolute
  3. width: 100%
  4. top: 50%
  5. transform: translateY(-50%)

注:项目来自慕课网

【音乐App】—— Vue-music 项目学习笔记:歌手详情页开发的更多相关文章

  1. 【音乐App】—— Vue-music 项目学习笔记:搜索页面开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 搜索歌手歌曲 搜索历史保存 ...

  2. 【音乐App】—— Vue-music 项目学习笔记:推荐页面开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 上一篇总结了项目概述.项目准备.页面骨架搭建.这一篇重点梳理推荐页面开发.项目github地址:https://github.com/66We ...

  3. 【音乐App】—— Vue-music 项目学习笔记:歌单及排行榜开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 歌单及详情页 排行榜及详情 ...

  4. 【音乐App】—— Vue-music 项目学习笔记:项目准备

    前言: 学习慕课网Vue高级实战课程后,在实践中总结一些这个项目带给自己的收获,希望可以再次巩固关于Vue开发的知识.这一篇主要梳理:项目概况.项目准备.页面骨架搭建.项目github地址:https ...

  5. 【音乐App】—— Vue-music 项目学习笔记:歌手页面开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 一.歌手页面布局与设计 需 ...

  6. 【音乐App】—— Vue-music 项目学习笔记:播放器内置组件开发(二)

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 播放模式切换 歌词滚动显示 ...

  7. 【音乐App】—— Vue-music 项目学习笔记:用户个人中心开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 歌曲列表 收藏歌曲 一.用 ...

  8. 【音乐App】—— Vue-music 项目学习笔记:歌曲列表组件开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 当前歌曲播放列表 添加歌曲 ...

  9. 最新 Vue 源码学习笔记

    最新 Vue 源码学习笔记 v2.x.x & v3.x.x 框架架构 核心算法 设计模式 编码风格 项目结构 为什么出现 解决了什么问题 有哪些应用场景 v2.x.x & v3.x.x ...

随机推荐

  1. sed处理大txt文件(1G) 比如替换某一串字符串,或者删除一行

    1.将11.sql文件中"prompt"替换为"--prompt",然后保存为111.sql文件 sed -e "s,prompt,--prompt, ...

  2. Solr 配置连接数据库

    前面我们将solr安装并创建了core同时也配置可IK分词器,接下来我们通过配置连接Mysql数据库并把数据导入到solr(使用ik分词器). 1.配置managed-schema文件 Request ...

  3. C++字符串高效查找替换,有空分析分析

    void CWebTransfer::Substitute(char *pInput, char *pOutput, char *pSrc, char *pDst) { char *pi, *po, ...

  4. 通过Url网络编程实现下载

    import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputS ...

  5. iOS之UITraitCollection

    UITraitCollection 为表征 size class 而生,用来区分设备.你可以在它身上获取到足以区分所有设备的特征. UITraitEnvironment 协议.UIContentCon ...

  6. Java语法糖(二)

    语法糖之四:内部类 内部类:顾名思义,在类的内部在定义一个类.内部类仅仅是编译时的概念,编译成字节码后,内部类会生成单独的Class文件. 四种:成员内部类.局部内部类.匿名内部类.静态内部类. 1. ...

  7. BZOJ 2728: [HNOI2012]与非

    2728: [HNOI2012]与非 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 786  Solved: 371[Submit][Status][ ...

  8. android基本控件学习-----Date&Time

    Date&Time这里一共讲解下面6个: TextClock(文本时钟),AnalogClock(模拟时钟),Chronometer(计时器),DatePicker(日期选择器),TimePi ...

  9. VIM使用技巧4

    使移动和修改都能重复,对重复的操作能够回退比能够重复更加重要: 目的操作重复回退序号 执行修改{edit}.u1 在行内查找下一个指定字符 f{char}/t{char};,2 在行内查找上一个指定字 ...

  10. linux 实现共享内存同步

    本文主要对实现共享内存同步的四种方法进行了介绍. 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝.它是IPC对象的一种. 为了在多个进程间交换信息,内核专门留出了 ...