javascript原理&项目实战

234 阅读5分钟

基础知识

js事件流

  1. 一个完整事件流从window开始,最后回到window 的过程
  2. 事件流分为三个阶段:捕获阶段、目标过程、冒泡阶段

跨域

  1. 同源策略
    • 同源:协议+域名+端口 三者相同 限制一下行为
    1. cookie、LocalStorage和IndexDB无法读取
    2. Dom和js对象无法获得
    3. Ajax无法发送 有三个标签是允许跨域加载资源
    4. <img src>
    5. <link href=xxx>
    6. <script src=xxx> img,link,script 都可以跨域 比如引入外部地址,cdn的库 img 可用于统计打点,可使用第三方统计服务
  2. 解决方案
    • jsop跨域
       function jsonp() {
         let script = document.createElement('script')
         script.type = 'text/javascript'
         script.src = `http://www.baidu.com?callback=onBack`
         document.body.appendChild(script)
         function onBack() {
           alert('执行onBack')
         }
       }
       jsonp()
     </script>
    
    • document.domain + iframe跨域 两个页面都通过js强制设置document.domain为基础主域,就实 现了同域
     var user='parent'
     document.domain='domain.com'
     // iframe 获取父元素
     window.parent.user
    
    • postMessage 跨域

weakMap

class MyTest{};
let my =new MyTest(); // 对象
let map = new WeakMap(); // key 只能是对象
let map2 = new Map();
map.set(my,1);
my = null; // 当你给一个变量设置为null的时候 不会马上回收,
map 运用的时候,不会被回收掉,weakMap引用的对象被设置null,后续会被清空。

compose函数

    function compose(...fns) {
      return function (...args) {
        const lastFn = fns.pop();
        const r = lastFn(...args);
        return fns.reduceRight((prev, current) => {
          return current(prev)
        }, r)
      }
    }

发布订阅模式

const util = require('util')

function EventEmitter() {
  this._event = {}
}

EventEmitter.prototype.on = function (eventname, callback) {
  if (!this._event) {
    this._event = {}
  }
  if (this._event[eventname]) {
    this._event[eventname].push(callback)
  } else {
    this._event[eventname] = [callback]
  }
}
EventEmitter.prototype.emit = function (eventname, ...arg) {
  this._event[eventname].forEach((callback) => callback(...arg))
}

EventEmitter.prototype.off = function (eventname, callback) {
  if (this._event && this._event[eventname]) {
    this._event[eventname] = this._event[eventname].filter(
      (x) => x !== callback && x.l !== callback
    )
  }
}

EventEmitter.prototype.once = function (eventname, callback) {
  const one = () => {
    callback()
    this.off(eventname, on)
  }
  one.l = callback
  this.on(eventname, one)
}

项目实战

后端一次丢给你10万条数据,前端你要怎么处理?

  1. 前端懒加载+分页
import React, { useRef } from 'react';
import { debounce } from 'lodash'
import './App.less';

