博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过todoList实例快速react16入门(vue基础更容易理解)
阅读量:5818 次
发布时间:2019-06-18

本文共 11504 字,大约阅读时间需要 38 分钟。

author: thomaszhou

我以前是学习vue的,没有接触过react,但是现在是react16,要学肯定是最新的啊,所以我们这里也是学习react16,既然vue和react都是mvvm框架,学下来,我发现其实很多东西虽然有区别,但是很熟悉,react语法熟悉起来也是非常的快,对于有vue基础的童鞋可以在边学习react语法的时候,一遍去类比自己的vue的语法,这样会很快的上手,好了,开始正题

构建项目

react那些构建工具都是热加载(不是自动刷新浏览器,只是自动更新修改的部分)

方法一:create-react-app(用于快速构建开发环境的脚手架工具)

  • 安装需要在命令行中进行,在安装create-react-app前,你需要安装node。然后在命令行中输入下面的命令:
Liunx和Mac电脑下:(这里的 -g 是全局安装的意思。)sudo npm install -g create-react-app复制代码
  • 创建React项目(项目名为react2)
    • 小坑:文件名不要使用大写,这样作只要是为了严谨性,因为在Linux下是严格区分大小写的
create-react-app react2复制代码
  • 启动服务
cd react2npm start 复制代码

缺点:虽然利用官方脚手架很全面,但是对于修改webpack非常不方便,所以考虑使用第二种方法

方法二:generator-react-webpack

  • 安装 安装还是在命令行用npm进行安装,不过在全局安装generator-react-webpack之前,你可以先安装yeoman。命令如下:
npm install -g yonpm install -g generator-react-webpack复制代码
  • 创建目录 我们先用命令自行创建一个文件:new-react-demo
mkdir new-react-demo     // 创建一个new-react-demo文件夹cd new-react-demoyo react-webpack     // 用生成器生成我们的项目目录复制代码
  • 运行
npm start复制代码

优点

优点介绍:    1、基于webpack构建,可以很容易的配置自己需要的webpack。    2、支持ES6,集成了Babel-Loader。    3、支持不同风格的CSS(sass,less,stylus)。    4、支持PostCSS转换样式。    5、集成了esLint功能。    6、可以轻松配置单元测试,比如Karma和Mocha缺点:就是要依靠yeoman来生成。复制代码

路由

  • react-router:是基本的router包,里边函的内容较多,但是在网页开发中有很多用不到,现在的市面上的课程讲的基本都是这个包的教程。
  • react-router-dom:随着react生态环境的壮大,后出现的包,这个包比react-router包轻巧了很多。
  • 其实安装了react-router包就不用安装了react-router-dom包了,因为react-router-dom是react-router的子集
react-router 和 react-router-dom 二选一即可npm install --save react-router react-router-dom复制代码

通过react脚手架了解目录文件

通过脚手架创建的初始项目的目录如下(挑部分讲)

  • pubilc
    • index.html 页面的整体html模板
  • src
    • App.js 是一个组件,负责显示页面的内容
    • App.css
    • App.test.js 自动化测试文件
    • index.js 入口文件,并且引入App.js并挂载到index.html中的root节点中
    • index.css

index.html

      
React
复制代码

App.js

在js文件写html就是JSX语法,下面的return包裹的html就是JSX,使用JSX就必须要在开头引入react

// 功能:负责显示页面的内容import React, { Component } from 'react';// 相当于 // import React from 'react'// const Component = React.Componentimport logo from './logo.svg'; // 引入logo的svg图片import './App.css';  // 引入App.css文件// react创建组件的方式// 通过继承react.Component来创建组件class App extends Component {  render() { // 返回什么,App组件就返回什么    return (      
hello React
); }}export default App;复制代码

index.js

// 入口文件// 功能:引入APP文件,渲染到页面上import React from 'react'; // 引入react包import ReactDOM from 'react-dom';import './index.css';import App from './App';import registerServiceWorker from './registerServiceWorker'; // 支持PWA// * 
表示JSX语法,所以要在开头引入react *// 将
组件挂载到root节点下ReactDOM.render(
, document.getElementById('root'));registerServiceWorker(); // 使PWA生效复制代码

