在过去的一年中,Vue的火热让人印象深刻,但是React在github上王者地位依然巩固,在本文发布前拥有5.7W个⭐️,在当下依然是最热门的Javascript框架。本文将从最基础的点讲起并且尽量用简短的篇幅,一步步完成一个简单的TodoList :D


准备工作
首先新建一个项目,项目结构如下:
.
├── dist
├── index.html //主页面
├── package.json
├── src
│ ├── components
│ ├── vendor
│ ├── styles
│ └── entry.js //源js文件
└── webpack.config.js
** index.html ** 代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TodoList</title>
</head>
<body>
<div id="container">
</div>
<script src="./dist/bundle.js"></script>
</body>
</html>
** package.json ** 代码如下,使用 * npm install * 安装好依赖包:
{
"name": "react-todos",
"version": "1.0.0",
"description": "test",
"main": "index.js",
"scripts": {
"dev": "webpack && node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^15.4.1",
"react-dom": "^15.4.1",
"sass": "^0.5.0",
},
"devDependencies": {
"babel-core": "^5.5.8",
"babel-loader": "^5.1.4",
"css-loader": "^0.14.5",
"express": "^4.14.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.4",
"jsx-loader": "^0.13.2",
"node-libs-browser": "^0.5.2",
"node-sass": "^3.2.0",
"react-redux": "^5.0.1",
"sass-loader": "^1.0.4",
"style-loader": "^0.12.3",
"url-loader": "^0.5.6",
"webpack": "^1.9.11"
}
}
webpack配置
webpack是当下流行的打包工具,使用webpack可以帮助我们模块化代码、解析依赖、压缩代码等等,这里我们使用webpack将JSX语法转换成纯Javascript代码,如果对webpack不太熟悉的可以先行了解下用法。其配置代码如下:
const path = require('path');
module.exports = {
entry: './src/entry.js',
output: {
path: path.join(__dirname, '/dist'),
filename: 'bundle.js'
},
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.js|jsx$/,
loaders: ['babel']
},
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
}
JSX语法
JSX语法,像是在Javascript代码里直接写XML的语法,实质上这只是一个语法糖,每一个XML标签都会被JSX转换工具转换成纯Javascript代码,React 官方推荐使用JSX, 当然你想直接使用纯Javascript代码写也是可以的,只是使用JSX,组件的结构和组件之间的关系看上去更加清晰。使用JSX编写的代码长这样:
React.render(
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);
搭建基本结构
做好了上述的准备工作和知识了解后,就可以开始编写代码了。
TodoList有用户提交的表单和列表,先把基本的骨架写好,之后在逐个完善,在 ** entry.js ** 写入以下代码:
import React, { Component } from 'react'
import { render } from 'react-dom'
// 父组件
class ListBox extends Component {
render() {
return(
<div>
<h1>React-todoList</h1>
<ListForm />
<List />
</div>
)
}
}
// 表单组件
class ListForm extends Component {
render() {
return(
<h2>This is ListForm</h2>
)
}
}
// 列表组件
class List extends Component {
render() {
return(
<h2>This is List</h2>
)
}
}
render(
<ListBox />,document.getElementById('container')
)
以上的代码就是写好了ListBox父组件,和两个子组件,最后调用render函数将它们都渲染到页面上。
执行webpack打包后,打开 ** index.html ** 在浏览器可以看到以下效果。

列表循环渲染
列表里面有多条todo,而且是动态渲染的,首先我们在父组件的构造函数中模拟一个假数据data来集中管理,然后用data属性传递给子组件:
class ListBox extends Component {
constructor(props) {
super(props)
this.state = {
data: [
{author: "John", text: "This is first todo"},
{author: "Allen", text: "This is second todo"}
]
}
}
render() {
return(
<div>
<h1>React-todoList</h1>
<ListForm />
<List data={this.state.data}/>
</div>
)
}
}
在子组件中使用 ** this.props.data ** 接收到父组件数据,然后循环遍历出来:
class List extends Component {
render() {
let todoNodes = this.props.data.map(function(item, index){
return(
<div key={index}>
<h2>{ item.author }</h2>
<p>{ item.text }</p>
</div>
)
})
return(
<div>
{todoNodes}
</div>
)
}
}
PS:大家可能会注意到 <div key={index}>
,是因为在React中数组遍历出来的节点需要定义一个唯一的 key 属性,这里我用的是index。
webpack打包好后,可以在页面上看到data里面的数据都已经被渲染出来了:

表单提交
在表单中我们需要两个输入框和一个提交按钮,还需要绑定提交事件,将输入框的内容提交给父组件:
class ListForm extends Component {
handleSubmit(e){
e.preventDefault()
let author = this.refs.author.value.trim()
let text = this.refs.text.value.trim()
if(!author && !text){
alert('please confirm input box non-blank')
return
}
//pass value to ListBox
this.props.onTodoSubmit({
author: author,
text: text
})
//clear input
this.refs.author.value = ''
this.refs.text.value = ''
}
render() {
return (
<form onSubmit={this.onTodoSubmit.bind(this)}>
<div>
<input type="text" placeholder="Name" ref="author" required/>
</div>
<div>
<input type="text" placeholder="What to do..." ref="text" required/>
</div>
<input type="submit" value="Post" className="submit-btn"/>
</form>
)
}
}
在html中写好了一个表格,绑定了一个提交事件 handleSubmit,在两个输入框中分别定义了 ref 属性方便操作dom。
在提交事件中首先是阻止了默认事件,通过 this.refs 操作dom节点拿到输入框内容,再调用父组件的 onTodoSubmit 函数将数据传递过去。
数据处理
现在要做的就是在ListBox传递 onTodoSubmit 属性,并且写好数据处理的函数就ok了:
class ListBox extends Component {
constructor(props) {
super(props)
this.state = {
data: [
{author: "John", text: "This is first todo"},
{author: "Allen", text: "This is second todo"}
]
}
}
handleTodoSubmit(todo) {
let data = Object.assign([], this.state.data)
data.push(todo)
this.setState({ data: data })
}
render() {
return(
<div>
<h1>React-todoList</h1>
<ListForm onTodoSubmit={this.handleTodoSubmit.bind(this)}/>
<List data={this.state.data}/>
</div>
)
}
}

至此一个React版的TodoList就完成了,在填写好表单后点击提交,页面就会实时更新了,其实还可以再修改下样式和丰富一下功能,比如:本地存储、删除todo等等。对于React我也是处在摸索阶段,如果文中出现错误的地方,希望大家提出来,我会马上修改。
源码放在了Github,如果这篇文章对您有帮助的话,可以点个⭐️支持一下谢谢哈。
网友评论