function App() {
  const prevY = useRef(0)
  const poll = useRef();
  let arr = [];
  for (let i = 0; i < 100000; i++) {
    arr.push({
      name: `name${i}`,
      title: `title${i}`,
      text: `text${i}`,
      uid: `uid${i}`
    })
  }
  let curPage = 1;
  let pageSize = 16;
  const [list, setList] = React.useState([]);
  function scrollAndLoading() {
    // 判读用户向下滚动
    if (window.scrollY > prevY.current) {
      prevY.current = window.scrollY
      const pollTop = poll.current.getBoundingClientRect().top - 25;
      if (pollTop < window.innerHeight) {
        curPage++;
        setList(arr.slice(0, pageSize * curPage))
      }
    }
  }

  React.useEffect(() => {
    poll.current = document.querySelector('#poll');
    const getData = debounce(scrollAndLoading, 300);
    window.addEventListener('scroll', getData, false)
    setList(arr.slice(0, 16))
    return () => {
      window.removeEventListener('scroll', getData, false)
    }
  }, [])

Axios取消重复请求

import axios, { AxiosRequestConfig } from 'axios';
const pendingRequest = new Map();
function generateRegKey(config){
  const {method,url,params,data} = config;
  return [method,url,JSON.stringify(params),JSON.stringify(data)].join("&")
}
function addPendingRequest(config:AxiosRequestConfig){
  const requestKey = generateRegKey(config);
  config.cancelToken = new axios.CancelToken((cancel)=>{
    if(!pendingRequest.has(requestKey)){
      pendingRequest.set(requestKey,cancel)
    }
  })
}

function removePendingRequest(config){
   const requestKey = generateRegKey(config);
   if(pendingRequest.has(requestKey)){
     const cancelToken = pendingRequest.get(requestKey);
     cancelToken(requestKey);
     pendingRequest.delete(requestKey)
   }
}

axios.interceptors.request.use(function(config){
  removePendingRequest(config);
  addPendingRequest(config)
},(err)=>{
  return Promise.reject(err)
});

axios.interceptors.response.use((response)=>{
   removePendingRequest(response.config);
   return response;
},(error)=>{
  removePendingRequest(error.config ||{});
  if(axios.isCancel(error)){
     console.log('已取消重复请求'+ error.message)
  }else{
    console.log('进行异常处理')
  }
  return Promise.reject(error)
})

前端大文件分片上传&断点续传

  1. 文件根据内容生成hash值
self.importScripts('https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.2/spark-md5.js');
self.onmessage = (e) => {
  const { fileChunkList } = e.data;
  const spark = new self.SparkMD5.ArrayBuffer();
  console.log(spark, 'spark')
  let percentage = 0;
  let count = 0;
  const loadNext = (index) => {
    const reader = new FileReader();
    reader.readAsArrayBuffer(fileChunkList[index].file)
    reader.onload = (e) => {
      count++
      spark.append(e.target.result);
      if (count === fileChunkList.length) {
        self.postMessage({
          percentage: 100,
          hash: spark.end(),
        });
        self.close();
      } else {
        percentage += 100 / fileChunkList.length;
        self.postMessage({
          percentage
        });
        loadNext(count)
      }
    }
  }
  loadNext(0)
}
  1. vue示例相关代码
<template>
  <div class="hello">
    <input type="file" @change="handleFileChange" />
    <el-button @click="handleUpload">上传</el-button>
    <el-button @click="handleMerge">合并</el-button>
    <el-button @click="handleFast">秒传</el-button>
    <el-button @click="handlePause">暂停</el-button>
  </div>
</template>

<script>
const SIZE = 10 * 1024 * 1024; // 10MB切片
import request from "../api/request";
export default {
  name: "Home",
  props: {
    msg: String,
  },
  data: () => ({
    container: {
      file: null,
    },
    data: [],
    requestList: [],
  }),
  computed: {
    uploadPercentage() {
      if (!this.container.file || !this.data.length) return 0;
      const loaded = this.data
        .map((item) => item.size * item.percentage)
        .reduce((acc, cur) => acc + cur);
      console.log(
        parseInt((loaded / this.container.file.size).toFixed(2)),
        "loaded=="
      );
      return parseInt((loaded / this.container.file.size).toFixed(2));
    },
    Worker,
  },
  methods: {
    handlePause() {
      console.log("暂停");
      this.requestList.forEach((xhr) => {
        console.log(xhr, "xhr===");
        xhr?.abort();
      });
      this.requestList = [];
    },
    handleFileChange(e) {
      const [file] = e.target.files;
      Object.assign(this.$data, this.$options.data());
      this.container.file = file;
    },
    // 生成断点续传的hash
    calculateHash(fileChunkList) {
      return new Promise((resolve) => {
        this.container.worker = new Worker("/hash.js");
        this.container.worker.postMessage({ fileChunkList });
        this.container.worker.onmessage = (e) => {
          const { percentage, hash } = e.data;
          this.hashPercentage = percentage;
          if (hash) {
            resolve(hash);
          }
        };
      });
    },
    // 生成文件切片
    createFileChunk(file, size = SIZE) {
      const fileChunkList = [];
      let cur = 0;
      while (cur < file.size) {
        fileChunkList.push({
          file: file.slice(cur, cur + size), // 调用Blob上方法切割字节 返回Blob
        });
        cur += size;
      }
      return fileChunkList;
    },
    // 文件秒传:服务器上已上传相同文件对比hash 相同则直接提示成功
    async verifyUpload(filename, fileHash) {
      const result = await request({
        url: "http://localhost:3000/verify",
        headers: {
          "content-type": "application/json",
        },
        data: JSON.stringify({
          filename,
          fileHash,
        }),
      });
      console.log(result, "ssss===");
      // return JSON.parse(data);
    },
    //上传切片
    async uploadChunks() {
      const requestList = this.data
        .map(({ chunk, hash, index }) => {
          const formData = new FormData();
          formData.append("chunk", chunk);
          formData.append("hash", hash);
          formData.append("filename", this.container.file.name);
          return { formData, index };
        })
        .map(async ({ formData, index }) =>
          request({
            url: "http://localhost:3000/upload",
            data: formData,
            requestList: this.requestList,
            onProgress: this.createProgressHandler(this.data[index], index),
          })
        );
      await Promise.all(requestList); // 并发切片
      // 合并切片
      // await this.mergeRequest();
    },
    createProgressHandler(item, index) {
      return (e) => {
        item.percentage = parseInt(String((e.loaded / e.total) * 100));
        this.data.splice(index, 1, item);
      };
    },
    async handleMerge() {
      await this.mergeRequest();
    },
    async handleFast() {
      if (!this.container.file) return;
      // 获取切片列表
      const fileChunkList = this.createFileChunk(this.container.file);
      this.container.hash = await this.calculateHash(fileChunkList);
      // 文件秒传 看是否上传过
      const result = await this.verifyUpload(
        this.container.file.name,
        this.container.hash
      );

      console.log(result, "result");

      // if (!shouldUpload) {
      //   alert("上传成功");
      //   return;
      // }
    },
    async handleUpload() {
      if (!this.container.file) return;
      // 获取切片列表
      const fileChunkList = this.createFileChunk(this.container.file);
      this.container.hash = await this.calculateHash(fileChunkList);
      // 文件秒传 看是否上传过
      // const { shouldUpload } = await this.verifyUpload(
      //   this.container.file.name,
      //   this.container.hash
      // );

      // if (!shouldUpload) {
      //   alert("上传成功");
      //   return;
      // }

      this.data = fileChunkList.map(({ file }, index) => {
        return {
          fileHash: this.container.hash,
          percentage: 0,
          chunk: file,
          index,
          hash: this.container.hash + "-" + index, // 文件名 + 数组下标
        };
      });
      console.log("upLoad==");
      await this.uploadChunks();
    },
    async mergeRequest() {
      await request({
        url: "http://localhost:3000/merge",
        headers: {
          "content-type": "application/json",
        },
        data: JSON.stringify({
          filename: this.container.file.name,
          size: SIZE,
        }),
      });
    },
  },
};
</script>
  1. request 相关封装
const request = ({
  url,
  method = "post",
  data,
  headers = {},
  onProgress = e => e,
  requestList,
}) => {
  return new Promise((resolve) => {
    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = onProgress
    xhr.open(method, url);
    Object.keys(headers).forEach(key => {
      xhr.setRequestHeader(key, headers[key])
    })
    xhr.send(data);
    xhr.onload = (e) => {
      // 将请求成功的xhr 从列表中删除
      if (requestList) {
        const xhrIndex = requestList.findIndex(item => item === xhr)
        xhrIndex > -1 && requestList.splice(xhrIndex, 1);
      }
      resolve({
        data: e.target.resolve
      })
    };
    requestList?.push(xhr)
  })
}
export default request;

判断文件的上传类型

1.通过文件头信息判断文件类型 常见文件头部:

  • jpeg(jpg):FFDBFF;PNG(png):89504E47;gif:47494638
  • psd:38425053;pdf:255044462D312E
async function blobToString(blob) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = function () {
      const res = reader.result
        .split("")
        .map((v) => v.charCodeAt(v))
        .map((v) => v.toString(16).toUpperCase())
        .map((v) => v.padStart(2, "0"))
        .join(" ");
      resolve(res);
    };
    reader.readAsBinaryString(blob);
  });
}

