React 和 jQuery 的对比:

React是一个聪明的建筑工人,而jQuery是一个比较傻的建筑工人,开发者就是一个建筑的设计师。

  • 如果是jQuery为你工作,你不得不事无巨细地告诉jQuery“如何去做”,要告诉他这面墙要拆掉重建,那面墙上要新开一个窗户
  • 如果是React为你工作,你所要做的就是告诉这个工人“我想要什么样子”,只要把图纸递给React,他就会替你搞定一切。

当然他不会把整个建筑拆掉重建,而是很聪明地把这次的图纸和上次的图纸做一个对比,发现不同之处,然后只去做适当的修改就完成任务了。

jQuery:

选中一些DOM元素的,然后对这些元素做一些操作

  1. 根据CSS规则找到特定的DOM元素,给按钮或者文本框绑定监听事件
  2. 如果事件触发则开始判断,各个DOM对象的当前状态(通常会借助css class来表示状态)
  3. 改变对应的DOM来实现想要达到的效果

jQuery项目逐渐变得庞大时,写出的代码往往互相纠缠,难以维护。
React基础语法 - 图1


React:

“我想要显示什么”,而不用操心“怎样去做”
React的理念,归结为一个公式,就像下面这样:(声明式编程)
UI=render(data)
render**()** 函数是一个纯函数,输出完全依赖于输入的函数,两次函数调用如果输入相同,得到的结果也绝对相同。
我们只需要维护自己的data,当data改变时,React可以根据最新的data去渲染我们的UI界面;

React实践的是 “响应式编程”(Reactive Programming)的思想
React中,无论何种事件,引发的都是React组件的重新渲染,至于如何只修改必要的DOM部分,则完全交给React去操作。
React基础语法 - 图2


React开发依赖:

开发React必须依赖三个库:

  • react:包含react所必须的核心代码
  • react-dom:react渲染在不同平台所需要的核心代码
  • web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
  • native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)
  • babel:将jsx转换成React代码的工具

ReactDOM.render(

{message}

, document.getElementById(“app”))

  1. <body>
  2. <div id="app">
  3. <button class="change-btn">改变文本</button>
  4. </div>
  5. <script src="react.development.js"></script>
  6. <script src="react-dom.development.js"></script>
  7. <script src="babel.min.js"></script>
  8. <script type="text/babel">
  9. // 将数据定义到变量中
  10. let message = "Hello World";
  11. // 通过ReactDom对象来渲染内容
  12. ReactDOM.render(<h2>{message}</h2>, document.getElementById("app"));
  13. </script>
  14. </body>

JSX :

为什么使用 JSX?

React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如:

  • 比如UI需要绑定事件(button、a原生等等);
  • 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI

JSX语法的优点

  1. JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  2. 它的类型是安全的,在编译过程中就能发现错误。
  3. 防注入攻击,所有的内容在渲染之前都被转换成了字符串,可以有效地防止 XSS(跨站脚本) 攻击。

JSX的书写规范:

  • JSX的顶层只能有一个根元素,所以很多时候会在外层包裹一个div原生;
  • JSX中的标签可以是单标签,也可以是双标签;
  • 注意:如果是单标签,必须以/>结尾;

JSX的本质

JSX语法:

大括号{ }语法:

  1. 注释

    1. <div>
    2. {/* 我是一段注释 */}
    3. <h2>Hello World</h2>
    4. </div>
  2. {}中嵌入变量

    • 情况一:当变量是 NumberStringArray 类型时,可以直接显示;

    • 情况二:当变量是 nullundefinedBoolean 类型时,内容为空;

      • 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
        • 如toString方法、和空字符串拼接,String(变量)等方式;
    • 情况三: 对象类型 不能作为子元素(not valid as a React child)

**

  1. 嵌入表达式
    • 运算表达式
    • 三元运算符
    • 执行一个函数
      1. class App extends React.Component {
      2. constructor(props) {
      3. ...
      4. }
      5. render() {
      6. return (
      7. <div>
      8. {/* 运算表达式 */}
      9. <h2>{this.state.firstName + " " + this.state.lastName}</h2>
      10. {/* 三元运算符 */}
      11. <h2>{this.state.age >= 18 ? "成年人": "未成年人"}</h2>
      12. {/* 执行一个函数 */}
      13. <h2>{this.sayHello("kobe")}</h2>
      14. </div>
      15. )
      16. }
      17. sayHello(name) {
      18. return "Hello " + name;
      19. }
      20. }

