JSNeedAttention

JS Attention

push 返回的是数组增加后的长度!!!

对象名可变

setFieldsValue({ [`bank${index}`]: val });

判断是否空对象

Object.keys(obj);
JSON.stringify(obj) !== "{}";

还有

$.isEmptyObject(data2);

Object.getOwnPropertyNames(data3); // 和Object.keys类似

关闭标签替换背景图

注意这里的background-size属性是cover,也就是让背景图撑满的关键属性。

  .exit-btn {
    position: fixed;
    right: 30px;
    top: 30px;
    color: white;
        background-image: url(../../../../../assets/closeNormal@2x.png);

    &:hover {

      background-image: url(../../../../../assets/closeHover@2x.png);

    }

    background-size: cover;

    border-radius: 2px;

    width: 50px;

    height: 49px;

    cursor: pointer;

  }

获取鼠标点击 DOM 的类名

Event.currentTarget
Event 接口的只读属性 currentTarget 表示的,标识是当事件沿着 DOM 触发时事件的当前目标。它总是指向事件绑定的元素,而 Event.target 则是事件触发的元素。

Event.currentTarget - Web API 接口参考 | MDN (mozilla.org)

// 根据当前点击的选色器,取index
const clickWhichOne = (e: any) => {
  const clickDom = e.currentTarget;
  if (clickDom) {
    // 得到当前类名 形如 ChartColorConfig-x
    const rightClassName = clickDom.classList[2];
    // 转换成数字
    const name = rightClassName.split("ChartColorConfig-")[1];
    setChooseIndex(name - 0);
  }
};

splice 删除与 slice 保留

splice 移除 start 到 end 的词

slice 保留 start 到 end-1 的词

树的修改

JS 树结构操作:查找、遍历、筛选、树结构和列表结构相互转换 - 沐码小站 (wintc.top)

// 树的过滤方法
export function treeFilter(tree, func) {
  // 使用map复制一下节点,避免修改到原树
  return tree
    .map((node) => ({ ...node }))
    .filter((node) => {
      node.children = node.children && treeFilter(node.children, func);
      return func(node) || (node.children && node.children.length);
    });
}
getGlobalChooseValues;
// 树的寻找
export function treeFind(tree, func, keyWord) {
  for (const data of tree) {
    if (func(data, keyWord)) return data;
    if (data.children) {
      const res = treeFind(data.children, func, keyWord);
      if (res) return res;
    }
  }
  return null;
}

enum 枚举类型

首字母大写

// 首字母大写 tableTrColor -> TableTrColor
function upperFirstWord(str: string) {
  const wordList = str.split("");
  const oneWord = wordList[0];
  const otherWord = wordList.slice(1);
  otherWord.unshift(oneWord.toUpperCase());

  return otherWord.join("");
}

掘金更换动头

// 沸点界面F12 输入以下内容 回车发起 等待新头像审核通过即可拥有 动头!
var ajax = new XMLHttpRequest();
ajax.open("post", "https://juejin.cn/web/user/update/user_info/", true);
ajax.setRequestHeader("content-type", "application/x-www-form-urlencoded");
// avatar更换为你想要的图片链接
ajax.send(
  "aid=2608&avatar=https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c38e0574bf6949c28e3c75957a3e3893~tplv-k3u1fbpfcp-jj-mark:345:345:345:345:q75.awebp"
);
ajax.onreadystatechange = function () {
  if (ajax.readyState == 4 && ajax.status == 200) {
    var c = ajax.responseText;
    console.log(c);
  }
};

文件下载