async function isJpg(file) {
  const res = await blobToString(file.slice(0, 3))
  return res === 'FF DB FF'
}

async function isPng(file) {
  const res = await blobToString(file.slice(0, 4))
  return res === '89 50 4E 47'
}

async function isGif(file) {
  const res = await blobToString(file.slice(0, 4))
  return res === '47 49 46 38'
}

vue长列表虚拟滚动技术

虚拟滚动,根据容器可视区域的列表数量,监听用户滑动或滚动事件,动态截取长列表数据中的部分数据渲染到页面上,动态使用空白占位填充容器上下滚动区域内容,模拟实现原生滚动效果。

  1. VScroll插件
<template>
  <div
    class="scroll-container"
    ref="scrollConainer"
    @scroll.passive="handleScroll"
  >
    <div :style="blankFill">
      <div class="listItem" v-for="(item, index) in showDataList" :key="index">
        <slot :thisItem="item" />
      </div>
      <div v-if="isRequestStatus">
        <h2>{{ msg }}</h2>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "index",
  props: {
    msg: {
      default: () => "小二正在努力,请等待。。。",
      type: String,
    },
    // 记录单条数据的高度
    oneHeight: {
      default: () => 100,
      type: Number,
    },
    // 请求数据的url
    requestUrl: {
      default: () => "http://192.168.1.107:4000/data?num=",
      type: String,
    },
    // 请求数据的条数
    requestNum: {
      default: () => 10,
      type: Number,
    },
  },
  data() {
    return {
      allDataList: [],
      containSize: 0,
      startIndex: 0,
      isRequestStatus: true,
      currentScrollTop: 0,
    };
  },
  activated() {
    this.$nextTick(() => {
      this.$refs.scrollConainer.scrollTop = this.currentScrollTop;
    });
  },
  computed: {
    endIndex() {
      let endIndex = this.startIndex + this.containSize;
      if (!this.allDataList[endIndex]) {
        endIndex = this.allDataList.length - 1;
      }
      return endIndex;
    },
    showDataList() {
      const arr = this.allDataList.slice(this.startIndex, this.endIndex);
      return arr;
    },
    blankFill() {
      return {
        paddingTop: this.startIndex * this.oneHeight + "px",
        paddingBottom:
          (this.allDataList.length - this.endIndex) * this.oneHeight + "px",
      };
    },
  },
  methods: {
    getNewsData() {
      this.isRequestStatus = true;
      return this.$axios
        .get(`${this.requestUrl}${this.requestNum}`)
        .then((res) => {
          this.isRequestStatus = false;
          return res.data.list;
        })
        .catch(() => {
          this.isRequestStatus = false;
          return false;
        });
    },
    getContainerSize() {
      const offsetHeight = this.$refs.scrollConainer.offsetHeight;
      this.containSize = Math.floor(offsetHeight / this.oneHeight) + 2;
      console.log(offsetHeight, this.containSize, "containerSize");
    },
    handleScroll() {
      let fps = 30;
      let interval = 1000 / fps;
      let then = Date.now();
      requestAnimationFrame(() => {
        let now = Date.now();
        this.setDataStart();
        if (now - then >= interval) {
          then = now;
          requestAnimationFrame(arguments.callee);
        }
      });
    },
    async setDataStart() {
      this.currentScrollTop = this.$refs.scrollConainer.scrollTop;
      this.currentIndex = ~~(this.currentScrollTop / this.oneHeight);
      if (this.currentIndex === this.startIndex) return;
      this.startIndex = this.currentIndex;
      if (
        this.startIndex + this.containSize > this.allDataList.length - 1 &&
        !this.isRequestStatus
      ) {
        let newList = await this.getNewsData();
        if (!newList) return;
        this.allDataList = [...this.allDataList, ...newList];
      }
    },
  },
  async created() {
    const result = await this.getNewsData();
    if (!result) return;
    this.allDataList = result;
  },
  mounted() {
    this.getContainerSize();
    window.onresize = this.getContainerSize;
    window.onorientationchange = this.getContainerSize;
  },
};
</script>
<style  scoped>
.scroll-container {
  height: 100vh;
  overflow-y: scroll;
}
.title {
  font-size: 16px;
  font-weight: bold;
  text-align: left;
}
.listItem {
  height: 100px;
}
.itemContent {
  display: flex;
  justify-content: space-between;
}
</style>
  1. vue 中虚拟列表调用
