最近接触的一个项目中使用了React为主要框架,虽然之前也多多少少学过一些React,但是还需要更深入的学习和更熟练的掌握,所以最近我打算好好的读上几本React的书,今天带来第一本书:《react-tutorial》的学习总结。
技术在没有真正使用之前,没法评价哪一个好,没有最好的,只有最合适的。
搭建一个现代的前端开发环境配套的工具有很多,比如 Grunt / Gulp / Webpack / Broccoli,
这些都是要解决前端工程化问题。
而ES6 编译工具 Babel,则内置了对 JSX 语法的支持。
前端资源加载/打包工具,只需简单的配置就可提供前端工程化需要的各种功能,
如有需要还可被整合到其他如 Grunt / Gulp 的工作流
npm install -g webpack
使用 webpack.config.js 配置文件
要编译 JSX,先安装对应的 loader: npm install babel-loader –save-dev
webpack配置与使用可参考:
http://www.cnblogs.com/vajoy/p/4650467.html
http://www.jianshu.com/p/b95bbcfc590d
详细了解:
webpack官网: http://webpack.github.io/
文档地址: http://webpack.github.io/docs/
module下的loaders 是最关键的一块配置,它告知 webpack 每一种文件都需要使用什么加载器来处理。
webpack所有的loader列表和使用方法可参考:http://webpack.github.io/docs/list-of-loaders.html
传统的 MVC 是将模板放在其他地方,比如 script 标签或者模板文件,再在 JS 中通过某种手段引用模板。按这种思路,想想多少次我们面对四处分散的模板片段不知所措?纠结模板引擎,纠结模板存放位置,纠结如何引用模板。
React 认为组件才是王道,而组件是和模板紧密关联的,组件模板和组件逻辑分离让问题复杂化了。所以就有了 JSX 这种语法,就是为了把 HTML 模板直接嵌入到 JS 代码里面,这样就做到了模板和组件关联,但是 JS 不支持这种包含 HTML 的语法,所以需要通过工具将 JSX 编译输出成 JS 代码才能使用。
1.小写的字符串是 HTML 标签,大写开头的变量是 React 组件。
2.HTML 里的 class 在 JSX 里要写成 className,因为 class 在 JS 里是保留关键字。同理某些属性比如 for 要写成 htmlFor。
3.表达式
属性值使用表达式,只要用 {} 替换 “”。
表达式是非常强大的,我们可以使用它在JSX标签间或属性中放置js的各种表达式或函数实现各种功能。
子组件也可以作为表达式使用,如:
// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
4.JSX注释:
在< >内部可使用js的注释,如果在一个组件的子元素位置使用注释要用 {} 包起来。
5.自定义 HTML 属性:
如果在 JSX 中使用的属性不存在于 HTML 的规范中,这个属性会被忽略。如果要使用自定义属性,可以用 data- 前缀。
6.属性扩散:
使用es6的写法,可给组件设置多个属性,如:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
props 对象的属性会被设置成 Component 的属性。
另外,jsx中:
style 属性接受由 CSS 属性构成的 JS 对象
onChange 事件表现更接近我们的直觉(不需要 onBlur 去触发)
组件的两个核心概念:props和state。
1.props
props 即组件的属性,由外部通过 JSX 属性传入设置,一旦初始设置完成,就可以认为 this.props 是不可更改的,所以不要轻易更改设置 this.props 里面的值(虽然对于一个 JS 对象你可以做任何事)。
2.state
state 即组件的当前状态,可以把组件简单看成一个“状态机”,
根据状态 state 呈现不同的 UI 展示。一旦状态(数据)更改,组件就会自动调用 render 重新渲染 UI,这个更改的动作会通过 this.setState()来触发。
划分状态的原则:让组件尽可能地少状态,便于组件易维护。
当更改某个数据需要更新组件 UI 的就可以认为是 state。
3.无状态组件
也可以用纯粹的函数定义无状态组件(没有状态和生命周期,只简单的接受 props 渲染生成 DOM 结构)。
无状态组件非常简单,开销低,可能的话尽量去使用无状态组件。比如使用箭头函数定义:
const HelloMessage = (props) => <div> Hello {props.name}</div>;
render(<HelloMessage name="John" />, mountNode);
因为无状态组件只是函数,没有实例返回,这点在想用 refs 获取无状态组件的时候要注意。
4.组件生命周期
1.初始化 this.state 的值,只在组件加载之前调用一次。
ES6中可以在构造函数中初始化状态,如:
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: props.initialCount };
}
render() {
// ...
}
}
2.加载(渲染)组件触发的生命周期函数:
componentWillMount()
只在加载之前调用,在 render 之前调用,可以在这个方法里面调用 setState 改变状态,并且不会导致额外调用一次 render
componentDidMount()
只在加载完成之后调用,在 render 之后调用,从这里开始可以通过 ReactDOM.findDOMNode(this) 获取到组件的 DOM 节点。
3.更新(重新渲染)组件触发的生命周期函数:
这些方法不会在首次 render 组件的周期调用。
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
4.卸载组件触发的生命周期函数:
componentWillUnmount
1.绑定事件
例:
...
handleClick(e) {
this.setState({ liked: !this.state.liked });
}
render() {
const text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick.bind(this)}>
You {text} this. Click to toggle.
</p>
);
}
...
React 里面绑定事件的方式和在 HTML 中绑定事件类似,使用驼峰式命名指定要绑定的事件类型(比如 onClick)属性为组件定义的一个方法如( {this.handleClick.bind(this)} )。
注意要显式调用 bind(this) 将事件函数上下文绑定要组件实例上,这也是 React 推崇的原则:尽量使用显式的容易理解的js代码。
2.合成事件和原生事件
React 实现了一个“合成事件”层,这个事件模型保证了和 W3C 标准保持一致,“合成事件”提供了额外的好处:事件委托。
“合成事件”会以事件委托的方式绑定到组件最上层,并且在组件卸载(unmount)的时候自动销毁绑定的事件。
原生事件:比如在 componentDidMount 方法里面通过 addEventListener 绑定的事件就是浏览器原生事件。
所有通过 JSX 方式绑定的事件都是绑定到“合成事件”,建议都使用 React 的方式处理事件。
3.给事件处理函数传递参数
方法:bind(this, arg1, arg2, …)
例:
render: function() {
return <p onClick={this.handleClick.bind(this, 'extra param')}>;
},
handleClick: function(param, event) {
// handle click
}
大部分情况下不需要通过查询 DOM 元素去更新组件的 UI,只要关注设置组件的状态(setState)。可能在某些情况下确实需要直接操作 DOM。
ReactDOM.render 组件返回的是对组件的引用,也就是组件实例(对于无状态状态组件来说返回 null),注意 JSX 返回的不是组件实例,它只是一个 ReactElement 对象。
例:
// A ReactElement
const myComponent = <MyComponent />
// render
const myComponentInstance = ReactDOM.render(myComponent, mountNode);
myComponentInstance.doSomething();
findDOMNode()
当组件加载到页面上之后(mounted),都可以通过 react-dom 提供的 findDOMNode() 方法拿到组件对应的 DOM 元素。
import { findDOMNode } from 'react-dom';
// Inside Component class
componentDidMound() {
const el = findDOMNode(this);
}
注意:findDOMNode() 不能用在无状态组件上。
Refs
另一种方式是通过在要引用的 DOM 元素上面设置一个 ref 属性指定一个名称,然后通过 this.refs.name 来访问对应的 DOM 元素。
例:
clearAndFocusInput() {
this.setState({ userInput: '' }, () => {
this.refs.theInput.focus(); //2.
});
}
render() {
return (
<div>
<div onClick={this.clearAndFocusInput.bind(this)}>
Click to Focus and Reset
</div>
<input
ref="theInput" //1.
value={this.state.userInput}
onChange={this.handleChange.bind(this)}
/>
</div>
);
}
如果 ref 是设置在原生 HTML 元素上,它拿到的就是 DOM 元素,如果设置在自定义组件上,它拿到的就是组件实例,这时候就需要通过 findDOMNode 来拿到组件的 DOM 元素。
因为无状态组件没有实例,所以ref 不能设置在无状态组件上,如果想要拿无状态组件的 DOM 元素的时候,就需要用一个状态组件封装一层,然后通过 ref 和 findDOMNode 去获取。
可以使用 ref 到的组件定义的任何公共方法,比如 this.refs.myTypeahead.reset()
Refs 是访问到组件内部 DOM 节点唯一可靠的方法
Refs 会自动销毁对子组件的引用(当子组件删除时)
refs注意:
不要在 render 或者 render 之前访问 refs
不要滥用 refs,比如只是用它来按照传统的方式操作界面 UI:找到 DOM -> 更新 DOM
未完待续
未完待续
可以参考我关于Redux的总结,后续还会不断进行完善:
http://www.atatech.org/articles/58588
React中文网:
http://reactjs.cn/
React中文社区:
http://react-china.org/
React/React Native 的ES5 ES6写法对照表:
http://bbs.reactnative.cn/topic/15/react-react-native-%E7%9A%84es5-es6%E5%86%99%E6%B3%95%E5%AF%B9%E7%85%A7%E8%A1%A8