坑在这,速埋

简介: 坑在这,速埋

前言

开发过程中,总免不了遇坑,遇坑不要紧,填坑是紧要. 文中不会对问题有过多的解释,这里会先列出对应的解决方案,如果要解释其中缘由必定篇幅过长,且当这是一个简单的填坑指南.

填坑

Number.prototype.toFixed() 方法对保留对应小数位不正确问题?

原因可看这篇文章 toFixed四舍五入的不准确性.

注意:js 中的 Number 类型计算时存在精度缺失问题Number 类型使用 IEEE 754 格式表示 整数浮点值(在某些语言中也叫双精度值),通常在 js 中通过计算正常都会使用十进制,但实际上计算过程是 十进制 --> 转成二进制 --> 二进制进行计算 --> 转成十进制,这时候就会有精度问题.

解决方法既然 Number.prototype.toFixed() 方法既然存在四舍五入问题,那么最简单的就是重写这个方法:

// 重写方法
      Number.prototype.toFixed = function (d) {
        var s = this + '' // number -> string
        if (!d) d = 0 // 参数默认值
        if (s.indexOf('.') == -1) s += '.' // 格式化数字字符串,如 '100' -> '100.')
        s += new Array(d + 1).join('0') //若 d = 2, 则 '100.' -> '100.00', '100.123' -> '100.12300'
        if (
          new RegExp('^(-|\\+)?(\\d+(\\.\\d{0,' + (d + 1) + '})?)\\d*$').test(s)
        ) {
          var s = '0' + RegExp.$2, // $2 为匹配到完整数字,如 '100.666' -> '0100.666'
            pm = RegExp.$1, // $1 正负符号
            a = RegExp.$3.length, // $3 获取包含 . 即后面需要 d + 1 长度的数值
            b = true
          if (a == d + 2) {
            // a 长度包含了 . 和 对应 d 长度的后一位
            a = s.match(/\d/g) // 获取数字部分字符串
            // 判断最后完整数字字符最后一位是否 > 4, 即是否需要四舍五入
            if (parseInt(a[a.length - 1]) > 4) {
              for (var i = a.length - 2; i >= 0; i--) {
                // 倒序遍历
                a[i] = parseInt(a[i]) + 1 // 进一
                if (a[i] == 10) {
                  // 满 10 进制,本位置 0 ,前一位进一
                  a[i] = 0
                  b = i != 1 // 当前遍历是到第二位时,b = false,第一位为 0
                } else break // 没满 10 直接跳出循环
              }
            }
            // 只获取小数点前后的数字,通过 . 拼接
            s = a
              .join('')
              .replace(new RegExp('(\\d+)(\\d{' + d + '})\\d$'), '$1.$2')
          }
          if (b) s = s.substr(1) // 删除首位的 0
          return (pm + s).replace(/\.$/, '') // 以 . 结尾替换成空字符
        }
        // 不符合正则,直接转 string 返回
        return this + ''
      }
复制代码

window.open() 方法被浏览器拦截?

原因:浏览器出于安全考虑,不允许非用户操作情况下打开新的页面.示例代码

<button onclick="getUrlAndOpen()">点我</button>
<script>
  async function getUrlAndOpen() {
    let res = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('http://www.baidu.com')
      }, 500)
    })
    console.log("异步请求结果:", res)
    window.open(res)
  }
</script>
复制代码

image.png

解决方法:在调用异步请求之前先通过 window.open() 打开页面,获取这个窗口的引用,等待异步响应之后去设置新开窗口的 url

示例代码

<button onclick="getUrlAndOpen()">点我</button>
<script>
  async function getUrlAndOpen() {
    let newWindowReference = window.open();
    let res = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('http://www.baidu.com')
      }, 500)
    })
    console.log("异步请求结果:", res)
    newWindowReference.location.href = res;
  }
</script>
复制代码

<img draggable="true" /> 时,在浏览器中拖拽图片时会自动下载或新开页面预览?

直接通过效果来解释:

<img id="img1" src="./img/xm.jpg" alt="xm">
复制代码

