2018年2月25日星期日

React 导读(三)

前言

React 导读(一)
React 导读(二)

在之前 2 篇文章中中学习到了写第一个 Web 组件以及常用的生命周期函数的使用,这篇文章将继续之前的目录,开始新的知识点补充:

  1. React 如何编写 Hello World!
  2. React 中三个最基础、最重要的东西
  3. React 中的 JSX
  4. 你的第一个 Web 组件
  5. React 中最开始需要关注的生命周期
  6. React 一个组件集合的简单交互
  7. React 开始一个项目的一点建议
  8. React 简单的项目结构组织

这篇文章主要会介绍第6、7的知识点。

六 & 七、React 一个组件集合的简单交互以及开始一个项目的一点建议

为什么要将6、7合在一起写呢?不是因为想偷懒...其实是脱离一个场景和合适的开始去规划组件等设计都是不合理的,多多少少都有点交集,所以将这 2 点融合在一起是更利于学习和理解的,到这里就已经不是太基础的内容了,基本上代码量会有所提高,但是分析依然会很细致。

这里用一个简单的表格的添加删除编辑搜索四个功能来作为实例吧。
因为这应该是日常开发过程中遇到过程最多的,我将参考 bootstrap-table 的方式来开发一个简单的表格组件和约定配置来做,感觉比较自由,如果你动手能力好且业务稍大和复杂可以参考 antd 设计规范来实现,目前市面上应该蚂蚁这套用的比较多,但是这并不意味着我们就一定是按照他来做,实际项目不复杂的情况是可以使用更简单的方式。

做这个开始之前,首先要假设一点场景和基本需求,这样才能带着去思考如何实现以及更接近需求目标。

(1) 场景

为了更清晰的安排年前年后的工作和值班,现在要对过年期间人员请假的情况进行统计,并且进行一个简单的管理。

(2) 功能性需求

  • 添加员工的请假信息
  • 展示添加的员工请假的列表
  • 能够对信息进行修改
  • 能够删除添加的信息,由于不可恢复,所以需要一个提示
  • 能够根据员工的名字进行搜索

简单描述了一下,其实就之前说的几个功能。

最后做出来的效果如下(=.=没有设计,对齐就行哈):

看之前可以下载源代码对照着看,不过代码可能会不断修改 BUG,哈哈~有 BUG 不要虚,没有 BUG 我们可能就失业了。
源码-GitHub

(3) 准备工作

  1. 整理需要用到的技术
  2. 开发要用的基础 UI 组件
  3. 看下 bootstrap-table 的基本设计
  4. 搭建项目目录

1. 需要用到的技术
需要用到的技术:React/ES6, CSS 即可

2. 基础 UI 组件
根据我们这里的功能来看,我们只需要下面这几个基础组件即可:
Button, Dialog, Input, Table, Radio

在这个例子项目里面,组件的划分结构如下:

为什么要这样划分呢?

  • 基础组件:其实这个是每一个项目都需要的,如果说太小的项目不需要其实大多数是考虑掉了项目的迭代周期的考量以及以后代码的可复用性,顾名思义,基础组件就是你要在以后的组件编写过程中需要依赖的最基础的组件,基本是只负责 UI 层面的职责,当然你还能够再剥离,这里就不太展开了,知道这一层是为了以后写组件能够有自己的基础组件即可。

  • 业务组件、模块组件:在我们开发好基础组件过后,其实这些基础组件是不具备任何业务价值的,比如有了业务设计稿后,我们需要针对业务然后编写业务中公用的组件或者封装使用操作2次的组件代码,形成一个可复用的业务组件或者业务模块类型的组件。比如我这里会将每个模块用到的模块标题封装成一个 ModTitle 组件,这样以后修改这里样式的时候全部就在一个地方修改,或者在业务系统上会有 Layout 相关的布局组件需求,再比如系统中表格整个一块的需求,包含搜索、头部操作按钮、数据展示表格,这三者能够进行一个通用性的封装来形成业务模块上的表格使用组件,增加编写模块的效率,当然这里我并没有封装,因为封装和重构并不是软件初始开发更应该注重的,而是遇到第二次的时候再反过来思考如何避免重复或者让组件内部封装。

  • 展示组件、容器组件:这一层就是网上流行的展示型组件、容器组件的一层,我这里划分主要是跟具体业务功能有关系的一层。由于我这里没有 react-router,所以复杂度会低一些,后面有时间也可以介绍。

