vue音乐app的笔记

本文深入解析了JSONP的实现原理及其在跨域请求中的应用,同时对比了JSONP与反向代理在处理跨域问题时的异同。通过具体案例,如QQ音乐API的抓取,阐述了如何利用反向代理绕过跨域限制,以及在Vue项目中实现数据抓取和组件联动的技巧。

** jsonp的实现原理**

jsonp发送的并不是ajax请求,而是动态创建的script标签,script表签没有同源策略的,是没有限制,可以跨域的,创建的script标签把src指向我们请求正式的服务端地址

这个地址和我们的ajax的地址有什么不同
那是因为在url这个地址有一个参数通常会叫callback = a(比如=a),这样服务端就会解析这个url 然后它带一个callback = a这样的参数,它就会在返回的数据里调用a然后去包裹一个方法,包裹一段数据,然后去执行,相当于在前端执行a这个方法,那前端没有a这个方法,所以在发送请求之前,也就是通过script的src这个url之前我们要去在window上注册这样一个方法,这样的话,服务端返回这个a()这个方法执行的时候,就可以在window上定义的这个方法中获得这个数据
https://blog.youkuaiyun.com/inite/article/details/80333130

jsonp的promise封装

import originJSONP from 'jsonp'
// data:通常传给服务端的是一个地址 url往往是带一些参数,但是originJSONP这个库是不支持传入一个object 一个data的,需要你先吧这个url拼好,然后再去调用这个originJSON库,实际上,当我们使用的时候,希望这个url是比较纯净的地址,所有的query都通过这个data去把它拼到这个url上,这样的话我们调用就更加方便
export default function jsonp(url, data, option) {
  // 实际上url有可能会有?,判断如果没有?就拼接一个,如果有?就用&连接    [url拼接的方式]
  url += (url.indexOf('?') < 0 ? '?' : '&') + param(data)
  return new Promise((resolve,reject) => {
    originJSONP(url, option, (data,err) => {
      if(!err) {
        resolve(data)
      }else{
        reject(err)
      }
    })
  })
}
//jsonp的函数里传入了data的参数,需要把data的json对象拼到url参数里
function param(data) {
  let url = ''
  for(var k in data) {
    let value = data[k]!== undefined ? data[k] : ''  //判断如果data有值就传data[k]否则传一个空给后端
    url += `&${k}=${encodeURIComponent(value)}`  // 拼接url
  }
  return url?url.substring(1):'' // 如果这个url有data要把第一个&符号去掉(substring() 从字符串中提取一些字符) 如果data什么都没有就返回一个空
}

总结

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
Object.assign(target, source1, source2)
encodeURIComponent(URIstring)函数可把字符串作为 URI 组件进行编码。 URIstring 必需。一个字符串,含有 URI 组件或其他要编码的文本。
substring()方法用于提取字符串中介于两个指定下标之间的字符。
stringObject.substring(start,stop)包括 start 处的字符,但不包括 stop 处的字符。不接受负的参数。

jsonp抓取qq音乐的数据
1、npm install jsonp
2、jsonp 原理内容 (上述内容创建jsonp.js)
3、创建confiig.js文件

export const commonParams = {
  g_tk: 5381,
  inCharset: 'utf-8',
  outCharset: 'utf-8',
  notice: 0,
  format: 'jsonp'
}
export const options = {
  param: 'jsonpCallback'
}
export const ERR_OK = 0

4、创建recommend.js文件

import jsonp from 'assets/js/jsonp'
import {commonParams, options} from 'src/api/config'
export function getRecommend() {
  const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg'
  const data = Object.assign({}, commonParams, {
    platform: 'h5',
    uin: 0,
    needNewCode: 1
  })
  return jsonp(url, data, options)
}

5、在组件中使用

import {getRecommend} from 'src/api/recommend'
import {ERR_OK} from 'src/api/config'
export default {
  name: 'Recommend',
  data() {
    return {

    }
  },
  created() {
    this._getRecommend()
  },
  methods: {
    _getRecommend() {
      getRecommend().then((res) => {
        if (res.code === ERR_OK) {
          console.log(res.data.slider)
        }
      })
        .catch((err) => {
          console.log(err)
        })
    }
  }
}