<template>
  <div class="news-box">
    <VScroll
      :msg="msg"
      :oneHeight="oneHeight"
      :requestUrl="requestUrl"
      :requestNum="requestNum"
      v-slot:default="oneItem"
    >
      <router-link
        :to="{ path: '/article?id=' + oneItem.thisItem.id }"
        class="itemContent"
      >
        <div class="itemleft">
          <div class="title">{{ oneItem.thisItem.title }}</div>
          <div class="bottomContent">
            <div class="from">{{ oneItem.thisItem.from }}</div>
            <div class="date">{{ oneItem.thisItem.date }}</div>
          </div>
        </div>
        <div class="img">
          <img class="img2" :src="imgList[oneItem.thisItem.image]" alt="" />
        </div>
      </router-link>
    </VScroll>
  </div>
</template>
<script>
import imgList from "../imgs/index";
export default {
  name: "index",
  data() {
    return {
      imgList,
      msg: "小二正在努力,请耐心等待...",
      oneHeight: 100,
      requestUrl: "http://192.168.1.107:4000/data?num=",
      requestNum: 10,
    };
  },
  activated() {},
  computed: {},
  methods: {},
};
</script>
<style  scoped>
.news-box {
  height: 100%;
  /* padding-bottom: 50px; */
}

.title {
  font-size: 16px;
  font-weight: bold;
  text-align: left;
}
.listItem {
  height: 100px;
}
.itemContent {
  display: flex;
  justify-content: space-between;
}
.itemleft {
  width: 220px;
}
.img {
  width: 150px;
  height: 80px;
  background: pink;
  overflow: hidden;
}
.img2 {
  height: auto;
  max-width: 100%;
  max-height: 100%;
  vertical-align: bottom;
  object-fit: fill;
}
.bottomContent {
  margin-top: 10px;
  display: flex;
  justify-content: space-between;
}
.from {
  font-size: 12px;
  color: #999;
}
.date {
  font-size: 12px;
  color: #999;
}
</style>