image.png


image.png

如上图所示,在不同浏览器下,对于图片、链接等元素的 draggable 属性的默认值是不同的,这就导致了它们在浏览器上进行拖拽后产生了差异,下面就以 谷歌浏览器QQ 浏览器进行对比:

  • QQ 浏览器draggable = true 时拖拽图片,鼠标下会有图片缩影,放开鼠标后,浏览器会打开新页面并访问对应资源的 url 地址
  • image.png
  • Google 浏览器draggable = true || false 都不会有上述的行为产生

问题描述QQ 浏览器中对图片进行拖拽时,若 img 标签中是预览地址,则打开新页面后会访问这个预览地址,若当前 img 标签中是下载地址,则会询问是否需要下载这个图片资源.

解决方法

  • 给普通的 imga 标签设置 draggable="false",禁止拖拽行为产生
  • 若当前 img 元素需要进行拖拽,即  draggable="true",此时只能通过 ondragover 事件处理程序中使用 ev.preventDefault(); 阻止浏览器的默认行为

下面是示例代码:

<div class="box" ondrop="drop(event)" ondragover="dragover(event)"></div>
  <div class="box" ondrop="drop(event)" ondragover="dragover(event)">
    <img id="img1" src="./img/xm.jpg" ondragstart="dragstart(event)" draggable="true" alt="xm">
  </div>
  <script>
    function dragover(ev) {
      ev.preventDefault();
    }
    function dragstart(ev) {
      ev.dataTransfer.setData("text", ev.target.id);
    }
    function drop(ev) {
      var data = ev.dataTransfer.getData("text");
      ev.target.appendChild(document.getElementById(data));
    }
  </script>
复制代码


image.png

image.png


项目地址支持 httphttps,怎么保证项目中的请求和用户访问页面时的协议保持一致?

问题描述: 根据用户在浏览器地址栏中访问时,使用的协议决定项目中静态资源和请求的协议要一致,如果不一致会导致请求产生跨域问题.解决方法: 要动态修改协议,还得考虑项目中不同位置的请求和外链:

  • 对于 <script src="http://xxx"> 的形式,改写为 <script src="//xxx"> 让浏览器自己去匹配当前协议
  • 对于 api 接口中的协议
  • 若使用的是 axios 则指定 baseURL 为空字符 '' 或者不进行设置即可
// 创建axios实例
const service = axios.create({
baseURL: '', // api的base_url
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: reqConf.responseType,
});
复制代码
  • 若项目中的 api 是使用完整路径的形式,那么只能通过 js 进行动态替换协议头

在 Google 浏览器使用 Google 翻译插件翻译页面导致原有页面文字显示异常?

解决方法方法就是通过 head 标签禁止 Google 翻译插件翻译页面文字:

<html translate="no" lang="en">
  <head>
    <meta charset="utf-8" />
    ...
    <meta content="telephone=no" name="format-detection" />
    <meta name="google" content="notranslate" />
    <meta http-equiv="Expires" content="0" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Cache-control" content="no-cache" />
    <meta http-equiv="Cache" content="no-cache" />
    <link rel="icon" href="./favicon.ico" />
    <title>Tencent</title>
  </head>
复制代码

什么时候需要使用 HTML crossorigin 属性?

问题描述:由于最近在做的项目需要显示页面加载的进度条,所以使用了下面的代码引入这个库:

<script src="https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js" crossorigin="anonymous"></script>
复制代码

其中的 crossorigin="anonymous" 属性是不是很显眼?

事情起因就是项目中进行前端架监控,即 aegis.js,它存放在公司的另一个 cdn 服务下,并且对应的开发人员建议不要使用 npm 包,于是就通过 script 进行全局引入。 根据往常的开发,就正常的复制了上面的 nprogress.min.jsscript 标签,并将其 src 属性直接进行了替换,于是报了跨域 的错误.

解决方案:正常情况下

其实没有必要使用这个属性,所以以上问题直接删除掉这个 crossorigin 就好了.

特殊情况下