banner运用better-scroll时初始化时不能滚动
better-scroll初始化时不能滚动,甚至初始化时报错,其原因主要是因为在初始化的时候,这个组建没有真正的被渲染,或者是高度或者宽度没有计算不正确,所以要先保证渲染实际是正确的
'better-scroll’利用的原理就是:wrapper为父容器,具有固定高度;content为父容器的第一个子元素;当content的高度超过wrapper时,就可以滚动。

安装 “better-scroll”: “^0.1.15”,

①轮播图的功能有:循环轮播,自动轮播,轮播间隔。
在这里插入图片描述
实现水平轮播最重要的是计算轮播宽度

//引用dom.js  封装addClass
import {addClass} from 'src/assets/js/dom'
 mounted () {
    setTimeout(() => {
      this._setSliderWidth()
      this.initSlider()
    }, 20)
  },
  methods: {
    _setSliderWidth() {
      this.children = this.$refs.sliderGroup.children
      let width = 0
      let silderWidth = this.$refs.slider.clientWidth
      for (let i = 0; i < this.children.length; i++) {
        // 先获取每个子元素
        let child = this.children[i]
        addClass(child, 'slider-item')   //每一个子元素添加className
        child.style.width = silderWidth + 'px'
        width += silderWidth
      }
      if (this.loop) {
        width += 2 * silderWidth
      }
      this.$refs.sliderGroup.style.width = width + 'px'
    }

宽度定义好以后,就可以初始化滚动了

_initSlider() {
      this.slider = new BScroll(this.$refs.slider, {
        scrollX: true,//横向滚动
        scrollY: false,//禁止纵向滚动
        momentum: false,//禁止惯性运动
        snap: true,
        snapLoop: this.loop,
        snapThreshold: 0.3,
        snapSpeed: 400
      })

dom.js 封装的addClass

export function addClass(el, className) {
  if (hasClass(el, className)) {
    return
  }
  let newClass = el.className.split(' ') // split将获取的这个className拆成一个数组split()方法用于把一个字符串分割成字符串数组。 比如
  newClass.push(className) // 再把他添加进去
  el.className = newClass.join(' ') // join()方法用于把数组中的所有元素放入一个字符串。
}
// 判断是否有这个class
export function hasClass(el, className) {
  let reg = new RegExp('(^|\\s)' + className + '(\\s|$)') // 创建一个正则  class开头,或者是空白字符 \\因为是字符串所以要转义一下
  return reg.test(el.className) // test()方法用于检测一个字符串是否匹配某个模式,如果满足这个条件说明有这个className
}

注意当执行mounted 钩子的时候,slot里插槽里内容会不会有,slot的内容在re’commed.vue 里的created钩子里获取的,是一个异步的过程,在服务器取数据会有几毫秒的延迟,为了确保slot里是有内容的要用v-if判断

<div class="slider-content" v-if="recommends.length">
          <slider>
            <div
              v-for="item in recommends"
              :key="item.id"
            >
              <a :href="item.linkUrl">
                <img :src="item.picUrl" alt="">
              </a>
            </div>
          </slider>
        </div>

在获取歌单数据的时候,我们发现用jsonp获取接口的时候会报错,这是为什么呢?
借鉴:https://blog.youkuaiyun.com/sunnyjingqi/article/details/89404538

原因是qq音乐在请求头里面加了authority和refer等 ,如果我们通过jsonp实现跨域来请求数据的话 是根本不能够修改请求头的 ,如果要使用axios直接进行跨域访问是不可以的,这时候我们可以进行后端接口代理 ,客户端请求服务端的数据是存在跨域问题的,而服务器和服务器之间可以相互请求数据,是没有跨域的概念(如果服务器没有设置禁止跨域的权限问题),也就是说,我们可以配置一个代理的服务器可以请求另一个服务器中的数据,然后把请求出来的数据返回到我们的代理服务器中,代理服务器再返回数据给我们的客户端,这样我们就可以实现跨域访问数据啦。

在HTTP请求头里referer是代表这个请求是请哪个URL过来的
origin是在HTML5中跨域操作所引入的,当一个链接或者XMLHttpRequest去请求跨域操作,浏览器事实上的确向目标服务器发起了连接请求,并且携带这origin
host在视频里存在歌单的请求头里,但是现在发现没出现,可能隐藏了,但是我在反向代理ProxyTable里设置了这个参数是同样可以请求成功的。这个参数表示了客户端指定了自己想访问的服务器地址,只要我们使用反向代理向改服务器发送相同的请求头,就可以成功抓取数据到数据,因为目标服务器无法区分是否来自它本身服务器发送的请求。

  1. npm install axios -S
  2. build/webpack.dev.conf.js文件
const axios = require('axios')
const express = require('express')
const app = express()
const apiRoutes = express.Router() //后端路由
app.use('/api', apiRoutes)
  1. // devServer 里添加
    before(app){
      app.get('/api/getDiscList', function(req,res) {
        var url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg'
        axios.get(url, {
          headers: {
            referer: 'https://c.y.qq.com',
            host: 'c.y.qq.com'
          },
          params: req.query // 前端传过来的数据
        })
          .then((response) => {
            res.json(response.data)
          })
          .catch((e) => {
            console.log(e)
          })
      })
    }
  1. 在api里的js文件里:将方法里的url替换成步骤2里自定义的接口,即 ‘/api/getDiscList’,再通过axios获取返回的数据。
export function getDiscList() {
  const url = '/api/getDiscList'
  const data = Object.assign({}, commonParams, {
    platform: 'yqq',
    hostUin: 0,
    sin: 0,
    ein: 29,
    sortId: 5,
    needNewCode: 0,
    categoryId: 10000000,
    rnd: Math.random(),
    format: 'json'
  })
  return axios.get(url, {
    params: data
  })
    .then((res) => {
      return Promise.resolve(res.data)
    })
}

记住params这个名字千万不能写错,我因为把它写成了param,导致请求参数都没有传过去,找问题的能力太差,最终在header里面发现了,回想起教程视频里老师提醒过这个。

(大家很容易查到:json和jsonp的区别,json是一种格式,jsonp是一种请求跨域资源的方式。这里就不做详细解释了。)

跨域:是指浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的,是浏览器施加的安全限制。在跨域情况下,XMLHTTPRequest是不能发送异步请求的。
所谓同源是指域名、协议、端口均相同。

那么,同是跨域方法,为什么轮播图的请求可以用jsonp的方式,而歌单的请求要使用反向代理,两个都是跨域方法。

比较两个请求jsonp和proxyTable反向代理的异同:

jsonp原理:

反向代理:本方法是在自己的浏览器创建一个服务器,然后让自己的服务器去请求目标服务器。而且跨域是针对JavaScript来说的,JavaScript 是插入HTML页面后在浏览器上执行的脚本。服务器之间是可以随便请求数据而不受限制的。我们通过自己创建的服务器去请求目标服务器,然后在从我们客户端去请求我们自己创建的服务器,这就不存在跨域了。
5. 组件里的调用

 _getDiscList() {
      getDiscList().then((res) => {
        if (res.code === ERR_OK) {
          console.log(res)
        }
      })
    }

banner的图片还未加载完毕时,歌单推荐的better-scroll上拉到最底部后不能展示全部的内容

在这里插入图片描述
并监听loadImage
在这里插入图片描述
不利用jasonp用反向代理完成数据抓取
在这里插入图片描述
config文件下的index.js 配置proxyTable
在这里插入图片描述

使用

import axios from 'axios'
// import {commonParams} from 'src/api/config'
export function getSingerList() {
  const url = '/qqmusic/v8/fcg-bin/v8.fcg'
  const data = {
    channel: 'singer',
    page: 'list',
    key: 'all_all_all',
    pagesize: 100,
    pagenum: 1,
    hostUin: 0,
    platform: 'yqq',
    g_tk: 5381,
    loginUin: '0',
    format: 'json',
    inCharset: 'utf8',
    outCharset: 'utf-8',
    notice: 0,
    needNewCode: 0
  }
  return axios.get(url, {
    params: data
  })
    .then((res) => {
      return Promise.resolve(res.data)
    })
}

歌手列表与右侧a-zA-Z之间的联动
联动的思路:
如果想要达到左边和右边的联动,首先需要实时的知道他的滚动位置,然后根据他的滚动位置来算他当前的滚动位置是落在歌手列表哪个group的区间,如果算到落到的区间以后,我们就知道右侧对应的哪个索引,然后哪个区间索引的高亮就显示
在vue中主要用watch观测这个变化然后配合scroll,实时派发出scrollY去观测Y值得变化,观测到y值得变化后,就去计算currentIndex,然后根据currentIndex vue中index的映射(:class)等于当前的索引时,就给他一个active的样式,让对应的索引高亮
1、

<template>
  <scroll class="listview"
          :data="data"
          ref="listview"
          :listenScroll = 'listenScroll'
          :probeType="probeType"
          @scroll="scroll"
  >
    <ul>
      <li v-for="(group, index) in data"
          class="list-group"
          :key="index"
          ref="listGroup"
      >
        <h2 class="list-group-title">{{group.title}}</h2>
        <ul>
          <li
            v-for="item in group.items"
            :key="item.id"
            class="list-group-item"
          >
            <img v-lazy="item.avatar" class="avatar" alt="">
            <span class="name">{{item.name}}</span>
          </li>
        </ul>
      </li>
    </ul>
    <div class="list-shortcut"
         @touchstart="onShortcutTouchStart"
         @touchmove.stop.prevent="onShortcutTouchmove"
    >
      <ul>
        <li
          class="item"
          v-for="(item, index) in shortcutList"
          :key="index"
          :data-index="index"
          :class="{'current': currentIndex === index}"
        >
          {{item}}
        </li>
      </ul>
    </div>
    <div class="list-fixed" v-show="fixedTitle" ref="fixed">
      <h1 class="fixed-title">{{fixedTitle}}</h1>
    </div>
    <div v-show="!data.length" class="loading-container">
      <loading></loading>
    </div>
  </scroll>
</template>
<script>
import Scroll from 'src/base/scroll/scroll'
import {getData} from 'src/assets/js/dom'
import Loading from 'src/base/loading/loading'
const ANCHOR_HEIGHT = 18
const TITLE_HEIGHT = 30
export default {
  data() {
    return {
      scrollY: -1,
      currentIndex: 0,
      diff: -1 // 表示区块上限和当前滚动位置的一个差值
    }
  },
  // 在vue里面不管是data还是props里面的东西都会被vue添加一个getter和setter,它会去观测props data以及computed里面值的变化,如果变化都会做监听,主要是为了与dom做数据绑定
  props: {
    data: {
      type: Array,
      default: null
    }
  },
  created() {
    this.touch = {} // 为了让touchstar和touchmove两个函数间共享数据
    this.listenScroll = true // 监听scroll
    this.listHeight = []
    this.probeType = 3
  },
  watch: {
    data() {
      setTimeout(() => {
        this._calculateHeight()
      }, 20)
    },
    scrollY(newY) { // 观测scrollY变化
      const listHeight = this.listHeight
      // 当滚到顶部,newY>0
      if (newY > 0) {
        this.currentIndex = 0
        return
      }
      // 中间部分滚动
      for (let i = 0; i < listHeight.length - 1; i++) {
        let height1 = listHeight[i]
        let height2 = listHeight[i + 1]
        // 如果他落在了height1和height2的区间内
        if (-newY >= height1 && -newY < height2) {
          this.currentIndex = i
          this.diff = height2 + newY // 得到fixed title上边界距顶部的偏移距离
          console.log(this.currentIndex)
          return
        }
      }
      // 当滚动到底部,且-newY大于最后一个元素的上限
      // currentIndex 比listHeight中的height多一个, 比元素多2个
      this.currentIndex = listHeight.length - 2
    },
    diff(newVal) {
      let fixedTop = (newVal > 0 && newVal < TITLE_HEIGHT) ? newVal - TITLE_HEIGHT : 0
      if (this.fixedTop === fixedTop) {
        return
      }
      this.fixedTop = fixedTop
      this.$refs.fixed.style.transform = `translate3d(0, ${fixedTop}px,0)`
    }
  },
  components: {
    Scroll,
    Loading
  },
  computed: {
    shortcutList() {
      return this.data.map((group) => {
        return group.title.substr(0, 1)
      })
    },
    fixedTitle() {
      if (this.scrollY > 0) { // 边界条件判断
        return ''
      }
      return this.data[this.currentIndex] ? this.data[this.currentIndex].title : ''
    }
  },
  methods: {
    onShortcutTouchStart(e) {
      // 获取data-index的值  index 得到的是字符串
      let anchorIndex = getData(e.target, 'index')
      // console.log(anchorIndex)
      let fistTouch = e.touches[0] // 获取手指触碰的第一个位置
      this.touch.y1 = fistTouch.pageY // 记录一个第一次touch时pageY的值
      // 给touch初始化记录一个当前的anchorIndex,你当前点击的index是多少
      this.touch.anchorIndex = anchorIndex
      this._scrollTo(anchorIndex)
    },
    onShortcutTouchmove(e) {
      let fistTouch = e.touches[0] // move touch时的第一个位置
      this.touch.y2 = fistTouch.pageY // 同样获取并记录到第一次touchmove时 pageY的值
      let delta = Math.floor((this.touch.y2 - this.touch.y1) / ANCHOR_HEIGHT) // 计算y轴上面的一个偏移,并且知道偏移了几个delta(锚点)
      // move的时候又可以定义this.touch.anchorIndex获取的是个字符串
      let anchorIndex = parseInt(this.touch.anchorIndex) + delta
      // console.log(anchorIndex)
      this._scrollTo(anchorIndex)
    },
    scroll(pos) {
      this.scrollY = pos.y // 子元素传递的方法,父元素监听这个方法,实时的获取scrollY
    },
    _scrollTo(index) {
      // 处理index边界
      // 点击边界的情况
      if (!index && index !== 0) {
        return
      }
      // 拖动到边界的情况
      if (index < 0) {
        index = 0
      } else if (index > this.listHeight.length - 2) {
        index = this.listHeight.length - 2
      }
      this.scrollY = -this.listHeight[index] // 点击右侧出现高亮
      this.$refs.listview.scrollToElement(this.$refs.listGroup[index], 0) // 列表滚动定位
    },
    _calculateHeight() { // 计算每个group的高度
      this.listHeight = [] // 每次重新计算每个group高度时,恢复初始值
      const list = this.$refs.listGroup
      let height = 0 // 初始位置的height为0
      this.listHeight.push(height)
      for (let i = 0; i < list.length; i++) {
        let item = list[i] // 得到每一个group的元素
        height += item.clientHeight // DOM元素可以用clientHeight获取元素高度
        this.listHeight.push(height) // 得到每一个元素对应的height
      }
    }
  }
}
</script>

2、
在这里插入图片描述
3、

<template>
  <div ref="wrapper">
    <slot></slot>
  </div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
  name: 'Scroll',
  props: {
    probeType: { // 控制better-scroll的probeType
      type: Number,
      default: 1
    },
    click: {
      type: Boolean,
      default: true
    },
    data: {
      type: Array,
      default: null
    },
    listenScroll: { // 监听scroll滚动
      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
      })
      if (this.listenScroll) {
        let that = this
        this.scroll.on('scroll', (pos) => {
          that.$emit('scroll', pos) // 这个this指向的是scroll所以要重新声明一个变量保存this,这样使用的时候this指向就是vue实例的this
        })
      }
    },
    // better scroll 方法代理
    enable() {
      // 启用better-scroll默认开启
      this.scroll && this.scroll.enable()
    },
    disable() {
      // 禁用better-scroll, 如果不加,scroll的高度会高于内容的高度
      this.scroll && this.scroll.disable()
    },
    refresh() {
      this.scroll && this.scroll.refresh()
    },
    // 滚动到指定的位置;这里使用apply 将传入的参数,传入到this.scrollTo()
    scrollTo() {
      this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
    },
    // 滚动到指定的目标元素
    scrollToElement() {
      this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
    }
  },
  watch: {
    data() {
      setTimeout(() => {
        this.refresh()
      }, 20)
    }
  }
}
</script>

