this迷失
由于React
开发的灵活性,在组件属性传递时,操作函数也常常作为属性被传递进去。由于未使用使用箭头函数造成函数在执行时根据上下文确定this
指针的值,常常造成this is undefined
的问题。
import React, { Component } from 'react';
export default Button extends Component {
handleClick = () => {
const { onClick } = this.props
if (onClick) {
onClick()
}
}
render () {
return <button onClick={onClick}>确认</button>
}
}
// onClick作为属性传递是特别需要注意,当函数内部使用this指针的值时,有可能会存在this迷失的问题
DOM XSS
在前端的server
进行html
文件的拼装时,尤其是需要函数写入script
标签的内容时需要特别注意,需要对写入的内容做校验,如果存在html
语义标签时可能存在DOM XSS
的风险。
const jsContent = fetchFromServer()
// ejs进行内容写入
<script>
window.globalVar = JSON.stringify(jsContent)
</script>
// 此时需要注意如果jsContent包含html语义标签时可能会造成漏洞,如jsContent = {name: '<script>测试漏洞</script>'}, 组装好的html文件放回前端时就会执行报错
// 使用htmlEncode对html语义内容进行encode
const htmlEncode = (html) => {
return html.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
}
Safari文本选择高度计算问题
Safari
浏览器中光标拖拽选择文本非首行包含起始文本内容时,在使用原生的window.getSelection()
方法获取选区时,并通过Range
的getBoundingClientRect()
方法获取选区高度时有问题的,是正常选区高度的两倍(暂时没有找到相关文档有说明过该问题,如果有哪位大佬知道该问题产生的原因可留言告知)。目前推测原因是因为在选取非首行首个文本内容时会默认从上一行的末尾开始选择,因此造成高度是两行。
Safari
浏览器的选择高度和所选内容高度是正常高度的两倍,且选区内容有两部分。在Chrome
和FireFox
选择相同的内容表现则不是如此。
这种浏览器差异造成了开发时高度位置有误,暂时没有找到特别通用的解决方案,只是暴力的对Safari
浏览器进行单独的处理。
const getSelectionRectHeight = () => {
const isSafariBrowser = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)
const nativeSelection = window.getSelection()
if (nativeSelection.rangeCount) {
const nativeSelectionRange = nativeSelection.getRangeAt(0)
if (isSafariBrowser) {
const nativeSelectionRects = nativeSelectionRange.getClientRects()
if (nativeSelectionRects.length === 2) {
const hasZeroRect = [...nativeSelectionRects].some(rect => !rect.width)
return hasZeroRect ? [...nativeSelectionRects].find(rect => !!rect.width) : nativeSelectionRange.getBoundingClientRect()
}
}
return nativeSelectionRange.getBoundingClientRect()
}
}
react组件销毁单例内容的坑
项目中使用一个全局的svg
的图标库,因此封装了一个svg
图标组件,而图标组件在使用时销毁报错,原因时this.svgEle
与this.styleEle
并未在document.body
和document.head
内不是其子元素因此使用removeChild
函数时会报错。
// SVGSymbols.tsx组件,全局注入svg库与样式文件
import React from 'react';
// 获取浏览器全局globalThis并在其上挂载__svg__symbols__loaded属性标识是否已经全局加载过svg库
import LoadedManager from './loaded-manager'
import SVG_CONTENT from '!!raw-loader!svg-symbols.svg';
import STYLE_CONTENT from '!!raw-loader!svg-symbols.css';
export default SVGSymbols extends React.PureComponent {
private svgContainerEle = document.createElement('div');
private styleContainerEle = document.createElement('style');
private generateSvgContent () {
this.svgContainerEle.setAttribute('data-role', 'svg-symbols-container');
this.svgContainerEle.insertAdjacentHTML('afterbegin', SVG_CONTENT);
document.body.insertBefore(this.svgContainerEle, document.body.firstChild);
}
private generateStyleContent () {
this.styleContainerEle.setAttribute('data-role', 'svg-symbols-css');
this.styleContainerEle.insertAdjacentHTML('afterbegin', STYLE_CONTENT);
document.head.appendChild(this.styleContainerEle);
}
componentDoneMount () {
if (!LoadedManager.isLoaded) {
this.generateSvgContent();
this.generateStyleContent();
}
}
componentWillUnmount () {
if (LoadedManager.isLoaded) {
// 此处有埋坑
document.body.removeChild(this.svgContainerEle);
document.head.removeChild(this.styleContainerEle);
}
}
render () {
return null;
}
}
// SVGIcon.tsx组件加载生成对应的svg图标
import React from 'react';
import SVGSymbols from './SVGSymbols'
export eunm SVGIconSize {
large = 'xl',
middle = 'l',
small = 'm'
}
interface SVGIconProps extends React.HTMLAttributes<HTMLSpanElement> {
type?: string
size?: SvgSize
}
export default SVGIcon extends React.PureComponent<SVGIconProps> {
render () {
const {type} = this.props
return (
<>
<SVGSymbols />
<span>
<svg>
<use xlinkHref={`#${type}`} />
</svg>
</span>
</>
)
}
}
该组件看起来没有问题但是如果使用map
函数生成全局过个SVGIcon
进行使用时,在组件进行销毁时,我们知道其中一个SVGSymbols
组件的componentWillUnmount
会页面中移除全局插入的svg
内容,那么其他组件的componentWillUnmount
将会报错,因为this.svgContainerEle
与this.styleContainerEle
元素并未插入到页面中,因此其也不是body
与head
的子元素,因此调用removeChild
函数会报错。
// 增加判断逻辑进行remove
if (document.body.contains(this.styleContainerEle)) {
document.body.removeChild(this.styleContainerEle);
}
if (document.head.contains(this.styleContainerEle)) {
document.head.removeChild(this.styleContainerEle);
}
优化可以使用一个LoadedManager
进行单例模式的注入与删除而无需在单写一个SVGSymbols
组件。