<div id="app">
<div class="header"></div>
<div class="views"></div>
<div class="footer"></div>
</div>
<style>
#app{display: flex;flex-direction: column;height: 100%;}
.views{flex: 1; overflow-y: scroll;-webkit-overflow-scrolling: touch;} /*-webkit-overflow-scrolling: touch; 解決蘋果手機下網頁滑動不暢問題*/
.header{} /*高度隨意設置*/
.footer{} /*高度隨意設置*/
</style>
src/omponents/loading/Loading.vue
<template>
<div class="loading" v-show="show">
<i class="i-loading"></i>
</div>
</template>
<script>
export default {
props: {
show: Boolean
}
}
</script>
<style lang="scss" scoped>
.loading{
width: 200px;
height: 200px;
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
border-radius: 6px;
background: rgba(0,0,0,0.6);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}
.i-loading {
width: 90px;
height: 90px;
display: inline-block;
vertical-align: middle;
-webkit-animation: loading 1s steps(12, end) infinite;
animation: loading 1s steps(12, end) infinite;
background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
background-size: 100%;
}
@keyframes loading {
0% {
-webkit-transform: rotate3d(0, 0, 1, 0deg);
transform: rotate3d(0, 0, 1, 0deg);
}
100% {
-webkit-transform: rotate3d(0, 0, 1, 360deg);
transform: rotate3d(0, 0, 1, 360deg);
}
}
</style>
src/omponents/loading/index.js
<script>
import LoadingComponent from './loading'
let $vm
export default {
install (Vue, options) {
if (!$vm) {
const LoadingPlugin = Vue.extend(LoadingComponent)
$vm = new LoadingPlugin({
el: document.createElement('div')
})
console.log($vm)
}
$vm.show = false
let loading = {
show (text) {
$vm.show = true
$vm.text = text
document.body.appendChild($vm.$el)
},
hide () {
$vm.show = false
}
}
if (!Vue.$loading) {
Vue.$loading = loading
}
Vue.mixin({
created () {
this.$loading = Vue.$loading
}
})
}
}
//使用
import Loading from '@/components/loading/index.js' //loading 插件
Vue.use(Loading) //使用loading插件
Vue.$loading.show() //顯示
Vue.$loading.hide() //隱藏
</script>
/*
狀態碼
200:操做成功
400:請求沒法處理,檢查參數是否正確
401:未經過身份驗證
402: 帳號在別處登陸
403:已經過身份驗證,但不具有訪問權限
404:請求的資源不存在
500:已收到請求,服務器處理髮生了意外
503:服務器中止工做
*/
import Vue from 'vue'
import axios from 'axios'
import Qs from 'qs'
import 'vuex'
import store from './../vuex/store' //項目數據倉庫
import router from './../router/index' //路由文件
import Loading from '@/components/loading/index.js' //loading 插件
Vue.use(Loading) //使用loading插件
// 基地址
const baseUrl = 'xxxx'
axios.defaults.withCredentials = true
// 添加請求攔截器
axios.interceptors.request.use(function (config) {
config.headers.Authorization = JSON.stringify(store.state.user) !== '{}' ? store.state.user.token : '' //把拿到的token放到請求頭部
Vue.$loading.show() //每一個請求都增長loading效果
return config
}, function (error) {
return Promise.reject(error)
})
// 添加響應攔截器
axios.interceptors.response.use(function (response) {
// console.log(response)
Vue.$loading.hide() //服務器響應後把loading隱藏掉
//後面根據不一樣的狀態碼,可作不一樣的操做
switch (response.data.code) {
case 401:
store.dispatch('setUser', {}) //未經過驗證,把store裏的數據清空
break
case 402:
store.dispatch('setUser', {}) //帳號在別處登陸,把store裏的數據清空以後,跳轉到登陸頁面
router.push({ name: 'signIn' })
break
case 403:
router.push({ name: 'vipRecharge', query: { type: 'life' } }) //已經過身份驗證,但不具有訪問權限,跳轉到充值頁面
break
case 404:
break
case 500:
break
case 503:
break
default:
}
return response
}, function (error) {
// 對響應錯誤作點什麼
return Promise.reject(error)
})
// AXIOS GET請求
export const videoIndex = function () {
return new Promise((resolve, reject) => {
axios.get(`${baseUrl}/videoIndex`)
.then(res => resolve(res.data))
.catch(res => reject(res))
})
}
// AXIOS POST請求
export const videoCollect = function (id) {
return new Promise((resolve, reject) => {
let data = {
vid: id
}
axios({
method: 'post',
url: `${baseUrl}/videoCollect`,
data: Qs.stringify(data)
})
.then(res => resolve(res.data))
.catch(res => reject(res))
})
}
router.beforeEach((to, from, next) => {
// 動態更改頁面title
if (to.meta.title) {
document.title = to.meta.title
}
// 驗證是否須要登錄
if (to.meta.requireAuth && JSON.stringify(store.state.user) === '{}') {
next({ name: 'signIn' })
}
// 若是登陸狀態進入登陸頁面則,返回到我的中心頁面
if (JSON.stringify(store.state.user) !== '{}') {
if (to.name === 'signIn' || to.name === 'resetPassword' || to.name === 'mobileLogin') {
next({ name: 'personal', query: { type: 'records' } })
}
}
next()
})
安裝
npm install fastclick --save
使用
import fastclick from 'fastclick'
fastclick.attach(document.body)
父向子傳遞參數
Parent.vue(父組件)
<template>
<div>
<Child :name="name"></Child>
</div>
</template>
<script>
import Child from './Child'
export default{
components:{
Child
},
data(){
return{
name:'hello'
}
}
}
</script>
Child.vue(子組件)
<template>
<div>
<!-- 這裏的name接收了父組件傳過來的參數,這裏會變成hello -->
{{name}}
</div>
</template>
<script>
export default{
props:{
name:String
}
}
</script>
子向父傳遞參數
Child.vue(子組件)
<template>
<div>
<button @click="toParentMsg()">我要向父節點傳遞參數</button>
</div>
</template>
<script>
export default{
method:{
toParentMsg(){
this.$emit('listenToChildEvent','我是要向父組件傳送的數據') //listenToChildEvent 自定義事件,後面須要再父組件接收這個自定義事件
}
}
}
</script>
Parent.vue(父組件)
<template>
<div>
<Child @listenToChildEvent = "receiveChildMsg"></Child>
</div>
</template>
<script>
import Child from './Child'
export default{
components:{
Child
},
data(){
},
method:{
receiveChildMsg(val){
console.log(val) //我是要向父組件傳送的數據
}
}
}
</script>
使用vuex-persistedstate插件
const store = new Vuex.Store({
state: {
// 用戶信息
user: {},
// 分類頁數據篩選
videoListFilterTerm: {
catId: 0,
courseId: 0,
software: 0,
diff: 0,
sort: 3,
page: 1
}
},
//獲取state數據
getters: {
getUser (state) {
return state.user
}
},
//操做state數據
mutations: {
setUser (state, user) {
state.user = user
},
setVideoListFilterTerm (state, videoListFilterTerm) {
state.videoListFilterTerm = videoListFilterTerm
}
},
//觸發mutations函數
actions: {
setUser ({ commit }, user) {
commit('setUser', user)
},
setVideoListFilterTerm ({ commit }, videoListFilterTerm) {
commit('setVideoListFilterTerm', videoListFilterTerm)
}
},
//插件配置
plugins: [createPersistedState({
storage: window.localStorage,
reducer (val) {
return {
user: val.user
}
}
})]
})
Vue m3u8視頻播放配置
播放m3u8視頻須要用到 videojs-contrib-hls插件
安裝 npm install videojs-contrib-hls --save
導入import 'videojs-contrib-hls'
Vue-router 實現模塊化加載
使用該方式導入組件,打包模塊會自動把組件進行模塊化打包
const xxx = () => import('@/views/xxx')
Vue路由切換增長動畫效果
90sheji-video/src/App.vue
<template>
<div id="app">
<transition name="fade" mode="out-in">
<router-view />
</transition>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
src/components/layout/Layout.vue
<template>
<div class="layout flex">
<Header/>
<transition name="fade-transform" mode="out-in">
<keep-alive>
<router-view :key="key"/>
</keep-alive>
</transition>
<Footer/>
</div>
</template>
<script>
import Footer from '@/components/common/Footer'
import Header from '@/components/common/Header'
export default {
data () {
return {
catId: ''
}
},
components: {
Footer,
Header
},
computed: {
key () {
return this.$route.name ? this.$route.name : this.$route.fullPath
}
}
}
</script>
<style>
/*********************動畫·start**************************/
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.2s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-20px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(20px);
}
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.2s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all 0.2s;
}
.breadcrumb-leave-active {
position: absolute;
}
/*********************動畫·end**************************/
</style>
組件切換有兩種狀況,一種是兄弟與兄弟組件切換,一種是子組件與父組件之間切換,因此這裏轉場動畫用的不同,因此貼出了兩個組件用法
<keep-alive> 增長這個標籤表示組件能夠被緩存起來,強烈加上
<router-view :key="key"/> 前面用了組件緩存,因此路由視圖的key必定要加上,否則路由和頁面有些會不匹配
vue使用官方腳手架打包上線配置
/config/index.js
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),//入口文件
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),//編譯後全部須要部署的文件都放到了這裏
assetsSubDirectory: 'public',//靜態資源存放的文件目錄
assetsPublicPath: './',// ./是相對路徑,/是絕對路徑,這裏改成相對路徑,否則打包後上線圖片訪問不了
productionSourceMap: true,//是否開啓SourceMap壓縮
devtool: '#source-map',
productionGzip: false,//是否開啓Gzip壓縮
productionGzipExtensions: ['js', 'css'],//Gzip壓縮
bundleAnalyzerReport: process.env.npm_config_report
}
後端解決跨域配置
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
package.json文件中dependencies與devDependencies的區別
dependencies:打包上線須要用到的插件 使用npm inatall --save xxx 安裝插件
devDependencies:開發環境須要用到的插件 使用npm install --save-dev xxx 安裝插件