React 入门-写个 TodoList 实例
React 是一个用于构建用户界面的 JavaScript 库,主要特点有:
- 声明式渲染:设计好数据和视图的关系,数据变化 React 自动渲染,不必亲自操作DOM
- 组件化:页面切分成多个小部件,通过组装拼成整体页面,利于代码复用
本文通过写个简单的 TodoList
实例,不求甚解,熟悉下 React 的开发过程。
1. 安装 Node.js
Node.js
是一个运行环境,类似 jdk
,用以支持在服务端运行 JavaScript。
您可以在这里下载安装包:
http://nodejs.cn/download/
以绿色版安装为例,将 node-v10.16.1-win-x64.zip 解压到 E:\software\ 并命名为 node-v10.16.1
在 Path 环境变量中增加两项:
E:\software\node-v10.16.1\
E:\software\node-v10.16.1\node_global
在 cmd 中使用 node -v
显示版本号,表示安装成功。
Node.js 中有个 npm
软件包管理器,可以很方便的管理下载和使用第三方开源包,类似 maven
,使用 npm -v
显示版本号,表示 npm 也没有问题。
绿色版安装完成后一些必要的配置:
npm config set prefix "E:\software\node-v10.16.1\node_global"
设置全局安装的模块存储路径
npm config set cache "E:\software\node-v10.16.1\node_cache"
设置下载缓存的存储路径
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 应用程序:
cmd> npm install -g create-react-app
cmd> cd E:
cmd> create-react-app react-todoapp
cmd> cd react-todoapp
脚手架会在当前目录创建一个 react-todoapp
目录:
react-todoapp
├── README.md
├── node_modules
├── package.json
├── package-lock.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
└── setupTests.js
目录中主要的文件和文件夹说明:
- README.md: 项目简介,支持 Markdown 语法
- node_modules: 项目的依赖包,类似
Maven Repository
- package.json: 配置项目依赖的第三方包,类似
pom.xml
- package-lock.json: 锁定第三方包的版本号,保证
npm install
版本一致 - public: 公开资源,网站路径,类似 nginx 的 html 目录
- src: 核心组件代码文件
为了便于开发,删除目录中不必要的文件,最终结构如下:
react-todoapp
├── README.md
├── node_modules
├── package.json
├── package-lock.json
├── .gitignore
├── public
│ └── index.html
└── src
├── App.css
├── index.js
├── TodoApp.js
├── TodoItem.js
└── TodoList.js
接下来,设计与实现一个 TodoList 的例子,我们把所有代码过一下,敲一遍,先不管为什么,跑起来,最后再整理下知识点。
3. 实例 TodoApp
主要实现功能有:
- 添加一个待办事项
- 删除一个待办事项
- 勾选复选框标记事项已完成
如图所示,总共将页面拆分成了三个组件:TodoApp
, TodoList
和 TodoItem
。
3.1 index.js 入口文件
应该可以类比 java 的 main
方法,在 src 目录新建 index.js
内容如下:
// 引入 React, ReactDOM
import React from 'react';
import ReactDOM from 'react-dom';
// 引入 TodoApp 组件
import TodoApp from './TodoApp';
// 将渲染结果挂在到 root 节点,该节点在 index.html 中
ReactDOM.render(
<React.StrictMode>
<TodoApp />
</React.StrictMode>,
document.getElementById('root')
);
先导入需要使用的组件(类),然后调用它们提供的方法和服务,有没有些许眼熟?
3.2 TodoApp.js
TodoApp 设计了页面整体布局,它包含全部数据以及操作这些数据的方法,是其他两个组件的父组件
:
import React, { Component } from 'react';
import TodoList from './TodoList';
import './app.css';
class TodoApp extends Component {
constructor(props) { // 构造方法,props 应该是父类的一个成员变量
super(props);
this.state = { // 组件状态数据
text: '',
items:[{id: 1, status: 1, text: "去月球"},{id: 2, status: 0, text: "去火星"}]
};
// 设置 this 指向,默认 undefined
this.handleChange = this.handleChange.bind(this);
this.handleAdd = this.handleAdd.bind(this);
this.handleComplete = this.handleComplete.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
// 渲染解析 jsx
render() {
return (
<div className="todo">
<h3 className="text-center">Todos App</h3>
<TodoList
items={this.state.items}
handleComplete={this.handleComplete}
handleDelete={this.handleDelete} />
<input className="input" type="text" placeholder="添加新任务"
value={this.state.text}
onChange={this.handleChange} />
<button className="btn-add" onClick={this.handleAdd}>添加</button>
</div>
);
}
handleChange(e) {
this.setState({ text: e.target.value })
}
handleAdd(e) {
e.preventDefault();
if (this.state.text.length === 0) {
return;
}
const newItem = {
id: Date.now(),
text: this.state.text,
status: 0
};
this.setState({
items: [...this.state.items, newItem],
text: ''
});
}
handleComplete(taskid) {
// 临时变量,不直接修改原数据
let items = this.state.items;
let findItem = items.find(item => item.id === taskid);
findItem.status = findItem.status === 0 ? 1 : 0;
this.setState({
items: items
});
}
handleDelete(taskid) {
let items = this.state.items;
items = items.filter(item => item.id !== taskid);
this.setState({
items: items
});
}
}
export default TodoApp;
3.3 TodoList.js
TodoList 接收父组件 TodoApp 中的数组,并将其渲染成一个 ul
列表:
import React, { Component } from 'react';
import TodoItem from './TodoItem';
class TodoList extends Component {
render() {
return (
<ul className="list">
{
this.props.items.map((item)=>{
return (
<TodoItem key={item.id}
taskid={item.id}
status = {item.status}
text={item.text}
handleComplete={this.props.handleComplete}
handleDelete={this.props.handleDelete} />
)
})
}
</ul>
);
}
}
export default TodoList;
3.4 TodoItem.js
在 TodoList 遍历数组时,把每一项元素交给 TodoItem 组件,它会渲染成一个 li
元素:
import React, { Component } from 'react';
class TodoItem extends Component {
constructor(props) {
super(props);
this.taskComplete = this.taskComplete.bind(this);
this.taskDelete = this.taskDelete.bind(this);
}
render() {
let isCompleted = this.props.status === 1;
return (
<li className={isCompleted?'complete':''}>
<input type="checkbox"
checked={isCompleted}
onChange={this.taskComplete}/>
<span>{this.props.text}</span>
<button className="btn-del" onClick={this.taskDelete}>删除</button>
</li>
);
}
taskComplete() {
this.props.handleComplete(this.props.taskid);
}
taskDelete() {
this.props.handleDelete(this.props.taskid);
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.text !== this.props.text || nextProps.status !== this.props.status) {
return true;
} else {
return false;
}
}
}
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
是只读的,不能修改,改了就会报错:
TypeError: Cannot assign to read only property 'items' of object '#<Object>'
这样设计是为了保证相同的输入,每次都输出相同的结果。
4.3 组件状态
组件分有状态和无状态,比如 TodoApp 是有状态的,TodoList 和 TodoItem 是无状态的。这个状态 state
和 props
类似,也是一组数据,但它是组件内部私有的,其他组件访问不了。
所以,TodoApp 组件只有在自己内部才可以对 this.state.items
内部增删改,就算把它传给其他组件,也是只读的。
在更新 state 时需要注意:
- 不能直接修改 state:统一使用
setState()
更新 setState
是一个异步方法,可传递一个函数在执行结束后回调,setState({},()=>{..})
setState
会合并更新,就是可以只传递变更的部分
组件的 state 可以随着用户交互而产生变化,但 props 一旦定义就不再发生改变。
4.4 单向数据流
子组件不能直接修改父组件的数据,数据只能由父组件传给子组件,更新只能通过回调,这个特性被称作单向数据流
。
它保证了组件相同的输入,每次都是相同的输出。所有数据都在父组件,代码易于理解,方便维护。
4.5 其他
import React, { Component } from 'react';
这是 ES6 解构赋值
的用法,解构赋值是对赋值运算符的扩展,可以将属性/值从对象/数组中取出,赋值给其他变量。
这个导入就相当于:
import React from 'react';
const Component = React.Component;
类比 Java 的话,可以这样理解,React 相当于包,Component 相当于包下的类,要使用都要先导入。
5. 总结
简单写了下自己的理解,仅供参考!还是要看官方文档:
- 中文:https://react.docschina.org/docs/getting-started.html
- 英文:https://reactjs.org/docs/getting-started.html
此次编写的 react-todoapp 源码地址:https://github.com/chuondev/react-todoapp
React 入门-写个 TodoList 实例的更多相关文章
- React入门最好的学习实例-TodoList
前言 React 的核心思想是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件. 最近前端界闹的沸沸扬扬的技术当属react了,加上项目需要等等原因,自己也决定花些时间来好 ...
- React入门——制作一个TodoList App
源码 import React, { Component, Fragment } from "react"; class TodoList extends Component { ...
- React 入门实例教程
现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...
- 2015年最热门前端框架React 入门实例教程
现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...
- React入门实例教程
文章转自:阮一峰 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React ...
- React 入门实例教程(转载)
现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...
- 【转】react入门实例教程
作者: 阮一峰 日期: 2015年3月31日 写在前面:原文链接http://www.ruanyifeng.com/blog/2015/03/react.html github地址https:/ ...
- React 入门实例
React 入门实例教程 一.安装 React 的安装包,可以到官网下载. $ git clone git@github.com:ruanyf/react-demos.git 如果你没安装 git, ...
- React 入门实例教程【转】
Any day will do. 哪一天都行 Are you kidding? 你在开玩笑吧! Congratulations! 祝贺你! I don’t mean it. 我不是故意的. 原文作者: ...
随机推荐
- SpringBoot打包成Docker镜像
1. 本文环境 Maven:3.6.3(Maven配置参考) SpringBoot version:2.3.4.RELEASE Docker version: 19.03.11(Docker搭建参考) ...
- Linux之centos包管理【rpm】、【yum】、【tar】
rpm包是二进制格式,无需编译安装便可使用,tar包是源码格式,需要编译安装才可使用 rpm包管理: rpm:redhat package manager,红帽的包管理器,其主要的操作参数有如下: - ...
- PyQt(Python+Qt)学习随笔:Qt Designer组件属性编辑界面中对话窗QDialog的modal属性
modal属性表示窗口执行show()操作时是以模态窗口还是非模态窗口形式展示,缺省为False,设置该值与QWidget.windowModality的值设置为 Qt.ApplicationModa ...
- Python3安装且环境配置(三)
1.在Window 平台安装 Python3 以下为在 Window 平台上安装 Python3 的简单步骤: 打开WEB浏览器访问http://www.python.org/download/ 在下 ...
- 《深入理解计算机系统》实验三 —— Buf Lab
这是CSAPP的第三个实验,主要让我们熟悉GDB的使用,理解程序栈帧的结构和缓冲区溢出的原理. 实验目的 本实验的目的在于加深对IA-32函数调用规则和栈结构的具体理解.实验的主要内容是对一个可执 ...
- 微信小程序下拉加载下一页
小程序做得多了,有些常用功能就有必要记录一下 请看详解: 微信小程序之下拉触底时加载下一页 wxml参考: <scroll-view class='dataContainer' scroll-y ...
- C++异常之五 异常和继承
异常和继承 异常也是类,我们可以创建自己的异常类,在异常中可以使用(虚函数,派生,引用传递和数据成员等), 下面用一个自制的数组容器Vector,在对Vector初始化时来对Vector的元素个数进行 ...
- 数组问题:a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头--数组赋值: int LEN = 10000; int[][] arr = new int[LEN][LEN]; for (int i = 0; i < LEN; i+ ...
- DVWA各级文件包含漏洞
File Inclusion文件包含漏洞 漏洞分析 程序开发人员通常会把可重复使用的函数写入到单个文件中,在使用某些函数时,直接调用此文件,而无需再次编写,这种调用文件的过程被称为包含. 有时候由于网 ...
- Python 学习笔记 之 随着学习不断更新的Python特性搜集
大小写敏感 缩进敏感--tab和空格不要混用,最好使用4个空格进行缩进.可使用vim配置缩进字符为4个空格 编写py文件时注意文件的编码,UTF-8 without BOM, 并且记得声明coding