属性绑定

  1. 常规属性属性名 = { }

    • titlesrchref
  2. 特殊属性

    • class : 使用className替代

      • className = { ... }
    • style内联样式:

      • style后面跟的是一个对象类型KV 是样式的属性名和属性值;
      • 属性名使用驼峰标识,而不是连接符 -;

如:style={{fontSize: "30px", color: "red", backgroundColor: "blue"}}

  • label标签的for属性:htmlFor

事件绑定

<button onClick={handleClick}>click me</button>

  • 事件的命名采用小驼峰式(camelCase),而不是纯小写;
  • 通过{}传入一个函数作为事件处理函数,这个函数会在事件发生时被 React 调用执行;
  • 使用函数不能加括号,不然会直接执行。

this绑定问题

  1. class Foo extends React.Component {
  2. handleClick () {
  3. this.setState({ xxx: aaa }); /*Cannot read property 'state' of undefined*/
  4. console.log(this) /*undefined*/
  5. }
  6. render() {
  7. return (
  8. <button onClick={this.handleClick}>
  9. Click me
  10. </button>
  11. )
  12. }
  13. }

这里会报 thisundefined 的错。官网的事件处理有下面一段话:

必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定this。 如果忘记绑定 this.handleClick 并把它传入了 onClick,当调用这个函数的时候 this 的值为 undefined。 这并不是 React 特有的行为。通常情况下,如果没有在方法后面添加 (),例如 onClick={this.handleClick},应该为这个方法绑定 this

这里传递给onClick的,仅仅是 this.handleClick的定义,没有调用,故其this并不会指向类组件这个对象。至于为什么this会为undefined,这和React事件执行机制有关,还未搞明白,后续补充。


React 是如何处理事件的?

React 的事件是合成事件

  • React 在组件加载(mount)和更新(update)时,将事件通过 addEventListener 统一注册到 document 上;
  • 有一个事件池存储了所有的事件,当事件触发的时候,通过 dispatchEvent 进行事件分发。
  • 最终 this.handleClick 会作为一个回调函数调用。

为什么就会丢失 this。**

在函数内部,this的值取决于函数被调用的方式。

在类组件的render 函数中, 做了类似这样的操作:

  1. class Foo {
  2. sayThis () {
  3. console.log(this); // 这里的 `this` 指向谁?
  4. }
  5. exec (cb) {
  6. cb();
  7. }
  8. render () {
  9. this.exec(this.sayThis); // 这里并未调用,只是进行了传值
  10. // this.sayThis(); -> 直接调用:Foo {}
  11. }
  12. }
  13. var foo = new Foo();
  14. foo.render(); // undefined

为什么React没有自动的把 bind 集成到 render 方法中呢?
如下述这样:

  1. class Foo {
  2. sayThis () {
  3. console.log(this); // 这里的 `this` 指向谁?
  4. }
  5. exec (cb) {
  6. cb().bind(this);
  7. }
  8. render () {
  9. this.exec(this.sayThis);
  10. }
  11. }
  12. var foo = new Foo();
  13. foo.render(); // undefined


因为 render 多次调用每次都要 bind 会影响性能,
所以官方建议自己在 `constructor` 中手动 bind 达到性能优化。**


解决this绑定的问题

这里主要针对类组件中的this绑定。而在函数组件中,不用绑定 this ,可以直接调用。

1. **事件那里**直接 **bind this**

  1. class Foo extends React.Component {
  2. handleClick () {
  3. this.setState({ xxx: aaa })
  4. }
  5. render() {
  6. return (
  7. <button onClick={this.handleClick.bind(this)}>
  8. Click me
  9. </button>
  10. )
  11. }
  12. }


优点:写起来顺手,逻辑顺畅
缺点:**

  • 性能不太好,这种方式跟 React 内部帮你 bind 一样的,每次 **render** 都会进行 bind
  • 如果有两个元素的事件处理函数式同一个,也还是要进行 bind,这样会多写点代码,而且进行两次 bind,性能不是太好。

2. **constuctor** 手动 bind 型

  1. class Foo extends React.Component {
  2. constuctor(props) {
  3. super(props)
  4. this.handleClick = this.handleClick.bind(this)
  5. }
  6. handleClick () {
  7. this.setState({ xxx: aaa })
  8. }
  9. render() {
  10. return (
  11. <button onClick={this.handleClick}>
  12. Click me
  13. </button>
  14. )
  15. }
  16. }


优点:**

  • 因为构造函数只执行一次,那么只会 bind 一次,
  • 如果有多个元素都需要调用这个函数,也不需要重复 bind,基本上解决了第一种的两个缺点。

