一些前端开发中容易遇到的坑

最近项目事情很多,开发过程中也碰到了很多值得记录的问题,今天就早点来公司,把最近一些问题和经验总结一下,以便以后进行参考。

JS数值精度丢失

这其实是一个众所周知的问题,但他本身并不是js的bug,js采用的是IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。所以这个BUG并不是只有js有,只要是采用了这个规范的语言都会有这个问题。
JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。所以当数值大于该值时就会出现精度丢失。在项目中后端传给前端的ID是数值并且超过了最大数值,最后导致了精度的丢失,当时也是找了很久才发现是这个原因,对于这个问题前端并没有很好的解决办法,只能是由后端将数值转为字符串传给前端解决问题。

async的使用

这是ES2017标准中引入的一个函数,其实并不是很新的东西,已经很多人在使用了,但是我一直还停留在使用promise的阶段,这次项目中碰见了需要加载很多图片,然后进行压缩的情况,想起来用async应该会很方便,然后就用上了,代码确实简介了不少,增强了代码的可读性,比起回调和promise又更加方便了一点。
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err);
  });

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

await命令只能用在async函数之中,如果用在普通函数,就会报错。也就是说不能用在foreach函数中,可以用for循环代替。

async function dbFuc(db) {
  let docs = [{}, {}, {}];

  // 报错
  docs.forEach(function (doc) {
    await db.post(doc);
  });
}
//正确写法
async function dbFuc(db) {
  let docs = [{}, {}, {}];

  for (let doc of docs) {
    await db.post(doc);
  }
}

用上述的写法发送请求所有远程操作都是继发。只有前一个 URL 返回结果,才会去读取下一个 URL,这样做效率很差,非常浪费时间。我们需要的是并发发出远程请求。项目里需要请求上百张图片,然后对返回的图片压缩成zip包,之前就是用上述的方法进行压缩,效率真的很差,后面看到了阮一峰老师的写法,顿时感觉大神就是不一样。

async function compressionImage(imgUrlArray, id) {
    const zip = new JSZip()

    //并发发出远程请求。
    const result = imgUrlArray.map(async url => {
      const response = await props.getImageStream(url.material_path)
      return { url: url.material_path, response }
    })

    //按顺序输出。
    for (const elementPromise of result) {
      const temp = await elementPromise
      zip.file(temp.url, temp.response)
    }

    const content = await zip.generateAsync({ type: 'blob' })
    let temp = formData
    temp[id] = content
    setFormData(temp)
  }

react清空state

这个其实算不上什么难点和问题,当你在请求中使用setState时,由于请求是异步的,所以在你退出组件的时候可能请求还没有发完,这时候就有可能还会调用setState,所以需要在退出组件的时候清空state,之前react用的是class的写法,现在已经用react hook写了,在react hook中清空的写法与之前有些不同:

  useEffect(() => {
    if (!props.hideEdit) {
      computerMaterialSize(dataSet.materials || [])
    }
    
    return () => {
      setMaterialsSize = () => {
        return
      }
    }
  }, [])

这个写法其实并不是很好,如果你有很多个setState的话,你就得一个个return,可能是现在用hook的人并不是很多,所以也没有看到更好的写法,如果有的话,还请指点一下。

react hook高阶组件

这次的项目中用了全新的react hook,也尝试了用react hook来写高阶组件。

import React, { useEffect, useState } from 'react'
import { getfilesize } from '../../util/common'

//后台给出的每个图片url中有图片的大小信息,对每个组的数据大小进行计算,判断大小是否符合要求
export default function useSetsIsValid(modelData) {
  const [allowEnd, setAllowEnd] = useState(false)
  const [allSetsSize, setAllSetsSize] = useState(0)
  const [everySetSize, setEverySetSize] = useState({})

  useEffect(() => {
    isAchieveTenImage()
    computerAllImageSize()
    return () => {
      return
    }
  }, [modelData])

  function isAchieveTenImage() {
    if (modelData.data_sets) {
      setAllowEnd(false)
      for (const set of modelData.data_sets) {
        if (!set.materials || set.materials.length < 10) {
          setAllowEnd(true)
          break
        }
      }
    } else {
      setAllowEnd(true)
    }
  }

  function computerAllImageSize() {
    if (modelData.data_sets) {
      const dataSets = modelData.data_sets
      let tempSize = 0
      let tempSingleSizeArr = {}
      dataSets.map(set => {
        let tempSingleSize = 0
        set.materials.map(material => {
          tempSize = tempSize + material.file_size
          tempSingleSize = tempSingleSize + material.file_size
        })
        // tempSingleSizeArr.push({ size: tempSingleSize, id: set.id })
        tempSingleSizeArr[set.id] = tempSingleSize
      })
      setAllSetsSize(getfilesize(tempSize))
      setEverySetSize(tempSingleSizeArr)
    }
  }
  //返回结果
  return [allowEnd, allSetsSize, everySetSize]


//在另一个组件中引用
 const [allowEnd, allSetsSize, everySetSize] = useSetsIsValid(modelData)
}

首先最直观的感受就是相比于react class中高阶组件的写法要简洁了很多,特别是当你需要从一个已经写完的组件中提炼出能够复用的逻辑,将它写成可复用的高阶组件时,提炼的成本很小,你只需要简单的将需要复用的逻辑复制粘贴就好了。其次就是引用的时候也不需要像以前那样层层的嵌套,看起来明显更加的简洁和舒服。有一点需要注意的是,高阶组件的命名必须要以use开头,否则react不会去实时的监控这个高阶组件的状态的变化。

css object-fit

这是一个可以应用于img和video标签的属性,拥有以下的设置项:
contain
被替换的内容将被缩放,以在填充元素的内容框时保持其宽高比。 整个对象在填充盒子的同时保留其长宽比,因此如果宽高比与框的宽高比不匹配,该对象将被添加“黑边”。
cover
被替换的内容在保持其宽高比的同时填充元素的整个内容框。如果对象的宽高比与内容框不相匹配,该对象将被剪裁以适应内容框。
fill
被替换的内容正好填充元素的内容框。整个对象将完全填充此框。如果对象的宽高比与内容框不相匹配,那么该对象将被拉伸以适应内容框。
none
被替换的内容将保持其原有的尺寸。
scale-down
内容的尺寸与 none 或 contain 中的一个相同,取决于它们两个之间谁得到的对象尺寸会更小一些。
这个属性在IE浏览器中不支持,其他主流浏览器是支持的,contain属性可以在保持图像不被拉伸的情况下保持原有比例展示。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值