React 是一个用于构建用户界面的 JavaScript 库,主要特点有:

  • 声明式渲染:设计好数据和视图的关系,数据变化 React 自动渲染,不必亲自操作DOM
  • 组件化:页面切分成多个小部件,通过组装拼成整体页面,利于代码复用

本文通过写个简单的 TodoList 实例,不求甚解,熟悉下 React 的开发过程。

1. 安装 Node.js

Node.js 是一个运行环境,类似 jdk,用以支持在服务端运行 JavaScript。

您可以在这里下载安装包:

  1. http://nodejs.cn/download/

以绿色版安装为例,将 node-v10.16.1-win-x64.zip 解压到 E:\software\ 并命名为 node-v10.16.1

在 Path 环境变量中增加两项:

  1. E:\software\node-v10.16.1\
  2. E:\software\node-v10.16.1\node_global

在 cmd 中使用 node -v 显示版本号,表示安装成功。

Node.js 中有个 npm 软件包管理器,可以很方便的管理下载和使用第三方开源包,类似 maven,使用 npm -v 显示版本号,表示 npm 也没有问题。

绿色版安装完成后一些必要的配置:

  1. npm config set prefix "E:\software\node-v10.16.1\node_global"

设置全局安装的模块存储路径

  1. npm config set cache "E:\software\node-v10.16.1\node_cache"

设置下载缓存的存储路径

  1. npm config set registry https://registry.npm.taobao.org`

设置 npm 下载源为淘宝镜像

简单使用:

  • npm install xxx: 安装到项目目录
  • npm install -g xxx 安装到全局目录
  • npm install -save xxx: 安装到项目目录,并在 package.json 中的 dependencies 节点记录依赖
  • npm install --save-dev xxx: 安装到项目目录,并在 package.json 中的 devDependencies 节点记录依赖

2. 脚手架创建项目

React 官方出的脚手架工具 create-react-app ,可以一键创建一个 Web 应用程序:

  1. cmd> npm install -g create-react-app
  2. cmd> cd E:
  3. cmd> create-react-app react-todoapp
  4. cmd> cd react-todoapp

脚手架会在当前目录创建一个 react-todoapp 目录:

  1. react-todoapp
  2. ├── README.md
  3. ├── node_modules
  4. ├── package.json
  5. ├── package-lock.json
  6. ├── .gitignore
  7. ├── public
  8. ├── favicon.ico
  9. ├── index.html
  10. └── manifest.json
  11. └── src
  12. ├── App.css
  13. ├── App.js
  14. ├── App.test.js
  15. ├── index.css
  16. ├── index.js
  17. ├── logo.svg
  18. └── serviceWorker.js
  19. └── setupTests.js

目录中主要的文件和文件夹说明:

  • README.md: 项目简介,支持 Markdown 语法
  • node_modules: 项目的依赖包,类似 Maven Repository
  • package.json: 配置项目依赖的第三方包,类似 pom.xml
  • package-lock.json: 锁定第三方包的版本号,保证 npm install 版本一致
  • public: 公开资源,网站路径,类似 nginx 的 html 目录
  • src: 核心组件代码文件

为了便于开发,删除目录中不必要的文件,最终结构如下:

  1. react-todoapp
  2. ├── README.md
  3. ├── node_modules
  4. ├── package.json
  5. ├── package-lock.json
  6. ├── .gitignore
  7. ├── public
  8. └── index.html
  9. └── src
  10. ├── App.css
  11. ├── index.js
  12. ├── TodoApp.js
  13. ├── TodoItem.js
  14. └── TodoList.js

接下来,设计与实现一个 TodoList 的例子,我们把所有代码过一下,敲一遍,先不管为什么,跑起来,最后再整理下知识点。

3. 实例 TodoApp

主要实现功能有:

  • 添加一个待办事项
  • 删除一个待办事项
  • 勾选复选框标记事项已完成