3. bootstrap-table 生成表格的方式

可以查看 github-bootstrap-table 的使用例子来看下使用方式,这里我用它做例子并不是说此库完全好或者不好,而是以前项目用了 bootstrap-table 然后就模仿了 columns 配置的方式,对于它 API 设计的其他部分暂时没采用。
表格组件其实是管理类系统很核心的部分,一是用的多,二是本身也比较复杂,封装太死缺少灵活性,封装太简单缺少效率,种类也比较多。大体上我会采用字段进行配置的方式,具体看后面的代码和分析。

4. 项目的目录规划
上面介绍了一些概念性的东西,那么项目主要的目录单独提一下,这里的项目目录不适合大型项目,但是需要一个这个过程,来理解每一项的意思以及为什么我们还需要其他技术来解决你遇到的问题,堆技术的做法是不可取的,至少在不疯狂 KPI 模式的情况下。

├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── server // 网站后端的目录,这里我们不需要关系
├── src // 前端的源代码目录
│   ├── App.js // App 的入口组件
│   ├── apis // API 请求层的相关文件,Ajax 的方法也是需要适配的,比如常见的拦截器做法
│   ├── app.css
│   ├── assets // 一些静态资源
│   ├── components // 包含了业务组件、模块组件、展示组件,这里项目较小的时候不需要划分太细,但是要有这样的分层来组织代码
│   ├── containers // 容器组件,主要的副作用等逻辑组件,基本上是数据初始化、维护一个较顶层的数据入口
│   ├── index.css
│   ├── index.js // 网站的入口 JS 文件,主要是负责组件挂载到 DOM,或者你也可以做一些全局注入的一些操作
│   ├── normalize.css
│   ├── registerServiceWorker.js
│   ├── smarty // 基础组件的目录,这里我叫它 smarty,命名空间使用 st-,这个随你高兴
│   ├── stores // 数据操作的主要聚焦地方,每一个 Store 都能是一个事件订阅者,用于连接 React View 组件
│   └── utils // 一些工具辅助函数,目前我这里没有使用,真实项目肯定会用上的

(4) 开始思考要如何开始写代码

1. 需要一个 React 的容器组件来渲染我想要的一个功能模块;
2. 功能模块的数据需要一个地方进行管理。

要解决第一个问题,假设我们的容器组件叫 EmployeeManage,那么在最外层的 App 组件中应该声明要渲染它,代码就会是这样:

class App extends Component {
    render() {
        return (
            <div className="App">
                <EmployeeManage />
            </div>
        );
    }
}

好了,假设这样会出现最初的那个效果图的样子,那么数据并不想写的太过于零散,所以我定义了一个 Store 类进行管理,为什么是类呢?现在不是流行 Redux 之类函数式的么?一是在最开始学习的时候增加太多技术栈心累,二是不一定要用 Redux 我们才能写好 React,三是感觉也不太必要就我们目前的需求来看,四是我就想最初简简单单的。
但是现在我们是数据驱动方式的编程,数据变了来通知 React 的 state 变了然后 React 去帮我们做视图的更新,所以,我们的 Store 得是一个基于事件的类,要有事件应该有的特征:监听。所以最后我需要一个 EmployeeStore

// 用下自带的,你也可以自己实现一个简单的
import EventEmitter from 'events';
import assign from 'object-assign';

const state = {};

const EmployeeStore = assign({}, EventEmitter.prototype, {
    // 把容器组件的 this.state 在这里管理
    getState() {
        return state;
    }
});