也就是当我们需要收集或显示外部第三方包中的具体错误时,就需要使用这个 crossorigin HTML 属性,正常情况下,如果外部的第三方包中出现异常时,在全局监听的 window.addEventListener('error', function(msg, url, lineno, colno, error) {xxx}) 中得到的错误信息是不详细的,比如可能只会显示 script error

通过使用 crossorigin 属性可以使跨域 js 暴露出跟同域 js 同样的报错信息。但资源服务器必须返回一个 Access-Control-Allow-Origin 的 header,否则资源无法访问。

拓展

crossorigin 属性的作用:

  • crossorigin会让浏览器启用CORS访问检查,检查 http 响应头的 Access-Control-Allow-Origin
  • 对于传统 script 需要跨域获取的 js 资源,控制暴露出其报错的详细信息
  • 对于module script,控制用于跨域请求的凭据模式

crossorigin的属性值可以是anonymoususe-credentials,如果没有属性值或者非法属性值,会被浏览器默认做anonymous.

@Component({...}) 和 keep-alive 一起使用出现的 bug?

问题描述:由于项目中主框架使用的 vue-property-decorator + vue2 + typeScript ,因此在定义组件的时候需要使用 @Component 进行修饰,主要也是为了让 vue2 更好的支持 TS.

@Component({
  ...
})
export default class MyComponent extends Vue {...}
复制代码

某天的某需求导致某同事需要使用 keep-alive + router-view 实现缓存页面的功能,但是在这个被 keep-alive 包裹下的 router-view 中的其他几个页面并不需要被缓存,因此我们使用了 keep-alive 上的 exclude 排除掉对应的其他不需要被缓存的组件.

<keep-alive :exclude="['MyComponent', ...]">
    <router-view></router-view>
</keep-alive>
复制代码

开发环境exclude 属性按预期达到最终效果

测试环境 & 生成环境exclude 不生效,该 router-view 对应的所有页面被缓存

问题原因:首先 exclude 中使用的是在定义组件时 class 关键字后面的 MyComponent,由于开发环境(本地环境)并不会将代码按生产模式进行打包(比如压缩、混淆代码等),所以这个 MyComponent 是不会变化的,因此和 exclude 中的字符串是对应的,但是按生产模式进行打包以后,类似于 MyComponent 这样的命名会被构建工具进行代码压缩、代码混淆等操作,简单来说就是这个 MyComponent 真正打包后就一定叫 MyComponent 可能会变成类似 x 之类的形式。

此时,组件默认名称不再是原来的 MyComponent,因此也就会和 exclude 中的字符名称对应不上,导致页面被缓存.

解决方案:既然默认定义的组件名称会被改变,那么我们就定义一个不会被改变的组件名称,也就是定义组件时定义 name: xxx,即:

@Component({
    name: 'MyComponent',
    ...
})
export default class MyComponent extends Vue {...}
复制代码

【移动端】在真机上 Calendar 日历组件展示时为空白内容?

问题描述:需要在 H5 移动端实现日期违反选择,于是使用了 vant 中的日历组件,但在真机上在展示日期内容时可能出现空白(偶现),必须要手动滚动才会展示原本内容.PS:出现空白可能是某些场景下,设置的默认日期格式有误,导致组件报错,因此复现问题时最好确认是否控制台是否有异常信息。

image.png

解决方案:目前暂时没有发现原因,但按测试描述:需要手动滑动一下才能展示内容,那么解决方法就是打开日历组件后,通过 js 模拟滚动的动作,即通过设置 scrollTop 的值即可.

this.$nextTick(() => {
    const dom = document.querySelector('.van-calendar__body')
    // 模拟滑动,避免白屏
    if (dom) dom.scrollTop = dom.scrollTop - 4
})
复制代码

【移动端】IOS/Android 下软键盘弹出并将页面顶上去,收起时无法复原?

问题描述:直接上图(网图)

image.png

解决方案:但是在调试过程中发现当点击其他输入框时,整个视图自动复原了.

于是,解决方法就是在页面上顶部放置一个隐藏的 input 标签,然后当其他视图内的输入框失焦时,让这个隐藏的 input 聚焦.