如图所示,总共将页面拆分成了三个组件:TodoApp, TodoListTodoItem

3.1 index.js 入口文件

应该可以类比 java 的 main 方法,在 src 目录新建 index.js 内容如下:

  1. // 引入 React, ReactDOM
  2. import React from 'react';
  3. import ReactDOM from 'react-dom';
  4. // 引入 TodoApp 组件
  5. import TodoApp from './TodoApp';
  6. // 将渲染结果挂在到 root 节点,该节点在 index.html 中
  7. ReactDOM.render(
  8. <React.StrictMode>
  9. <TodoApp />
  10. </React.StrictMode>,
  11. document.getElementById('root')
  12. );

先导入需要使用的组件(类),然后调用它们提供的方法和服务,有没有些许眼熟?

3.2 TodoApp.js

TodoApp 设计了页面整体布局,它包含全部数据以及操作这些数据的方法,是其他两个组件的父组件

  1. import React, { Component } from 'react';
  2. import TodoList from './TodoList';
  3. import './app.css';
  4. class TodoApp extends Component {
  5. constructor(props) { // 构造方法,props 应该是父类的一个成员变量
  6. super(props);
  7. this.state = { // 组件状态数据
  8. text: '',
  9. items:[{id: 1, status: 1, text: "去月球"},{id: 2, status: 0, text: "去火星"}]
  10. };
  11. // 设置 this 指向,默认 undefined
  12. this.handleChange = this.handleChange.bind(this);
  13. this.handleAdd = this.handleAdd.bind(this);
  14. this.handleComplete = this.handleComplete.bind(this);
  15. this.handleDelete = this.handleDelete.bind(this);
  16. }
  17. // 渲染解析 jsx
  18. render() {
  19. return (
  20. <div className="todo">
  21. <h3 className="text-center">Todos App</h3>
  22. <TodoList
  23. items={this.state.items}
  24. handleComplete={this.handleComplete}
  25. handleDelete={this.handleDelete} />
  26. <input className="input" type="text" placeholder="添加新任务"
  27. value={this.state.text}
  28. onChange={this.handleChange} />
  29. <button className="btn-add" onClick={this.handleAdd}>添加</button>
  30. </div>
  31. );
  32. }
  33. handleChange(e) {
  34. this.setState({ text: e.target.value })
  35. }
  36. handleAdd(e) {
  37. e.preventDefault();
  38. if (this.state.text.length === 0) {
  39. return;
  40. }
  41. const newItem = {
  42. id: Date.now(),
  43. text: this.state.text,
  44. status: 0
  45. };
  46. this.setState({
  47. items: [...this.state.items, newItem],
  48. text: ''
  49. });
  50. }
  51. handleComplete(taskid) {
  52. // 临时变量,不直接修改原数据
  53. let items = this.state.items;
  54. let findItem = items.find(item => item.id === taskid);
  55. findItem.status = findItem.status === 0 ? 1 : 0;
  56. this.setState({
  57. items: items
  58. });
  59. }
  60. handleDelete(taskid) {
  61. let items = this.state.items;
  62. items = items.filter(item => item.id !== taskid);
  63. this.setState({
  64. items: items
  65. });
  66. }
  67. }
  68. export default TodoApp;

3.3 TodoList.js

TodoList 接收父组件 TodoApp 中的数组,并将其渲染成一个 ul 列表:

  1. import React, { Component } from 'react';
  2. import TodoItem from './TodoItem';
  3. class TodoList extends Component {
  4. render() {
  5. return (
  6. <ul className="list">
  7. {
  8. this.props.items.map((item)=>{
  9. return (
  10. <TodoItem key={item.id}
  11. taskid={item.id}
  12. status = {item.status}
  13. text={item.text}
  14. handleComplete={this.props.handleComplete}
  15. handleDelete={this.props.handleDelete} />
  16. )
  17. })
  18. }
  19. </ul>
  20. );
  21. }
  22. }
  23. export default TodoList;