原始是原始了一点,但是应该很好理解,那就是我的 EmployeeStore 拥有了 EventEmitter.prototype 的东西,比如常用的 on(), off(), emit() 等方法来实现事件特性。
然后我们需要把 EmployeeManageEmployeeStore 连接起来,最简单的连接像这样子:

class EmployeeManage extends React.Component {
    constructor() {
        super();
        // 看这里
        this.state = EmployeeStore.getState();
    }
}

连接了这个基础的东西,我们的 EmployeeStore 不是还可以订阅事件么?然后数据修改了我们就触发一下订阅的事件去告诉 EmployeeManage 然后通过 this.setState 去更新视图即可,整个关系如下:

看图可能就更直观的知道数据和组件之间的关系了,用过 Flux 可能可以发现还比较像,但是这是两个不同的理念,我这里只是一个最基础的事件系统,所以会特别简单。

我们现在来订阅一个名为 updateList 的事件,用来表示表格中需要展示每条数据。我们需要在 EmployeeManage 中加入下面的代码:

componentDidMount() {
    EmployeeStore.on('updateList', this.handleUpdateList);
}

componentWillUnMount() {
    EmployeeStore.off('updateList', this.handleUpdateList);
}
    
handleUpdateList(list) {
    this.setState(prevState => {
        return {
            list: list,
        };
    });
}

这三个方法能跟上面的图对应一下,就对应上了 EmployeeManageComponent,那么我们的 Store 需要怎么做呢?

getList() {
    // API 请求列表数据的方法,返回一个 Promise
    EmployeeApi.get().then(result => {
        if(result.status === 200) {
            // 刚好,就通知了 EmployeeManage 说我数据获取成功了,可以更新视图了
            this.emit('updateList', result.data);
        }
    });
},

以上就完成了连接工作了,基本上剩下的就是码代码,往上累积功能。
先写到这里吧,太长看着也累,分一下章节吧~其实架子已经差不多了,剩下的就是写功能点了。如果觉得看文章太慢可以直接看源码可能会更快更直接一点,没有数据层,其实并不是太好,先理解视图和关系吧。

2018年2月3日星期六

React 导读(二)

前言

在上篇文章React 导读(一)中学习到了写第一个 Web 组件,这篇文章将继续之前的目录,开始新的知识点补充:

  1. React 如何编写 Hello World!
  2. React 中三个最基础、最重要的东西
  3. React 中的 JSX
  4. 你的第一个 Web 组件
  5. React 中最开始需要关注的生命周期
  6. React 一个组件集合的简单交互
  7. React 开始一个项目的一点建议
  8. React 简单的项目结构组织

五、React 中最开始需要关注的生命周期

其实在学习 React 之前,就应该了解目前前端推荐的是组件化开发的方式,React 是让组件化更加简单的库。那么组件开发必不可少的就是生命周期,说直白一点就是运行组件的过程是 React 来做,运行过程中需要有一些代码钩子来让我们去调用,在组件执行的某一个地方去执行我们自己写的代码。这里先介绍拥有的生命周期钩子,下面的方法 constructorrender 不属于生命周期,我按功能分类了一下,也就是学习的时候不一定要按部就班,应该以学习之后能真正写一些东西为目标:

(1) 与组件挂载相关的方法,包括构造函数

  • constructor
  • componentWillMount
  • componentDidMount
  • componentWillUnmount

最常用的生命周期应该是最后 2 个,constructorcomponentWillMount 目前先理解成能满足的功能大体相同,如果这里解释太复杂不太好。

对于最开始关注的是:this.state 的初始化以及 ajax 在哪里请求。

this.state 在 constructor 进行初始化,ajax 推荐在 componentDidMount 中进行请求。

  • componentDidMount 就是在组件已经挂载到 DOM 中后的钩子,可以理解为 jQuery 中提供的 ready 方法。
  • componentWillUnmount 是在组件即将被卸载前一刻的钩子,一般用于取消 componentDidMount 中订阅的事件等作用,清理一些不要的变量等,避免内存泄漏。

