目录
目录
文章目录
  1. ###入门印象
  2. ###JSX
  3. ###版本演进
  4. ###生命周期函数
  5. ###数据流
  6. ###事件处理
  7. ###组件
  8. ###动画
  9. ###实例
  10. ###技术栈(babel)
  11. ###ERROR集合
  12. ###工作流程
  13. ###番外(ESLint)
  14. ###番外($.ajax)
  15. ###资源链接

React入门

###入门印象

  • 传统组件化的特点是把组件和原生DOM节点的渲染割裂起来
  • 现代的组件架构鼓励原生DOM节点和自定义组件的统一渲染融合,比如React以及未来的Web Components规范
  • React最为人称道的是,它是一个专注于组件架构的类库。
  • 要想用好React,我们必须跳出以往的思维,拥抱React的理念和思想,比如状态,虚拟DOM,组合优于继承,单向数据流。
  • React专注于组件架构,所以模块系统可以直接采用CommonJS,测试框架可以使用Mocha,生态圈则可以直接依托npm,工具可以采用现成的Browserify或Webpack。
  • React本质上是一个“状态机”,可以帮助开发者管理复杂的随着时间而变化的状态。它以一个精简的模型实现了这一点。React只关心两件事:更新DOM和响应事件。
  • React不处理Ajax、路由和数据处理,也不规定数据组织的方式,它不是一个MVC框架。事实上,它已经在数个MVC框架中被用来渲染视图了。
  • React 不是一个完整的MVC,MVVM框架
  • React 跟 Web Components不冲突
  • React 的特点就是轻

###JSX

  • 在React中,组件是用于分离关注点的,而不是被当作模版或处理显示逻辑的。
  • React包含了一种可选的类HTML标记语言。
  • JSX即JavaScript XML,一种在React组件内部构建标签的类XML语法。
  • JSX 会转换为原生的JS函数,因此有一些关键词我们是不能用的
    • for 要转换成 htmlFor
    • class 要转换成 className
  • React把所有的内联样式都规范化为了驼峰形式,与JS中DOM的style属性一样。
  • var R = React.DOM; React在React.DOM.*命名空间下提供了一系列的工厂,如:R.div(),R.hr(),R.h2(null,'label text');
  • React 自动绑定了组件所有方法的作用域,因此你不需要手动绑定。
  • this.props.children React将开始标签和结束标签之间的所有的子节点保存在一个名为 this.props.children 的特殊组件属性中。
  • React.createClass()

###版本演进

  • React 「一分为二」
  • 原本的 react 被拆分为 react 及 react-dom 两个 package。

    • 其中 react package 中包含React.createElement、 .createClass、 .Component, .PropTypes, .Children 这些 API。
    • 而 react-dom package 中包含 ReactDOM.render、 .unmountComponentAtNode、 .findDOMNode。
  • 原本在服务端渲染用的两个 API .renderToString 和 .renderToStaticMarkup 被放在了 react-dom/server 中。

  • 此外,原本 React.addons 下面的工具全部变成了独立的 package

    • react-addons-clone-with-props
    • react-addons-create-fragment
    • react-addons-css-transition-group
    • react-addons-linked-state-mixin
    • react-addons-perf
    • react-addons-pure-render-mixin
    • react-addons-shallow-compare
    • react-addons-test-utils
    • react-addons-transition-group
    • react-addons-update
    • ReactDOM.unstable_batchedUpdates (在 react-dom 中)
  • refs 变成了真正的 DOM 节点。当我们需要获取 React 组件上某个 DOM 节点时,React 提供了 refs 方法方便我们快速引用。为了方便我们使用,React 还「贴心」地对 refs 做了一层封装,使用 。this.refs.xxx.getDOMNode() 或 React.findDOMNode(this.refs.xxx) 可以获取到真正的 DOM 节点。
  • react-tools 及 JSXTransformer.js 已弃用。
  • React.initializeTouchEvents 已弃用。
  • props 一旦创建永远不可修改,因此 .setProps 及 .replaceProps 已废弃。
  • children 不可以传对象类型,推荐传入数组,或使用 React.createFragment 方法(其实就是转换为了数组)。
  • React.addons.classSet 已经移除,使用 classnames package 替代。

