前端移動端開發總結(Vue)

上下固定,中間滾動佈局(FLEX)

<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>

Vue插件封裝(loading實例)

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>

axios全局路由攔截及結合promise對axios請求進行處理

/*
狀態碼
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()
})

解決移動端click300ms問題

安裝
npm install fastclick --save
使用
import fastclick from 'fastclick'
fastclick.attach(document.body)

Vue父子組件通信

父向子傳遞參數
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數據操做及數據持久化

使用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 安裝插件
相關文章
相關標籤/搜索