下面通过一个简单的例子说明一下:

先有一个 foods.json 文件来模拟请求的后台数据。

[
    {
        "id": 1,
        "name": "香蕉"
    },
    {
        "id": 2,
        "name": "苹果"
    },
    {
        "id": 3,
        "name": "猕猴桃"
    }
]
// 1. 挂载相关的生命周期
class CycleMount extends React.Component {
    constructor() {
        super();
        this.state = {
            foods: []
        };

        console.log('1. constructor 执行了...');
    }
    componentDidMount() {
        // 这里使用原生的 fetch API 进行 ajax 请求,你也可以使用 $.ajax 进行请求,原理是一样的,重点是关注 setState 的地方
        fetch('/mock/foods.json',
            {
                method: 'GET',
                headers: new Headers({
                    'Accept': 'application/json'
                })
            }
        ).then(dataResult => {
            if(dataResult.status === 200) {
                return dataResult.json();
            } else {
                return [];
            }
        }).then(data => {
            // 这里的 data 就是 foods.json 里面的数据
            // 调用 setState 来更新 render 里面调用的 this.state 的值
            this.setState({
                foods: data
            });
        });

        console.log('2. componentDidMount 执行了...');
    }
    render() {
        // foods 是一个数组,map 方法是数组自带的方法,可以查询相关 api
        const foodItems = 
            this.state.foods.map(food => {
                return (<li key={food.id}>{food.name}</li>);
            });

        // 这里是返回的最终组件结构
        return (
            <ul>
                {foodItems}
            </ul>
        );
    }
}

上面有了完整的注释,也能看到基本上项目中可能会将代码写到何处,我也打了两个日志,来识别到底是谁先执行,结果可以自己运行一下,执行顺序就是我标记的1,2。

好了,基本的学习了,可以自己动手试试订阅一个事件,然后在卸载的时候取消这个事件。

(2) 优化相关

  • shouldComponentUpdate

这个方法比较重要,但是我这里不会介绍得太过于复杂,太复杂只会让重要的部分不那么突出。这个方法的返回值是一个 boolean 类型,分别代码的意义:

  • true 组件应该更新,执行 render 方法以及相关生命周期;
  • false 组件状态没有更新,不执行 render 等方法,意味着网页界面不会改变。

那么它直观上的作用是能够通过返回值来决定界面是否改变,实际的意义就是当我们知道当前 oldState = this.state 的值和新的 newState = this.state 值完全相等的时候(或者是新传入的 props)就不用再浪费性能去重新渲染组件了。

API 上的定义是这样的:

/* nextProps: 新的 props, nextState: 新的 state */
shouldComponentUpdate(nextProps, nextState): boolean

举个例子来直观说明一下:


class ComponentOptimize extends React.Component {
    state = {
        count: 0
    };
    shouldComponentUpdate(nextProps, nextState) {
        // 当 count > 10 的时候就不能再重新渲染组件了
        if(nextState.count > 10) {
            return false;
        }

        return true;
    }
    handleUpdateCount() {
        console.log('我点了一下哦!');
        this.setState(prevState => {
            return {
                count: prevState.count + 1
            };
        });
    }
    render() {
        return (
            <div onClick={this.handleUpdateCount.bind(this)} style={{cursor: 'pointer'}}>
                <h1>{this.state.count}</h1>
            </div>
        );
    }
}

你会发现 10 过后界面就没有再更新过了,这样应该很直观了。

(3) Props 相关的生命周期

  • componentWillReceiveProps

这个主要是在组件的 props 传入新的值后被调用,不管是不是传的一样的值或者 shouldComponentUpdate 返回了 false,看下例子吧:

