前端移动端开发总结(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 安装插件
相关文章
相关标签/搜索