iframe 下载

      function iframeDownload(url) {
        try {
          const elemIF = document.createElement('iframe');
          elemIF.src = url;
          elemIF.style.display = 'none';
          document.body.appendChild(elemIF);
        } catch (e) {
          console.log('下载异常!');
        }
      }
      ```
> https://www.cnblogs.com/xiong950413/p/14209813.html

重定向下载

window.location.href = "www.baidu.com";

需要注意的一个问题
如果是在非 https 环境下,重定向到其他页面,会有一个报错提示

The file at 'http://192.168.208.163/OI9G84ddvg2-0aF_QWpYsxIqhgJCfYI9M5GnJ90ZXyWA4xMpHYP2bGFZw9-GJOqaIQDNEleKjF4G_5t9AmQN_XHuH7gKrzSjpImjNHK5xsJwjivlNU9Zs1YWAuv9yqSh.ofd' was loaded over an insecure connection. This file should be served over HTTPS

与代码和网页安全性相关。当浏览器尝试通过不安全的 HTTP 连接加载资源时,会出现此警告。要解决这个问题,你应该确保所有资源都通过安全的 HTTPS 连接提供。

https://juejin.cn/post/6964958019998187557

https 是如何加密的
个人网站配置 HTTPS 证书

关于 debounce

今天修 bug 的时候,遇到一个需用防抖的,但我发现即使用了debounce方法,期内需要运行的fn妹有运行,实际上就是对函数的不理解。
我的写法就是把他当作一个普通的箭头函数直接用了,实际上,当时debounce返回的只是fn,还需要debounce()(即fn())使用。
也可以参照下面这种写法,直接运行debounce

btnGetResumeContainer.onclick = debounce(function () {
  getResume(dataResume);
}, 3000);

//防抖debounce代码:
function debounce(fn, delay) {
  var timer = null; // 创建一个标记用来存放定时器的返回值
  return function (e) {
    // 每当用户输入的时候把前一个 setTimeout clear 掉
    if (timer !== null) {
      clearTimeout(timeout);
    }
    // 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发,就不会执行 fn 函数
    timer = setTimeout(() => {
      timer = null;
      fn.apply(this, arguments);
    }, delay);
  };
}

https://segmentfault.com/q/1010000040099131

清除页面路由

const cleanQuery = () => {
  // 获取当前URL
  const url = window.location.href;
  const isHaveQuery = url.includes("isJumpToBussinessVoucherTemplate");
  if (isHaveQuery) {
    // 创建URLSearchParams对象
    const params = new URLSearchParams(url);

    // 清除查询参数
    params.delete("isJumpToBussinessVoucherTemplate"); // 假设要清除的查询参数名为queryParam1

    // 跳转到新的URL
    router.location("/setting/voucher-template");
  }
};

科学计数法

Number 强转 出来的还是科学计数法

function getFullNum(num) {
  // 处理非数字
  if (isNaN(num)) return num;

  // 处理不需要转换的数字
  var str = "" + num;
  if (!/e/i.test(str)) return num;

  return num.toFixed(18).replace(/\.?0+$/, "");
}

https://juejin.cn/post/7194002669755695161

项目里用到的 util

// 科学计数法数字转为正常数字字符串
util.getFullNum = (num) => {
  if (isNaN(num)) {
    return num;
  }

  const str = "" + num;
  if (!/e/i.test(str)) {
    return num;
  }

  const fixed = ("" + num).match(/\d+$/)[0];

  return Number(num).toFixed(fixed);
};

edge 中拓展不可用

找到隐私和服务中的Microsoft Defender Smartscreen并关掉他

关于批量按顺序下载

参考文章

promise resolve reject
https://www.cnblogs.com/lunlunshiwo/p/8852984.html#4917337
reduce 按顺序调用
https://juejin.cn/post/7030625338065420302?searchId=202311041036275432B88F9F3A984960AA

demo

// import React from 'react';
import { Button } from "antd";

const Test = () => {
  // 准备回调的次数 模拟待发送的文件数量
  const waitForUpdateList = [1, 2, 3];

  let resultList = [];

  function simulateRequest(lastOneValue) {
    // 这里传入的index - 1,是为了获取上一轮的返回值
    console.log("lastOneValue", lastOneValue);
    const time = 500;

    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        let num = Math.ceil(Math.random() * 10);
        if (num > 5) {
          // 上一轮值存在 且是res 就绑定在这一轮
          if (lastOneValue !== undefined) {
            resolve(num + lastOneValue);
          } else {
            resolve(num);
          }
        } else {
          reject(num);
        }
      }, time);
    });
  }

  // 循环顺序请求

  function cycleRequest() {
    console.log("新的一轮开始请求");

    // 一个请求周期,这边为了模拟方便长度为 10,实际情况可能是 10000 或 99999 这样的
    const arr = waitForUpdateList;

    arr.reduce(async (last, _, index) => {
      let lastOneValue;
      await last;
      if (index) {
        lastOneValue = resultList[index - 1];
      }
      return simulateRequest(lastOneValue)
        .then((res) => {
          console.log("res", res);
          resultList.push(res);
        })
        .catch((e) => {
          console.log("e", e);
          resultList.push(e);
        })
        .finally(() => {
          // 达到边界 显示所有的promise结果
          // 为什么不用useState? 会有延迟
          if (index + 1 === arr.length) {
            console.log("最终", resultList);
          }
        });
    }, undefined);
  }

  // 启动
  const everyThingDone = () => {
    console.log("每轮重新开始,清空result");
    resultList = [];
    cycleRequest();
  };

  return <Button onClick={everyThingDone}>探索链式调用</Button>;
};
export default Test;

关于轮询 以获取数电文件为例

注意这里每次调用的时机,是在确保上次任务存在的情况下,再调用

  async getBtnLoading() {
    const { dispatch } = this.props;

    try {
      const res = await services.getNoDiskExtractStateForFile();
      if (res) {
        // 递归 如果当前还是任务中 就再次调用

        this.times = setTimeout(() => {
          if (this.times) {
            this.getBtnLoading();
          }
        }, 10000);
      }
    } catch (e) {
      // 丢到这里的如果是 执行失败\成功 需要更新横幅
// ...
    }
  }

如果是多个轮询同时调用,这里要将定时器的优先级提高!

// 置于当前/effect/index.js的顶级
let timer;

  async getList(params) {
    const { year, pageSize, current, risk, accountName = '' } = this.getState();

    const deliver = { pageSize, current, year, accountName, risk, ...params };

    try {
      const res = await services.list(deliver);
      const { list, executingAccountIdList, total } = res;
      this.updateState({ dataSource: list, executingAccountIdList, total });
      // 当前是否存在正在生成的列表 是的话 继续轮询
      const isExistBuilding = executingAccountIdList && executingAccountIdList.length;
      if (isExistBuilding) {
        // 似乎这两步可以去掉一个?
        timer && clearTimeout(timer);
        timer = setTimeout(() => {
          if (timer) {
            // 当前定时器存在 就清除再开始轮询
            clearTimeout(timer);
            this.getList(params);
          }
        }, 10000);
      }
    } catch (e) {
      // ...
    }
  }

简略策略模式优化 if else

// 客户列表
const currencyList = ["美元", "澳元", "泰铢", "欧元"];
// 汇率
const rateList = {
  美元: 0.144881,
  澳元: 0.216258,
  泰铢: 5.3383,
  欧元: 0.143649,
};

// 实际开发中,上述两个数据都是动态从后端获得的
const rateKeys = Object.keys(rateList);
// 策略容器
const stratagy = {};

// 为每种汇率添加一个key,并赋值为一个计算对应价格的方法
rateKeys.forEach((item) => {
  // 传入 RMB 价格作为基数
  stratagy[item] = (RMPPrice) => {
    return RMPPrice * rateList[item];
  };
});

function getAbroadPrice(RMBPrice = 100, target = "美元") {
  return stratagy[target](RMBPrice);
}

getAbroadPrice(100, "美元"); // 14.481...

input 无法重上传图片

e.target.value=''
https://www.cnblogs.com/momo798/p/13926851.html

原生列表美化

<table>
  <thead>
    <tr>
      <th class="risk-level">风险等级</th>
      <th>风险明细</th>
    </tr>
    <tr>
      <td>高</td>
      <td>往来款项存在长期挂账</td>
    </tr>
    <tr>
      <td>低</td>
      <td>广告和业务宣传费累计发生85,302,23,超过营业收入的15%</td>
    </tr>
  </thead>
</table>
table {
  margin-top: 1rem;
  width: 100%;
  border: 1px #e6e7e8 solid;
  border-collapse: collapse;
  text-align: center;
}

td,
th {
  border: 1px solid #e6e7e8;
}
th {
  background: #e9ecf0;
  border: 1px solid #dcdddf;
}

thead {
  height: 40px;
  background-color: #e9ecf0;
  font-family: MicrosoftYaHei-Bold;
  font-size: 18px;
  color: #3f3f3f;
  letter-spacing: 0.9px;
  text-align: center;
  font-weight: 700;
}
tr {
  height: 4rem;
  background-color: #fff;
}
td {
  font-family: MicrosoftYaHei;
  font-size: 18px;
  color: #3f3f3f;
  letter-spacing: 0.9px;
  font-weight: 400;
}
/* 注意 04 05 页中的数字项是右对齐 写在了单独样式里面
通用的是左对齐
 */
td:not(:first-child) {
  text-align: left;
  padding-left: 1rem;
}
/* 奇数项添加背景色 */
tr:nth-child(odd) {
  background-color: #f8fafb;
}

按钮禁用样式 不触发点击动作

          .delete-disabled {
            color: #c8c9cc;

            &:hover {
              pointer-events: none;
            }
          }

关于文件流下载

https://juejin.cn/post/7027431356443394084

https://juejin.cn/post/6878912072780873742

// 使用
    // downloadType 0 预览 1 下载
    const deliver = { year, quarters, accountIdList, downloadType: 1 };
    const url = '/instead/v2/customer/financial/report/download.do';

    const res = await axios({
      method: 'post',
      url,
      responseType: 'blob', // 必须,服务器返回的数据类型
      headers: {
        'Content-Type': 'application/json',
      },
      data: deliver,
    });

    // 下载正常处理
    downloadFileByBlod(res, fileName);
```

