项目描述:Web App,QQ音乐界面风格,SPA应用
技术描述:
1.fast-click处理移动端点击事件的延迟
2.create-keyframes-animation处理css动画
3.jsonp npm封装,跨域获取数据
4.vue-lazyload实现页面懒加载
5.loading实现网络加载过程中的界面效果
6.axios数据请求
7.webpack+vue-cli自动化构建项目
8.vue,mixins的运用
9.vuex状态管理器
10.mintui,组件实现区域滚动,首页,歌手详情页面的图片轮播效果
11.webpack实现路由懒加载(异步加载),优化代码,提高首页访问速度
具体实现,要点分析:
1 . 文件目录结构
dist目录:
build 打包编译 生成 dist目录(index,js,css,image)vendor文件是打包后的node_modules文件
线上 gzip 压缩 减少代码量 例如 300kb代码 gzip 后是: --》100kb
vendor打包后的 hash值不变 ( static/js/vendor.52caf94d568661fbe99f.js.map)
build打包后dist文件问们可以在启动的node服务器里面进行静态托管(也就是将资源放本地服务器),参照prod.server.js
prod.server.js 文件
import { match } from "./C:/Users/Administrator/AppData/Local/Microsoft/TypeScript/2.6/node_modules/@types/minimatch";
import { response } from "./C:/Users/Administrator/AppData/Local/Microsoft/TypeScript/2.6/node_modules/@types/spdy
const express = require("express")
const config = require("./config/index")
const axios = require("axios")
const app = express();
const apiRoutes = express.Router() // server的路由分发代理
apiRoutes.get("/getDiscLisc", (req, res) => {
let url = "https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg"
axios.get(url, {
headers: { // 请求数据的时候 采取了两种: 一种是 jsonp npm封装 一种是node代理这种方式:请求后端发起请求数据服务器,设置headers的referer(请求来源地址)host:来源主机, 绕过同源策略,“欺骗数据服务器”
referer: "https://c.yy.qq.com/",
host: "c.y.qq.com"
},
params: req.query
}).then((response) => {
res.json(response.data)
}).catch((e) => {
console.log(e)
})
})
apiRoutes.get("/lyric", (req, res) => {
const url = 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg'
axios.get(url, {
headers: {
referer: "https://c.yy,qq.com/",
host: "c.yy.com"
},
params: req.query
}).then((response) => {
let ret = response.data
if (typeof ret === 'string') {
let reg = /^\w+\(({[^()]+})\)$/
let matches = ret.match(reg)
if (matches) {
}
}
})
// apiRoutes.get("/lyric", (req, res) => {
// })
})
app.use("/api", apiRoutes) // /api 作为代理地址,前端请求使用这个来请求后端数据 /api+你的前端路由
// 直接将静态资源(托管后的dist目录)作为 托管
app.use(express.static("./dist")) //这个就是之前说的 将 build 后的静态资源托管,优化,
// index/.html 里面的img icon 资源 可以放在 托管 static目录下
let port = process.env.PORT || config.build.port
module.exports = app.listen(port, (err) => {
if (err) {
console.log(err)
return
}
console.log("LIstening at http://localhost:" + port + "\n")
})
main.js:
import store from './store' // ./store -- > 直接载入我的index.js文件
state.js 定义数据初始值 : 如下
const state ={
singer : {},
playing: false,
fullScreen:true,
playList:[],
sequenceList:[],
mode:playMode.sequence,
currentIndex:-1,
// 这几个test数据
songs_test:[]
}
export default state
mutations.js 文件定义对state.js数据的修改函数
import * as types from "./mutation-type" // 导入预定义变量 * as 写法:代表以 types 点的方式获取变量
// mutations.js 文件数据: 函数名 在mutations-types 里面配置
export const SET_SINGER = 'SET_SINGER'
export const SET_PLAYING_STATE = 'SET_PLAYING_STATE'
export const SET_FULL_SCREEN = "SET_FULL_SCREEN"
export const SET_PLAYLIST = "SET_PLAYLIST"
export const SET_SEQUENCE_LIST = "SET_SEQUENCE_LIST"
export const SET_PLAY_MODE = "SET_PLAY_MODE"
export const SET_CURRENT_INDEX = "SET_CURRENT_INDEX"
const mutations = {
// import * as xx from " " 引入的: 使用的时候 [xx....]
[types.SET_SINGER](state,singer){
state.singer = singer
console.log(state.singer);
console.log("state.singer!!!");
} ,
[types.SET_PLAYING_STATE](state,flag){
state.playing = flag
},
[types.SET_FULL_SCREEN](state,flag){
state.fullScreen = flag
},
[types.SET_PLAYLIST](state,list){
state.playList = list
},
[types.SET_SEQUENCE_LIST](state,list){
state.sequenceList = list
},
[types.SET_PLAY_MODE](state,list){
state.mode = list
},
[types.SET_CURRENT_INDEX](state,index){
state.currentIndex = index
},
// songs_test
songs_test(state,test){
state.songs_test = test
}
}
export default mutations
getters.js 文件
// 在getters 中取数据 映射到 state上
export const singer = state => {
return state.singer // 返回值必须加 stste => state.singer || (state) =>{ return ...}
}
export const playing = state => state.playing
export const fullScreen = state => state.fullScreen
export const playList = state => state.playList
export const sequenceList = state => state.sequenceList
export const mode = state => state.mode
export const currentIndex = state => state.currentIndex
export const currentSong = (state) => {
return state.playList[state.currentIndex] || {}
}
// songs_test 数据
export const songs_test = state=>state.songs_test
actions.js 是作为对mutations的提交
// 一个动作多次提交修改mutations 封装actions文件
import * as types from "./mutation-type"
export const selectPlay = function ({commit,state},{list,index}){
// commit 去改变 mutations -- 从而改变 state commit第二个参数是传入到了 对应函数的第二个参数位置上
commit(types.SET_SEQUENCE_LIST,list)
commit(types.SET_PLAYLIST,list)
commit(types.SET_CURRENT_INDEX,index)
commit(types.SET_FULL_SCREEN,true)
commit(types.SET_PLAYING_STATE,true)
}
router文件 index.js
// 路由懒加载: 首页加载速度的优化 !!!!!!
// 路由懒加载 语法:
const MyRecommend = (resolve) => {
import('@/components/MyRecommend/MyRecommend').then((module) => {
resolve(module)
})
}
main.js
import 'babel-polyfill'
import Vue from 'vue'
import App from './App'
import router from './router' // vue-router
import store from './store' // vuex
import '@/common/scss/index.scss'
// 消除 click 移动浏览器300ms延迟
import attachFastClick from 'fastclick'
attachFastClick.attach(document.body)
// 图片懒加载
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, { // 懒加载
loading: require('@/common/img/default.png')
})
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
store,
template: '<App/>', // render: h => h(App)
components: { App }
})