仅作为个人学习进步的记录,也可以看作是我的一个错题本。
今天听了十年前端袁志佳的一个腾讯课堂,醍醐灌顶,也对自己未来的发展有了一个更清晰的视界,现在就先把自己的目前工作中遇到的问题整理成集,以便自己回顾或者是为有遇到同样问题的人提供思路,当然面试的时候也能派上用场。
怎么在 ssr(服务器端渲染) 应用中判断当前是否处于浏览器中?
因为对于 ssr 的程序来说(比如说使用 next.js),我们经常会碰到一种报错就是 ReferenceError: document is not defined这种情形,这是因为程序不知道当前的渲染环境。
我在一个 ssr 架构的项目中使用 bizCharts 时遇到了该问题,在参考了 这个 issue 后了解到了可以通过:
- 判断当前环境是否浏览器:
process.browser - 是浏览器环境则动态加载 bizCharts 库,并将该库赋到一个变量上去:
bizcharts = require('bizcharts') - 最后在 render() 再判断一次是否浏览器环境,并使用 bizcharts 的前缀命名空间:
<Chart>=><bizcharts.Chart>
后来报错变成了process is not defined,这个问题原因是浏览器端因为不存在 process 所以会报错,把上述 1. 和 3. 的判断条件改为 typeof window !== 'undefined',问题即可解决。
为什么 umi.js 使用中会报错:TypeError: api.onOptionChange is not a function
因为 umi-plugin-react 被新的 @umijs/preset-react 替换掉了。
舍弃掉 umi-plugin-react,并在 package.json 中删掉 "umi-plugin-react": "^1.15.8" 这种代码,然后升级到对应的 @umijs/preset-react (如"@umijs/preset-react": "^1.7.8") 即可。
用 npx react-native run-android 打开安卓模拟器出错
报错1
报错内容如下:
error failed to launch emulator. reason: emulator exited before boot..
这是由于安卓模拟器要求使用 HAXM 进行硬件加速,但是我没有打开相应权限,因此要安装 HAXM,可以直接在官方 release 下载相关的压缩包,解压并将文件移动到相关的文件夹执行 .exe 即可。
举个例子,我从 intel/haxm 下载了最新的发行版,然后解压后包内的东西放到了对应的文件夹里 D:\apps\AndroidSDK\extras\intel\Hardware_Accelerated_Execution_Manager,再执行 haxm-7.6.5-setup.exe 即可。但是会报错,显示我的电脑不支持 VMX vmx supported: no。
解决办法如下:
Step 1
要从 windows 10 家庭版 升级到 专业版 啊,因为家庭版是不够权限打开 Hyper-V 哒~
我用的办法是参考 这篇文章 的方法,直接看最后“系统激活”那里,给亦是美网络一个好评。
Step 2
要打开 Hyper-V,参考这个回答,然后就能够成功开启模拟器了!!
报错2
报错内容如下:
java.lang.NoClassDefFoundError: Could not initialize class org.codehaus.groovy.vmplugin.v7.Java7
然后执行 npx react-native run-android 的时候死活下载不了gradle-6.2-all.zip,这是因为墙的原因导致网络不稳定,后来突然网络一好就下载完成了。但是仍旧报错,内容如下:
java.lang.NoClassDefFoundError: Could not initialize class org.codehaus.groovy.vmplugin.v7.Java7
上 SO 查了一下 ,原来是因为我用的 JDK 版本是 JDK15,最低需要的 gradle6.3 才能正确运行,而默认配置的是 gradle6.2 ,所以报错了。
因此需要:
Step 1
在C:\Users\<your username>\.gradle\删掉之前下载的 gradle 6.2 的所有内容
Step 2
在当前构建的 RN 项目的目录中找到<Your projectname>/android/gradle/wrapper/gradle-wrapper.properties,然后修改 gradle 版本为 6.3 :
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
然后再执行 npx react-native run-android 重新 build 一下即可。
实现 react 中数据共享,让一个状态在不同页面中共享?
应该是可以用 context 来写的,但是因为不安全而且不太熟悉,所以用了 状态提升 的方式,即通过在共同父组件中定义 state/setState,然后通过 attribute 传给子组件,子组件中通过 props 来获取到资源。
一个重要的思路,给 {this.props.children} 加上属性的写法:
{React.cloneElement(this.props.children, { isHr: this.state.isHr })}
异步的数据获取?
使用了 Hook,阿里的 ahooks 中的 useRequest(serviceFn, {onSuccess, onError}) 的方式来写。
const { data, error, loading } = useRequest(getUserInfoFromRemote, {
onSuccess: (data) => {
console.log(data);
const { result } = data;
console.log(JSON.parse(result));
setIsHr(IsHrEnum.TRUE);
},
onError: (e) => {
console.log(e);
setIsHr(IsHrEnum.LOADING);
},
});
参考 useRequest
PS:最后发现是同事写的 SSO 获取信息的问题,然后通过直接在 _app.js 中添加异步获取数据的生命周期。
async componentDidMount() {
let res = await init();
let userInfo;
if (typeof res.result === "string") {
userInfo = JSON.parse(res.result);
} else {
userInfo = res.result;
}
// console.log("********", userInfo);
this.setState({ userInfo: userInfo });
}
使用 socket 来进行的传输方法
socket.emit("msg", msg)
socket.on("msg", cb)
将路径字符串数组 treepath 的解析成一个 json 目录对象
参考 这里
前端异步获取的数据怎么使用 useState 处理
当使用 useState 这个 hook 的时候要知道,其实 setState() 的参数还能够是一个 callback 函数,而不仅仅是一个要设置的值。
这个 callback 能够让我们获取到 previousState,并更新它的值。
因为
setState是异步设置的原因,如果不用 callback 的方法去获取值,往往会获取到默认值。 例如:
const [users, setUsers] = useState([]);
setUsers([{id:1, name:"jack"}]);
setUsers([...users, {id:2, name:"amy"}]; // 错误!拿到的 users 只能是空数组 []
setUsers((users) => [...users, {id:2, name:"amy"}]); // 正确写法,能够成功添加到 users 数组里面
同理,异步获取的数据只能够通过这种回调函数的方式去设置,否则将不能够更新到 state 里面去。
git 项目回退
本地回退
本地硬回退到上一个版本
$ git reset --hard HEAD^
远程回退
将本地版本强制 push 到 orign 远端的 dev 分支上去
$ git push -u origin dev -f
Slate 中粘贴代码块之后点击选择语言的 select 框就会报错
error: cannot resolve a slate point from dom point: [object htmlspanelement],0
产生的原因是因为 ANTD 默认的 Select 组件是包含了 search 搜索框功能的,该功能放在了一个 SPAN 中,如果没有用到该功能,对应的该 SPAN 会自动设置为 display: none。
但是由于 Select 组件的渲染先出来了,然后该搜索框再被加上样式,因此用户仍然可以点击到该搜索框的 SPAN。在 Slate 中,会尝试把 Selection 的焦点放到该搜索框中,所以才会报错。
解决方法:
span.ant-select-selection-search {
display: none;
}
直接通过样式把这个搜索框移除掉即可。
Slate 中输入中文光标会跑到段落的开头
因为尝试使用了通过 script 动态引入 js 包来优化性能(并行引入,且用到了 CDN 服务,减少打包的大小),但是由于这种引入因为某种奇怪的原因导致 react 版本的不匹配,所以出现了异常。
后来把 react 版本改成了 16.8.12 就好了(原本用的一直都是16.8.6,有可能是 node_modules 里的 react 版本超前了)。
Slate 中无法对 SPAN 里面的内容解析,并很容易报错
后来发现,原来在 withHtml 中对 SPAN 的处理
- 要么就是作为 Text 内联元素处理(若 SPAN 里有嵌套的 Element 结构将无法对其处理,并报错:Uncaught Error: The
<text>hyperscript tag can only contain text content as children.) - 要么就是作为 Element 包起来处理,容易导致 slate editable 的元素
div#editorarea结构中出现了没有被包裹的 SPAN 结构。从而导致一系列定位 path 的错乱,毕竟 SPAN 不应该被作为单独的 Element 处理的。
所以最后的解决方案反而是不对 SPAN 进行处理,即既不把它当成 Text 又不把它当成 Element 处理,然后报错就好了。
Mac 中和 Windows 中的字体渲染结果不一样导致富文本编辑器的 placeholder 错位
这是一个 CSS 导致的问题,参考这个回答
设置两个平台共有的字体Helvetica 并且将 body 的 line-height 设置为 unset,并对其他元素进行微调,即可保证在 Mac 中能够正常显示了。
在 Slate 的 Transforms.setNodes(editor, Partial, options) 中,第二个参数用一个新创建的对象{rowspan: child[i].rowspan},会报错 TypeError: Unsupported type of value: undefined,导致表格插入1行后无法 automerge 保存
通过将这个 Partial 对象进行序列化/反序列化 JSON.parse(JSON.stringify({rowspan: child[i].rowspan})) 问题就解决了。
原因未知,推测是新建对象中存在一些原型链上的数据,导致参数传入时出现指针指向错误。
使用forwardRef 传递 ref 到子组件时,子组件打印的 ref.current 始终为 undefined
原因是组件中 ref 的位置不对,放在了 {...attributes} 的前面。
<div ref={ref} {...attributes} className={cx(["card", "sider-menu-wrapper", fakeSelected ? `fake-selected` : null])} data-activated={cn} onClick={_onClick} onDoubleClick={_onDoubleClick}>
{children}
</div>
改成这样就好了:
<div {...attributes} ref={ref} className={cx(["card", "sider-menu-wrapper", fakeSelected ? `fake-selected` : null])} data-activated={cn} onClick={_onClick} onDoubleClick={_onDoubleClick}>
{children}
</div>
\
html 页面引用静态资源报错
当我们运行程序的时候,我们不能直接在浏览器中打开带有引用本地静态资源的 HTML 文件的,因为跨域请求是不支持 file 协议的。我们需要将我们的输出文件运行在HTTP协议上,具体方式可以使用 koa/express 将文件内容放在本地 localhost 服务器上。
react 复杂组件(三重 array.map 渲染的组件)的重复渲染
通过使用 useMemo 进行缓存比较,减少重复渲染的次数。但是有一个组件的依赖是一个对象,因此每次都重复渲染。解决方案:通过使用 JSON.stringify(comments) 方式将对象转化为字符串的形式减少重复渲染。
将对象作为 useEffect 第二个参数uhttps://stackoverflow.com/a/53744050
slate 富文本编辑器的评论的嵌套显示问题
=> rangeIdList & commentId 的含义
表格的合并单元格设计
=> colspan rowspan 的含义
slate 中的 card 自定义类型 card 删除报错问题
=> slate 中 Transforms.delete 删除只支持删除 slate 默认的结构,对于自定义结构,需要在插件(withXXX)中重写 editor.deleteFragment() 并将该结构先转换为 slate 能够识别的结构如:{type: "paragraph", children: [{text: ""}]}
富文本编辑器中,目录大纲的滚动
粘性布局
设置一个 position:sticky 但是 height 为 0 的父元素,用于让目录大纲子元素能够实现脱出文档流的粘性布局而不占据位置。
滚动
设置 anchor-container 中 滚轮相关 事件的处理:
- 到目录滚动的末端时,禁止滚动传导到外层 editor : 通过 css 属性
overscroll-behavior - 滚动时触发 anchorTrigger 更新: 监听
wheel事件(因为scroll是默认事件,会在事件已经真实发生过后才触发,相当于触发后“通知”你一下,无法e.preventDefault()),添加 throttle 机制