你觉得你在写 JSX:
~~~jsx
<marquee bgcolor="#ffa7c4">hi</marquee>
~~~
其实,你在调用一个方法:
~~~jsx
React.createElement(
/* type */ 'marquee',
/* props */ { bgcolor: '#ffa7c4' },
/* children */ 'hi'
)
~~~
之后方法会返回一个对象给你,我们称此对象为React的*元素*(element),它告诉 React 下一个要渲染什么。你的组件(component)返回一个它们组成的树(tree)。
~~~jsx
{
type: 'marquee',
props: {
bgcolor: '#ffa7c4',
children: 'hi',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'), // 🧐是谁}
~~~
如果你用过 React,对`type`、`props`、`key`、 和`ref`应该熟悉。**但`$$typeof`是什么?为什么用`Symbol()`作为它的值**?
这又是一个与你学习使用 React 不*相关*的点,但了解后你会觉得舒坦。这篇文章里也提到了些关于安全的提示,你可能会感兴趣。也许有一天你会有自己的UI库,这些都会派上用场的,我真的希望如此。
* * *
在客户端 UI 库变得普遍且具有基本保护作用之前,应用程序代码通常是先构建 HTML,然后把它插入 DOM 中:
~~~jsx
const messageEl = document.getElementById('message');
messageEl.innerHTML = '<p>' + message.text + '</p>';
~~~
这样看起来没什么问题,但当你`message.text`的值类似`'<img src onerror="stealYourPassword()">'`时,**你不会希望别人写的内容在你应用的 HTML 中逐字显示的。**
(有趣的是:如果你只是在前端渲染,这里为`<script>`标签,JavaScript 代码不会被运行。但[不要因此](https://gomakethings.com/preventing-cross-site-scripting-attacks-when-using-innerhtml-in-vanilla-javascript/)让你陷入已经安全的错觉。)
为什么防止此类攻击,你可以用只处理文本的`document.createTextNode()`或者`textContent`等安全的 API。你也可以事先将用户输入的内容,用转义符把潜在危险字符(`<`、`>`等)替换掉。
尽管如此,这个问题的成本代价很高,且很难做到用户每次输入都记得转换一次。**因此像React等新库会默认进行文本转义:**
~~~jsx
<p>
{message.text}
</p>
~~~
如果`message.text`是一个带有`<img>`或其他标签的恶意字符串,它不会被当成真的`<img>`标签处理,React 会先进行转义*然后*插入 DOM 里。所以`<img>`标签会以文本的形式展现出来。
要在 React 元素中渲染任意 HTML,你不得不写`dangerouslySetInnerHTML={{ __html: message.text }}`。**其实这种愚蠢的写法是一个功能**,在 code reviews 和代码库审核时,你可以非常清晰的定位到代码。
* * *
**这意味着React完全不惧注入攻击了吗?不**,HTML 和 DOM 暴露了[大量攻击点](https://github.com/facebook/react/issues/3473#issuecomment-90594748),对 React 或者其他 UI 库来说,要减轻伤害太难或进展缓慢。大部分存在的攻击方向涉及到属性,例如,如果你渲染`<a href={user.website}`,要提防用户的网址是`'javascript: stealYourPassword()'`。 像`<div {...userData}>`写法几乎不受用户输入影响,但也有危险。
React[可以](https://github.com/facebook/react/issues/10506)逐步提供更多保护,但在很多情况下,威胁是服务器产生的,这不管怎样都[应该](https://github.com/facebook/react/issues/3473#issuecomment-91327040)要避免。
不过,转义文本这第一道防线可以拦下许多潜在攻击,知道这样的代码是安全的就够了吗?
~~~jsx
// 自动转义
<p>
{message.text}
</p>
~~~
**好吧,也不总是有效的**。这就是`$$typeof`的用武之地了。
* * *
React 元素(elements)是设计好的*plain object*:
~~~jsx
{
type: 'marquee',
props: {
bgcolor: '#ffa7c4',
children: 'hi',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'),
}
~~~
虽然通常用`React.createElement()`创建它,但这不是必须的。有一些 React 用例来证实像上面这样的*plain object*元素是有效的。当然,你不会*想*这样写的,但这[可以用来](https://github.com/facebook/react/pull/3583#issuecomment-90296667)优化编译器,在 workers 之间传递 UI 元素,或者将 JSX 从 React 包解耦出来。
但是,如果你的服务器有允许用户存储任意 JSON 对象的漏洞,而前端需要一个字符串,这可能会发生一个问题:
~~~jsx
// 服务端允许用户存储 JSON
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {
__html: '/* 把你想的搁着 */'
},
}, // ...};
let message = { text: expectedTextButGotJSON };
// React 0.13 中有风险
<p> {message.text}</p>
~~~
在这个例子中,React 0.13[很容易](http://danlec.com/blog/xss-via-a-spoofed-react-element)受到 XSS 攻击。再次声明,**这个攻击是服务端存在漏洞导致的**。不过,React 会为了大家的安全做更多工作。从 React 0.14 开始,它做到了。
React 0.14 修复手段是用 Symbol 标记每个 React 元素(element):
~~~jsx
{
type: 'marquee',
props: {
bgcolor: '#ffa7c4',
children: 'hi',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'),}
~~~
这是个有效的办法,因为JSON不支持`Symbol`类型。**所以即使服务器存在用JSON作为文本返回安全漏洞,JSON 里也不包含`Symbol.for('react.element')`**。React 会检测`element.$$typeof`,如果元素丢失或者无效,会拒绝处理该元素。
特意用`Symbol.for()`的好处是**Symbols 通用于 iframes 和 workers 等环境中**。因此无论在多奇怪的条件下,这方案也不会影响到应用不同部分传递可信的元素。同样,即使页面上有很多个 React 副本,它们也 「接受」 有效的`$$typeof`值。
## 摘自
[ 为什么React元素有一个$$typeof属性?](https://overreacted.io/zh-hans/why-do-react-elements-have-typeof-property/)
- 文档说明
- 大厂面试题
- HTML
- 001.如何遍历一个dom树
- 002.为什么操作DOM会很慢
- 003.浏览器渲染HTML的步骤
- 004.DOM和JavaScript的关系
- JS
- 001.数组扁平化并去重排序
- 002.高阶函数
- 003.sort() 对数组进行排序
- 004.call 、 apply 和bind的区别
- 006.0.1+0.2为什么等于0.30000000000000004
- 011.var、let、const 的区别及实现原理?
- 010.new操作符都做了什么
- 009.a.b.c.d 和 a['b']['c']['d'],哪个性能更高?
- 016.什么是防抖和节流?有什么区别?如何实现?
- 017.['1', '2', '3'].map(parseInt) what & why ?
- 018.为什么 for 循环嵌套顺序会影响性能?
- 019.介绍模块化发展历程
- 020.push输出问题
- 021.判断数组的三个方法
- 022.全局作用域中,用 const 和 let 声明的变量不在 window 上,那到底在哪里?如何去获取?
- 023.输出以下代码的执行结果并解释为什么
- 024.ES6 代码转成 ES5 代码的实现思路是什么
- 025.为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因。
- 026.数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少
- 027.变量类型
- 028.原型和原型链
- 029.作用域和闭包
- 030. 异步
- 031.ES6/7 新标准的考查
- 024.事件冒泡/事件代理
- 025.手写 XMLHttpRequest 不借助任何库
- 026.什么是深拷贝?
- 0027.克隆数组的方法
- 0028.ES6之展开运算符(...)
- 0029.arguments
- 0030. requestAnimationFrame
- 0031.递归爆栈问题与解决
- 021.简单改造下面的代码,使之分别打印 10 和 20
- 032.箭头函数与普通函数
- 033.去除掉html标签字符串里的所有属性
- 034.查找公共父节点
- 035.Promise
- 0036.JSON.stringify ()
- CSS
- 001. BFC
- 002.介绍下 BFC、IFC、GFC 和 FFC
- 003.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景
- 004.怎么让一个 div 水平垂直居中
- 005.重排重绘
- 006.inline/block/inline-block的区别
- 007.选择器的权重和优先级
- 008.盒模型
- 009.清除浮动
- 010.flex
- 011.nth-child和nth-of-type的区别
- 0012.overflow
- 0013.CSS3中translate、transform和translation的区别和联系
- 0014.flex
- 0015.px、em、rem
- 0016.width:100%
- 网络
- 001.讲解下HTTPS的工作原理
- 002.介绍下 HTTPS 中间人攻击
- 003.谈谈你对TCP三次握手和四次挥手的理解
- 004.A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态
- 005.简单讲解一下http2的多路复用
- 006. 介绍下 http1.0、1.1、2.0 协议的区别?
- 007.永久性重定向(301)和临时性重定向(302)对 SEO 有什么影响
- 008.URL从输入到页面展示的过程
- 009.接口如何防刷
- 010.http状态码?
- 0111.跨域/如何解决?
- 012.cookie 和 localStorage 有何区别?
- 013.Fetch API
- 014.跨域Ajax请求时是否带Cookie的设置
- 0015.协商缓存和强缓存
- 性能优化
- 001.前后端分离的项目如何seo
- 002.性能优化的方法
- 003.防抖和节流
- React
- 001.React 中 setState 什么时候是同步的,什么时候是异步的?
- 002.Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的想法。
- 003.Hooks 的特别之处
- 004.元素和组件有什么区别?
- 005.什么是 Pure Components?
- 006.HTML 和 React 事件处理有什么区别?
- 007.如何将参数传递给事件处理程序或回调函数?
- 008.如何创建 refs?
- 009.什么是 forward refs?
- 010.什么是 Virtual DOM?
- 011.什么是受控组件、非受控组件?
- 012.什么是 Fragments ?
- 013.为什么React元素有一个$$typeof属性?
- 014.如何在 React 中创建组件?
- 015.React 如何区分 Class 和 Function?
- 016.React 的状态是什么?
- 017.React 中的 props 是什么?
- 018.状态和属性有什么区别?
- 019.如何在 JSX 回调中绑定方法或事件处理程序?
- 020.什么是 "key" 属性,在元素数组中使用它们有什么好处?
- 021.为什么顺序调用对 React Hooks 很重要?
- 022.setState如何知道该做什么?
- 023.hook规则?
- 024.Hooks 与 Class 中调用 setState 有不同的表现差异么?
- 025.useEffect
- 026.fiber的作用
- 027.context的作用?
- 028.setState何时同步何时异步?
- 029.react性能优化
- 030.fiber
- 031.React SSR
- 异步
- 001.介绍下promise
- 002.Async/Await 如何通过同步的方式实现异步
- 003.setTimeout、Promise、Async/Await 的区别
- 004.JS 异步解决方案的发展历程以及优缺点
- 005.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
- 006.模拟实现一个 Promise.finally
- 012.简单手写实现promise
- 015.用Promise对象实现的 Ajax
- 007.简单实现async/await中的async函数
- 008.设计并实现 Promise.race()
- 009.Async/await
- 010.珠峰培训promise
- git
- 001.提交但没有push
- 002.gitignore没有作用?
- Node
- 001.用nodejs,将base64转化成png文件
- Koa
- 001.koa和express的区别
- 数据库
- redux
- 001.redux 为什么要把 reducer 设计成纯函数
- 002.在 React 中如何使用 Redux 的 connect() ?
- 003.mapStateToProps() 和 mapDispatchToProps() 之间有什么区别?
- 004.为什么 Redux 状态函数称为 reducers ?
- 005.如何在 Redux 中发起 AJAX 请求?
- 006.访问 Redux Store 的正确方法是什么?
- 007.React Redux 中展示组件和容器组件之间的区别是什么?
- 008.Redux 中常量的用途是什么?
- 009.什么是 redux-saga?
- 设计模式
- 公司题目
- 001.饿了么
- 001.div垂直水平居中(flex、绝对定位)
- 002.React子父组件之间如何传值
- 003.Emit事件怎么发,需要引入什么
- 004.介绍下React高阶组件,和普通组件有什么区别
- 005.一个对象数组,每个子对象包含一个id和name,React如何渲染出全部的name
- 006.在哪个生命周期里写
- 007.其中有几个name不存在,通过异步接口获取,如何做
- 008.渲染的时候key给什么值,可以使用index吗,用id好还是index好
- 009.webpack如何配sass,需要配哪些loader
- 010.配css需要哪些loader
- 011.如何配置把js、css、html单独打包成一个文件
- 012.监听input的哪个事件,在什么时候触发
- 013.两个元素块,一左一右,中间相距10像素
- 014.上下固定,中间滚动布局如何实现
- 016.取数组的最大值(ES5、ES6)
- 017.apply和call的区别
- 018.ES5和ES6有什么区别
- 019.some、every、find、filter、map、forEach有什么区别
- 020.上述数组随机取数,每次返回的值都不一样
- 021.如何找0-5的随机数,95-99呢
- 022.页面上有1万个button如何绑定事件
- 023.如何判断是button
- 024.页面上生成一万个button,并且绑定事件,如何做(JS原生操作DOM)
- 025.循环绑定时的index是多少,为什么,怎么解决
- 026.页面上有一个input,还有一个p标签,改变input后p标签就跟着变化,如何处理
- 浏览器相关
- 001.性能优化
- 002.web安全
- 003.获取浏览器大小
- 004.从输入 URL 到页面加载完成的过程中都发生了什么事情?
- 后端
- 001.分布式
- zuku
- 字节
- webpack
- webpack的打包原理是什么
- Webpack-- 常见面试题
- webscoket