class Cat extends React.Component {
    componentWillReceiveProps(nextProps) {
        console.log('改一次我执行一次!');
    }
    shouldComponentUpdate(nextProps, nextState) {
        // 改的名字一样的时候
        return this.props.name !== nextProps.name;
    }
    render() {
        console.log('猫改了一次名字!');
        return (
            <h1>我有新名字了!{this.props.name}</h1>
        );
    }
}

class App extends React.Component {
    state = {
        catName: '噜噜'
    };
    handleChangeCatName() {
        const catNames = ['噜噜', '小白', '小黄', '小黑', '皮卡丘'];
        const catIndex = this.getSomeOneIndex();

        this.setState({
            catName: catNames[catIndex]
        });
    }
    getSomeOneIndex() {
        return Math.floor(Math.random() * 5 + 0);
    }
    render() {
        return (
            <div>
                {/* 给 Cat 传新的名字 */}
                <Cat name={this.state.catName} />
                <button onClick={this.handleChangeCatName.bind(this)}>点我给猫咪取新名字!</button>
            </div>
        );
    }
}

最后肯定每次点击按钮都会输出这句的结果 console.log('改一次我执行一次!');

(4) 更新组件相关

  • componentWillUpdate
  • componentDidUpdate

因为都是讲解 API,所以国际惯例的先看下 API 的定义吧:

// 组件更新前执行
componentWillUpdate(nextProps, nextState)
// 组件更新后执行
componentDidUpdate(prevProps, prevState)

可以从定义中看出,它们都接受了两个参数:props && state,不过看变量前缀能够联想点什么。

暂时想不到什么实际项目的例子,随便假设点内容吧。不过这里需要注意的地方是:

  • 1. 这两个方法里面都不要调用 setState!
  • 2. 第一次初始化组件 render 的时候不会执行
  • 3. shouldComponentUpdate 返回 false 不会执行

第一条的原因:容易形成一个递归的调用,不作就不会死...所以尽量不要在这里调~目前还没有碰到需要在这里调的需求。
第二条的原因:额,说好的更新才调,初始化不调用是符合逻辑的。
第三条的原因:额,这 2 个钩子是与组件更新相关的,所以也符合逻辑的,组件是否更新就是靠 shouldComponentUpdate 返回值。

在上面 Cat 的例子中加入下面的代码可以看下结果:

componentWillUpdate() {
    console.log('componentWillUpdate 执行了!')
}
componentDidUpdate() {
    console.log('componentDidUpdate 执行了!')
}

(5)组件错误

  • componentDidCatch

就是在组件发生异常的时候可能会被调用的钩子,需要注意的有下面的地方:

  • 只能在父级组件捕获子组件的异常;
  • 如果异常被 try...catch 包裹父级组件的钩子就不会执行了。

看个例子吧:

class Cat extends React.Component {
    componentWillReceiveProps(nextProps) {
        // 这里手动抛一个异常,触发我们的钩子 componentDidCatch
        throw new Error('miao miao~');
    }
    render() {
        let miao = this.props.name;

        return (
            <div>
                {miao}
            </div>
        );
    }
}

class App extends React.Component {
    state = {
        catName: '噜噜',
        isError: false,
    };
    handleChangeCatName() {
        const catNames = ['噜噜', '小白', '小黄', '小黑', '皮卡丘'];
        const catIndex = this.getSomeOneIndex();

        this.setState({
            catName: catNames[catIndex]
        });
    }
    getSomeOneIndex() {
        return Math.floor(Math.random() * 5 + 0);
    }
    componentDidCatch(error, info) {
        console.log(error, info);
        if(error) {
            // 如果有错误信息,就重新渲染一下组件,可能是更好的交互
            this.setState({
                isError: true
            });
        }
    }
    render() {
        return (
            <div>
                <Cat name={this.state.catName} />
                {!this.state.isError ?
                    <button onClick={this.handleChangeCatName.bind(this)}>点我给猫咪取新名字!</button> :
                    <p>不要奴才给我取名字了!</p>
                }
            </div>
        );
    }
}