###生命周期函数

  • 创建时

    • getDefaultProps() // 对于那些没有被父辈组件指定props属性的新建实例来说,这个方法返回的对象可用于为实例设置默认的props值
    • getInitialState() // 初始化每个实例的state
    • componentWillMount() // 该方法在完成首次渲染之前被调用,这也是在render方法调用前可以修改组件state的最后一次机会
    • render() // 创建一个虚拟DOM,用来表示组件的输出。对于一个组件来说,render是唯一一个必需的方法,并且有特定的规则。(是什么规则?)
    • componentDidMount() // 在render方法成功调用并且真实的DOM已经被渲染出来之后,你可以在componentDidMount内部通过(?)来访问到它。(this.getDOMNode())
  • 存在时

    • componentWillReceiveProps() // 任意时刻,组件的props都可以通过父辈组件来修改,在这个函数中你也将获得更改props对象以及更新state的机会
    • shouldComponentUpdate()
    • componentWillUpdate() // 组件会在接收到新的props或者state进行渲染之前,调用该方法,不可以在该方法中更新state或者props
    • componentDidUpdate() //
  • 销毁时

    • componentWillUnmount() // 在组件被销毁之前调用这个方法,来做一些清除工作

###数据流

  • 在React中,数据的流向是单向的,从父节点传递到子节点,因而组件是简单且易把握的,他们只需从父节点获取props渲染即可。
  • React内部还具有自己的状态state,这些状态只能在组件内修改。
  • 你可以把React组件看成一个函数,它接受props和state作为参数,返回一个虚拟的DOM表现。
  • 使用props把任意类型的数据传递给组件。
  • 可以通过this.props访问props,但绝对不可以通过这种方式修改它,一个组件绝对不可以自己修改自己的props。
  • propTypes 通过在组件中定义一个配置对象,React提供了一种验证props的方式。
  • state只存在于组件内部
  • 千万不能直接修改this.state,永远记得要通过this.setState方法来修改。
  • props are immutable: they are passed from the parent and are “owned” by the parent.
  • To implement interactions, we introduce mutable state to the component. this.stateis private to the component and can be changed by calling this.setState(). When the state updates,the component rerenders itself.
  • 使用props在整个组件树中传递数据和配置
  • 避免在组件内部修改this.props或调用this.setProps,请把props当作只读的(immutable)
  • 使用props来做事件处理器,与子组件通信
  • 使用state存储简单的视图状态
  • 使用this.setState来设置状态,而不要使用this.state直接修改状态
  • 不要尝试把props复制到state中,要尽可能把props当作数据源。
  • 更新组件内部状态会触发组件重绘。
  • 更新组件状态有两种方案,组件的setState()replaceState(),更多的情况下会使用setState(),仅仅是把传入的对象合并到已有的state对象上。

###事件处理

  • React 通过将事件处理器绑定到组件上来处理事件,在事件被触发的同时,更新组件的内部状态(state),组件内部状态(state)的更新会触发组件重绘。
  • React 绑定事件处理器的语法和HTML语法非常类似,但性能是不一样的。

###组件

  • 在传统HTML中,元素是构成页面的基础单元,但在React中,构成页面的基础单元是React组件。你可以把React组件理解成混入了JS表达能力的HTML元素,实际上写React代码主要就是构建组件,就像编写HTML文档时使用元素一样。
  • 复合(composability)
  • 本质上,一个组件就是一个JS函数,它接受属性(props)和状态(state)作为参数,并输出渲染好的HTML。
  • 组件的核心理念就是可预测性和可预知性。
  • 在React中,表单组件有两种类型:约束组件和无约束组件。
  • 无约束组件可以用在基本的无须任何验证或者输入控制的表单中。
  • 约束组件的模式与React其他类型组件的模式一致,表单组件的状态交由React组件控制,状态值被存储在state中。

