前端应用状态管理——状态提升

React.js 并没有提供好的解决方案来管理这种组件之间的共享状态。而后续引入的 Redux 这样的状态管理工具来帮助我们来管理这种共享状态。

挂载阶段的组件生命周期

React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载。React.js 为了让 coder 能更好地掌握组件的挂载过程:

  1. constructor()
    所有关于组件自身的状态的初始化工作;
  2. componentWillMount()
    一些组件启动的动作,包括想 Ajax 数据的拉取操作、一些定时器的启动等等;
  3. render()
  4. // 构造 DOM 元素插入页面
  5. componentDidMount()
    有些组件启动工作是依赖 DOM 的,例如动画的启动,而 componentWillMount 的时候组件还没挂载完成,所以无法进行这些启动工作,这时候就可以放在 componenyDidMount 当中;
  6. // ...一系列操作
  7. componentWillUnmount()
    在组件销毁的时候,做定时器和其他数据的清理工作;
  8. // 从页面中删除

补充更新阶段的组件生命周期:

  1. shouldComponentUpdate(nextProps, nextState)
    可以通过该方法控制组件是否重新渲染;若返回 false,组件就不会重新渲染;该生命周期在 React.js 性能优化上很有用;
  2. componentWillReceiveProps(nextProps)
    组件从父组件接受到新的 props 之前调用;
  3. componentWillUpdate()
    组件开始重新渲染之前调用;
  4. componentDidUpdate()
    组件重新渲染并且把更改变更到真实的 DOM 以后调用;

关于生命周期的官网文档:https://facebook.github.io/react/docs/react-component.html

深度剖析:如何实现一个 Virtual DOM 算法:https://github.com/livoras/blog/issues/13

ref 和 React.js 中的 DOM 操作

React.js 当中提供了 ref 属性来帮助我们获取已经挂载的元素的 DOM 节点,该属性值要是一个函数,当宿主元素在页面上挂载完成以后,React.js 就会调用这个函数,并且把该 DOM 节点传给这个函数。

能不用 ref 就不用。特别是要避免用 ref 来做 React.js 本来就可以帮助你做到的页面自动更新的操作和事件监听。多余的 DOM 操作其实是代码里面的“噪音”,不利于我们理解和维护。

props.children 和容器类组件

使用自定义组件时,可以在其中嵌套 JSX 结构,嵌套的结构在组件内部都可以通过 props.children 获取到,这种组件编写方式在编写容器类型的组件当中非常有用。

dangerouslySetInnerHTML 和 style 属性

出于安全考虑的原因(XSS 攻击),在 React.js 当中所有的表达式插入的内容都会被自动转移掉;就相当于 jQuery 里面的 text(...) 函数一样,任何的 HTML 格式都会被转移掉。

React.js 提供了一个属性 dangerouslySetInnerHTML,可以让我们设置动态设置元素的 innerHTML

1
2
3
4
5
6
7
8
class Dyn extends Component {
render () {
return (
<div dangerouslySetInnerHTML={{__html: this.state.content}}
></div>
);
}
}

PropTypes 和组件参数验证

JavaScript 的灵活性体现在弱类型、高阶函数等语言特性上;因为它的弱类型,常常意味着不是很安全。
所以近年来出现了类似 TypeScriptFlow 等技术,来弥补 JavaScript 这方面的缺陷。

React.js 提供了一种机制,让 coder 可以给组件的配置参数加上类型验证。引入 React 提供的第三方库 prop-types 来进行类型验证。示例如下:

1
2
3
4
5
6
7
8
9
10
11
import PropTypes from 'prop-types';
class P extends Component {
static propTypes = {
user: PropTypes.Object.isRequired,
}
render () {
return (
<div>{this.user.name}</div>
);
}
}

prop-types 第三方库的官方文档:[https://facebook.github.io/react/docs/typechecking-with-proptypes.html]{https://facebook.github.io/react/docs/typechecking-with-proptypes.html}

组件参数验证在构建大型的组件库的时候相当有用:

  1. 可以帮助我们迅速定位这种类型错误,让我们组件开发更加规范;
  2. 也起到了一个说明文档的作用,如果大家都约定写 propTypes,那在使用别人写的组件时,只要看到组件的 propTypes 就清晰地知道该组件能接受什么参数、什么参数是可选、什么参数是必选。

组件的命名和方法的定义顺序

统一规范化处理事件命名和方法会带来语义化组件的好处:

  1. 组件的私有方法都用 _ 开头;
  2. 所有事件监听的方法都用 handle 开头;
  3. 把事件监听方法传给组件时,属性名用 on 开头;

组件的内容编写顺序如下:

  1. static 开头的类属性,如 defaultProps, propTypes
  2. 构造函数 constructor
  3. getter / setter
  4. 组件生命周期
  5. _ 开头的私有方法
  6. 事件监听方法,hanle*
  7. render* 开头的方法
    有时候 render() 方法里面的内容会分开到不同函数里面进行,这些函数都以 render* 开头
  8. render() 方法

渲染显示代码块

要注意进行一系列的敏感符号的替换,以避免用户可以输入任意的 HTML 标签,用 <script> 执行任意的 JS 代码造成的 XSS 漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const CodeBlock = (props)=>{
const _getCodeContent = (content)=>{
return content
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quote;")
.replace(/'/g, "&#039;")
.replace(/`([\S\s]+?)`/g, "<code>$1</code>");
};
return ( <div dangerouslySetInnerHTML={{__html: _getCodeContent(props.content)}}></div> );
}
ReactDOM.render(
<CodeBlock content="`<script>console.log('XSS攻击');</script>`" />,
document.getElementById('root')
);