(6) 渲染相关

  • render

额...这个不想写了,先睡觉吧~应该写了这么多个小例子也差不多了~可以动手试试哦!还不清楚的可以 Google 一下,你就知道。

生命周期很重要,其实学到这里也差不多可以上手写点项目熟练一下了,其他的更多是思维和编程方面的东西,周期的篇幅单独来一篇吧~其他主题之后再继续吧!

2018年2月1日星期四

React 导读(一)

前言

写这篇文章的主要目标是让初学者更快的上手 React 的项目开发,能有一个循循渐进的理解过程。需要有一定的 JavaScript 基础和 NPM 的使用经验。不多说了,下面会按这个顺序进行介绍:

  1. React 如何编写 Hello World!
  2. React 中三个最基础、最重要的东西
  3. React 中的 JSX
  4. 你的第一个 Web 组件
  5. React 中最开始需要关注的生命周期
  6. React 一个组件集合的简单交互
  7. React 开始一个项目的一点建议
  8. React 简单的项目结构组织

开始前需要安装的环境:node.js、yarn

一、React 如何编写 Hello World!

1.使用脚手架直接避开环境搭建的问题

// 安装脚手架
➜ npm install -g create-react-app

2.使用脚手架创建项目

// react-study 是项目的根文件夹
➜ create-react-app react-study

// 执行后的第一行提示语,会提示创建的完整路径
Creating a new React app in /Users/lulin/Desktop/react-study.

// 安装成功后会提示下面的内容
Success! Created react-study at /Users/lulin/Desktop/react-study
Inside that directory, you can run several commands:

// 使用 yarn 启动项目
yarn start
    Starts the development server.
// 要发布项目的时候运行
yarn build
    Bundles the app into static files for production.
// 做测试的时候执行,目前没用
yarn test
    Starts the test runner.
// 可以自定义配置,目前也不用
yarn eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:
  cd react-study
  yarn start

Happy hacking!

3.使用 Visual Studio Code 打开 react-study

先只需要关注 src 目录中的 index.js,如下:

├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js // 代码的入口文件
│   ├── logo.svg
│   └── registerServiceWorker.js

4.修改 index.js

删除 index.js 中所有的内容,贴以下代码运行:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
    <h1>Hello World!</h1>,
    document.getElementById('root')
);

ReactDOM.render 方法2个参数,第一个参数就是要渲染的界面结构或者一个 React 组件,第二个参数是要把这个结构渲染到真实网页 DOM 的什么位置。所以这份代码的结果就是在 id=root 的DOM节点下渲染出来 Hello World!

二、React 中三个最基础、最重要的东西

一中介绍了 React 能够渲染一个 HTML 到指定的 DOM 中,但是 React 发明出来主要不是做这个事情的,因为这个事情可以直接通过 原生 JavaScript 的 innerHTML 也能实现。

React 主要作用可以认为是以下几点:
(1) 使用数据来驱动界面更新;
(2) 使用单向变化的数据来让 BUG 更好调试一点;
(3) 更方便、更声明式的编写 Web 组件;

那么这节主要介绍三个东西:
(1) state
(2) props
(3) setState 方法

如果要实现一个简单的加法器,这三个东西已经可以很好实现:

根据上面标的数字:
(1) this.state 里面有一个属性叫 count,这个属性能够通过 this.setState 方法重新传入一个对象来重新赋值。赋值的同时 render() 方法中 this.state.count 也会跟随自动变化,最后体现到网页上。这就是 state 属性的作用。
(2) this.setState() 方法接收一个新对象来重新赋值 this.state
(3) this.setState() 也接收一个函数,这个回调函数这里我默认有一个 prevState 参数,表示之前的 this.state 的值,这个函数的返回值就是最新的 this.state

大家还应该注意 button 上绑定的 onClick 事件,这就是跟 DOM 上直接绑定事件的写法一样(目前先这样理解),不过需要都写成驼峰标识。