下拉刷新

  /**
     * @param element 元素
     * @param callback 回调
    */
    function downRefresh(element, callback) {
      let startY;  // 开始下拉时候纵坐标
      let distance // 本次下拉距离
      let originalTop = element.offsetTop; // 刚开始元素距离顶部的高度
      let startTop;
      element.addEventListener('touchstart', function (event) {
       if(element.scrollTop <= 0){
        startTop = element.offsetTop;
        startY = event.touches[0].pageY; // 当前点击纵坐标
        element.addEventListener('touchmove', touchMove);
        element.addEventListener('touchend', touchEnd);
       }
        function touchMove(event) {
          let pageY = event.touches[0].pageY;
          if(pageY>startY){
            distance = pageY - startY //当前移动距离
            element.style.top = startTop + distance + 'px'
          }else{
           element.removeEventListener('touchmove', touchMove);
           element.removeEventListener('touchend', touchEnd);
          } 
        }

        function touchEnd(event) {
          element.removeEventListener('touchmove', touchMove);
          element.removeEventListener('touchend', touchEnd);
          if (distance > 30) {
            callback()
          }
          function _back() {
            let currentTop = element.offsetTop;
            if (currentTop - originalTop >= 1) {
              element.style.top = currentTop - 1 + 'px';
              requestAnimationFrame(_back);
            } else {
              element.style.top = originalTop + 'px'
            }
          }
          requestAnimationFrame(_back)
        }
      })
    }

计算刷新率(帧数)

 var then = Date.now();
    var count = 0;
    function nextTime() {
      requestAnimationFrame(function () {
        count++;
        if (count % 60 === 0) {
          var time = (Date.now() - then) / count;
          var ms = Math.round(time * 1000) / 1000;
          var fps = Math.round(1000 / ms)
          console.log(`count:${count}\t ${ms}:ms;fps:${fps}`)
        }
        nextTime()
      })
    }
    nextTime()

flat函数实现

 function flat(arr) {
      if (!arr instanceof Array) {
        return arr
      };
      let isDeep = arr.some(item => (item instanceof Array))
      if (!isDeep) return arr;
      let res = Array.prototype.concat.apply([], arr);
      res = flat(res);
      return res;
    }

深拷贝

