Loading

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请求进行处理

src/utils/request.js
import axios from 'axios'
import {Notification, MessageBox} from 'element-ui'
import store from '@/store'
import {getToken} from '@/utils/auth'

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时
  timeout: 10000
})
// request拦截器
service.interceptors.request.use(
  config => {
    if (getToken()) {
      config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(res => {
    const code = res.data.code
    if (code === 401) {
      MessageBox.confirm(
        '登录状态已过期,您可以继续留在该页面,或者重新登录',
        '系统提示',
        {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        store.dispatch('LogOut').then(() => {
          location.reload() // 为了重新实例化vue-router对象 避免bug
        })
      })
    } else if (code !== 200) {
      Notification.error({
        title: res.data.msg
      })
      return Promise.reject('error')
    } else {
      return res.data
    }
  },
  error => {
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

src/api.js
import request from '@/utils/request'
// AXIOS GET请求

// get
export function listConfig(query) {
  return request({
    url: '/system/config/list',
    method: 'get',
    params: query
  })
}
// post
export function addConfig(data) {
  return request({
    url: '/system/config',
    method: 'post',
    data: data
  })
}

  

路由的其他一些配置

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>

 

对函数进行封装全局使用

src/utils/global.js //公共方法写在这
exports.install = function (Vue, options) {
  /**
   * 对小数位进行格式化
   * @data 数据
   * @num 格式位数
   * */
  Vue.filter('decimalPlaceFormat', function (data, num) {
    if (!(data && num)) return ''
    return Number(data).toFixed(num)
  });
}

src/main.js //引用安装
import global from './utils/global'
Vue.use(global)

 

对指令封装全局使用

src/directive/hasPermiss.js
/**
* 按钮权限控制
* */

import store from './../store'
export default {
  inserted(el,binding) {
    const {value} = binding;
    const btnPermiss = store.getters.btnPermiss;
    let status =  btnPermiss.some(item => item == value);
    if(!status){
      el.parentNode && el.parentNode.removeChild(el);
    }
  }
}


src/directive/index.js
import hasPermiss from './hasPermiss'
const install = function(Vue) {
  Vue.directive('hasPermiss',hasPermiss)
}
if (window.Vue) {
  window['hasPermiss'] = hasPermiss;
  Vue.use(install);
}
export default install

src/main.js
import install from './directive'
Vue.use(global);

  

VUEX模块使用

 

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 安装插件

 

 

posted @ 2019-08-21 10:33  冯叶青  阅读(1308)  评论(0编辑  收藏  举报