【移动端】在真机上 sessionStorage 无法获取对应数据内容?

最近在开发移动端项目时,需要用到的本地存储的地方不少,都是一些只要记住当前打开窗口的用户数据就行,所以我选择用的 sessionStorage(关闭当前窗口或标签可会被删除)

使用场景:

A.html 页面需要记录一条数据: {a:1, b:2}

sessionStorage.setItem("data","{a:1, b:2}");
复制代码

B.html 页面取出使用:

sessionStorage.getItem("data"); // 获取结果为 null
复制代码

问题描述:

如果项目不是单页面复应用,AB 是两个 html 文件,需要跳转 href

  • 发现有些 Andiron 系统的浏览器在 B 页获取是到的结果是 null (如:vivo 手机自带的世界之窗浏览器)
  • 直接在 微信 中访问对应页面时,由于拉取微信卡包需要打开一个新的 webview ,选择数据并通过 sessionStorage 保存数据,接着返回上一个页面时,无法获取保存的数据

产生原因:

其实这并不是 某些手机浏览器微信 不支持 sessionStorage,因为仍然可以获取到 sessionStorage 对象.

其实,是因为 sessionStorage 是基于当前窗口的会话级的数据存储,移动端浏览器微信 中在跳转新页面的时候,可能打开的是一个新的 webView,这就相当于我们在浏览器一个新窗口中进行的存储,是没办法在之前的窗口中读取的

解决方案:可以直接使用 localStorage 进行代替,但是要在离开页面时,是否需要清空本次的存储内容,因为 sessionStorage 本身是支持自动清除的,但 localStorage 是持久存储,需要手动清除数据.

Safari 浏览器中 antd@2.12.1 的表单出现样式错乱?

问题描述:

Safari 16.0/16.1 版本中出现如下布局:

image.png

产生原因:

Safari 浏览器 中格式错乱是因为给 label 设置了 text-align-last: justifySafari 16.0/16.1 版本对这个属性的支持有问题(16.2 不清楚,16.3 正常),会让 display: inline-block 的元素的宽度变成 block 的宽度。

image.png

解决方案:

给这些错乱的 label 添加一下 固定宽度 即可,不过需要注意的是项目涉及不同语言环境,所以需要特别处理一下。

未完待续...


目录
相关文章
|
6月前
Mybatis+mysql动态分页查询数据案例——条件类(HouseCondition)
Mybatis+mysql动态分页查询数据案例——条件类(HouseCondition)
|
6月前
|
JavaScript 前端开发
【vue】iview如何把input输入框和点击输入框之后的边框去掉
【vue】iview如何把input输入框和点击输入框之后的边框去掉
139 0
|
5月前
|
JSON 数据处理 数据安全/隐私保护
Ktor库的高级用法:代理服务器与JSON处理
Ktor库的高级用法:代理服务器与JSON处理
|
6月前
|
JavaScript Go
VUE3+vite项目中动态引入组件和异步组件
VUE3+vite项目中动态引入组件和异步组件
791 1
|
6月前
Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associate
Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associate
182 2
|
6月前
【UI】 elementui input输入框自动获取失去焦点
【UI】 elementui input输入框自动获取失去焦点
503 1
|
6月前
equals方法中变量在前和在后的区别
equals方法中变量在前和在后的区别
113 0
阿里云 Aliplayer高级功能介绍
Aliplayer除了一些基本功能,还有一些高级的功能,可能需要云端配合才可以使用,或者播放器本身需要做更多的配置,希望写一些文件介绍如何使用和介绍一下简单的实现原来,让用户了解这些功能,更好的使用播放器,文章不仅介绍内置的功能,还会包含通过插件写的其他功能。
37945 0
|
小程序
微信小程序 tdesign图片上传组件 上传后端
微信小程序 tdesign图片上传组件 上传后端
423 0
|
JavaScript 前端开发
VSCode .vue 文件 html css 无智能提示
VSCode .vue 文件 html css 无智能提示
896 0