// fn,undefined 会被过滤掉, date会被转成时间,reg:{}变成空对象
  let obj = {
      product: { name: "传感器", version: '123' },
      age: 12,
      fn: function () { return 1 },
      date: new Date(),
      reg: /\d/g,
      a: undefined,
      b: null
    }
    const copyObj = JSON.parse(JSON.stringify(obj));
   function deepClone(obj, hash = new WeakMap()) {
      // 判断是undefined 和null
      if (obj == null) return obj
      if (obj instanceof Date) return new Date(obj)
      if (obj instanceof RegExp) return new RegExp(obj);
      if (typeof obj !== "object") return obj;
      // 要不是数组,要不是对象
      if (hash.has(obj)) return hash.get(obj);
      let cloneObj = new obj.constructor;
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          cloneObj[key] = deepClone(obj[key], hash)
        }
      }
      return cloneObj;
    }

Object.create 实现

  function create(parentPropertype) {
      let Fn = function () { }
      Fn.prototype = parentPropertype;
      return new Fn();
    }

es6 class

    class Animal {
          constructor(name) {
            this.name = name;
            this.eat = "吃肉"
          }
          say() {
            console.log(this, 'this')
          }
     }
    let animal = new Animal();
    let say = animal.say;
    say(); // this is undefined

判断变量的类型

1. typeof 不能判断对象类型 [] ,{}
2. constructor 可以找到这个变量是通过谁构造出来的
3. instanceif 判断谁是谁的实例 __proto__
4. Object.prototype.toString.call() 缺陷就是不能细分谁是谁的实例

函数柯里化

 const currying = (fn, arr = []) => {
      let len = fn.length;
      return function (...arg) {
        arr = [...arr, ...arg];
        if (arr.length < len) {
          return currying(fn, arr)
        } else {
          return fn(...arr)
        }
      }
    }
 function isType(type,value) {
    return Object.prototype.toString.call(value) === `[object ${type}]`
    }

判断相等

function isObjectEqual(a, b) {
  const aProps = Object.getOwnPropertyNames(a)
  const bProps = Object.getOwnPropertyNames(b)
  if (aProps.length !== bProps.length) return false
  for (var i = 0; i < aProps.length; i++) {
    const propName = aProps[i]
    const propA = a[propName]
    const propB = b[propName]
    if (typeof propA === 'object') {
      return isObjectEqual(propA, propB)
    } else if (propA !== propB) {
      return false
    }
  }
  return true
}

aliplayer播放器

const playerCssUrl =
  "https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css";
const playerJsUrl = `https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/aliplayer-h5-min.js`;

export function loadJs(url: string) {
  return new Promise(function (resolve, reject) {
    let script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    script.onerror = reject;
    script.onload = resolve;
    document.head.appendChild(script);
  });
}

export function loadCss(href: string) {
  return new Promise(function (resolve, reject) {
    let link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = href;
    link.onload = resolve;
    link.onerror = reject;
    document.head.appendChild(link);
  });
}

export async function loadPlayer() {
  await loadCss(playerCssUrl);
  await loadJs(playerJsUrl);
}

export default class VideoPlayer {
  id: string;
  videoUrl: string;
  width: string;
  height: string;
  player: any;
  cover: string;
  constructor(props: any) {
    this.id = props.id;
    this.cover = props.cover;
    this.videoUrl = props.videoUrl;
    this.width = props.width || "100%";
    this.height = props.height || "450px";
    this.player = null;
    this.init();
  }
  async init() {
    await loadPlayer();
    this.player = new Aliplayer({
      id: this.id,
      cover: this.cover,
      source: this.videoUrl,
      width: this.width,
      height: this.height,
      playsinline: true,
      isLive: false, // 直播
      replay: false, // 循环直播
      preload: true, // 播放器字段加载
      autoplay: true, // 自动播放
      diagnosisButtonVisible: false, //是否显示检测按钮
      keyShortCuts: true, //是否启用快捷键
      keyFastForwardStep: 5, //快进快退的时间长度
      controlBarVisibility: "hover", // 控制条的显示方式:鼠标悬停
      useH5Prism: true, //启用H5播放器
    });
  }
  pause() {
    this.player.pause();
  }
  seek(time: number) {
    this.player.seek(time);
  }
  getCurrentTime() {
    this.player.getCurrentTime();
  }
  getStatus() {
    this.player.getStatus();
  }
  mute() {
    this.player.mute();
  }
  replay() {
    this.player.replay();
  }
}