• vue music-抓取歌单列表数据(渲染轮播图)


    下载安装新依赖

    babel-runtime:对es6语法进行转译
    fastclick:对移动端进行点击300毫秒延迟 ,,取消掉
    babel-polyfill:API

    先添加,在npm install

    main.js

    import 'babel-polyfill'
    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import fastclick from 'fastclick'
    
    import 'common/stylus/index.styl'
    
    
    Vue.config.productionTip = false
    
    // 取消点击300毫秒的延迟
    fastclick.attach(document.body) 
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      components: { App },
      template: '<App/>'
    })

    头部栏引用header组件

    1:

    <template>
      <div class="m-header">
        <div class="icon"></div>
        <h1 class="text">Chicken Music</h1>
        <router-link tag="div" class="mine" to="/user">
          <i class="icon-mine"></i>
        </router-link>
      </div>
    </template>
    
    <script type="text/ecmascript-6">
      export default {}
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
      @import "~common/stylus/variable"
      @import "~common/stylus/mixin"
    
      .m-header
        position: relative
        height: 44px
        text-align: center
        color: $color-theme
        font-size: 0
        .icon
          display: inline-block
          vertical-align: top
          margin-top: 6px
           30px
          height: 32px
          margin-right: 9px
          bg-image('logo')
          background-size: 30px 32px
        .text
          display: inline-block
          vertical-align: top
          line-height: 44px
          font-size: $font-size-large
        .mine
          position: absolute
          top: 0
          right: 0
          .icon-mine
            display: block
            padding: 12px
            font-size: 20px
            color: $color-theme
    </style>
    m-header。vue

    2:在app.vue

    <template>
        <div id="app">
    3:显式
            <m-header></m-header>
        </div>
    </template>
    
    <script>
    1:导入
        import MHeader from './components/m-header/m-header'
        export default {
          components:{
    2:注册
              MHeader
          }
      }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
    
    </style>

    二:导入歌手页面,搜索页面,排行榜,推荐页面

    1:先在index.js入口注册这4个组件

    import Reacommed from 'components/reacommed/reacommed'
    import Search from 'components/search/search'
    import Singer from 'components/singer/singer'
    import Rank from 'components/rank/rank'

    2:配置url

    export default new Router({
      routes: [
        {
          path: '/reacommed',
          component:Reacommed
        },
        {
            path:'/singer',
            component:Singer
        },
        {
            path:'/rank',
            component:Rank
        },
        {
            path:'/search',
            component:Search
        }
      ]
    })
    import Vue from 'vue'
    import Router from 'vue-router'
    import Reacommed from 'components/reacommed/reacommed'
    import Search from 'components/search/search'
    import Singer from 'components/singer/singer'
    import Rank from 'components/rank/rank'
     
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/reacommed',
          component:Reacommed
        },
        {
            path:'/singer',
            component:Singer
        },
        {
            path:'/rank',
            component:Rank
        },
        {
            path:'/search',
            component:Search
        }
      ]
    })
    index。js

    3:如何引入router实例

    import 'babel-polyfill'
    import Vue from 'vue'
    import App from './App'
    // 1这里的router是index。js的实例
    import router from './router'
    import fastclick from 'fastclick'
    
    import 'common/stylus/index.styl'
    
    
    Vue.config.productionTip = false
    
    // 取消点击300毫秒的延迟
    fastclick.attach(document.body) 
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      // 2
      router,
      components: { App },
      template: '<App/>'
    })
    main.js

    注册url

    import Vue from 'vue'
    import Router from 'vue-router'
    import Recommend from 'components/recommend/recommend'
    import Search from 'components/search/search'
    import Singer from 'components/singer/singer'
    import Rank from 'components/rank/rank'
     
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/recommend',
          component:Recommend
        },
        {
            path:'/singer',
            component:Singer
        },
        {
            path:'/rank',
            component:Rank
        },
        {
            path:'/search',
            component:Search
        }
      ]
    })
    index.js

    4:显示在App.vue页面

    知识点:

    router-linnk

    a:里面有一个tag属性,控制其显示的为什么标签

    如:tag=“a”,即显示为a标签

    b:router-link-active

    当前某个router-link被激活的时候,会添加样式

    5:将导航条添加进去

    a:先在app.vue导入进去tab组件

    b:注册

    c:渲染

    <template>
        <div id="app">
            <m-header></m-header>
            <tab></tab>
            <router-view></router-view>
        </div>
    </template>
    
    <script>
        import MHeader from './components/m-header/m-header'
        import Tab from './components/tab/tab'
        export default {
          components:{
              MHeader,
              Tab
          }
      }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
    
    </style>
    app.vue

    url重定向

    redirect

    三:在app.vue导入组件

        import MHeader from './components/m-header/m-header'
        import Tab from './components/tab/tab'

    首字母要大写,因为其本质上是class,class书写规范为首字母大写


    jsonp

    jsonp(url, opts, fn)
     url (String) url to fetch
     opts (Object), optional
        param : 指定回调函数名
        timeout : 超时时间,默认一分钟
        prefix : __jp 默认添加前缀
        name
    fn callback (回调函数)

    四:抓取qq音乐的数据

    XHR:ajax请求

    1:可以自己手写

    2:引用插件

    下载安装jsonp

    npm install jsonp

    .jsonp是什么?jsonp是目前可以跨域的(基本上标签带有src属性的都是可以不受任何访问限制),且要动态生成script标签在ajax无法跨域的情况下可以使用jsonp进行请求但它跟ajax是不一样的..jsonp利用url链接进行请求发送和调用回调函数(callblack)使用数据。

    1:封装一个jsonp方法

    用于拼接url
    import originJsonp from 'jsonp' // 引用 export default function jsonp(url, data, option) {
      // 将url和data对象进行拼接成url拼接 url
    += (url.indexOf('?') < 0 ? '?' : '&') + param(data) // param 是一个拼接函数将data专门转化成url形式
      
      return new Promise((resolve, reject) => { originJsonp(url, option, (err, data) => { if (!err) { resolve(data) } else { reject(err) } }) }) } export function param(data) { let url = '' for (var k in data) { let value = data[k] !== undefined ? data[k] : '' url += '&' + k + '=' + encodeURIComponent(value) } return url ? url.substring(1) : '' }

     Promise对象有2个参数(resolve,reject)

    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),
    在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),
    在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    promise()充当异步操作和回调函数的中介,起到代理的作用

    param()data数据对象转化成url格式

    function param(data){
        let url = '';
         //遍历拼接对象
        for(var k in data){
          //以&a=123=yu=789 这样形式拼接
          let value = data[k] !== undefined ?data[k] : '';
          url += `&${k}=${encodeURIComponent(value)}`;        //es6语法
        }
        return url ? url.substring(1):'';
    }

    在创建的config.js下写入jsonp请求通用公共参数

    xport const commonParams = {
        g_tk: 5381,
        inCharset: 'utf-8',
        outCharset: 'utf-8',
        notice: 0,
        format: 'jsonp',
    }
    // 设置常量 param export
    const options = { param: 'jsonpCallback' }
    // 设置常量 错误信息 0 是 ok export
    const ERR_OK = 0

    recommend.js文件下写入抓取QQ音乐轮播图需要发送数据

    import jsonp from '../common/js/jsonp' //引入自定义封装的jsonp函数
    import { commonParams, options } from './config'
    export function getRecommend() {
        //轮播图请求地址
       const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg'; 
        利用es6对象方法进行浅拷贝将数组对象合并到第一个{}对象中
        const data = Object.assign({}, commonParams, {
            platform: 'h5',
            uin: 0,
            needNewCode: 1
        })
        //调用jsonp方法 进行url拼接
        return jsonp(url, data, options)
    }

    .组件页面得到数据

    将其js文件引入组件中

    import {getRecommend} from 'api/recommend';
    import {ERR_OK} from 'api/config'g'

    获取数据

      _getRecommend(){
              //因为return new Promise中有.then表示如果异步成功完成就执行
            getRecommend().then(res=>{
              if(res.code === ERR_OK){
                 console.log(res.data.slider)
                 this.recommend = res.data.slider;        
    //放入组件data中接着传入到轮播组件中使用v-for渲染 } }) }

    编写拼接url的方法(getRecommend)

    MusicJsonCallback30832226944503405({"code":0,
    "subcode":0,"msg":"",
    "data":{"total":4,"items":[{"from":2,"status":0,"msg_num":0},
    {"from":4,"status":0,"msg_num":0},{"from":5,"status":0,"msg_num":0},
    {"from":6,"status":0,"msg_num":0}]}})

    从recommend调用getRecommend方法

    推荐页面

    recommend。vue

    _getRecommend() {
         getRecommend().then((res) => {
               if(res.code === ERR_OK){
                   // 轮播图数据
                this.recommends = res.data.slider
           }
         })
    },


    手写轮播图组件 base/slider

    better-scroll文档**

    slider.vue

    <!-- 轮播图 -->
    <template>
    // slider为最外容器
        <div class="slider" ref="slider">
      // sliderGroup为内层容器
            <div class="slider-group" ref="sliderGroup">
                <slot>
    
                </slot>
            </div>
        </div>
    </template>
    
    <script>
       // 轮播图组件
        import BScroll from 'better-scroll'
        import {addClass} from 'common/js/dom'
    
        export default{
            data(){
                return {
                    dots:[]
                }
            },
            props:{
                loop:{
                    // 循环轮播
                    type:Boolean,
                    default: true
                },
                autoPlay:{
                    // 自动轮播
                    type:Boolean,
                    default: true
                },
                interval:{
                    // 时间间隔 
                    type:Number,
                    default:4000
                }
            },
            mounted(){
                setTimeout(() =>{
                    this._setSliderWidth()
                    this._initSlider()
                    this._initDots()
                },20)
            },
            methods:{
                _setSliderWidth(){
                    this.children = this.$refs.sliderGroup.children
    
                    let width = 0
                    let sliderWidth = this.$refs.slider.clientWidth
                    for (let i = 0; i < this.children.length;i++){
                        let child = this.children[i]
                        addClass(child,'slider-item')
    
                        child.style.width = sliderWidth + 'px'
                        width += sliderWidth
                    }
                    if(this.loop){
                        width += 2 * sliderWidth
                    }
                    this.$refs.sliderGroup.style.width = width + 'px'
                },
                _initDots() {
                    this.dots = new Array(this.children.length-2)
                },
                _initSlider(){
                    this.slider = new BScroll(this.$refs.slider,{
                        scrollX:true,
                        scrollY:false,
                        momentum:false,
                        snap:true,
                        snapLoop:this.loop,
                        snapThreshold:0.3,
                        snapSpeed:400,
                        click:true
                    }
    
                    )
                }
            }
        }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
      @import "~common/stylus/variable"
    
      .slider
        min-height: 1px
        .slider-group
          position: relative
          overflow: hidden
          white-space: nowrap
          .slider-item
            float: left
            box-sizing: border-box
            overflow: hidden
            text-align: center
            a
              display: block
               100%
              overflow: hidden
              text-decoration: none
            img
              display: block
               100%
        .dots
          position: absolute
          right: 0
          left: 0
          bottom: 12px
          text-align: center
          font-size: 0
          .dot
            display: inline-block
            margin: 0 4px
             8px
            height: 8px
            border-radius: 50%
            background: $color-text-l
            &.active
               20px
              border-radius: 5px
              background: $color-text-ll
    </style>
    slider。vue
    <div class="slider" ref="slider">---> 最外层容器
    <div class="slider-group" ref="sliderGroup">---> 内层容器
    
    
    sliderGroup轮播图,只能设置宽度,高度固定
    1:获取整个列表有多少个元素
    this.children = this.$refs.sliderGroup.children
    
    2:父元素的宽度,图片撑开的宽度
    let sliderWidth = this.$refs.slider.clientWidth

     给轮播图图片添加样式

    (手动写的class,耦合性太强)

    a、dom。js--->只对dom元素进行操作的方法(通用)

    b、再导入dom。js

        // 对dom元素操作的组件
        import {addClass} from 'common/js/dom'

    c、将slider-item样式添加进去

    addClass(child,'slider-item')


    计算部分
    // 子容器的宽度 要等于父容器的宽度 需要添加单位'px'
    child.style.width = sliderWidth + 'px'
    // 总宽度需要+父容器的宽度
    width += sliderWidth

    loop:{
           // 循环轮播
          type:Boolean,
          default: true
    },

    // 保证其能进行循环切换,需克隆2个sliderWidth-->即宽度需要加2倍sliderWidth
    if(this.loop && !isResize){
         width += 2 * sliderWidth
    }

    最后设置宽度
    this.$refs.sliderGroup.style.width = width + 'px'

    问题:当后面方法需要获取数据时,而获取数据的方法还未执行完毕

    在recommend。vue进行判断数据长度
    <div v-if='recommends.length' class="slider-wrapper"> <slider> <div v-for="item in recommends"> <a :href="item.linkUrl"> <img :src="item.picUrl"> </a> </div> </slider> </div>

    recommend.vue推荐页面

    <!-- 推荐页面 -->
    <template>
        <div class="recommend">
            <div class="recommend-content">
                <!-- 轮播图 -->
                <div v-if='recommends.length' class="slider-wrapper">
                    <slider> 
                        <div v-for="item in recommends">
                          <a :href="item.linkUrl">
                            <img :src="item.picUrl">
                        </a>
                    </div>
                    </slider> 
                </div>
            <div class="recommend-list">
                <h1 class="list-title">热门歌单推荐</h1>
            </div>
            <ul>
    
            </ul>
        </div>
    </div>
    </template>
    
    <script>
      // 导入轮播图组件
      import Slider from 'base/slider/slider'
     // 导入获取轮播数据的方法
     import {getRecommend} from 'api/recommend'
     // 语义化下面的err_ok
     import {ERR_OK} from 'api/config'
     // 获取数据created
     export default {
        data() {
                // 定义轮播图数据
                return{
                    recommends:[]
                }
            },
            // 钩子函数
            created() {
                this._getRecommend()
            },
            methods: {
                _getRecommend() {
                    getRecommend().then((res) => {
                        if(res.code === ERR_OK){
                            // 轮播图数据
                            this.recommends = res.data.slider
                        }
                    })
                }
            },
            components:{
              Slider,
          }
      }
    
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
    @import "~common/stylus/variable"
    
      .recommend
        position: fixed
         100%
        top: 88px
        bottom: 0
        .recommend-content
          height: 100%
          overflow: hidden
          .slider-wrapper
            position: relative
             100%
            overflow: hidden
          .recommend-list
            .list-title
              height: 65px
              line-height: 65px
              text-align: center
              font-size: $font-size-medium
              color: $color-theme
            .item
              display: flex
              box-sizing: border-box
              align-items: center
              padding: 0 20px 20px 20px
              .icon
                flex: 0 0 60px
                 60px
                padding-right: 20px
              .text
                display: flex
                flex-direction: column
                justify-content: center
                flex: 1
                line-height: 20px
                overflow: hidden
                font-size: $font-size-medium
                .name
                  margin-bottom: 10px
                  color: $color-text
                .desc
                  color: $color-text-d
          .loading-container
            position: absolute
             100%
            top: 50%
            transform: translateY(-50%)
    </style>
    推荐页面

    轮播图下的圆圈

    <!-- 轮播图 -->
    <template>
        <div class="slider" ref="slider">
            <div class="slider-group" ref="sliderGroup">
                <slot>
    
                </slot>
            </div>
            <!-- 圆圈 -->
            <div class="dots">
                <span class="dot" v-for = 'item in dots'></span>   
            </div>
        </div>
    </template>
    
    <script>
       // 轮播图组件
        import BScroll from 'better-scroll'
        import {addClass} from 'common/js/dom'
    
        export default{
            data(){
                return {
                    dots:[]
                }
            },
            props:{
                loop:{
                    // 循环轮播
                    type:Boolean,
                    default: true
                },
                autoPlay:{
                    // 自动轮播
                    type:Boolean,
                    default: true
                },
                interval:{
                    // 时间间隔 
                    type:Number,
                    default:4000
                }
            },
            mounted(){
                setTimeout(() =>{
                    this._setSliderWidth()
                    this._initSlider()
                    this._initDots()
                },20)
            },
            methods:{
                _setSliderWidth(){
                    this.children = this.$refs.sliderGroup.children
    
                    let width = 0
                    let sliderWidth = this.$refs.slider.clientWidth
                    for (let i = 0; i < this.children.length;i++){
                        let child = this.children[i]
                        addClass(child,'slider-item')
    
                        child.style.width = sliderWidth + 'px'
                        width += sliderWidth
                    }
                    if(this.loop){
                        width += 2 * sliderWidth
                    }
                    this.$refs.sliderGroup.style.width = width + 'px'
                },
                _initDots() {
                    this.dots = new Array(this.children.length-2)
                },
                _initSlider(){
                    this.slider = new BScroll(this.$refs.slider,{
                        scrollX:true,
                        scrollY:false,
                        momentum:false,
                        snap:true,
                        snapLoop:this.loop,
                        snapThreshold:0.3,
                        snapSpeed:400,
                        click:true
                    }
    
                    )
                }
            }
        }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
      @import "~common/stylus/variable"
    
      .slider
        min-height: 1px
        .slider-group
          position: relative
          overflow: hidden
          white-space: nowrap
          .slider-item
            float: left
            box-sizing: border-box
            overflow: hidden
            text-align: center
            a
              display: block
               100%
              overflow: hidden
              text-decoration: none
            img
              display: block
               100%
        .dots
          position: absolute
          right: 0
          left: 0
          bottom: 12px
          text-align: center
          font-size: 0
          .dot
            display: inline-block
            margin: 0 4px
             8px
            height: 8px
            border-radius: 50%
            background: $color-text-l
            &.active
               20px
              border-radius: 5px
              background: $color-text-ll
    </style>
    slider.vue

    将轮播图当前页与小圆圈绑定样式

     <div class="dots">
    <span class="dot" v-for = '(item,index) in dots' :class="{active: currentPageIndex === index}">
      </span> </div>

    index--->索引当前第几个元素

    添加样式active

     :class="{active: currentPageIndex === index}">
    currentPageIndex:标识当前页数  
    需要先在data() 方法里面初始化currentPageIndex

    export default{ data(){ return { dots:[], // 标识当前页数 currentPageIndex: 0 } },

    到索引的页面就添加active类

    当前对象。也就是指.dot

    当dot是active的时候,就添加样式

    维护currentPageIndex

    currentPageIndex什么时候切换的?如何将滚动到的页面与currentPageIndex结合?
     
    当初始化slider的时候,绑定事件
    
     // 当滚动到下一张会触发scrollEnd事件
    scrollEnd事件会派发一个回调函数
     this.slider.on('scrollEnd', ()=>{
        // 获取当前 getCurrentPage()为slider的方法  返回的对象有个pageX方法
    pageX--->当前第几个元素
             let pageIndex = this.slider.getCurrentPage().pageX
               if (this.loop) {
                     pageIndex -= 1
                            //再循环模式下,会默认在第一个元素添加一个拷贝 所以要减1
                        }
               this.currentPageIndex = pageIndex
     })
    
    
    这里我们用到了 Vue 的特殊元素—— slot 插槽,它可以满足我们灵活定制列表 DOM 结构的需求。接下来我们来看看 JS 部分:
    <script type="text/ecmascript-6"> 
        import BScroll from 'better-scroll' 
        export default { 
            props: { 
            /** * 1 滚动的时候会派发scroll事件,会截流。 
                * 2 滚动的时候实时派发scroll事件,不会截流。 
                * 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件 
                */ 
                probeType: { 
                    type: Number, 
                    default: 1 
                }, 
                /** 
                * 点击列表是否派发click事件 
                */ 
                click: { 
                    type: Boolean, 
                    default: true 
                }, 
                /** * 是否开启横向滚动 */
                scrollX: { 
                    type: Boolean, 
                    default: false 
                }, 
                /** * 是否派发滚动事件 */ 
                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 
                }, 
                /** * 当数据更新后,刷新scroll的延时。 */ 
                refreshDelay: { 
                    type: Number, 
                    default: 20 } 
                }, 
                mounted() { 
                // 保证在DOM渲染完毕后初始化better-scroll 
                setTimeout(() => { 
                    this._initScroll() 
                    }, 20) 
                }, 
                methods: { 
                    _initScroll() { 
                        if (!this.$refs.wrapper) {
                                return 
                            } 
                            // better-scroll的初始化 
                            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的disable方法 
                    this.scroll && this.scroll.disable() 
                }, 
                enable() { 
                    // 代理better-scroll的enable方法 
                    this.scroll && this.scroll.enable() 
                }, 
                refresh() { 
                    // 代理better-scroll的refresh方法 
                    this.scroll && this.scroll.refresh() 
                }, 
                scrollTo() { 
                    // 代理better-scroll的scrollTo方法 
                    this.scroll && this.scroll.scrollTo.apply(this.scroll, 
                        arguments) 
                }, 
                scrollToElement() { 
                    // 代理better-scroll的scrollToElement方法 
                    this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments) 
                    } 
                }, 
                watch: { 
                    // 监听数据的变化,延时refreshDelay时间后调用refresh方法重新计算,保证滚动效果正常 
                    data() {
                        setTimeout(() => { 
                        this.refresh() 
                        }, this.refreshDelay) 
                    }
                } 
            }
    </script>
    
    
    JS 部分实际上就是对 better-scroll 做一层 Vue 的封装,通过 props 的形式,
    把一些对 better-scroll 定制化的控制权交给父组件;
    通过 methods 暴露的一些方法对 better-scroll 的方法做一层代理;
    通过 watch 传入的 data,当 data 发生改变的时候,
    在适当的时机调用 refresh 方法重新计算 better-scroll 确保滚动效果正常,
    这里之所以要有一个 refreshDelay 的设置是考虑到如果我们对列表操作用到了 transition-group 做动画效果
    ,那么 DOM 的渲染完毕时间就是在动画完成之后。 有了这一层 scroll 组件的封装,我们来修改刚刚最复杂的代码(假设我们已经全局注册了 scroll 组件)。
    <template> <scroll class="wrapper" :data="data" :pulldown="pulldown" @pulldown="loadData"> <ul class="content"> <li v-for="item in data">{{item}}</li> </ul> <div class="loading-wrapper"></div> </scroll> </template> <script> import BScroll from 'better-scroll' export default { data() { return { data: [], pulldown: true } }, created() { this.loadData() }, methods: { loadData() { requestData().then((res) => { this.data = res.data.concat(this.data) }) } } } </script> 可以很明显的看到我们的 JS 部分精简了非常多的代码,没有对 better-scroll 再做命令式的操作了,
    同时把数据请求和 better-scroll 也做了剥离,父组件只需要把数据 data 通过 prop 传给 scroll 组件,
    就可以保证 scroll 组件的滚动效果。
    同时,如果想实现下拉刷新的功能,只需要通过 prop 把 pulldown 设置为 true
    并且监听 pulldown 的事件去做一些数据获取并更新的动作即可,整个逻辑也是非常清晰的。

     

    动态的给子元素设置宽度,当能滑动或者自动轮播的时候,子元素的宽度乘以2,用来切换,因为轮播图想无缝滑动,
    必须将第一张图放到最后一个位置,最后一张图放到第一张的位置前面,假如有五个轮播图的话,
    事实上,获取的宽度是七张图的宽度。

    自动播放

    slider.vue

     props:{
         loop:{
          // 循环轮播
          type:Boolean,
         default: true
      },
     autoPlay:{
           // 自动轮播
          ype:Boolean,
          default: true

    优化:

    当屏幕宽度变化的时候,图片错位,而宽度是由sliderWidth控制的

    方法: 监听Windows的resizer事件,重新渲染sliderWidth

            //监听窗口改变事件
            window.addEventListener('resize', () => {
                if (!this.slider){
            // 初始化的时候直接返回
    return } // 重新调用计算宽度的方法 this._setSliderWidth(true) }) },
    
    

    window窗口改变事件:resize

    addEventListener() 方法

    用于向指定元素添加事件句柄

    element.addEventListener(event, function, useCapture)

    参数:
    event:
    必须。字符串,指定事件名。
    
    注意: 不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"
    
    
    function
    必须。指定要事件触发时执行的函数。
    
    当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如,
     "click" 事件属于 MouseEvent(鼠标事件) 对象。
    
    
    useCapture
    可选。布尔值,指定事件是否在捕获或冒泡阶段执行。
    
    可能值:
    
        true - 事件句柄在捕获阶段执行
        false- false- 默认。事件句柄在冒泡阶段执行
    参数

    添加标志位,重置过的设置为true

            methods:{
            // isResize标志位判读是不是重置过来的 _setSliderWidth(isResize){
    this.children = this.$refs.sliderGroup.children let width = 0 let sliderWidth = this.$refs.slider.clientWidth for (let i = 0; i < this.children.length;i++){ let child = this.children[i] addClass(child,'slider-item') child.style.width = sliderWidth + 'px' width += sliderWidth }
              // && !isResize
    if(this.loop && !isResize){
              // 刚开始进来只需要加一次宽度 width
    += 2 * sliderWidth } this.$refs.sliderGroup.style.width = width + 'px' },
    
    

    知识点

    1:slot---> 插槽

    2:使用better-scroll

    3:created和mounted

    created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
    mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
    其实两者比较好理解,通常created使用的次数多,而mounted通常是在一些插件的使用或者组件的使用中进行操作,比如插件chart.js的使用: var ctx = document.getElementById(ID);通常会有这一步,而如果你写入组件中,你会发现在created中无法对chart进行一些初始化配置,一定要等这个html渲染完后才可以进行,那么mounted就是不二之选。下面看一个例子(用组件)。
  • 相关阅读:
    quotaon
    quotacheck
    quota
    query_module
    数据库连接驱动
    PHP 开发 APP 接口 学习笔记与总结
    Java实现 LeetCode 76 最小覆盖子串
    Java实现 LeetCode 74 搜索二维矩阵
    Java实现 LeetCode 74 搜索二维矩阵
    Java实现 LeetCode 74 搜索二维矩阵
  • 原文地址:https://www.cnblogs.com/jassin-du/p/9431789.html
Copyright © 2020-2023  润新知