前言:
因为公司数据不能暴露,本文中的数据都是临时创建出来的,可能有问题,不要全看。并且这里都是用react的代码。
日常代码中的问题和技巧
问题:
问题1:请求接口报400(请求数据有问题)
场景:发送请求后报400,然而传过去的数据没有错
原因:传入进去的是JSON字符串,发现请求中的数据“{”,“}”,“[”,“]”没有转码
解决方法:把这串JSON字符串转码后再发送请求
export default (data) => data.replace(/\[/g, '%5B').replace(/\]/g, '%5D').replace(/\{/g, '%7B').replace(/\}/g, '%7D');
问题2:在input框内连续输入数据会感觉很卡,一直快速输入数据显示需要时间
场景:写项目时发现像input框这种可以连续输入的组件,连续输入,每次点击键盘时间很短的时候,input框内有延迟显示。但是没有绑定任何属性的input框没有这种情况。
原因:我是用react中的useState绑定了input的value,并且把对应的set方法绑定到onChange上。由于set方法是异步的,并且会重新渲染组件,如果键盘输入太快,就会触发很多次set方法,导致异步的set方法会出现问题,进而导致页面渲染input框内的数据卡死。
解决方法1:如果其他地方的操作不会影响这里的值。
不要绑定value,就把onChange里面使用的set方法加一个节流,不过时间设置小一点,要不然会出现用户修改完,都点击了提交这种操作,onchange里面的set函数还没有触发
解决方法2:如果其他地方的操作会影响这里的值。
就在“解决方法1”的基础上,可以用默认值(defaultValue)控制这个input的值。
我理解defaultValue是会在input框第一次渲染的时候展示的值,如果value有值,就会展示value的值。
可以先销毁这个input,再把input重新渲染
import React, { useState, useEffect } from 'react';
import { throttle } from 'lodash';
export default () => {
const [data, setData] = useState();
const [show, setShow] = useState(true);
useEffect(() => {
if(!show) {
setShow(true);
setData('改变了');
}
},[show]);
return (
{show &&
<input
defaultValue={data}
onChange={throttle(setData, 50)}
/>
}
<button
onClick={() => {
setShow(false);
}}
>点击</button>
);
}
这里用show这个参数把input销毁了,然后重新创建了,这里只是随手写的代码,项目里面要根据具体情况写
解决方法3:把绑定的数据改成同步的数据。
可以在组件“export default”上面let的数据,不过这种要用useEffect模拟销毁生命周期的时候重置,否则切换路由回到这个页面,会导致页面数据还是修改后的数据。
还可以使用useRef.current存一个同步的数据。
这个会导致一个问题,就是页面不会重新加载,导致input框没有重新渲染里面的数据
问题3:表格需要渲染大量数据,导致页面进入时有几秒卡顿
场景:因为需求导致进入时渲染两个表格数据,加在一起最少有800条,虽然用了懒加载,但是进入时还是有3秒以上的卡顿
原因:大量数据渲染表格自然会卡(具体为什么表格渲染会卡,还要去看看,如果您知道,希望您可以评论一下,万分感谢)
解决方法:最后是改了需求,并且控制台这里把表格做了分页。一开始是因为没有想到其他办法展示这里的数据给用户操作,最后借鉴其他项目渲染的方式把这里的需求改了
问题4:正则判断有问题
场景:写了一个正则,使用正则方法出现奇怪的情况
var reg = /test/g;
console.log(reg.test('test'), reg.lastIndex); // true 4
console.log(reg.test('test'), reg.lastIndex); // false 0
console.log(reg.test('test'), reg.lastIndex); // true 4
console.log(reg.test('test'), reg.lastIndex); // false 0
原因:正则最后写了一个"/g"导致每次使用完方法,会用“lastIndex”记录一次当前位置,如果找到就是当前位置,没有找到就会把“lastIndex”变成0
解决方法1:不要写"g",使用“g”要谨慎
var reg = /test/;
console.log(reg.test('test'), reg.lastIndex); // true 0
console.log(reg.test('test'), reg.lastIndex); // true 0
console.log(reg.test('test'), reg.lastIndex); // true 0
console.log(reg.test('test'), reg.lastIndex); // true 0
解决方法2:在每次使用之后,直接改变“lastIndex”的值
var reg = /test/g;
console.log(reg.test('test'), reg.lastIndex); // true 4
reg.lastIndex = 0;
console.log(reg.test('test'), reg.lastIndex); // true 4
reg.lastIndex = 0;
console.log(reg.test('test'), reg.lastIndex); // true 4
reg.lastIndex = 0;
console.log(reg.test('test'), reg.lastIndex); // true 4
reg.lastIndex = 0;
问题5:项目运行的时候报"getaddrinfo ENOTFOUND …"
场景:换一台电脑运行项目的时候报这个错
原因:120.0.0.1没有和后面那个地址绑定上
解决方法:修改hosts文件
例:
127.0.0.1 对应的地址
问题6: echarts tooltip 里面写 react代码
这个过程太多,全部放到这篇文章中: https://blog.csdn.net/weixin_44726476/article/details/127301152
问题7: react中,引入图片之后,页面直接报错(没有找到这个模块)
场景:
直接看伪代码:
// 这样运行之后页面会报错,找不到这张图片
const url = './xxx.png';
<img src={require(url)} />
// 这样运行之后页面正常运行
<img src={require('./xxx.png')} />
原因:我猜测是webpack打包的时候,并没有执行js代码,觉得require里面是一个参数名,就没有把这张图片打包进去
直接看webpack打包后的文件:
先看直接引用字符串后的打包文件和代码:
打包时,有找到这个图片,并打包过去
然后看看引用参数时,打包生成的文件和代码:
dist文件夹里面并没有根据我配置的webpack,生成对应的images文件夹
解决办法:以后引入图片的时候,如果图片是存在前端文件里面的,引入的时候一定要引入对应路径。
就算图片路径是根据代码来判断用那张图片,也要先全部引入。可以用对象包裹起来,然后判断用哪张图片,不要为了少些几个require,导致这个bug找半天找不到原因。
问题8: 圆角导致图片位置错误
场景: 图片(img)外面有一个div,预期让div包裹图片,加一个圆角和边框
具体展示: 图片盖住上面两个圆角,下面两个圆角不会盖住,但是下面两个圆角和图片相隔一个圆角的距离
原因: 不明!
解决方法: 直接在img上加边框和圆角
技巧:
技巧1:后端传过来的是带HTML字符串,不让这些HTML标签用字符串直接展示
场景:后端传过来一个带html标签的字符串,但是展示不需要把这个html当字符串展示出来 (因为后端代码是好久之前某个离职的实习生写的,不好修改,就不改后端代码了)
解决方法1:react中dangerouslySetInnerHTML这个参数是可以把字符串转成html的标签(不建议使用)
<div dangerouslySetInnerHTML={{'__html': 后端传过来的数据}} />
解决方法2:根据后端传过来的字符串,把字符串拆分,并重新组合了一下,放到对应的位置。(内容格式固定的情况下)
技巧2:需要修改组件库的样式
场景:使用组件库时,发现组件库的样式不能满足需求,自己写的话需要时间,而且没有别人封装的全,这时就需要更改样式。
解决方法:
- 在开发页面中打开控制台,找到需要更改样式的元素,找到对应的class
- 在对应的文件里面写css样式,最好在 ‘:global’ 外面套一层,要不然样式会作用到整个项目
.box {
:global{
.GeTMDd {
color: red;
}
}
}
技巧3:修改hosts文件
场景:前端一个很频繁操作,不过都是项目运行前配置一次就可以了
- 在cmd中运行一串指令
$ sudo vi /etc/hosts
- 输入密码后就进入文件了
- 退出的话直接输入
$ :wq
技巧4:react中获取dom样式
场景:需要获取当前这个dom的一些样式的值
- 用useRef绑定这个dom
import React, { useRef } from 'react';
export default () => {
const ref = useRef(null);
return (
<div
ref={ref}
>dom
</div>
)
}
- 使用window上的这个方法,就可以拿到这个dom的参数了
window.getComputedStyle(ref.current)
注意:这里的ref.current必须要绑定上这个dom才可以拿到
技巧5:react 18前useState 的set方法批处理
场景:在18之前,react的set方法写一次重新渲染一次,可以用unstable_batchedUpdates包裹住,就只渲染一次
import React, { useState } from 'react';
import { unstable_batchedUpdates } from 'react-dom';
export default () => {
const [dataA, setDataA] = useState(0);
const [dataB, setDataB] = useState(0);
const [dataC, setDataC] = useState(0);
setTimeout(() => {
unstable_batchedUpdates(() => {
setDataA(dataA + 1);
setDataB(dataB + 1);
setDataC(dataC + 1);
})
}, 10000)
console.log(123); // 10秒后,这个123只执行一次
return <div />
};
技巧6: 在iframe中,将iframe外的滚动条滚动到iframe的指定位置
场景: 滚动条是在iframe外的dom,没有主动修改这个dom的滚动条,也不能在iframe里面拿到外面的dom,也没有将浏览器窗口宽高、页面整个高度传进iframe
解决方法1: iframe中,可以通过dom上的scrollIntoView方法,将这个dom展示出来
解决方法2: textarea标签上的focus事件,也是可以让iframe外的滚动条滚动到这个textarea标签位置
手机兼容问题:
问题1:最好不要用replaceAll
场景:有些安卓机会报replaceAll不是一个函数
原因:有些安卓机不支持这个api
解决方法:使用replace
特殊的组件
选择本地文件,并获取其内容
需求:点击一个按钮之后,弹出弹窗,可以选择本地文件,获取这个文件内容,并转成base-64传到后端那里去,以及获取其文件名
代码:
import React, { useRef, useState } from 'react';
import { Button } from 'xxx组件库';
export default () => {
const fileRef = useRef();
const [fileInfo, setFileInfo] = useState({
name: '', // 文件名
content: '', // 文件内容
});
return (
<div>
<Button
text
onClick={() => {
fileRef.current.click();
}}
>{fileInfo.name || '点击选择文件'}
</Button>
<input
ref={fileRef}
type="file"
hidden
onChange={(e) => {
if (e.target.files.length !== 0) {
const reader = new FileReader();
const info = {};
// 按代码来看,获取文件名应该放到reader.onload里面,但是我在reader.onload里面拿不到名字,可能是我拿到方式有问题
info.name = e.target.files[0].name;
reader.readAsText(e.target.files[0], 'utf-8')
reader.onload = (res) => {
// 获取文件内容,并转成base-64的字符串
info.content = window.btoa(res.target.result);
setFileInfo(info);
}
}
}}
/>
</div>
)
};
fileInput按钮的小问题:
- fileInput样式不好看
- 第一次点击fileInput选择一个文件后。再次选择文件时,点击取消按钮,fileInput会展示未选择文件的样子,但是我不知道怎么触发取消事件
解决方式:把原本的fileInput隐藏,用useRef拿到fileInput的点击事件。然后像怎么展示就怎么展示。
ps:如果要选择多个文件需要改一下,我这里是因为只需要获取一个文件
处理ts项目飘红和飘黄
问题:
问题1:ts文件中,从window这种数据里拿一个后期添加的数据导致飘红
场景:代码飘红,报Property ‘*’ does not exist on type ‘window & typeof globalThis’.,而且window这种数据类型不是在这个项目里面设置的。(以window为例)
原因:window类型里面没有‘ABCD’的声明。
解决方法:在ts的配置文件 “自己命名.d.ts” 文件里面增加一条这个就可以了。
interface Window {
ABCD?: any;
}
ps:一开始想用ts的继承,然后发现我修改不了window的接口(可能可以修改,但是我是因为第一次用ts,好多东西不知道)。
问题2:ts文件里面写require引入飘红
场景:代码飘红,报 Require statement not part of import statement.
这里整行飘黄是因为第二行不是空白的代码
原因:ts文件里面不能用require引入 (原因需要再看看,我师兄说是因为ts文件需要预编译什么的)
解决方法:使用“import … from …”引入
问题3:tsconfig.json配置文件最上方飘红
场景:代码飘红,报:
File '文件名' is not under 'rootDir' 'rootDir里面填的路径'. 'rootDir' is expected to contain all source files.
The file is in the program because:
Part of 'files' list in tsconfig.json
原因:这个报错是因为在tsconfig.json文件“files”或“include”里面的文件不在“compilerOptions”配置中的“rootDir”配置里面。试过修改“rootDirs”里面的内容,但是没有用。
解决方法:虽然不理解这些配置,但是最后在网上找到一个方法,就是把"rootDir"改成默认的"./“,这样就可以把整个项目都放进去,原来这里"rootDir"里写的是”./src",就是不知道这样会不会对项目有什么影响,需要过几天测一下,我现在还没有测试一下,不确定有没有问题。
问题4:下载package.json中的包卡住,一直转圈圈
场景:项目是用ts的,用公司的npm下载插件,因为有些是公司自己的包,npm下载不了,就没有试npm了。几天前下载没有问题,今天突然就开始卡住,而且停止之后重新“npm i”又有用了。
而且文件里面会报有些依赖引用不到。
由于node_modules可以找到对应的文件,由于之前是有用的,所以排除两种修改方法
排除方法1:使用这个命令下载ts对应的包 “npm i --save-dev @types/依赖名”
排除方法2: 在ts的配置文件 “自己命名.d.ts” 文件里面增加一条 “ declare module ‘依赖名’ “
网上有个说是因为这个包没有下配置,第一个是下载一个ts的,如果报错,就是这个包没有ts的版本。这时候可以用第二个方法,但是这个方法我还是没有理解是干啥。不过大家都建议用第一个。
原因:未知,需要再看看
解决方法:除了上面两种排除的方法,网上没有找到其他解决方法,然后无意中发现,下载一部分package.json的包,然后再下载全部的就没有问题了,猜测是包太多了,导致下载卡住