3.4 TodoItem.js

在 TodoList 遍历数组时,把每一项元素交给 TodoItem 组件,它会渲染成一个 li 元素:

  1. import React, { Component } from 'react';
  2. class TodoItem extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.taskComplete = this.taskComplete.bind(this);
  6. this.taskDelete = this.taskDelete.bind(this);
  7. }
  8. render() {
  9. let isCompleted = this.props.status === 1;
  10. return (
  11. <li className={isCompleted?'complete':''}>
  12. <input type="checkbox"
  13. checked={isCompleted}
  14. onChange={this.taskComplete}/>
  15. <span>{this.props.text}</span>
  16. <button className="btn-del" onClick={this.taskDelete}>删除</button>
  17. </li>
  18. );
  19. }
  20. taskComplete() {
  21. this.props.handleComplete(this.props.taskid);
  22. }
  23. taskDelete() {
  24. this.props.handleDelete(this.props.taskid);
  25. }
  26. shouldComponentUpdate(nextProps, nextState) {
  27. if (nextProps.text !== this.props.text || nextProps.status !== this.props.status) {
  28. return true;
  29. } else {
  30. return false;
  31. }
  32. }
  33. }
  34. export default TodoItem;

这几个文件写完之后,进入 react-todoapp 目录,cmd 运行 npm start,访问 http://localhost:3000 就能查看最终的结果了。

4. 思考

4.1 JSX

TodoApp 组件在 render 方法渲染时,使用了一个既不是字符串也不是 HTML 的语法,它被称为 JSX,是 JavaScript 的语法扩展,使用它可以很方便的创建 DOM。

JSX 看起来像是模板语言,但它具有 JavaScript 的全部功能:

  • 遇到 <> 就当作 HTML 解析
  • 遇到 {} 就当作 JavaScript 解析

4.2 组件通信

这里主要有两种通信情况:

  • 父组件向子组件通信
  • 子组件向父组件通信

每个组件都有一个 props 对象,用以访问组件的属性,所以,父组件可以向子组件传递一组 props 供其使用,就像方法传参一样。

子组件向父组件通信,可以利用回调函数:父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可向父组件通信。

回调函数也是增删一组数据,那么为什么不直接把数据传给子组件,直接操作?这是因为 props只读的,不能修改,改了就会报错:

  1. TypeError: Cannot assign to read only property 'items' of object '#<Object>'

这样设计是为了保证相同的输入,每次都输出相同的结果。

4.3 组件状态

组件分有状态无状态,比如 TodoApp 是有状态的,TodoList 和 TodoItem 是无状态的。这个状态 stateprops 类似,也是一组数据,但它是组件内部私有的,其他组件访问不了。

所以,TodoApp 组件只有在自己内部才可以对 this.state.items 内部增删改,就算把它传给其他组件,也是只读的。

在更新 state 时需要注意:

  • 不能直接修改 state:统一使用 setState() 更新
  • setState 是一个异步方法,可传递一个函数在执行结束后回调,setState({},()=>{..})
  • setState 会合并更新,就是可以只传递变更的部分

组件的 state 可以随着用户交互而产生变化,但 props 一旦定义就不再发生改变。

4.4 单向数据流

子组件不能直接修改父组件的数据,数据只能由父组件传给子组件,更新只能通过回调,这个特性被称作单向数据流

它保证了组件相同的输入,每次都是相同的输出。所有数据都在父组件,代码易于理解,方便维护。

4.5 其他

import React, { Component } from 'react'; 这是 ES6 解构赋值 的用法,解构赋值是对赋值运算符的扩展,可以将属性/值从对象/数组中取出,赋值给其他变量。

这个导入就相当于:

  1. import React from 'react';
  2. const Component = React.Component;

类比 Java 的话,可以这样理解,React 相当于包,Component 相当于包下的类,要使用都要先导入。

5. 总结

简单写了下自己的理解,仅供参考!还是要看官方文档:

此次编写的 react-todoapp 源码地址:https://github.com/chuondev/react-todoapp

React 入门-写个 TodoList 实例的更多相关文章

  1. React入门最好的学习实例-TodoList

    前言 React 的核心思想是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件. 最近前端界闹的沸沸扬扬的技术当属react了,加上项目需要等等原因,自己也决定花些时间来好 ...

  2. React入门——制作一个TodoList App

    源码 import React, { Component, Fragment } from "react"; class TodoList extends Component { ...

  3. React 入门实例教程

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  4. 2015年最热门前端框架React 入门实例教程

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  5. React入门实例教程

    文章转自:阮一峰 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React ...

  6. React 入门实例教程(转载)

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  7. 【转】react入门实例教程

    作者: 阮一峰 日期: 2015年3月31日 写在前面:原文链接http://www.ruanyifeng.com/blog/2015/03/react.html    github地址https:/ ...

  8. React 入门实例

    React 入门实例教程 一.安装 React 的安装包,可以到官网下载. $ git clone git@github.com:ruanyf/react-demos.git 如果你没安装 git, ...

  9. React 入门实例教程【转】

    Any day will do. 哪一天都行 Are you kidding? 你在开玩笑吧! Congratulations! 祝贺你! I don’t mean it. 我不是故意的. 原文作者: ...

随机推荐

  1. 第10.2节 查看导入的Python模块

    在Python中,要查看导入模块,可以使用sys.modules来查看,不过sys包含了所有导入模块包括内建模块,如果需要过滤掉内建模块甚至扩展模块,则需要对sys.modules进行一下过滤. 一. ...

  2. crawlergo动态爬虫去除Spidername使用

    本来是想用AWVS的爬虫来联动Xray的,但是需要主机安装AWVS,再进行规则联动,只是使用其中的目标爬虫功能感觉就太重了,在github上面找到了由360 0Kee-Team团队从360天相中分离出 ...

  3. Linux使用inode(i节点号)删除文件

    今天学习Linux的时候遇到的知识点,想起了以前一次线下AWD攻防赛的时候的不死马,记录一下 在Linux里面,有的时候我们会遇到奇奇怪怪的文件名,以至于我们删除不了,比如说我们创建了一个叫做&quo ...

  4. 【学习笔记】使用 bitset 求解较高维偏序问题

    求解五维偏序 给定 \(n(\le 3\times 10^4)\) 个五元组,对于每个五元组 \((a_i, b_i, c_i, d_i, e_i)\),求存在多少个 \(1\le j\le n\) ...

  5. KVM初体验之virt-manager unable to connect to libvirt的处理办法

    解决方法 需要用root身份运行virt-manager

  6. mysql 迁移数据库到 oracle (sql注意问题)

    http://ykdn2010.iteye.com/blog/1511349 一. 项目已用到 oracle 函数的转换 1.  Oracle 中的 TO_DATE (),TO_CHAR () 示例: ...

  7. kafka-java消费者与生产者代码示例

    引入依赖 <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka_2.11 ...

  8. 冬季里有温度的 3D 可视化智慧供热系统

    前言 随着供暖季来临,我国北方大部分省市开始陆续供热.一年一度的供暖问题被提上了日程.在我们的印象里,供热的设施不论是锅炉.管道还是暖气片,都是坚硬的钢铁.铸铁.HT 通过自主研发的强大的基于 HTM ...

  9. Java File类的简单使用

    Java File的简单使用(创建.删除.遍历.判断是否存在等) Java文件类以抽象的方式代表文件名和目录路径名.该类本身不能用来读数据或写数据,它主要用于磁盘上文件和目录的创建.文件的查找和文件的 ...

  10. Unity 操作快捷键

            Q Hand(手形)工具 可以平移整个Scene视图       W Translate(移动)工具 移动所选择的游戏对象       E Rotate(旋转)工具 按任意角度旋转游戏 ...