美文网首页JavaScript 进阶营
VUE音乐播放器学习笔记(3) - vuex + ( route

VUE音乐播放器学习笔记(3) - vuex + ( route

作者: woow_wu7 | 来源:发表于2017-10-05 22:55 被阅读91次

    (1) export 和 export default 的区别

    • export ( import ) 可以有多个,export default ( 默认导出 ) 只能有一个
    • export导出后引用需要加 { } ,export default则不需要

    (2) vuex

    state.js

    功能:状态数据

    const state={
        singer:{}
    }
    
    export default state
    

    mutation-types.js

    功能:放置方法名,或者对mutations做封装

    export const SET_SINGER = 'SET_SINGER'
    

    mutations.js

    功能:操作state数据

    import * as types from './mutation-types'
    
    const matutaions = {
        [types.SET_SINGER] (state,singer){    // singer是传入的参数
            state.singer=singer
        }
    }
    
    export default matutaions
    

    getters.js

    功能:获取state里的数据,对state数据做映射

    export const singer = state => state.singer
    

    actions.js

    功能:放异步操作

    function findIndex(list, song) {
      return list.findIndex((item) => {
        return item.id === song.id
      })
    }
    

    index.js

    功能: 入口文件

    import Vue from 'vue'
    import Vuex from 'vuex'
    import * as actions from './actions'
    import * as getters from './getters'
    import state from './state'
    import mutations from './mutations'
    import createLogger from 'vuex/dist/logger'     // 打印motation的修改纪录
    
    Vue.use(Vuex)
    
    const debug = process.env.NODE_ENV !== 'production'
    // 调试工具,在dev环境开启,在production环境关闭
    
    export default new Vuex.Store({
      actions,
      getters,
      state,
      mutations,
      strict: debug,
      plugins: debug ? [createLogger()] : []
    })
    

    存数据 ( 到 vuex 的 state 中 )

    singer.vue
    
    
    
    import {mapMutations} from 'vuex'  // 语法糖
    
    
        methods: {
    
          selectSinger(singer) {     // 子组件派发的this.$emit('select',item),这个item就是singer
            this.$router.push({
              path: `/singer/${singer.id}`
            })
            this.setSinger(singer)   
    
            // 在...mapMutations中取得setSinger方法,singer数据作为参数传给该方法,该方法改变state中的singer
    
          },
          _getSingerList() {
            getSingerList().then((res) => {
              if (res.code === ERR_OK) {
                this.singers = this._normalizeSinger(res.data.list)
              }
            })
          },
    
         ...mapMutations({
            setSinger: 'SET_SINGER'    // 映射成一个setSinger方法,对应的是mutation-types.js中的常量
          })
    
        },
    

    取数据 ( 在 vuex 的 state 中 )

    singer-detail.js
    
    
    
    import {mapGetters} from 'vuex'
    
    computed: {
          ...mapGetters([      // 扩展到computed中,是一个数组
            'singer'
          ])
        }
    
    
    

    (3) router.beforeEach((to, from, next) => { })

    导航钩子:作用是拦截导航

    http://blog.csdn.net/wenyun_kang/article/details/70987840
    https://segmentfault.com/a/1190000011140870
    (详细)http://www.jianshu.com/p/2578cc444b70
    (详细)http://www.jianshu.com/p/9798e3e63998

    (1) 全局的钩子router.beforeEach((to, from, next) => { } )

    (2) 路由元信息 meta 字段,标记是否需要检测导航

    • 在router文件夹的index.js中
    router/index.js
    
    
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'home',
          redirect: '/recommend',
          meta: {                       // 路由元信息字段,表示是否需要检测导航,true表示需要检测
            needLogin: true
          }
        },
        {
          path: '/login',
          component: Login
        },
        {
          path: '/recommend',
          name: 'recommend',
          component: Recommend
        },
        {
          path: '/singer',
          name: 'singer',
          component: Singer,
          meta: {
            needLogin: true
          },
          children: [{
            path: ':id',
            component: SingerDetail,
            meta: {
              needLogin: true
            }
          }]
        }
      ]
    })
    
    
    
    • 在main.js中
    main.js
    
    
    
    router.beforeEach((to, from, next) => {
      if (to.meta.needLogin) {       // 如果目标页面需要登陆
        if (window.sessionStorage.data) {   // 但是同时已经登陆过了,有session
          next()     // 直接跳转到目标页面
        } else {
          alert('请先登录')  
          next('/login')     // 否则跳转到登陆页面
        }
      } else {
        next()     // 如果不需要登陆,直接跳转到目标页面
      }
    })
    
    
    • 在login.js中
    login.js
    
    
    
        methods: {
          login() {
            this.$http.post('https://www.easy-mock.com/mock/59d2fed09cabc90bb5e5c287/music/login', {
              phone: `${this.userName}`,
              password: `${this.passName}`
            }).then((response) => {
              console.log(response, 'response')
              console.log(response.data, 'response.data')
    
              if (response.data.data) {     // 如果提交成功,返回数据存在
                let ses = window.sessionStorage
                let data = JSON.stringify(response.data.data)
                ses.setItem('data', data)    // 设置session,需要以字符串方式存储
                this.$router.push('/')    // 跳转路由
              } else {
                console.log('有错误')
              }
            })
          }
        }
    
    

    (4) v-bind:style='' '' 绑定样式属性

    (1)
    
    
     <div class="bg-image" v-bind:style="bgStyle">    // 一般绑定到样式属性上,计算属性
    
        computed: {
          bgStyle() {
            return {
              'background-image': `url(${this.bgImage})`,
               border: '1px solid red'
            }
            // return `background-image:url(${this.bgImage})`
          }
        }
    
    -----------------------------------------------------------------------
    (2)
    
    
    
    // 这种方式不提倡,不直观
     <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>  
      data: {  
        activeColor: 'red',  
        fontSize: 30  
      }  
    

    (5) 在不同宽度的设备上,让图片宽度为设备宽度,且高度和宽度相等

    • 注意:这种布局不要在class="bg-image"的div中直接写内容(比如文字,这样不会在div中),需要在里面再加子div,在子div中写内容,子div用绝对定位
    
    <div class="bg-image" v-bind:style="bgStyle" ref="bgImage">
          <!--<div class="bg-image" v-bind:style="{color:red}">1-->
          <!--filter是一个加暗的浮层-->
          <div class="filter"></div>
        </div>
    
    
    
     .bg-image
          position: relative
          width: 100%
          height: 0
          padding-top: 100%
          transform-origin: top
          background-size: cover
          .filter
            position: absolute
            top: 0
            left: 0
            width: 100%
            height: 100%
            background: rgba(7, 17, 27, 0.4)
    
    

    (6) promise 封装axios请求

    test2.js
    
    
    
    import axios from 'axios'
    // import Vue from 'vue'
    // Vue.prototype.$http = axios
    
    export function getSongs() {
      return axios.get('/api/artist/album?id=6452&limit=30').then((response) => {
        return Promise.resolve(response)
      })
    }
    
    -----------------------------------------------------------------------
    
    search.vue
    
    
    
      import {getSongs} from 'api/test2.js'
      export default {
        created() {
          this.getData()
        },
        methods: {
          getData() {
            getSongs().then((data) => {
              console.log(data)
            })
          }
        }
      }
    
    ----------------------------------------------------------------
    
    上面的/api是使用了代理,如下配置:
    
    config/index.js
    
    
        proxyTable:{
          '/api': {
            target: 'http://localhost:3000/',
            changeOrigin: true,
            pathRewrite: {
              '^/api':''
            }
          }
        }
    
    访问 ('/api/artist/album?id=6452&limit=30') 
    其实是:http://localhost:3000/api/artist/album?id=6452&limit=30
    
    

    (7)

    this.$router.push() 跳转

    this.$router.back() 返回

    (1) this.$router.push
    
    
        selectSinger(singer) {     // 子组件派发的this.$emit('select',item),这个item就是singer
            this.$router.push({
              path: `/singer/${singer.id}`
            })
            this.setSinger(singer)           
            // 在...mapMutation中取得setSinger方法,singer数据作为参数传给state
          },
    
    --------------------------------------------
    
    (2) this.$router.back()
    
    
      <div class="back" v-on:click="back">
          <i class="icon-back"></i>
        </div>
    
      methods: {
          back() {
            this.$router.back()
          }
        },
    

    (8) watch 观察者

    例一:
    
    
    <script>
    
    var watchExampleVM = new Vue({
      el: '#watch-example',
      data: {
        question: '',
        answer: 'I cannot give you an answer until you ask a question!'
      },
    
      watch: {
        // 如果 question 发生改变,这个函数就会运行
        question: function (newQuestion) {
          this.answer = 'Waiting for you to stop typing...'
          this.getAnswer()
        }
      },
    
      methods: {
        getAnswer() {........}
      }
    })
    
    </script>
    
    
    例二:
    
    
    <span style="color:#006600;"><div id="app">  
        <input type="text" v-model:value="childrens.name" />  
        <input type="text" v-model:value="lastName" />  
    </div>  
      
    <script type="text/javascript">     
        var vm = new Vue( {  
            el: '#app',  
            data: {  
                childrens: {  
                    name: '小强',  
                    age: 20,  
                    sex: '男'  
                },  
                tdArray:["1","2"],  
                lastName:"张三"  
            },  
            watch:{  
                childrens:{  
                    handler:function(val,oldval){  
                        console.log(val.name)  
                    },  
                    deep:true//对象内部的属性监听,也叫深度监听  
                },  
                'childrens.name':function(val,oldval){  
                    console.log(val+"aaa")  
                },//键路径必须加上引号  
                lastName:function(val,oldval){  
                    console.log(this.lastName)  
                }  
            },//以V-model绑定数据时使用的数据变化监测  
        } );  
        vm.$watch("lastName",function(val,oldval){  
            console.log(val)  
        })//主动调用$watch方法来进行数据监测</span>  
    </script>  
    </body>  
    
    
    例三:
    
    
    var v_assetManage_controller = new Vue({  
        el: '.LSee-index',  
        data: {  
            trendQueryTimeCtr: {  
                startTime: '',  
                endTime: ''  
            }  
        },  
        ready: function() {  
            //  
        },  
        methods: {  
            queryTrendData: function(){  
                //do some here  
            }  
        },  
        watch: {  
            'trendQueryTimeCtr.startTime': 'queryTrendData',  
            'trendQueryTimeCtr.endTime': 'queryTrendData'  
        }  
      
    });  
    

    (8) vm.$watch( expOrFn, callback, [options] )

    • 参数:
      {string | Function} expOrFn
      {Function | Object} callback
      {Object} [options]
      {boolean} deep // 深度监听,一般用于对象
      {boolean} immediate
      返回值:{Function} unwatch
    • 用法:
      观察 Vue 实例变化的一个表达式或计算属性函数。回调函数得到的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
    • 选项:deep
      为了发现对象内部值的变化,可以在选项参数中指定 deep: true 。注意监听数组的变动不需要这么做。
    
    // 键路径
    vm.$watch('a.b.c', function (newVal, oldVal) {
      // 做点什么
    })
    
    // 函数
    vm.$watch(
      function () {
        return this.a + this.b
      },
      function (newVal, oldVal) {
        // 做点什么
      }
    )
    
    

    (9) better-scroll的封装

    • probeType
      类型:Number
      默认值:0
      可选值:1、2、3
      作用:有时候我们需要知道滚动的位置。
      当 probeType 为 1 的时候,会非实时(屏幕滑动超过一定时间后)派发scroll 事件;
      当 probeType 为 2 的时候,会在屏幕滑动的过程中实时的派发 scroll 事件;
      当 probeType 为 3 的时候,不仅在屏幕滑动的过程中,而且在 momentum 滚动动画运行过程中实时派发 scroll 事件。

    • momentum
      类型:Boolean
      默认值:true
      作用:当快速在屏幕上滑动一段距离的时候,会根据滑动的距离和时间计算出动量,并生成滚动动画。设置为 true 则开启动画。
      翻译:momentum是动量的意思

    • refresh
      参数:无
      返回值:无
      作用:重新计算 better-scroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常。

    
    <template>
      <div ref="wrapper">
        <slot></slot>
      </div>
    </template>
    
    <script type="text/ecmascript-6">
      import BScroll from 'better-scroll'
    
      export default {
        props: {
          /**
           * probeType
           * 1 滚动的时候会派发scroll事件,会截流。
           * 2 滚动的时候实时派发scroll事件,不会截流。
           * 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件
           */
          probeType: {       // 类型
            type: Number,
            default: 1
          },
          click: {         // 是否能点击
            type: Boolean,
            default: true
          },
          listenScroll: {   // 是否派发滚动事件
            type: Boolean,
            default: false
          },
          data: {         // 列表数据
            type: Array,
            default: null
          },
          pullup: {       // 是否派发滚动到底部的事件,用于上拉加载
            type: Boolean,
            default: false
          },
          pulldown: {    // 是否派发顶部下拉的事件,用于下拉刷新
            type: Boolean,
            default: false
          },
          beforeScroll: {    // 是否派发列表滚动开始的事件
            type: Boolean,
            default: false
          },
          refreshDelay: {   // 当数据更新后,刷新scroll的延时。
            type: Number,
            default: 20
          },
          scrollX: {  // 横向滚动
            type: Boolean,
            default: false
          }
        },
        mounted() {
          setTimeout(() => {
            this._initScroll()
          }, 20)
        },
        methods: {
          _initScroll() {
            if (!this.$refs.wrapper) {
              return
            }
            this.scroll = new BScroll(this.$refs.wrapper, {
              probeType: this.probeType,
              click: this.click,
              scrollX: this.scrollX
            })
            // 是否派发滚动事件
            if (this.listenScroll) {
              let me = this
              this.scroll.on('scroll', (pos) => {
                me.$emit('scroll', pos)
              })
            }
            // 是否派发滚动到底部事件,用于上拉加载
            if (this.pullup) {
              this.scroll.on('scrollEnd', () => {
                if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
                  this.$emit('scrollToEnd')
                }
              })
            }
            // 是否派发顶部下拉事件,用于下拉刷新
            if (this.pulldown) {
              this.scroll.on('touchend', (pos) => {
                // 下拉动作
                if (pos.y > 50) {
                  this.$emit('pulldown')
                }
              })
            }
            // 是否派发列表滚动开始的事件
            if (this.beforeScroll) {
              this.scroll.on('beforeScrollStart', () => {
                this.$emit('beforeScroll')
              })
            }
          },
    //      方法代理
          disable() {   // 作用:禁用 better-scroll,DOM 事件(如 touchstart、touchmove、touchend)的回调函数不再响应。
            this.scroll && this.scroll.disable()
          },
          enable() {  // 作用:启用 better-scroll
            this.scroll && this.scroll.enable()
          },
          refresh() {  // 作用:重新计算 better-scroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常。
            this.scroll && this.scroll.refresh()
          },
          scrollTo() { // 作用:滚动到指定的位置
            this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
          },
          scrollToElement() { // 作用:滚动到指定的目标元素
            this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
          }
    //      _watchData() {
    //        setTimeout(() => {
    //          this.refresh()
    //        }, this.refreshDelay)
    //      }
        },
        watch: {
          data() {
            setTimeout(() => {
              this.refresh()
            }, this.refreshDelay)
          }
    //      'data': '_watchData' //观察data的变化,当data变化是执行_watchData函数,上面的是直接执行
        }
      }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
    
    </style>
    
    
    
    

    (10) 事件修饰符

    .stop : 阻止冒泡
    .prevent : 阻止默认事件
    .capture
    .self : 指定元素触发(不包括子元素)
    .once : 事件只执行一次,特别之处:还能用在组件事件上

    ps: 给组件添加原生事件,需要添加.naive
    
    <!-- 阻止单击事件冒泡 -->
    <a v-on:click.stop="doThis"></a>
    
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="onSubmit"></form>
    
    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    
    <!-- 只有修饰符 -->
    <form v-on:submit.prevent></form>
    
    <!-- 添加事件侦听器时使用事件捕获模式 -->
    <div v-on:click.capture="doThis">...</div>
    
    <!-- 只当事件在该元素本身 (比如不是子元素) 触发时触发回调 -->
    <div v-on:click.self="doThat">...</div>
    
    

    (10) v-on:click.once="" 事件只触发一次

    • 不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上。
    
    <!-- 点击事件将只会触发一次 -->
    
    <a v-on:click.once="doThis"></a>
    
    
    

    (11) .snyc修饰符

    
    <comp :foo.sync="bar"></comp>
    
    会被扩展为:
    
    <comp :foo="bar" @update:foo="val => bar = val"></comp>
    
    当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
    
    this.$emit('update:foo', newValue)
    
    
    
           
          // (1) 父组件给子组件comp 传递一个foo同步属性,值是bar
         // (2) 当子组件想要改变foo属性时,由于子组件不能修改父组件传过来的任何数据,所以用.sync修饰符
        // (3)  当子组件想要改变foo属性时,向父组件派发一个'update:foo'事件,newValue为传递的数据
       // (4) 'update:foo'事件将foo的值改为从子组件传过来的newValue值
    
    
    
    
    rank-detail.vue
    
    
    
    
    <sync class="sync" v-bind:bg.sync="bb" v-if="bb"></sync>
    
    
    data() {
       return {
          bb: true
          }
        }
    
    -----------------------------------------------------------------------------
    
    sync.vue
    
    
    
     <div class="sync" v-on:click="goto" v-if="bb">
        这里是sync组件显示内容
      </div>
    
    
    
    <script type="text/ecmascript-6">
      export default {
        props: {
          bb: {
            type: Boolean,
            default: true
          }
        },
        methods: {
          goto() {
            this.$emit('update:bg', false)
          }
        }
      }
    </script>
    
    
    登陆页面

    相关文章

      网友评论

        本文标题:VUE音乐播放器学习笔记(3) - vuex + ( route

        本文链接:https://www.haomeiwen.com/subject/tcboextx.html