###动画

  • 动画可以让用户体验变得更加流畅与自然。
  • TransitionGroup(插件) + CSS3 => CSSTransitionGroup(CSS渐变组)
  • CSSTransitionGroup 会在合适的渲染、重渲染时间点有策略地添加和移除元素的class,以此来简化将CSS动画应用于渐变的过程。而你唯一需要做的是给些class写明合适的样式。
  • var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
  • <ReactCSSTransitionGroup transitionName='xx'>{...}</ReactCSSTransitionGroup>
  • 使用渐变组需要注意两点:1、渐变组会延迟子组件的移除直到动画完成。2、渐变组的每一个子组件都必须设置一个唯一的key属性。
  • 默认情况下,渐变组同时启用了进入和退出的动画,可以通过给组件添加transitionEnter={false}或者transitionLeave={false}属性来控制。

###实例

  • 官网留言组件,数据流,事件绑定,如果看不懂、迷糊,很简单,那就反复看上几十遍,手敲十几遍,就什么都懂了。
  • 官方案例基本思想:React构件组件,jQuery之Ajax获取数据,Express搭建服务器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<CommentBox url='/api/comments' pollInterval={2000} />
CommentBox
|---this.setState({data: data})
|---this.props.url this.props.pollInterval
|---CommentList
| |---data={this.state.data}
| |---Comment
| |
|
|---onCommentSubmit={this.handleCommentSubmit}
|---CommentForm
|---this.state.author
|---this.state.text
|---this.setState({author:'',text:'')
|---this.setState({author: e.target.value)
|---this.setState({text: e.target.value)

###官方示例(react-15.3.2)

  • 总体来说需要三个文件的支持,react.js、react-dom.js、babel.js,解释就是,react是核心,react-dom是操作DOM,babel.js是编译(因为jsx或者es6大部分浏览器还无法直接识别的缘故)。
  • 如果你直接使用babel.js插入到html中,那么这样是没有什么问题的,但是只能用于开发环境,如果是上线的话,是需要提前编译好,而不是在浏览器中来编译。如何手工来编译呢,那么先安装命令吧,npm isntall -g babel-cli,这是全局安装babel命令;接着再安装语言版本,这个直接放在工程中的开发依赖中好了,使用这个命令npm install --save-dev babel-preset-react,然后就可以命令行来编译了,babel example.js --presets react --out-dir=build,这个命令的意思是编译example.js并输出到build目录下。
  • basic-jsx(内置jsx),basic-jsx-external(外部jsx),basic-jsx-harmony(使用es6语法的jsx),basic-jsx-precompile(预编译jsx语法)
  • jquery-bootstrap(react和bootstrap结合)
  • jquery-mobile(react和jquery mobile结合)

###技术栈(babel)

  • 在React实例中发现这样一段 JS 引入<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>,这是什么意思?
  • 使用babel-standalone模块提供的浏览器版本,将其插入网页,网页中实时将ES6代码转为ES5,对性能会有影响。生产环境需要加载已经转码完成的脚本。浏览器会提示:You are using the in-browser Babel transformer. Be sure to precompile your scripts for production - https://babeljs.io/docs/setup/
  • 在react 0.14前,浏览器端实现对jsx的编译依赖jsxtransformer.js 。
  • 在react 0.14后,这个依赖的库改为browser.js,页面script标签的type也由text/jsx改为text/babel,但是以上只能用来测试学习react,生产环境需要借助编译工具事先将jsx编译成js,对应的这个工具也由react-tool更换为babel。
  • .babelrcbabel的配置文件,必需的,放置在根目录下。包含两个字段,"presets""plugins",其中 presets 很重要,官方提供了三个规则集,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ES2015转码规则
$ npm install --save-dev babel-preset-es2015
react转码规则
$ npm install --save-dev babel-preset-react
ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3
.babelrc文件内容如下:
{
"presets": ["es2015","react","stage-3"]
}

###ERROR集合

  • 在 render 中的标签需要闭合,自闭合标签也需要闭合。
  • Uncaught Invariant Violation: Minified React error #37;浏览器提示这种错误,最后在ReactDOM.render()发现错误,第二个参数指定的容器不存在,导致错误。
  • React 对this的态度,React 自动绑定了组件所有方法的作用域,因此你永远都不需要手动绑定。但是,如果产生了闭包,就需要手动来绑定了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
handleClick: function(event) {...}
render: function(){
return ( <div onClick={this.handleClick} >...</div> )
}
render: function(){
return ( <div onClick={this.handleClick.bind(this)} >...</div> )
}
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var TodoList = React.createClass({
getInitialState: function() {
return {items: ['hello', 'world', 'click', 'me']};
},
handleAdd: function() {
var newItems =
this.state.items.concat([prompt('Enter some text')]);
this.setState({items: newItems});
},
handleRemove: function(i) {
var newItems = this.state.items;
newItems.splice(i, 1);
this.setState({items: newItems});
},
render: function() {
var items = this.state.items.map(function(item, i) {
return (
<div key={item} onClick={this.handleRemove.bind(this, i)} className='item'>
{item}
</div>
)
}.bind(this));
return (
<div className='todoList'>
<button onClick={this.handleAdd} className='submit'>
Add Item
</button>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{items}
</ReactCSSTransitionGroup>
</div>
)
}
});

###工作流程

  • 一个是编译:将 JSX (或者你用了 ES6 的特性的话也需要这一步)编译到被浏览器广泛支持的 ES5 的代码。

  • 第二个是进行模块绑定:因为浏览器不识得这些模块(通常是编译成 ES5 的 CommonJS 规范实现的模块,这并不是浏览器支持的东西),不知道怎么去加载他们,模块绑定就是根据模块之间的依赖关系,将其“合并”到单个或者几个 JS 文件,让程序能直接被浏览器执行。

  • 第一个编译的步骤可以直接交给 Babel 搞定。

  • 第二个步骤的话,目前推荐用 webpack 做比较方便。

  • 再之就是用 Gulp 控制那两个工具去处理源代码和输出了,总体上是这么个流程。

  • 细节的比如 uglify,sourcemap 之类的事情都可以通过上述工具便捷地完成。

###番外(ESLint)

  • ESLint 支持JSX语法的检测,包含JSLint等功能,作者为红宝书的作者
  • 全局安装npm install eslint -g
  • 配置文件.eslintrc ,新建此文件在你需要的根目录下即可(并填入相应的配置项)
  • 忽略文件.eslintignore
  • .eslintrc的基本配置项如下所示:
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "extends": "eslint:recommended",
    "env":{
    "node":true,
    "es6":true
    },
    "rules": {
    "semi": ["error", "always"],
    "quotes": "off",
    "no-console":"off",
    "no-unused-vars":"off",
    "no-unreachable":"off",
    "no-redeclare":"warn"
    }
    }
  • 如需要自动修复一些不规范的代码问题,例如没有分号的问题,可以在目录终端下输入命令 eslint . --fix 来进行修复

###番外($.ajax)

  • 是jQuery最底层的Ajax实现
  • 语法$.ajax(options),options为参数对象,这个对象中包含了所需要的请求设置以及回调函数等信息,参数以key/value的形式存在,所有参数都是可选的。
  • 介绍一个体验优化参数,global参数,它的value是boolean类型的,默认为true,表示触发全局Ajax事件,设置为false将不会触发全局Ajax事件
  • 全局Ajax事件有6个:ajaxStart(callback) ajaxStop(callback) ajaxComplete(callback) ajaxError(callback) ajaxSend(callback) ajaxSuccess(callback)
  • 使用这些全局Ajax事件就是在用户发起Ajax请求后,为了让用户知道服务器正在处理数据,了解当前的变化,不至于让用户感到茫然,及时给用户一些反馈。
  • 比如在ajaxStart()中启动一个新的类名,在此类名下,按钮或者某块区域给出动态提示,接着在 ajaxStop()中移除这个类名,表示完成了Ajax请求。

###资源链接