包括子路由的配置及转场动画实现、Vuex 的介绍、Vuex 初始化歌手数据的配置、歌手详情页数据抓取和处理、Song 类的封装、music-list 组件开发。

6-1 歌手详情页布局和设计详解

6-2 子路由配置以及转场动画实现

src\router\index.js{path:'/singer',component:singer,name:'歌手',children:[{path:'/singer/:id',component:singerDetail}]},

给歌手项绑定点击事件,派发数据
src\components\base\listview\listview.vue

 <li class="list-group-item" v-for="singer in item.items" :key="singer.id"  @click="selectItem(singer)"><img class="avatar" v-lazy="singer.avatar"><span class="name">{{singer.name}}</span>
</li>
selectItem(singer){this.$emit('select',singer)},

src\components\singer\singer.vue

  <listview :data="singerList" @select="selectSinger"></listview>

singer-detail.vue监听到点击的事件,改变路由

 //监听到点击的事件,改变路由selectSinger(singer){this.$router.push({path:`/singer/${singer.id}`})
},

注意:子路由并不是一个页面,只是一个层,使用z-index将之前的层全部盖住

转场动画:从右向左滑动。添加transition

 <transition name="slide"><router-view class="singer-detail"></router-view>
</transition>
.slide-enter-active,.slide-leave-active{transition: all .3s
}
.slide-enter,.slide-leave-to{transform: translate3d(-100%,0,0);
}

6-3 初识 Vuex

vuex是什么

vue是一种设计思想,我们把组件的数据放在同一个内存空间去管理,这个内存空间我门称它为state。state的数据能非常方便映射到组件上,来渲染组件。

当组件的数据发生变化的时候,它可以dispatch一个action(action通常是一些异步操作,和后端的一些交互,或者同时改变多个mutation时,对mutation的封装)

action可以commit一个mutation(注意也可以直接在组件中commit一个mutation的)。除了这两种方式,其他的改变state的途径都时非法的。

然后在mutations里面修改state

vuex这样设计的目的就是为了state的状态可以预测,当state被修改之后,它又会直接映射到组件上,这样就形成了一个闭环

在vuex中,我们必须要通过dispatch来commit一个mutation。或者直接在组件上commit一个mutation来修改state的数据,这样比起不用vuex直接在组件上修改数据复杂多了,那么我们为什么还需要使用vuex呢

vuex的应用场景

  1. 解决多个组件之间的状态共享问题(比如多个兄弟组件,多级组件之间的通讯)
  2. 解决路由之间复杂的数据传递(当在路由中,需要传递的参数过多的时候)

vuex的文件结构

文件名称 描述
index.js 入口文件
state.js 管理多个组件公用的数据状态
mutation 更改 Vuex 的 store 中状态state的唯一方法
mutation-types.js 管理所有mutation 事件类型(type)–字符串常量
actions.js 处理异步操作和修改、以及对mutation的封装
getters.js 对获取的state 做一些映射

为什么要用mutation-types呢

  1. 便于书写方便
  2. IDE能够够帮我们检测出没我们写的对不对。通常mutations和mutation-types和actions是关联的

6-4 Vuex 初始化及歌手数据的配置

这里我们在需要在上一个路由中获取到歌手的相关信息,歌手的相关信息较多,我们不应该直接放在路由参数中传递过来,而是,我们应该通过vuex来管理singer数据

(1)确定状态
src\store\state.js

const state={singer:{}
}
export default state

(2)映射state里面的数据,做一层getters的封装,这样我们可以直接从getters拿到数据到组件上
src\store\getters.js

export  const singer=state=>state.singer

(3)在确定mutation之前,我们需要先确定mutation-types,在这里定义一些常量
src\store\mutation-types.js

export const SET_SINGER='SET_SINGER'

(4)在mutations里定义一些修改state的操作
src\store\mutations.js
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)

import * as types from './mutation-types'
const mutations={[types.SET_SINGER](state,singer){state.singer=singer}
}
export default mutations

(5)export store 出去,并且需要在main.js里面注册,这样每个组件都可以访问到vuex管理的状态了
src\store\index.js

import Vue from 'vue'
import Vuex  from 'vuex'
Vue.use( Vuex)
import * as getters from './getters'
import * as actions from './actions'
import state  from './state'
import mutations from './mutations'
export default new Vuex.Store({state,getters,actions,mutations
})