todolist快速了解React

我们这里就不讲究这么多了,就做一个简单的todolist,牵扯到父子组件的通信,以及一些常用的使用方法让我们快速了解react最基本的使用方法,后续的深入需要自己额外的学习了。

  • 不同Vue的是,vue可以通过v-model来进行双向绑定,改变任何一方都可以自动牵动另一方变化,但是react不是,他是单向绑定,也就是说数据改变驱动视图层的改变(input就是比较典型的例子,后续会详解)

react创建组件的方式

import React, { Component } from 'react';import TodoItem from './TodoItem';import './style.css'import './../index.css'export default class TodoList extends Component {    // (1)定义数据	// constructor是最先优先被执行	constructor(props) {		super(props); // 调用父类函数方法,继承		// 组件的状态state		this.state = {			inputValue: '',			list: []		}	}	// (2)render部分(也是写JSX语法部分)	render() {	    return (	        
// 同vue一样,组件的html部分,最外层必须要包一层标签,一般都是用div。 // 这里面就写的是html,但是,这是一个必须符合JSX语法的html
) } // (3)方法部分(事件部分) handleClick() {...} // ...等等方法 handleInputChange{...}}复制代码

先说几个(render中)要注意的点⚠️

  • 给html标签加class样式,要写成ClassName="样式名"
复制代码
  • 给html标签添加onclick或者onchange这些原生js有的事件,第二个字母必须首字母大写,并且要用{}来绑定方法
提交复制代码
  • 标签中的值(vue和react区别)
{
{vue的方式(双括号)}}
{react的写法(单括号)}
复制代码
  • 绑定html的属性,也是要用{}赋值
复制代码
  • vue的v-for的react写法
    {this.state.list.map((item, index) => { return (
  • {item}
  • ) })}
复制代码
  • react要修改数据层的值,不能像vue一样直接this.inputValue="1",必须要用this.setState
