整理过程中遇到了好用的前端js库:
immutable(持久化数据结构、结构共享):
Immutable 详解及 React 中实践 · Issue #3 · camsong/blog
classnames(支持对className的逻辑操作):
1.React 安装
使用Facebook提供的 create-react-app 脚手架工具构建react项目
create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。
create-react-app 自动创建的项目是基于 Webpack + ES6 。
配置 npm 的registry 下载package更快
$ npm config set registry https://registry.npm.taobao.org
配置后可通过下面方式来验证是否成功
$ npm config get registry
create-react-app构建启动项目
$ cnpm install -g create-react-app
$ create-react-app my-app
$ cd my-app/
$ npm start
附:create-react-app github地址
2.JSX语法
一种 JavaScript 的语法扩展。 推荐在 React 中使用 JSX 来描述用户界面
可以任意地在 JSX 当中使用 JavaScript 表达式,在 JSX 当中的表达式要包含在大括号里。
React DOM 在渲染之前默认会 过滤 所有传入的值。它可以确保你的应用不会被注入攻击。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(跨站脚本) 攻击。
如果你已经有了个 props 对象,并且想在 JSX 中传递它,你可以使用 ... 作为扩展操作符(Spread syntax)来传递整个属性对象。下面两个组件是等效的:
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
警告:
因为 JSX 的特性更接近 JavaScript 而不是 HTML , 所以 React DOM 使用 camelCase 小驼峰命名 来定义属性的名称,而不是使用 HTML 的属性名称。
例如,class 变成了 className,而 tabindex 则对应着 tabIndex。
3.state & props & render
state:
构造函数是唯一能够初始化 this.state 的地方
修改state通过this.setState({comment: 'Hello'})
状态更新可能是异步的,要是修改state的变量依赖本身,需要通过setState的callback解决:
incrementCount() {
const me = this;
me.setState((prevState) => {
return { count: prevState.count + 1 }
});
//ERROR
// me.setState({
// count : me.state.count + 1
// });
}
incrementTwo() {
const me = this;
me.incrementCount();
me.incrementCount();
}
props:
无论是使用函数或是类来声明一个组件,它决不能修改它自己的props
{props.children}获取包含的子组件列表
render:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
组件的返回值只能有一个根元素。这也是我们要用一个<div>来包裹所有<h1/><h2/>元素的原因。
布尔值、Null 和 Undefined 被忽略渲染时会被忽略
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
4.keys
React提供key属性,来提高元素变更之后重新渲染的效率
Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。
一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据的id作为元素的key
如果列表可以重新排序,我们不建议使用索引来进行排序,因为这会导致渲染变得很慢
不能通过props.key获取key的值。
举个例子:
<li>a</li>
<li>b</li>
改变后:
<li>c</li>
<li>a</li>
<li>b</li>
肉眼很容易看出来在第一行加了一个 c ,但React并不知道,而是一行一行的比较(a 与 c),所以需要在每个 li 都一个 key ,利于React辨别,从而提供效率
5.form 表单使用
html:
<textarea>
Hello there, this is some text in a text area
</textarea>
jsx:
<textarea value={this.state.value} onChange={this.handleChange} />
html:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
jsx:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
6.propTypes 属性约束 & 设置默认属性值
属性约束:
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
具体有哪些验证器:参考官网
属性默认值:
可以通过配置 defaultProps 为 props定义默认值:
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
// 为属性指定默认值:
Greeting.defaultProps = {
name: 'Stranger'
};
// 渲染 "Hello, Stranger":
ReactDOM.render(
<Greeting />,
document.getElementById('example')
);
提示:
类型检查发生在 defaultProps 赋值之后,所以类型检查也会应用在 defaultProps 上面。
7.refs
Refs 提供了一种访问在 render 方法中创建的 DOM 节点或 React 元素的方式
何时使用 Refs
下面是几个适合使用 refs 的情况:
处理焦点、文本选择或媒体控制。
触发强制动画。
集成第三方 DOM 库
如果可以通过声明式实现,则尽量避免使用 refs。
例如,不要在 Dialog 组件上直接暴露 open() 和 close() 方法,最好传递 isOpen 属性。
经典案例:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// 直接使用原生 API 使 text 输入框获得焦点
this.textInput.focus();
}
render() {
// 使用 `ref` 的回调将 text 输入框的 DOM 节点存储到 React
// 实例上(比如 this.textInput)
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
React 组件在加载时将 DOM 元素传入 ref
的回调函数,在卸载时则会传入 null
。ref
回调会在componentDidMount
或 componentDidUpdate
这些生命周期回调之前执行。
注意:
如果 ref 回调以内联函数的方式定义,在更新期间它会被调用两次,第一次参数是 null ,之后参数是 DOM 元素。这是因为在每次渲染中都会创建一个新的函数实例。
因此,React 需要清理旧的 ref 并且设置新的。通过将 ref 的回调函数定义成类的绑定函数的方式可以避免上述问题,但是大多数情况下无关紧要。
8.Fragments
React 中一个常见模式是为一个组件返回多个元素。Fragments 可以让你聚合一个子元素列表,并且不在DOM中增加额外节点
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
9.Error Boundaries错误边界
部分 UI 的异常不应该破坏了整个应用。为了解决 React 用户的这一问题,React 16 引入了一种称为 “错误边界” 的新概念。
如果一个类组件定义了一个名为 componentDidCatch(error, info):
的新方法,则其成为一个错误边界
错误边界无法捕获如下错误:
1.事件处理 (了解更多)
2.异步代码 (例如 setTimeout 或 requestAnimationFrame 回调函数)
3.服务端渲染
4.错误边界自身抛出来的错误 (而不是其子组件)
10.组件生命周期
组件的生命周期可分成三个状态:
Mounting:已插入真实 DOM
Updating:正在被重新渲染
Unmounting:已移出真实 DOM
生命周期的方法有:
componentWillMount | 在渲染前调用,在客户端也在服务端 |
componentDidMount | 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI) |
componentWillReceiveProps | 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用 |
shouldComponentUpdate | 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用 |
componentWillUpdate | 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用 |
componentDidUpdate | 在组件完成更新后立即调用。在初始化时不会被调用 |
componentWillUnmount | 在组件从 DOM 中移除的时候立刻被调用 |
forceUpdate:
component.forceUpdate(callback)
默认情况,当你的组件或状态发生改变,你的组件将会重渲。若你的render()方法依赖其他数据,你可以通过调用forceUpdate()来告诉React组件需要重渲。
调用forceUpdate()将会导致组件的 render()方法被调用,并忽略shouldComponentUpdate()。这将会触发每一个子组件的生命周期方法,涵盖,每个子组件的shouldComponentUpdate() 方法。若当标签改变,React仅会更新DOM。
通常你应该尝试避免所有forceUpdate() 的用法并仅在render()函数里从this.props和this.state读取数据。
11.ajax使用
React 组件的数据可以通过 componentDidMount 方法中的 Ajax 来获取,当从服务端获取数据时可以将数据存储在 state 中,再用 this.setState 方法重新渲染 UI。
当使用异步加载数据时,在组件卸载前使用 componentWillUnmount 来取消未完成的请求。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
fetch("https://api.example.com/items")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.name}>
{item.name} {item.price}
</li>
))}
</ul>
);
}
}
}
12.React UI库推荐
Element:一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库
https://eleme.github.io/element-react/#/zh-CN/quick-start
Ant Desgin:一个服务于企业级产品的设计体系,基于『确定』和『自然』的设计价值观和模块化的解决方案,让设计者专注于更好的用户体验
https://ant.design/docs/react/introduce-cn
UXCore:为企业后台而生
13.react router & react redux介绍
react router4.x :
官方文档:https://reacttraining.com/react-router/
官方文档提供了很多 examples ,文档也比较清楚,可以直接查看文档学习。
react redux:
核心思想:
1.单一数据源:
整个项目只有一个state树。
2.state只读:
只能通过触发action 修改state。
3.使用纯函数执行修改:
为了描述 action 如何改变 state tree ,你需要编写 reducers。
重要概念:
Action:
描述要干什么事情。准备需要的一些数据(一个简单对象)
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Reducer:
到底要怎么干。处理state,并返回新的state
function todoApp(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}
Store:
找到人正式干活。派发事件
store.dispatch(addTodo('Learn about actions'))
展示组件:
仅仅基于React,描述怎么展示,数据都从props里面获取
容器组件:
描述如何获取数据,状态更新,逻辑处理。基于react-redux
重要API:
createStore:http://www.redux.org.cn/docs/api/createStore.html
combineReducers:http://www.redux.org.cn/docs/api/combineReducers.html
applyMiddleware:http://www.redux.org.cn/docs/api/applyMiddleware.html
react-redux文档 :http://www.redux.org.cn/docs/react-redux/api.html
综合案例参见官网TODOS:https://github.com/reduxjs/redux/tree/master/examples/todos
效果图:
14.拓展
react动画:
https://reactcommunity.org/react-transition-group/
react单元测试:
React测试必须使用官方的测试工具库,但是它用起来不够方便,所以有人做了封装,推出了一些第三方库,其中Airbnb公司的Enzyme最容易上手。
官方也推荐使用 Enzyme 它在相同的功能上提供了一个更棒的高级 API。
Enzyme是官方测试工具库的封装,它模拟了jQuery的API,非常直观,易于使用和学习。
它提供三种测试方法:
shallow
render
mount