项目总结-统一认证平台


项目总结-认证平台

遇到的问题及解决

PDF展示

怎样实现

pdf-vue

​ 使用很简单,直接看教程就可以了,但在使用的过程中遇到了几个问题:

​ 1:pdf存在多页的加载

<pdf-vue
            v-for="i in pageCount"
            :key="i"
            :src="src"
            :page="i"
            style="display: inline-block; width: 25%"
>
</pdf>

​ 通过for循环来加载多页,虽然可以解决多页展示的问题,但是如果你的pdf存N页的话,就会发送N个请求,等所有请求结束后才会展示pdf内容,网速不好、pdf过大….都会导致加载慢,用户体验不好。其实并不是一开始就加载所有的pdf,看多少就加载多少,于是就有了下面的代码

<pdf-vue 
        v-for="i in copyPageCount" 
    :key="i" 
    :page="i" 
    :src="src" 
        style="display: inline-block; width: 25%"
>
</pdf-vue>

​ 就改了一个变量就这么神奇吗?当然不是,你以为我就只?少侠看下面

listenScroll () {
      this.dialogBody = document.querySelector('.dialog-confirm .el-dialog__wrapper .el-dialog .el-dialog__body')
      this.dialogBody.scrollTop = 0
      if (this.dialogBody) {
        this.dialogBody.addEventListener('scroll', () => {
          if (!this.loading && (this.dialogBody.scrollHeight - this.dialogBody.scrollTop -                          this.dialogBody.clientHeight < 10)) {
            this.loading = true
            this.copyPageCount = this.copyPageCount + 1 > this.pageCount ? this.pageCount : this.copyPageCount + 1
          }
       })
    }
},

​ 原理是这样的,copyPageCount是一个假象,它并不是所有的pdf页数,初始值为1,当用户向下查看的是的,由于监听了滚动,会自动增加copyPageCount的值,copyPageCount变化了,pdf也会相应的加载。首次加载只加载了第一页,速度就快多了。

​ 2:IE上的兼容

​ 当在IE上打开页面的时候,发现是白屏,并且报错语法不兼容(来自pdfjs-dist),虽然在vue.config.js里面加了 transpileDependencies: ['pdfjs-dist'],,虽然可以正常的打开页面,但是不能正常显示pdf,而且打包变得巨慢,所以需要在IE上使用的话,建议放弃。

说了这么多坏话,我们来夸一下它:

​ 1:使用方便(相对于pdfjs)

​ 2:展示效果更好,页面的缩放里面的内容也会相应的变化

pdfjs

​ 在使用之前最好看一下在线的demo看看能不能满足你的需求,首先需要安装:npm install pdfjs-dist,如果有莫名其奥妙的报错,建议安装不一样的版本在尝试(2.2.228 or 2.3.200)。使用的话,需要先建一个pdf.vue的文件封装




​ 在需要展示pdf的地方引用就好。但在使用的过程中还是遇到的问题,上图:

​ 两侧的留白太多,不美观。机智的我在官方的demo上看到了这个:

​ 如果选择适合页宽不就完美了吗?于是去找了下源码,发现了惊天大秘密,其实就是改变scale缩放来实现需要的效果。

源码,适配IE

​ 效果如下:

​ 但还是存在些许优化,浏览器缩放的时候不能实现内容的缩放,相应的方法是监听窗口变化,改变scale值,相信你一定会写~。pdf.js相对pdf-vue在多页pdf上使用比较友好,不需要自己额外写方法实现,IE上也可以使用,综合体验更胜一筹。

跨域

​ 如果存在pdf跨域展示的问题,需要后端的配合,允许跨域。

总结

​ 如果仅仅是展示pdf,为了获取更好的体验,可以考虑将pdf转成图片返回给前端进行展示,这样兼容和体验都不错。

文件下载

Vue.directive('down', {
  update: (el, binding) => {
    el.style.cssText = 'cursor: pointer;color:write;'
    el.addEventListener('click', () => {
      const link = document.createElement('a')
      const url = binding.value
      if (!url) return
      // 这里是将url转成blob地址,
      fetch(url).then(res => res.blob()).then(blob => { // 将链接地址字符内容转变成blob地址
        link.href = URL.createObjectURL(blob)
        link.download = ''
        document.body.appendChild(link)
        link.click()
      })
    })
  }
})

​ 最开始是通过vue directive实现的但是IE不兼容,会直接打开需要下载的pdf,于是写了两套逻辑。

​ 非IE,直接调用downloadFile这个方法就可以了,例如:downloadFile(this.pdfUrl, '授权书.pdf')

function downloadBlob (url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open("GET", url)
    xhr.responseType = "blob"

    xhr.onload = function () {
      resolve(xhr.response)
    }
    xhr.onerror = function () {
      reject(new Error("Download failed."))
    }
    xhr.send()
  })
}
export function downloadFile (url, fileName = "") {
  return downloadBlob(url, fileName)
    .then(resp => {
      if (resp.blob) {
        return resp.blob()
      }
      return new Blob([resp])
    })
    .then(blob => URL.createObjectURL(blob))
    // eslint-disable-next-line no-shadow
    .then(url => {
      // eslint-disable-next-line no-use-before-define
      downloadURL(url, fileName)
      URL.revokeObjectURL(url)
    })
    .catch(err => {
      throw new Error(err.message)
    })
}