vuex
把所有组件的所有状态、数据都存储在统一的内存空间去管理state里,state里的数据可以方便的映射到vue的组件上,去渲染组件,当组件的数据发生变化的时候,它通过Dispatch方法,触发Actions,Actions可以做一些异步的操作比如:后端的一些交互,之后它Commit,Mutations (注意:也可以直接在vue组件中直接commit一个Mutations),commit,Mutations是唯一一个可以修改state的途径,其他任何方式修改都是不允许的。vuex设计的目的是让state状态修改可以预测,当state被修改后,他又会反映到vue组件上,这样就实现一个闭环
使用场景
1、多个组件的状态共享(应用比较复杂,其中一些数据是被组件共享的,而这些组件是兄弟组件,甚至是关联度比较低的组件)
2、解决路由间的复杂的数据传递(一些路由跳转的场景,传递的参数很复杂)

歌单详情下拉图片的放大缩小、以及上拉置顶
在这里插入图片描述
在这里插入图片描述

<template>
  <div class="music-list">
    <div class="back">
      <i class="icon-back"></i>
    </div>
    <h1 class="title" v-html="title"></h1>
    <div class="bg-image" :style="bgStyle" ref="bgImage">
      <div class="filter" ref="filter"></div>
    </div>
    <div class="bg-layer" ref="bgLayer"></div>
    <scroll
      @scroll="scroll"
      :probe-type="probeType"
      :listen-scroll="listenScroll"
      :data="songs"
      class="list"
      ref="list">
      <div class="song-list-wrapper">
        <song-list :songs="songs"></song-list>
      </div>
    </scroll>
    <div class="loading-container" v-show="!songs.length">
      <loading></loading>
    </div>
  </div>
