备战秋招,复习基础。若有错误,欢迎批评指正,共同进步!javascript
声明式:建立用户交互界面,高效渲染页面。声明式编写UI方便调试。html
组件化:使用组件传递数据,将应用状态和DOM拆分开。当内部状态(this.state
)改变时,组件会调用方法(render()
)从新渲染。java
箭头函数:var func = (x,y) => {return x+y;};
react
class → className
for → htmlFor
算法
样式 style须要{{}}
express
JSX:很像XML的js语法扩展。可用{js表达式}
→ 没有if else,只有三元表达式redux
Key:每次构建动态列表的时候,指定一个合适的key。在同一级元素之间保证惟一!segmentfault
组件API:promise
参考资料:react基本原理及性能优化性能优化
react只负责解决view层的渲染
参考资料:浅谈React的最大亮点——虚拟DOM
虚拟DOM是在DOM的基础上创建了一个抽象层,是一个js对象。对数据和状态所作的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。
虚拟DOM能够确保只对界面上真正变化的部分进行实际的DOM操做。
真实的DOM tree结构:
<div id="container">
<p>Real DOM</p>
<ul>
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
</div>
复制代码
虚拟DOM结构:
let virtualDomTree = CreateElement('div', { id: 'container' }, [
CreateElement('p', {}, ['Virtual DOM']),
CreateElement('ul', {}, [
CreateElement('li', { class: 'item' }, ['Item 1']),
CreateElement('li', { class: 'item' }, ['Item 2']),
CreateElement('li', { class: 'item' }, ['Item 3']),
]),
]);
let root = virtualDomTree.render(); //转换为一个真正的dom结构或者dom fragment
document.getElementById('virtualDom').appendChild(root);
function CreateElement(tagName, props, children) {
if (!(this instanceof CreateElement)) {
return new CreateElement(tagName, props, children);
}
this.tagName = tagName;
this.props = props || {};
this.children = children || [];
this.key = props ? props.key : undefined;
}
CreateElement.prototype.render = function() {
let el = document.createElement(this.tagName);
let props = this.props;
for (let propName in props) {
setAttr(el, propName, props[propName]);
}
this.children.forEach((child) => {
let childEl = (child instanceof Element) ? child.render() : document.createTextNode(child);
el.appendChild(childEl);
});
return el;
};
复制代码
参考资料:React之diff算法
计算出Virtual DOM中真正变化的部分,并只针对该部分进行原生DOM操做,而非从新渲染整个页面。
diff对树进行分层比较,只对比两棵树同级别的节点。跨层级移动节点,将会致使节点删除,从新插入,没法复用。
diff对组件进行类比较,类相同的递归diff子节点,不一样的直接销毁重建。diff对同一层级的子节点进行处理时,会根据key进行简要的复用。两棵树中存在相同key的节点时,只会移动节点。
在对比同一层级的子节点时,diff算法会以新树的第一个子节点做为起点遍历新树,寻找旧树中与之相同的节点。若是节点存在,则移动位置。若是不存在,则新建一个节点。 在这过程当中,维护了一个字段lastIndex,这个字段表示已遍历的全部新树子节点在旧树中最大的index。在移动操做时,只有旧index小于lastIndex的才会移动。 这个顺序优化方案其实是基于一个假设,大部分的列表操做应该是保证列表基本有序的。 能够推倒倒序的状况下,子节点列表diff的算法复杂度为O(n2)
getDefaultProps()
设置默认的propsgetInitialState()
定义初始this.statecomponentWillMount()
修改state的最后一次机会render()
建立一个虚拟DOM,表示组件的输出(惟一必须的方法)componentDidMount()
访问真实DOM,进行数据监听、数据请求componentWillReceiveProps()
可更新state,触发rendershouldComponentUpdate()
决定是否须要从新渲染,优化渲染性能componentWillUpdate()
从新渲染前调用componentDidUpdate()
访问并修改DOM,监听props或state变化。这里放setState必须加条件,不然会致使死循环。componentWillUnmount()
将组件从DOM中卸载并销毁`理想状况:
setState是“异步”的,调用setState只会提交一次state修改到队列中,不会直接修改this.state。 等到知足必定条件时,react会合并队列中的全部修改,触发一次update流程,更新this.state。
所以setState机制减小了update流程的触发次数,从而提升了性能。
因为setState会触发update过程,所以在update过程当中必经的生命周期中调用setState会存在循环调用的风险。
另外用于监听state更新完成,可使用setState方法的第二个参数,回调函数。在这个回调中读取this.state就是已经批量更新后的结果。
特殊状况:在实际开发中,setState的表现有时会不一样于理想状况。主要是如下两种。
在mount流程中调用setState:不会进入update流程,队列在mount时合并修改并render。
在setTimeout/Promise回调中调用setState:将不会进行队列的批更新,而是直接触发一次update流程。 这是因为setState的两种更新机制致使的,只有在批量更新模式中,才会是“异步”的。
setState会引起一次组件的更新过程,从而引起页面的从新绘制。主要会涉及如下几个生命周期函数:
资料参考:详解在React.js中使用PureComponent的重要性和使用方式
资料参考:React 的 PureComponent Vs Component
资料参考:什么时候使用Component仍是PureComponent?
PureComponent改变了生命周期方法 shouldComponentupdate
,而且会自动检查组件是否须要从新渲染。这时,只有PureComponent检测到 state 或者 props 发生变化(引用类型的引用变化,或基本类型string number值变化)时,PureComponent才会调用 render 方法。
浅比较源码:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
复制代码
使用pureComponent至关于在component中优化检测:
-----这是component中的优化 在purecomponent中默认包含-----
shouldComponentUpdate(nextProps, nextState) {
return (nextState.person !== this.state.person);
}
复制代码
class MyComponent extends PureComponent {...}
复制代码
若是一个纯组件(PureComponent)的 state 或 props 引用了一个新对象,那么这个组件就会被从新渲染(re-render)。
handleClick() {
this.setState(prevState => ({
words: prevState.items.concat(['new-item']) ← 引用新对象 浅比较不一样
}));
}
复制代码
使用PureComponent的最佳状况就是展现组件,它既没有子组件,也没有依赖应用的全局状态。
若是prop和state每次都会变,那么PureComponent的效率还不如Component!(由于还要浅比较)
不要在PureComponent中使用shouldComponentUpdate,由于根本没有必要~
<button onClick = { (e) => this.deleteRow(id,e) } Delete </button>
复制代码
等价于
<button onClick = { this.deleteRow.bind(this,id) } Delete </button>
复制代码
具体方法 调用中 e/this → e
deleteRow (id, e){...}
复制代码
true && expression
复制代码
为真才执行,取代if条件渲染
参考资料:react-router的实现原理
react-router就是控制不一样的url渲染不一样的组件。react-router在history库的基础上,实现了URL与UI的同步。
原理:DOM渲染完成以后,给window添加onhashchange事件监听页面hash的变化,而且在state属性中添加了route属性,表明当前页面的路由。
具体步骤:
1 当点击连接,页面hash改变时,触发绑定在 window 上的 onhashchange 事件;
2 在 onhashchange 事件中改变组件的 state中的route属性,react组件的state属性改变时,自动从新渲染页面;
3 页面随着 state 中的route属性改变,自动根据不一样的hash给Child变量赋值不一样的组件,进行渲染。
数据管理中心:单一数据源(全部state维护在一个根级store);状态只读(没法直接修改,严控修改执行);纯函数(只能用reducer描述修改)
Store:全局单例。
方法:
getState:获取state;
dispatch:触发action,更新state;
subcribe:订阅数据变动,注册监听器。
const store = createStroe(Reducer, initStore);
action:行为载体,映射响应Reducer。可成为数据载体,是store惟一数据源。
Reducer:修改行为的实质,用于描述如何修改数据的纯函数。
纯函数:对于相同的输入,永远会获得相同的输出,并且没有任何可观察的反作用,也不依赖外部环境的状态。
缘由:Redux只经过比较新旧两个对象的存储位置来比较新旧两个对象是否相同(浅比较)。若是你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。所以Redux认为没有任何改变,返回的state将为旧的state。两个state相同的话,页面就不会从新渲染了。由于比较两个Javascript对象全部的属性是否相同的的惟一方法是对它们进行深比较。可是深比较在真实的应用当中代价昂贵,由于一般js的对象都很大,同时须要比较的次数不少。所以一个有效的解决方法是做出一个规定:不管什么时候发生变化时,开发者都要建立一个新的对象,而后将新对象传递出去。同时,当没有任何变化发生时,开发者发送回旧的对象。也就是说,新的对象表明新的state。
复制代码
处理异步:引入Redux-thunk或者redux-promise这种中间件,延迟事件的派发。
!!!!!!!!!!!!!写一个!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
在函数定义组件中使用React特性
layout.ftl 最初渲染页面
<html ...>
<body>
<div id = "app-container"></div>
<script type = "text/javascript" ... ></script
</body>
</html>
复制代码
client.js 配置redux的页面
在此引入 store router
...
ReactDom.render(
...引入其余设置及组件
document.getElementById('app-container');
);
复制代码