export function downloadURL (url, name = "") {
  const link = document.createElement("a")
  link.download = name
  link.href = url
  if ("download" in document.createElement("a")) {
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  } else {
    // 对不支持download进行兼容
    // eslint-disable-next-line no-use-before-define
    click(link, (link.target = "_blank"))
  }
}


function click (node) {
  try {
    node.dispatchEvent(new MouseEvent("click"))
  } catch (e) {
    const evt = document.createEvent("MouseEvents")
    evt.initMouseEvent(
      "click",
      true,
      true,
      window,
      0,
      0,
      0,
      80,
      20,
      false,
      false,
      false,
      false,
      0,
      null
    )
    node.dispatchEvent(evt)
  }
}

​ IE版本,没错就这么简单,点击下载会询问你打开/保存

<a :href="pdfUrl" class="down-a" v-else>下载《授权书》</a>

​ 只有为什么没有用vue directive的原因,我也忘了。。。。

文件上传

​ 没错还是IE的问题,使用的是vue directive,但是在IE上没有上传文件成功。

Vue.directive('uploadImg', {
  inserted: (el, binding) => {
    // eslint-disable-next-line no-param-reassign
    el.style.cssText = 'cursor: pointer;color:write;'
    el.addEventListener('click', () => {
      const upload = document.createElement('input')
      const uploadResult = binding.value
      upload.style.display = 'none'
      upload.type = 'file'
      upload.accept = 'image/png, image/jpeg'
      upload.click()
      if (isIE()) {
        setTimeout(() => {
          if (upload.files[0] !== null) {
            toBase64(upload.files[0]).then(res => {
              uploadResult(res.replace(/^data:image\/\w+;base64,/, ""), upload.files[0].type.replace("image/", ""))
            })
          }
        }, 0)
      } else {
        upload.addEventListener('change', () => {
          toBase64(upload.files[0]).then(res => {
            uploadResult(res.replace(/^data:image\/\w+;base64,/, ""), upload.files[0].type.replace("image/", ""))
          })
        })
      }
    })
  }
})

​ 这段代码是为了兼容IE设计的,解决change事件不响应,利用的是IE的一个特点:IE在关闭对话框之前都会挂起一个timeout

setTimeout(() => {
          if (upload.files[0] !== null) {
            toBase64(upload.files[0]).then(res => {
              uploadResult(res.replace(/^data:image\/\w+;base64,/, ""), upload.files[0].type.replace("image/", ""))
            })
          }
        }, 0)

前端视频压缩

​ 使用的是ffmpeg,DEMO代码如下:








​ 遇到的问题是在使用的过程中还需要下载20多兆的资源,导致体验不好,当然你也可以把这些资源放到本地or自己的服务器。依赖资源

Vue代理

​ 开发使用的Vue版本是2.6.11,需要代理的话,需要在vue.config.js里面配置:

 configureWebpack: {
    devServer: {
      proxy: {
        '/apis': {    // 将www.example.com映射为/apis
          target: 'xxx',  // 接口域名
          secure: true,  // 如果是https接口,需要配置这个参数
          changeOrigin: true,  // 是否跨域
          pathRewrite: {
            '^/apis': ''   // 需要rewrite的,
          }
        }
      }
    },
  },

​ 后面就直接用/apis就可以直接代替target

流水线处理

​ 用户的认证分为好几个步骤,每个步骤里面还包含子步骤,每个子步骤之间还可以相互跳转,用户可以从上次中断步骤开始操作,我可以从接口获取用户当前操作步骤以及整个需要经历的步骤,最开始我是想通过接口返回给我的参数把完整的流水线算出来,但是步骤间的相互跳转不好处理,然后想到我只算出主流水线,其他的细支小流我不处理,细支小流的处理放到每个主流水线的步骤中处理。

代码健壮性

​ 这次写项目中,对于异常操作的处理不好,下次需要加强。

设计模式

发布订阅模式

​ 有一个功能是通过Iframe(一个对话框)嵌入到系统的,有一个功能点需要用到接口轮询查询状态,但是用户直接关闭弹框的话,轮询还是会一直存在的,对于一个有着代码洁癖的我来说,肯定是不可以接受的,于是想到通过发布订阅模式来解决这个问题。对于轮询的操作发布一个取消轮询的时间,对于那些无操作的操作订阅这个事件,这样就解决了这个问题,在Vue中需要将这些事件挂载到同一个对象中, 。

单例模式

​ 需求需要需要写一个toast提示,但是最初的一版,存在一个问题,如果提示过于频繁会叠加在一起,就像下面的效果

​ 于是我动用我灵泛的大脑各种搜索,用单例模式解决了这个问题,下图是演示效果 :

贴心的我已经我大家准备好了NPM包,一起欢快的使用吧!

思考

组件封装

​ 这次写代码自我感觉技术上提升了,但是对于数据与视图之间的关系把握不好,组件之间的高聚合 低耦合处理不太好,设计模式需要加强练习。

接口

​ 用户之前的操作会保存一些信息下来,以备后续的操作,但是在项目中,所有的基础信息全部放到一个接口中返回,个人认为不太友好,最好是按模块、按功能返回这些信息。

不足

​ 正则表达式:需要加强学习,现在基本上不会写正则,全靠搜索引擎。

这次使用的脚手架

好用的话点了小星星


文章作者: 木叶勇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 木叶勇 !
  目录