(6)应用vuex
这里我们使用的是vuex的语法糖mapMutations,引入之后,我们需要在methods里面获取到SET_SINGER这个mutation。并且在组件上发起一个mutation
src\components\singer\singer.vue

import {mapMutations} from 'vuex'
 ...mapMutations({set_singer:'SET_SINGER'
})
selectSinger(singer){this.$router.push({path:`/singer/${singer.id}`})this.set_singer(singer);
},

6-5 歌手详情数据抓取

//获取到歌手详情页面列表数据,根据id来获取数据,所以要传入singerId
export function getSingerDetail(singerId){const url = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg'const data=Object.assign({},commonParams,{hostUin: 0,needNewCode: 0,platform: 'yqq',order: 'listen',begin: 0,num: 100,songstatus: 1,singermid: singerId})return jsonp(url,data,options)
}

src\components\singer-detail\singer-detail.vue

import {mapGetters}  from 'vuex'
export default {computed: {...mapGetters(['singer'])},created() {console.log(this.singer);},
}

得到vuex的singer的相关数据,我们需要获取到详情页的数据,当在当前页面刷新的时候,this.singer.id是取不到的,我们在这里需要做路由处理,当我们在当前页面刷新的时候,让页面回到歌手页

 created() {this._getSingerDetail();},
 _getSingerDetail(){//当在当前页面刷新的时候,跳转回singer页if(!this.singer.id){this.$router.push('/singer')return}//获取到列表getSingerDetail(this.singer.id).then(res=>{if(res.ERR_OK){//对得到的数据进行处理this._normallizeSingerDetail(res.data.list)}}).catch(err=>{console.log(err);})}

6-6 歌手详情数据处理和Song类的封装(上)

src\common\js\song.js

export default class Song{//因为参数过多,我们把参数放在一个对象中去constructor({id, mid, singer, name, album, duration, image, url}){this.id=id,this.mid=mid,this.singer=singer,this.name=namem,this.album=album,this.duration=duration,this.image=image,this.url=url}
}

在singer-detail\singer-detail.vue中维护一个song引入song类文件,我们将musicData的每一项都抽象成Song类,但是传入的参数过多了,而且每次使用的时候都需要new Song。我们希望能做到的是直接传入musicData返回一个Song实例数组。

 _normallizeSingerDetail(list){let ret=[];//返回值list.forEach(musicData => {ret.push(new Song({//...传入的参数过多}))});return ret}

6-7 歌手详情数据处理和Song类的封装(下)

在song.js中:处理musicData数据抽象出工厂方法,返回song实例

class Song{//因为参数过多,我们把参数放在一个对象中去constructor({id, mid, singer, name, album, duration, image, url}){this.id=id,this.mid=mid,this.singer=singer,this.name=name,this.album=album,this.duration=duration,this.image=image,this.url=url}
}//抽象出一个工厂方法:传入musicData对象参数,实例化一个Song
export default function createSong(musicData,songVkey){return new Song({id: musicData.songid,mid: musicData.songmid,singer: filterSinger(musicData.singer),name: musicData.songname,album: musicData.albumname,duration: musicData.interval, //歌曲时长image: `https://y.gtimg.cn/music/photo_new/T002R300x300M000${musicData.albummid}.jpg?max_age=2592000`,//播放源这里的songVkey需要动态获取。url: `http://dl.stream.qqmusic.qq.com/C400${musicData.songmid}.m4a?vkey=${songVkey}&guid=6319873028&uin=0&fromtag=66`})
}//格式化处理singer数据
function filterSinger(singer) {let ret=[]if(!singer){return ''}else{singer.forEach(item => {ret.push(item);});}return ret.join('/');}

播放源songVkey的获取

播放源路径

config\index.js配置代理

//获取到播放源地址
'/api/getMusicVkey':{target:'https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg',secure:false,changeOrigin:true,bypass:function(req,res,proxyOptions){req.headers.referer='https://y.qq.com/'req.headers.host='c.y.qq.com'},pathRewrite:{'^/api/getMusicVkey': ''}
}

src\api\singer.js

//获取到播放源的url
import axios  from 'axios'
export function getMusicVkey(songmid) {const url = '/api/getMusicVkey'const data = Object.assign({}, commonParams, {songmid: songmid,filename: 'C400' + songmid + '.m4a',guid: 6319873028, //会变,以实时抓取的数据为准platform: 'yqq',loginUin: 0,hostUin: 0,needNewCode: 0,cid:205361747,uin: 0,format: 'json'})return new Promise(function(resolve,reject){axios.get(url,{ params: data}).then(res=>{resolve(res.data)}).catch(err=>{reject(err)})})
}

src\components\singer-detail\singer-detail.vue

import createSong  from 'common/js/song.js'
_normallizeSingerDetail(list){let ret=[];//返回值list.forEach(item => {let {musicData} = item   //得到每一项的musicData对象if(musicData.songid && musicData.albummid){ //在这里要获取到valkeygetMusicVkey(musicData.songmid).then(res=>{if(res.code==ERR_OK){let vkey=res.data.items[0].vkey//传入musicData和vkeyret.push(createSong(musicData,vkey))}})}});return ret}

拿到数据

  _getSingerDetail(){//当在当前页面刷新的时候,跳转回singer页if(!this.singer.id){this.$router.push('/singer')return}//获取到列表getSingerDetail(this.singer.id).then(res=>{if(res.code==ERR_OK){//对得到的数据进行处理this.songs=this._normallizeSingerDetail(res.data.list)}}).catch(err=>{console.log(err);})},


拿到需要的数据之后我们可以进行开发了啦

6-8 music-list 组件发-基本布局(1)

因为歌手详情和歌单页布局上是极为相似的,在这里我们可以在singer-detail抽取出来一个music-list的业务组件,业务组件我们放在components中,当是基础组件我们放在base中
src\components\music-list\music-list.vue

<template><div class="music-list"><div class="back"><i class="icon-back"></i></div><h1 class="title" v-html="title"></h1><div class="bg-image" :style="bgStyle"><div class="filter"></div></div></div>
</template>
props:{bgImage:{type:String,default:'' },songs:{type:Array,default:[]},title:{type:String,default:''}},computed: {bgStyle(){return `background-image:url(${this.bgImage})`}},

6-9 music-list 组件开发-抽取基本歌曲基本组件(2)

我们发现设计稿的好多页面都反复使用了歌曲列表的布局样式,所以在这我们将歌曲列表项抽取出来,作为基础组件
src\components\base\song-list\song-list.vue

<template><div class="song-list"><ul><li v-for="(song, index) in songs" :key="index" class="item"><div class="content"><h2 class="name">{{song.name}}</h2><p class="desc">{{getDesc(song)}}</p></div></li></ul></div>
</template><script>
export default {props:{songs:{type:Array,default:[]}},methods: {getDesc(song){return `${song.singer} 。${song.album}`}},
}
</script><style scoped lang="stylus" rel="stylesheet/stylus">@import "~common/stylus/variable"@import "~common/stylus/mixin".song-list.itemdisplay: flexalign-items: centerbox-sizing: border-boxheight: 64pxfont-size: $font-size-medium.contentflex: 1line-height: 20pxoverflow: hidden.nameno-wrap()color: $color-text.descno-wrap()margin-top: 4pxcolor: $color-text-d
</style>

src\components\music-list\music-list.vue
在music-list引入scroll组件,song-list组件
在mounted中我们计算scroll的top值=图片的高度,让scroll可以滚动

<template><div class="music-list"><div class="back"><i class="icon-back"></i></div><h1 class="title" v-html="title">{{title}}</h1><div class="bg-image" :style="bgStyle"  ref="bgImage"><div class="filter"></div></div><scroll :data="songs" class="list" ref="list"><div class="song-list-wrapper"><song-list :songs="songs"></song-list></div></scroll></div></template><script>
import SongList  from 'components/base/song-list/song-list'
import scroll from 'components/base/scroll/scroll'
export default {props:{bgImage:{type:String,default:'' },songs:{type:Array,default:[]},title:{type:String,default:''}},components:{SongList,scroll},computed: {bgStyle(){return `background-image:url(${this.bgImage})`}},mounted() {//因为这个得到的是VueComponent,而不是元素,所以我们这里还需要加上$elthis.$refs.list.$el.style.top=this.$refs.bgImage.clientHeight+'px'}}
</script><style scoped lang="stylus" rel="stylesheet/stylus">@import "~common/stylus/variable"@import "~common/stylus/mixin".music-list position: fixedz-index: 100top: 0left: 0bottom: 0right: 0background: $color-background.backposition absolutetop: 0left: 6pxz-index: 50.icon-backdisplay: blockpadding: 10pxfont-size: $font-size-large-xcolor: $color-theme.titleposition: absolutetop: 0left: 10%z-index: 40width: 80%no-wrap()text-align: centerline-height: 40pxfont-size: $font-size-largecolor: $color-text.bg-imageposition: relativewidth: 100%height: 0padding-top: 70%transform-origin: topbackground-size: cover.play-wrapperposition: absolutebottom: 20pxz-index: 50width: 100%.playbox-sizing: border-boxwidth: 135pxpadding: 7px 0margin: 0 autotext-align: centerborder: 1px solid $color-themecolor: $color-themeborder-radius: 100pxfont-size: 0.icon-playdisplay: inline-blockvertical-align: middlemargin-right: 6pxfont-size: $font-size-medium-x.textdisplay: inline-blockvertical-align: middlefont-size: $font-size-small.filterposition: absolutetop: 0left: 0width: 100%height: 100%background: rgba(7, 17, 27, 0.4).bg-layerposition: relativeheight: 100%background: $color-background.listposition: fixedtop: 0bottom: 0width: 100%overflow hiddenbackground: $color-background.song-list-wrapperpadding: 20px 30px.loading-containerposition: absolutewidth: 100%top: 50%transform: translateY(-50%)
</style>

6-10 music-list 滚动效果优化


当我们在滚动页面的时候,我们只能滚动蓝色区域的列表,这并不是我们期望的效果,我们希望的是列表可以往上滚动,列表文字下面的黑色的层,随着列表的滚动往上推

首先我们得让列表可以滚动到顶部,这时,我们需要将 .list的overflow hidden
样式去掉,这样列表可以滚动了,但是文字底部的黑色的层我们也想跟着文字滚动,我们可以通过监听scrollY的值来获得滚动的距离,让文字底部的黑色的层跟着滚动

那么怎么让文字底部的黑色的跟着列表滚动呢?

create()中初始化probeType,和listenScroll

created() {this.probeType =3,this.listenScroll =true
},

将probeType,和listenScroll等传递给子组件,监听滚动

<scroll :data="songs" class="list" ref="list" :probeType='probeType' :listenScroll='listenScroll' @scroll="scroll"><div class="song-list-wrapper"><song-list :songs="songs"></song-list></div>
</scroll>

得到scrollY

methods: {scroll(pos){this.scrollY=pos.y//获取scroll滚动的值}}     

给页面加上黑色的层bg-layer

<!-- 加上黑色的层 -->
<div class="bg-layer" ref="layer"></div><scroll :data="songs" class="list" ref="list" :probeType='probeType' :listenScroll='listenScroll' @scroll="scroll"><div class="song-list-wrapper"><song-list :songs="songs"></song-list></div>
</scroll>

bg-layer设置了样式position: relative;
接着我们watch scrollY的变化。动态改变 bg-layer的值

 scrollY(newY){console.log(newY)this.$refs.layer.style['transform']=`translate3d(0,${newY}px,0)`this.$refs.layer.style['-webkit-transform']=`translate3d(0,${newY}px,0)`}

这样黑色的层可以跟着列表滚动了,但是,当滚动一定的距离之后,黑色的层并不能无限滚动了,因为它的高度只有屏幕高度的100%,超过屏幕高度的部分会露出来

解决:我们需要限制bg-layer的滚动范围,当滚动都标题以下的范围就不让它滚动了

const RESERVED_HEIGHT = 40 //滚动偏移距离,顶部标题的距离

最大的滚动的距离,就是背景图片的距离-头部标题的距离
在mouted中

this.imageHeight = this.$refs.bgImage.clientHeight
this.minTranslateY = -this.imageHeight + RESERVED_HEIGHT //最远滚动位置,不超过
  watch: {//监听scrollY的变化scrollY(newY){//让文字底部黑色的层跟着滚动let translateY=Math.max(newY,this.minTranslateY)//获取到最大滚动量this.$refs.layer.style['transform']=`translate3d(0,${translateY}px,0)`this.$refs.layer.style['webkitTransform']=`transilate3d(0,${translateY}px,0)` }},  

这样黑色的层可以跟着文字滚动而滚动了
但是当滚动到顶部的时候,我们希望白色的字不要盖着背景图片,我们应该怎么处理呢、

6-11 music-list 组件开发-顶部图片处理

档滚动到超过顶部40px的时候将图片的z-index变成大,将图片的高度变为40px

const RESERVED_HEIGHT=40
scrollY(newY){//...省略//对顶部样式进行处理let zIndex=0;//对图片的zindex做处理if(newY<this.minTranslateY){zIndex=10;//让z-index变大this.$refs.bgImage.style['paddingTop']='0%'this.$refs.bgImage.style['height']=`${RESERVED_HEIGHT}px`}else{this.$refs.bgImage.style['paddingTop']='70%'this.$refs.bgImage.style['height']=`0`}this.$refs.bgImage.style['z-index']=zIndex;}

6-12 music-list 组件开发-图片放大处理

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

 scrollY(newY){//...省略// //当往下拖的时候,让图片跟着等比放大let scale=1;let percent=Math.abs(newY/this.$refs.bgImage.clientHeight)//计算比例if(newY>0){scale=1+percentzIndex = 10}this.$refs.bgImage.style['transform']=`scale(${scale})`this.$refs.bgImage.style['webkitTransform']= `scale(${scale})`this.$refs.bgImage.style['z-index']=zIndex}

6-13 music-list 组件开发-高斯模糊效果实现

在bg-image里加上

 <!-- iphone手机下高斯模糊效果 --><div class="filter" ref="filter"></div>
 .filterposition: absolutetop: 0left: 0width: 100%height: 100%background: rgba(7, 17, 27, 0.4)

高斯模糊下,必须要给当前的高斯的图层加上透明的背景,然后再back-filter:blur(20px)
在监听scrollY的时候

当往上推的时候,希望图片出现高斯模糊效果,给ref="filter"加上高斯模糊效果

backdrop-filter CSS 属性可以让你为一个元素后面区域添加图形效果(如模糊或颜色偏移)。 因为它适用于元素背后的所有元素,为了看到效果,必须使当前元素透明。

let blur = 0
if(newY<0){blur = Math.min(20 * percent, 20)
}
this.$refs.filter.style['backdrop-filter']=`blur(${blur}px)`
this.$refs.filter.style['-webkit-backdrop-filter']=`blur(${blur}px)`

6-14 music-list 播放按钮

在图片部分bg-image里面加上播放按钮

<!-- 播放按钮 --><div class="play-wrapper"><div class="play"><i class="icon-play"></i><span class="text">随机播放全部</span></div></div>

这个播放按钮需要当有数据的时候才展示,所以我们需要加上

<div class="play-wrapper" v-show="songs.length">

但是当我们滚动到顶部的时候,播放按钮也跟着滚动上去了,当我们滚动到上方的时候,我们应该让播放按钮隐藏

if(newY < this.minTranslateY) {this.$refs.playBtn.style.display = 'none'
}else{this.$refs.playBtn.style.display = ''
}

优化:封装JS的prefixStyle

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

src\common\js\dom.js

//封装一个prefixStyle的方法
//能力检测: 查看elementStyle支持哪些特性
let elementStyle= document.createElement('div').style;//供应商: 遍历查找浏览器的前缀名称,返回对应的当前浏览器let vendor=(()=>{let transformNames ={webkit: 'webkitTransform',Moz: 'MozTransform',O: 'OTransform',ms: 'msTransform',standard: 'transform'}for(let key in  transformNames){if(elementStyle[transformNames[key]]!=undefined){return key}}return false})()export function prefixStyle(style) {if(vendor===false){return false}if(vendor === 'standard'){return style}return vendor+ style.charAt(0).toUpperCase() + style.substr(1)     }

在scrollY中将,将transform的部分替换掉

const transform = prefixStyle('transform')
this.$refs.layer.style[transform]=`translate3d(0,${translateY}px,0)`
this.$refs.bgImage.style[transform]=`scale(${scale})`
const backdrop = prefixStyle('backdrop-filter') this.$refs.filter.style[backdrop]=`blur(${blur}px)`

loading处理
因为songs的是异步获取的在获取到songs之前我们加上loading

 <scroll :data="songs" class="list" ref="list" :probeType='probeType' :listenScroll='listenScroll' @scroll="scroll"><div class="song-list-wrapper"><song-list :songs="songs"></song-list></div><!-- loading部分 --><div class="loading-container" v-show="!songs.length"><loading></loading></div>
</scroll>
.loading-containerposition: absolutewidth: 100%top: 50%transform: translateY(-50%)

github chapter6

第6章 歌手详情页开发相关推荐

  1. 微信小程序之网易云音乐(四)- 排行、歌手及歌手详情页模块开发

    微信小程序之网易云音乐(四)- 排行.歌手及歌手详情页模块开发 一. 排行模块开发 二. 歌手模块开发 三. 歌手详情页开发 微信小程序之网易云音乐导航 一. 排行模块开发 rank.vue文件: & ...

  2. vue音乐项目歌手详情页小结

    技术栈 1,vue 2,vuex 3,vue-router(子路由) 需求分析 1)歌手列表点击歌手会跳转到下级页面歌手详情页,歌手详情页由四个部分组成 歌手图片 返回按钮:点击返回歌手tab页 随机 ...

  3. 微信小程序电商项目商品详情页开发实战之数据绑定与事件应用

    各位CSDN的朋友,我们都知道,现在微信小程序电商平台特别火爆,所以我将以一个生鲜电商项目为例,为大家讲述微信小程序的实战化开发,价值几万元的成熟项目,你可千万不要错过哦. 大家直接通过视频链接直接看 ...

  4. uni-app实战之社区交友APP(8)搜索列表页和文章详情页开发

    文章目录 前言 一.搜索列表页开发 1.搜索类型传递和占位符设置 2.搜索功能实现 二.帖子详情页开发 1.pages.json配置和页面通信 2.公共列表组件功能优化 3.详情页关注和顶踩功能完善 ...

  5. 第8章.商品详情页面之thymeleaf

    第8章 Thymeleaf 学习目标 Thymeleaf的介绍 Thymeleaf的入门 Thymeleaf的语法及标签 搜索页面渲染 商品详情页静态化功能实现 1.Thymeleaf介绍 ​ thy ...

  6. music功能 vue_vue_music:歌手详情页

    在singer.vue中,点击某一歌手,根据路由跳转到singer-detail.vue 在vue-muscie:singer.vue页面的5.数据序列化后传入list-vue中相关逻辑 select ...

  7. 电商小程序实战教程-商品详情页开发

    我们上一篇开发了电商小程序的首页,本篇我们介绍一下详情页的开发.如果想开发详情页,首先要搞明白一个概念,在详情页展示数据的时候需要如何进行页面传参. 参数变量 在微搭中变量一共是分三种,普通变量.模型 ...

  8. 微信小程序开发--首页及详情页开发

    一.常用组件 在之前的封装请求数据的模块中请求轮播图的数据 1.首页轮播图数据的请求以及渲染 1.轮播图数据的请求pages/index/index.js data: {bannerlist:[] / ...

  9. 小程序 pagescrollto_微信小程序学习笔记(三)-- 首页及详情页开发

    一.常用组件 在上一个章节中讲解了封装请求数据的模块,在此处请求轮播图的数据 1.首页轮播图数据的请求以及渲染 1.1 轮播图数据的请求 pages/home/home.js import 2 使用组 ...

最新文章

  1. app h5 上传按钮多选_稿定小课堂之教你如何制作H5
  2. 干掉状态:从session到token
  3. 八十五、再探希尔排序,桶排序,计数排序和基数排序
  4. 5G的场景、需求、通信速率
  5. STM32F7xx —— 内部flash
  6. 一步一步写算法(之单向链表)
  7. 关于APP性能测试脚本录制的四种方法
  8. sql自动生成编码函数
  9. hadoop组件中的hive安装
  10. 微计算机与单片机原理及应用答案,电子科技大学《单片机原理及应用》20春期末考试【标准答案】...
  11. 谷歌浏览器字体由繁体改为简体
  12. BLP读书笔记(一)——通过shell访问终端模式
  13. 如何打造一个高效的研发团队
  14. kong翻译_最全的中国姓氏英文说法,你知道自己的姓氏怎么翻译吗?
  15. STRAIGHT特征提取算法学习
  16. 2020科大讯飞iFLYTEK A.I.开发者大赛
  17. 我的第一个tableau故事
  18. python 实现 复制文件 及 复制文件夹
  19. ACM-ICPC近年省赛汇总
  20. Choerodon前端环境变量方案

热门文章

  1. 记录四川移动盒子打开adb命令的方法 型号:UNT401H
  2. 欧拉定理及费马小定理
  3. 电子元器件封装设计规范
  4. 扬帆际海—shopee跨境店和本土店谁更有优势?
  5. 李理:详解卷积神经网络
  6. spark常见面试题
  7. JavaScript百炼成仙 1.20 函数七重关之二 (作用域)
  8. python批量测试网站加载时间_python测试网站访问速度
  9. 机器人程序设计入门(C++/Arduino/ROS)(转载2020版)
  10. java怎么编程class,深入理解Java Class文件格式(一)