```js
import { message } from "antd";

// 文件流下载
export const downloadFileByBlod = (res, fileName) => {
  // 兼容ie11
  if (window.navigator.msSaveOrOpenBlob) {
    try {
      const blobObject = new Blob([res.data]);
      window.navigator.msSaveOrOpenBlob(blobObject, fileName);
    } catch (e) {
      console.log(e);
    }
    return;
  }
  // a标签实现下载
  const curUrl = window.URL || window.webkitURL || window.moxURL;
  const link = document.createElement("a");
  link.style.display = "none";

  // 创建下载链接,将文件流转化为一个文件地址
  link.href = curUrl.createObjectURL(new Blob([res.data]));
  link.setAttribute("download", fileName); // 下载的文件名
  document.body.appendChild(link);
  link.click(); // 触发点击事件执行下载
  document.body.removeChild(link); // 下载完成进行释放
};

// 根据文件名后缀获取当前文件类型
export const getLastFileTypeByName = (value) => {
  const dotArray = value.split(".");
  const lastDotArray = dotArray[dotArray.length - 1];
  return lastDotArray;
};

export const getFileTypeBySource = (file, legitTypeList) => {
  // 注意这里的类型判断一定要从文件名读取! ios 无 file.type!!!
  const exactFileValue = file.name;
  const lastFileTypeByName = getLastFileTypeByName(exactFileValue);

  // 实测移动端 不走相册 直接选图片文件 尾缀为大写PNG 这里多加一层逻辑
  // ↑ 从H5抄过来的 问题不大
  // 这里全部转换成小写 进入匹配
  let isLegitType = false;
  isLegitType = legitTypeList.some((key) =>
    lastFileTypeByName.toLowerCase().includes(key)
  );
  return isLegitType;
};

// 1 KB = 1024 B
// 转换文件大小为可读单位
export const getFileSize = (fileSize) => {
  let result = "";

  if (fileSize >= 1073741824) {
    // B => GB
    result =
      fileSize % 1073741824 === 0
        ? `${fileSize / 1073741824}G`
        : `${Math.trunc(fileSize / 1073741824)}G`;
  } else if (fileSize >= 1048576) {
    // B => MB
    result =
      fileSize % 1048576 === 0
        ? `${fileSize / 1048576}MB`
        : `${Math.trunc(fileSize / 1048576)}MB`;
  } else if (fileSize >= 1024) {
    // B => KB
    result =
      fileSize % 1024 === 0
        ? `${fileSize / 1024}KB`
        : `${Math.trunc(fileSize / 1024)}KB`;
  } else if (fileSize !== undefined) {
    result = `${fileSize}B`;
  }
  return result;
};

// 上传文件前置校验 文件类型 文件大小
export const beforeUpload = (files, fileSizeLimit, legTypeList) => {
  const readableMaxFileSize = getFileSize(fileSizeLimit);
  const fileSupportTypeText = legTypeList.join("、");

  // if (!(files && files.length)) {
  //   message.error(`请添加${kind}`);
  //   return false;
  // }

  let flag = true;

  // eslint-disable-next-line consistent-return
  files.forEach((file) => {
    const isLegFileType = getFileTypeBySource(file, legTypeList);

    if (!isLegFileType) {
      message.error(`文件支持格式: ${fileSupportTypeText}`);
      flag = false;
    }
    if (file.size > fileSizeLimit) {
      message.error(`文件最大支持${readableMaxFileSize}`);
      flag = false;
    }
  });
  return flag;
};

关于安卓滚动条样式

一个关键样式
overflow:overlay
悬浮在上方,目前只有移动端支持webkit内核的可用,PC 上已废弃

https://zhuanlan.zhihu.com/p/88347981

https://zhuanlan.zhihu.com/p/88347981

使用 JS 直接获取子元素

https://www.coder.work/article/5383247

JQ 遍历元素方法

https://www.runoob.com/jquery/jquery-ref-traversing.html

禁止输入 emoji

请注意,由于 Emoji 字符的范围广泛且可能随时间变化,无法保证完全过滤所有的 Emoji 字符。因此,如果需要更严格的过滤或更全面的 Emoji 支持,可能需要使用专门的 Emoji 过滤库或更复杂的验证逻辑。

https://segmentfault.com/q/1010000007329875

JavaScript 下含有 emoji 字符串的处理

PHP 开发中涉及到 emoji 表情的几种处理方法

// 过滤掉emoji表情
// 基本思想就是遍历字符串中的每个字符,如果该字符的长度为4个字节,就将其删除。
function filterEmoji($str)
{
    $str = preg_replace_callback(
            '/./u',
            function (array $match) {
                return strlen($match[0]) >= 4 ? '' : $match[0];
            },
            $str);

     return $str;
 }

input 的聚焦动作无效 autofocus

https://juejin.cn/post/7034707796545241095
react 中要用autoFocus

moment 对象操作的影响去除

moment()再包裹 生成新的对象

npm yarn pnpm 的区别

https://zhuanlan.zhihu.com/p/494076214

posted @ 2023-09-13 16:28  乐盘游  阅读(21)  评论(0编辑  收藏  举报