那么 statesetState 都介绍了,props 又是什么呢?你可以暂时理解成一个组件外面传递的属性。
还是以计数器的代码为例子,简单修改一下:

class Counter extends React.Component {
    state = {
        count: 0
    };
    // 加 1
    onAdd() {
        this.setState({
            count: this.state.count + 1
        });
    }
    // 减 1
    onSub() {
        this.setState(prevState => {
            return {
                count: prevState.count - 1
            };
        });
    }
    render() {
        return (
            <div>
                {/* 这里的 this.props 属性 */}
                <h1>{this.props.name}</h1>
                <button onClick={this.onSub.bind(this)}>-</button>
                <span>{this.state.count}</span>
                <button onClick={this.onAdd.bind(this)}>+</button>
            </div>
        );
    }
}

// Counter 组件传了一个 name 属性
ReactDOM.render(
    <Counter name="计数器" />,
    document.getElementById('root')
);

这里注意代码中的注释,应该很直观了,我们在组件上添加的属性,都能在组件里面通过 this.props 属性获取到,拿一个其他方式来比喻,就相当于函数的参数,参数传递进函数,函数内部可以使用。但是不同的是 this.props 在组件内部是只读的。

看到这里,其实你已经能够使用 React 来构造一个网页了,是不是很简单,就三个东西,加上一个 ReactDOM.render 方法。当然,如果 ES6 不熟悉的话可能还是麻烦,但是这是必须要去学习和克服的,因为目前已经是主流而且是进入了规范的东西。

三、React 中的 JSX

接触 React 你肯定会问为什么要用 JSX,JSX 到底是什么。其实非官方的我只想这么解释,就是一个编写视图的模版而已,语法也不复杂,列下:
(1) 基本上是使用原始的 HTML;
(2) 事件绑定方法使用驼峰方式;
(3) 要插入 JavaScript 代码需要 {} 包裹,里面的代码就是原生的 JavaScript 代码;
(4) 避开一些 JavaScript 关键字,比如:class 要写成 className。

上面第二节的计数器中,render() 方法就是编写 JSX 的主要位置,其实 JSX 可以编写在 React 代码中的任意位置,但是推荐不要写得太过于零散。

其实理解这几点就已经足够了,具体可以看一下官方文档 JSX-简介

JSX 最终编译后也就是原生的 JavaScript 代码

四、你的第一个 Web 组件

学习了上面这些知识后,其实我们就已经能够封装一个简单的组件的,第一个我这里先以 CheckBox 为例子,比较简单也很常用,应该比较适合。先来分析下这个小组件最基础的功能:就是点击选中和取消选中,模拟的话可以通过换图来实现,就是切换 DOM class。那么我们开始动手吧!

class CheckBox extends React.Component {
    state = {
        checked: false // 默认没有选中
    };
    // 交替(选中/没有选中)的状态
    onClickCheckbox() {
        this.setState(prevState => {
            return {
                checked: !prevState.checked
            };
        });
    }
    render() {
        const checkboxClassArr = ['ui-checkbox'];
        // 如果选中,就添加一个 checked class 来给 css 做样式
        if(this.state.checked) {
            checkboxClassArr.push('checked');
        }

        // 组合最后的 class 结果
        const checkboxClass = checkboxClassArr.join(' ');

        return (
            <div onClick={this.onClickCheckbox.bind(this)}>
                {/* 这里模拟图标 */}
                <span className={checkboxClass}>
                    <i className="icon-checkbox"></i>
                </span>
                {/* 这里模拟图标内容 */}
                <span>{this.state.checked ? '选中' : '没有选中'}</span>
            </div>
        );
    }
}

这样一个简单的组件就已经完成了!效果如下:

今天就写到这里睡觉吧~可以动手试试,来点感觉哦~这基本是最常用的一些概念和意义了,有兴趣可以提前阅读中文官方文档,已经改版体验好多了,慢慢读下来应该很好理解。