handleDelete(index) {	// 不推荐直接在setState中直接修改数组的值(例如增加,删除)	// 遵循immutable特性,就是state不允许我们直接改变	const listTemp = [...this.state.list];	listTemp.splice(index, 1);	this.setState(() => ({		list: listTemp	}))}复制代码

todolist的源代码

首先我们要将数据inputValue和input标签的value属性进行绑定,但是react是单向绑定,也就是说,修改数据inputValue,input的value会跟着变化,但是我们在页面上是不能修改input的Value值,因为没有效果。。。

输入数组,添加list (思路):将inputValue和input的value进行绑定,然后再绑定onChange事件到handleInputChange()方法,使得当在页面修改input的value值时,会利用this.setState,将value值赋值给inputValue(相当于我们自己自主实现另一方向的绑定,vue是直接默认双向绑定的);点击按钮后,将inputValue 的值添加进list数组,然后将list数据渲染到下面的列表中

点击list数据,删除当前数据(思路):给下面列表中的每个数据都绑定click事件,点击列表数据,将index传递给函数handleDelete(index),删除list数组中对应的数据,就可以完成更新(但是要遵循immutable特性,即:不能直接修改state中的数据,而是通过建立一个copy,然后对copy进行修改,再利用setState()进行赋值

import React, { Component } from 'react'import TodoItem from './TodoItem';import './style.css'import './../index.css'export default class TodoList extends Component {    constructor(props) {        super(props);        this.state = {            inputValue: '',            list: []        }    }    render() {        return (            
    {this.state.list.map((item, index) => { return (
  • {item}
  • ) })}
) } handleClick() { this.setState(() => ({ list: [...this.state.list, this.state.inputValue], inputValue: "" })) } handleInputChange(e) { // 因为setState是异步的,需要将e.target.value先保存起来,为什么? const value = e.target.value; this.setState(() => ({ inputValue: value })) } handleDelete(index) { // 不推荐直接在setState中直接修改数组的值(例如增加,删除) // 遵循immutable特性,就是state不允许我们直接改变,好处是什么?? const listTemp = [...this.state.list]; listTemp.splice(index, 1); this.setState(() => ({ list: listTemp })) }}复制代码

父组件和子组件的传递(todolist改造)

将上面todoList中列表的实现部分封装成子组件TodoItem,然后通过父子组件的通信方式来实现功能

  • 父组件传递props给子组件
import TodoItem from './TodoItem'; // 引入子组件...export default class TodoList extends Component {    ... ...    
    {this.state.list.map((item, index) => { return (
    {/*传递值:父组件将item通过content属性传递给子组件 */} {/*传递方法:父组件将方法handleDelete传递给子组件,但是要将this同时传递到子组件 */} < TodoItem content={item} index={index} handleDeleteItem={this.handleDelete.bind(this)} />
    ) })}
}复制代码
  • 子组件接受,使用父组件传递的方法和参数

通过this.props.xxx来使用父组件传递来的属性和方法

//TodoItem.jsimport React, { Component } from 'react'export default class TodoItem extends Component {    constructor(props) {        super(props);        // 使得handleClick的this指向当前组件,不然默认是undefined        // 统一将方法的bind都写在constructor中        this.handleClick = this.handleClick.bind(this);    }    render() {        return (            
{/* this.props.content是父组件传递来的值 */} {this.props.content}
) } handleClick() { // 如果要修改父组件的data,则调用父组件的方法进行操作 // 但是在父组件要将this一并传递过来,才可以使用this this.props.handleDeleteItem(this.props.index); }}复制代码

优化成标准代码!⚠️

  • 通过es6的解耦简化-子组件接收父组件的props
render() {    const { content } = this.props; // 转换    return (        
{/* 只需要使用content就相当于之前的this.props.content */} {content}
)}handleClick() { const { handleDeleteItem, index } = this.props; handleDeleteItem(index); // ❌替代 this.props.handleDeleteItem(this.props.index);}复制代码
  • 统一将方法的bind都写在constructor中
constructor(props) {    super(props);    // 使得handleClick的this指向当前组件,不然默认是undefined    this.handleClick = this.handleClick.bind(this);}
{this.props.content}
// ❌避免下面的写法
{this.props.content}
复制代码
  • JSX中体积不要太大
render() {    return (    ...❌ul标签中写了一大堆的js的语句,应该剥离出来    ...    
    {this.state.list.map((item, index) => { return (
  • {item}
  • ) })}
)}// 改进后render() { return ( ... ...
    {this.getTodoItem()}
)}// 将过于负责的js逻辑放入函数中getTodoItem() { // 然后记得return回来 return this.state.list.map((item, index) => { return (
{/* 父组件将item通过content属性传递给子组件 */} < TodoItem content={item} index={index} handleDeleteItem={this.handleDelete} />
) })}复制代码
  • setState可以接受一个参数prevState,表示修改数据之前那一次的数组的数值
// 旧版本handleClick() {	this.setState(() => ({		list: [...this.state.list, this.state.inputValue],		inputValue: ""	}));}// 新版本handleClick() {	this.setState((prevState) => ({		list: [...prevState.list, prevState.inputValue],		inputValue: ""	}));}复制代码

this.setState(() => ({}))其实等价于this.setState(() => {return {} })

// 旧版handleDelete(index) {	const listTemp = [...this.state.list];	listTemp.splice(index, 1);	this.setState(() => ({		list: listTemp	}));}// 新版handleDelete(index) {	this.setState((prevState) => {		const listTemp = [...prevState.list];		listTemp.splice(index, 1);		return { list: listTemp }	});}复制代码

衍生的思考

声明式开发

直接操作dom那是命令式编程,我们每次要进行操作都要去获取dom节点,然后操作dom节点的一些属性或者值,但是MVVM框架(vue,react,angular)都是面向数据来编程,框架会根据数据来构建整个页面的dom,这会帮助我们减少大量dom操作

可以和其他框架并存

在index.js文件中我们可以看到如下语句:

// 将
组件挂载到root节点下ReactDOM.render(
, document.getElementById('root'));复制代码