</template>
<script>
import Scroll from 'src/base/scroll/scroll'
import SongList from 'src/base/song-list/song-list'
import Loading from 'src/base/loading/loading'
import {prefixStyle} from 'src/assets/js/dom'
const transform = prefixStyle('transform')
const backdrop = prefixStyle('background-filter')
const LIXIT_HEIGHT = 40
export default {
  data() {
    return {
      scrollY: 0
    }
  },
  props: {
    bgImage: {
      type: String,
      default: null
    },
    songs: {
      type: Array,
      default: null
    },
    title: {
      type: String,
      default: null
    }
  },
  computed: {
    bgStyle() {
      return `background-image:url(${this.bgImage})`
    }
  },
  components: {
    Scroll,
    SongList,
    Loading
  },
  mounted() {
    // $el获取他的一个dom
    this.imageHeight = this.$refs.bgImage.clientHeight
    this.minTranslateY = -this.imageHeight + LIXIT_HEIGHT
    this.$refs.list.$el.style.top = `${this.imageHeight}px`
  },
  created() {
    this.probeType = 3
    this.listenScroll = true
  },
  methods: {
    scroll(pos) {
      this.scrollY = pos.y
    }
  },
  watch: {
    scrollY(newY) {
      let translateY = Math.max(this.minTranslateY, newY)
      let zIndex = 0
      // 图片的放大缩小
      let scale = 1
      // 图片的高斯模糊
      let blur = 0
      this.$refs.bgLayer.style[transform] = `translate3d(0, ${translateY}px, 0)`
      // this.$refs.bgLayer.style['webkitTransform'] = `translate3d(0, ${translateY}px, 0)`
      // 判断图片的下拉放大
      const percent = Math.abs(newY / this.imageHeight) // 公式
      if (newY > 0) {
        scale = 1 + percent
        zIndex = 10
      } else {
        blur = Math.min(20 * percent, 20)
      }
      this.$refs.filter.style[backdrop] = `blur${blur}`
      // this.$refs.filter.style.webkitBackgroundFilter = `blur${blur}`
      if (newY < this.minTranslateY) {
        zIndex = 10
        this.$refs.bgImage.style.paddingTop = 0
        this.$refs.bgImage.style.height = `${LIXIT_HEIGHT}px`
      } else {
        this.$refs.bgImage.style.paddingTop = '70%'
        this.$refs.bgImage.style.height = 0
      }
      // 图片的层级
      this.$refs.bgImage.style.zIndex = zIndex
      this.$refs.bgImage.style[transform] = `scale(${scale})`
      // this.$refs.bgImage.style['webkitTransform'] = `scale(${scale}`
    }
  }
}
</script>

// 能力检测,利用浏览器的立减测特性
let elementStyle = document.createElement('div').style
let verdor = (() => {
  let transformName = {
    Webkit: 'WebkitTransform',
    Moz: 'MozTransform',
    O: 'OTransform',
    Ms: 'MsTransform',
    standard: 'transform'
  }
  for (let key in transformName) {
    if (elementStyle[transformName[key]] !== undefined) {
      return key
    }
  }
  return false // 所有都不支持就直接返回false
})()
export function prefixStyle(style) {
  if (verdor === false) {
    return false
  }
  if (verdor === 'standard') {
    return style
  }
  return verdor + style.charAt(0).toUpperCase() + style.substr(1)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值