缺点:没有明显缺点

3. 箭头函数型

  1. class Foo extends React.Component {
  2. handleClick () {
  3. this.setState({ xxx: aaa })
  4. }
  5. render() {
  6. return (
  7. // 此语法确保 `handleClick` 内的 `this` 已被绑定
  8. // 注意:这里的handleClick有括号
  9. <button onClick={(e) => this.handleClick(e)}>
  10. Click me
  11. </button>
  12. )
  13. }
  14. }


优点:顺手,好看。
缺点:**每次 render 都会创建不同的回调函数,性能会差一点。
在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。

4. public class fields 型(推荐)
这种 class fields还处于实验阶段,目前还没有被纳入标准

  • 使用了箭头函数,所以在当前函数中的this会去上一个作用域中查找;
  • 而上一个作用域中的this就是当前的对象
  1. class Foo extends React.Component {
  2. handleClick = () => {
  3. this.setState({ xxx: aaa })
  4. }
  5. render() {
  6. return (
  7. <button onClick={this.handleClick}>
  8. Click me
  9. </button>
  10. )
  11. }
  12. }


优点:好看,性能好。
缺点:**没有明显缺点,需要 babel 插件来支持这种语法。

条件渲染

1. if / else

逻辑上很清晰,但是会存在一些问题,如:重复代码会重新渲染,render 方法过于臃肿

  1. class App extends React.Component {
  2. // ...
  3. render() {
  4. if (this.state.isLogin) {
  5. return (
  6. <div className="container-login-register">
  7. <Login />
  8. </div>
  9. )
  10. } else {
  11. return (
  12. <div className="container-login-register">
  13. <Rejister />
  14. </div>
  15. )
  16. }
  17. }
  18. }

2. 变量

使用变量来存储元素,这样可以有条件地渲染组件的部分,剩余部分保持不变。

  1. class App extends React.Component {
  2. // ...
  3. render() {
  4. let element
  5. if (this.state.isLogin) {
  6. element = <Login />
  7. } else {
  8. element = <Rejister />
  9. }
  10. return (
  11. <div className="container-login-register">
  12. {element}
  13. </div>
  14. )
  15. }
  16. }

3. 三元运算符

适用于两个组件二选一的渲染,当然也可以多层嵌套,但不推荐使用。

  1. class App extends React.Component {
  2. // ...
  3. render() {
  4. return (
  5. <div className="container-login-register">
  6. {this.state.isLogin ? <Login /> : <Rejister />}
  7. </div>
  8. )
  9. }
  10. }

4. 行内条件渲染与运算符 &&

适用于一个组件有无的渲染, true && component 此时会渲染 component。

  1. class App extends React.Component {
  2. // ...
  3. render() {
  4. return (
  5. <div className="container-login-register">
  6. {this.state.isLogin && <Login />}
  7. {!this.state.isLogin && <Rejister />}
  8. </div>
  9. )
  10. }
  11. }

5. 返回 null 阻止渲染

如果我们只想在登录时显示登录状态,登录了就不显示内容。

  1. class App extends React.Component {
  2. // ...
  3. render() {
  4. return (
  5. <div>
  6. {this.state.isLogin ? <>已登录</> : null}
  7. </div>
  8. )
  9. }
  10. }

列表渲染

1. map

  • 如果箭头函数的代码块部分多于**一条**语句,就要使用大括号将它们括起来,并且使用return语句返回。

    • var sum = (num1, num2) => { return num1 + num2; }
  • 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

    • let getTempItem = id => ({ id: id, name: "Temp" });
  1. class App extends React.Component {
  2. render() {
  3. const tabMenu = [{
  4. title: '常用联系人',
  5. tag: ''
  6. }...]
  7. return (
  8. <ul className="tab">
  9. {tabMenu.map((item, index) => (
  10. <li key={item.title}>{item.title}</li>
  11. ))}
  12. </ul>
  13. );
  14. }
  15. }

一定要注意:key值一定要放在就近的数组上下文中。

_

2. for / forEach

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. render() {
  5. const tabMenus = [{
  6. title: '常用联系人',
  7. tag: ''
  8. }...]
  9. let tabMenuLi = []
  10. for(let i = 0; i < tabMenus.length; i++) {
  11. const item = tabMenu[i]
  12. tabMenuLi.push(
  13. <li key={item.title}>
  14. {item.title}
  15. </li>
  16. )
  17. }
  18. return (
  19. <ul className="tab">
  20. { tabMenuLi }
  21. </ul>
  22. )
  23. }
  24. }
  25. export default App;