目前框架的初始状态的入口文件index.js,是将App组件挂载到index.html中root这个dom上,也就是说,假设我们在index.html的root的dom节点后面写一个新的dom节点rootNew,这样的话,我们可以在里面创建另一个框架,然后仅仅是操作rootNew:

// 将
组件挂载到root节点下ReactDOM.render(
, document.getElementById('rootNew'));复制代码

这样的话,我们不同的框架只需要考虑自己对应的那个dom节点就可以了,这样也就不会相互影响了,可以共存多个框架

组件化

单向数据流

react要求编程有个单向数据流的概念,父组件可以向子组件传递内容,子组件只能使用这个值,但是不能改变这个值,所以如果子组件要修改父组件的值,那就要通过使用父组件的方法函数来修改父组件的值

react是视图层的框架

只解决数据和页面渲染上的一些问题,打个比方,我们的父子层已经好多层了,那兄弟组件或者亲戚组件(类似你父亲的兄弟组件,或者爷爷组件的兄弟组件)要进行通信,我们就需要一层层往上传递,又要一层层向下传递props,这样中间链路上的组件都要参与到这个传递的事情中,就会造成巨大的麻烦,这个时候就需要一些数据层的框架进行配合

函数式编程

写代码中,其实都是在写一个个的函数,这对自动化测试会带来巨大的便利,他们只需要给一个个函数设置输入,输出就可以去判断函数的正确性

最后的代码

  • TodoList.js
import React, { Component } from 'react'import TodoItem from './TodoItem';import './style.css'import './../index.css'export default class TodoList extends Component {	// 定义数据	constructor(props) {		super(props); // 调用父类函数方法,继承		this.handleClick = this.handleClick.bind(this);		this.handleInputChange = this.handleInputChange.bind(this);		this.handleDelete = this.handleDelete.bind(this);		// 组件的状态state		this.state = {			inputValue: '',			list: []		}	}	render() {		return (			
    {this.getTodoItem()}
) } getTodoItem() { return this.state.list.map((item, index) => { return (
< TodoItem content={item} index={index} handleDeleteItem={this.handleDelete} />
) }) } handleClick() { this.setState((prevState) => ({ list: [...prevState.list, prevState.inputValue], inputValue: "" })) } handleInputChange(e) { const value = e.target.value; this.setState(() => ({ inputValue: value })) } handleDelete(index) { this.setState((prevState) => { const listTemp = [...prevState.list]; listTemp.splice(index, 1); return { list: listTemp } }); }}复制代码
  • TodoItem.js
import React, { Component } from 'react'export default class TodoItem extends Component {    constructor(props) {        super(props);        this.handleClick = this.handleClick.bind(this);    }    render() {        const { content } = this.props;        return (            
{content}
) } handleClick() { const { handleDeleteItem, index } = this.props; handleDeleteItem(index); }}复制代码

转载地址:http://zzzdx.baihongyu.com/

你可能感兴趣的文章
Leetcode-Database-176-Second Highest Salary-Easy(转)
查看>>
构建Docker Compose服务堆栈
查看>>
最小角回归 LARS算法包的用法以及模型参数的选择(R语言 )
查看>>
CentOS7下zip解压和unzip压缩文件
查看>>
Hadoop生态圈-Kafka常用命令总结
查看>>
如何基于Redis Replication设计并实现Redis-replicator?
查看>>
Linux 环境下 PHP 扩展的编译与安装 以 mysqli 为例
查看>>
浮点数内存如何存储的
查看>>
贪吃蛇
查看>>
EventSystem
查看>>
用WINSOCK API实现同步非阻塞方式的网络通讯
查看>>
玩一玩博客,嘿嘿
查看>>
P1352 没有上司的舞会
查看>>
ios11文件夹
查看>>
【HLOJ 559】好朋友的题
查看>>
Electric Fence(皮克定理)
查看>>
【状压DP】【UVA11825】 Hackers' Crackdown
查看>>
nvl 在mysql中如何处理
查看>>
MyEclipse 快捷键
查看>>
快速傅里叶变换FFT
查看>>