React学习笔记

LeslieLin 编程技术
阅读量 0 评论量 0

前言

学习React是今年的计划之一,再加上之前学习了Vuejs,想了解两者之间的区别,因此大致学习下React,有机会再用React写一个项目。

React的三大体系

  • 用于Web开发和组件的编写
  • ReactNative用于移动端开发
  • ReactVR用于虚拟现实技术的开发

安装

安装Nodejs

使用Reactjs最原始的方法就是script标签引入,但这太low了,并且在工作当中也不会这样引用,因此安装Nodejs来使用react最佳~打开下面的网址,自行安装即可。
Nodejs中文网址:http://nodejs.cn/

脚手架安装

安装完Nodejs后,使用npm命令安装脚手架

npm install -g create-react-app

创建第一个React项目

create-react-app demo

目录结构

  • src:项目代码主目录
  • public:公共文件,例如可以存储样式文件、图标等等
  • node_modules:项目的依赖包
  • gitignore:git的选择性上传配置文件
  • package-lock.json:锁定安装时的版本号,以保证其他人再npm install时大家的依赖能保证一致

src文件夹

  • index.js:项目的入口文件
  • index.css:index.js里的css文件
  • app.js:相当于一个方法模块,也是一个简单的模块化编程
  • serviceWorker.js:用于移动端开发,PWA必须用到这个文件,有了这个文件,就相当于有了离线浏览的功能

编写第一个HelloWord

index.js 入口文件的编写

src目录下,新建一个文件index.js,写入下面4行代码

import React from 'react'
import ReactDOM form 'react-dom'
import App from './App' // 这里其实省略了.js,完整是./App.js,但这里可以省略,react会自动识别
ReactDOM.render(<App />,document.getElementById('root'))

上面的代码,首先引入了React必要的两个文件,然后引入了一个APP组件,再使用ReactDOM渲染到了rootID上.PS:这个root ID是在public\index.html中。

app组件的编写

import React, {Component} from 'react'  // ES6的语法-解构赋值,当然也可以写成下面两行
// import React from 'react'
// const Component = React.Component

class App extends Component{
    render(){
        return (
            <div>
                Hello React
            </div>
        )
    }
}
export default App;

React中JSX语法

简介

javascript + xml = jsx。当遇到<,JSX就当作HTML解析,遇到{就当JS解析,举个例子(JSX语法)

<ul className="list">
    <li>视觉志</li>
    <li>I love React</li>
</ul>

而在以前写一段JS:

var child1 = React.createElement('li', null, '视觉志');
var child2 = React.createElement('li', null, 'I love React');
var root = React.createElement('ul', { className: 'list' }, child1, child2);

从代码量上看出JSX语法大量简化了我们的工作.

小坑

自定义的组件必须首写字母要进行大写,而JSX是小写字母开头的

使用三元运算符


import React from 'react'
const Component = React.Component

class App extends Component{
    render(){
        return (
            <ul className="my-list">
                <li>{false?'视觉志':'iobiji.com'}</li>
                <li>I love React</li>
            </ul>
        )
    }
}

export default App;

实例

新建一个组件

import React,{Component} from 'react'

class Demo extends Component{
    render(){
        return  (
            <div>
               <ul>
                   <li>aaa</li>
                   <li>bbb</li>
               </ul> 
            </div>
        )
    }
}
export default Demo;

组件外层包裹原则

划重点,比如上面的代码,去掉最外层的<div>就会报错,因为React要求必须在一个组件的最外层进行包裹,这就类似于Vuejs中的只允许一个根div

Fragment标签

加上最外层的DIV,组件就是完全正常的,但布局就偏不需要这个最外层的标签怎么办?比如在作Flex布局时,外层还真的不能有包裹元素。这种矛盾其实React已经给了我们解决方案,也就是<Fragment>标签
引入:

import React,{Component,Fragment} from 'react'

然后把最外层的<div>换成标签即可.

响应式设计和数据的绑定

React不建议直接操作DOM元素,而是要通过数据进行驱动,改变界面中的效果。React会根据数据的变化,自动的帮助你完成界面的改变。所以在写React代码时,你无需关注DOM相关的操作,只需要关注数据的操作就可以了(这也是React如此受欢迎的主要原因,大大加快了我们的开发速度)。
数据定义在组件中的构造函数里constructor

constructor(props){
    super(props) //调用父类的构造函数,固定写法
    // 还有就是一般this在这里绑定,特别是在高级组件开发中,会有很大的作用
    this.state={
        inputValue:'' , // input中的值
        list:[]
    }
}

React中的数据绑定和Vue中几乎一样,就是使用{}来标注,其实这也算是js代码的一种声明。比如现在我们要把inputValue值绑定到input框中,只要写入下面的代码就可以了。其实说白了就是在JSX中使用js代码。

<input value={this.state.inputValue} /> 

绑定事件

这时候到界面的文本框中去输入值,是没有任何变化的,这是因为我们强制绑定了inputValue的值。如果要想改变,需要绑定响应事件,改变inputValue的值。比如绑定一个改变事件,这个事件执行inputChange()(当然这个方法还没有)方法。
render()方法下建立一个inputChange()方法:

inputChange(e){
  console.log(e.target.value);
  this.state.inputValue = e.target.value;
}

其实,这里犯了两个错误:

  1. this的指向不对,需要重新用bind设置指向(ES6的语法)
  2. React中改变数据需要使用this.setState方法
    解决:
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />

另外需要在inputChange方法加入setState方法来改变值:

inputChange(e){
    // console.log(e.target.value);
    // this.state.inputValue=e.target.value;
    this.setState({
        inputValue:e.target.value
    })
}

key值错误解决

<ul>
    {
        this.state.list.map((item,index)=>{
            return <li key={index+item}>{item}</li>
        })
    }
</ul>  

数组下标的传递

<ul>
    {
        this.state.list.map((item,index)=>{
            return (
                <li 
                    key={index+item}
                    onClick={this.deleteItem.bind(this,index)}
                >
                    {item}
                </li>
            )
        })
    }
</ul>  

遍历

<ul>
    {
        this.state.list.map((item,index)=>{
            return (
                <ServiceItem 
                key={index+item}  
                list={item}
                index={index} />
            )
        })
    }
</ul> 

JSX防踩坑的几个地方

JSX代码注释

    //第一次写注释,这个是错误的
    <div>
        <input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
        <button onClick={this.addList.bind(this)}> 增加服务 </button>
    </div>

正确的写法有以下两种:

# 第一种
{/* 正确注释的写法 */}
# 第二种
{
  //正确注释的写法 
}

第二种不太优雅,推荐第一种注释方法,当然使用编辑器的快捷注释也可以,最简单的方法

JSX中的class陷阱

使用class属性是错的,必须使用className,它是防止JS中的class类名冲突。

<input className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />

JSX中的html解析问题

如果想在文本框里输入一个<h1>标签,并进行渲染。默认是不会生效的,只会把<h1>标签打印到页面上,这并不是我想要的。如果工作中有这种需求,可以使用dangerouslySetInnerHTML属性解决。具体代码如下:

<ul>
    {
        this.state.list.map((item,index)=>{
            return (
                <li 
                    key={index+item}
                    onClick={this.deleteItem.bind(this,index)}
                    dangerouslySetInnerHTML={{__html:item}}
                >
                </li>
            )
        })
    }
</ul> 

JSX中<label>标签的坑

先看下面的代码,我们在文本框前面加入一个<label>

<div>
    <label for="service">加入服务:</label>
    <input id="service" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
    <button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>

console会有红色警告提示的。大概意思是不能使用for.它容易和javascript里的for循环混淆,会提示你使用htmlFor

<div>
    <label htmlFor="service">加入服务:</label>
    <input id="service" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
    <button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>

组件的拆分

新建一个组件

import React, { Component } from 'react';
class ServiceItem  extends Component { //cc

    render() { 
        return ( 
            <div>服务列表</div>
         );
    }
}
export default ServiceItem;

第二种方法

import React from 'react';
const ServiceItem = (props) => {
    <div>服务列表</div>
}
export default ServiceItem;

在另外一个组件中使用import引入这个组件:

import ServiceItem from './ServiceItem'

然后直接写入<ServiceItem />标签即可

父子组件的传值

使用组件属性的形式,父组件传值给子组件。比如加入list属性,然后给属性传值{item},这样就完成了父组件向子组件传值.

<ServiceItem list={item} />

在接收数据的组件通过使用this.props.xxx的形式接收即可,例如

import React, { Component } from 'react';
class ServiceItem  extends Component { //cc

    render() { 
        return ( 
            <div>{this.props.list}</div>
         );
    }
}
export default ServiceItem;

子组件向父组件传递数据

首先子组件绑定点击事件:

import React, { Component } from 'react';

class ServiceItem  extends Component {
    render() { 
        return ( 
            <div onClick={this.handleClick}>{this.props.list}</div>
         );
    }

    handleClick(){
        console.log('点击了')
    }

}

export default ServiceItem;

父组件向子组件传递点击删除方法

<ul>
    {
        this.state.list.map((item,index)=>{
            return (
                <ServiceItem 
                key={index+item}  
                list={item}
                index={index}
                //关键代码-------------------start
                deleteItem={this.deleteItem.bind(this)}
                //关键代码-------------------end
                />
            )
        })
    }
</ul>

子组件调用父组件传递的方法

import React, { Component } from 'react';
class ServiceItem  extends Component {
   constructor(props){
       super(props)
       this.handleClick=this.handleClick.bind(this)
   }

    render() { 
        return ( 
            <div onClick={this.handleClick}>
                {this.props.list}
            </div>
        );
    }
    handleClick(){
        this.props.deleteItem(this.props.index)
    }
}

export default ServiceItem;

ref的使用方法

代替原来的e.target.value

以前的案例中使用了e.target,这并不直观,也不好看。这种情况可以使用ref来进行解决。

inputChange(e){
    this.setState({
        inputValue:e.target.value
    })
}

如果要使用ref来进行,需要现在JSX中进行绑定, 绑定时最好使用ES6语法中的箭头函数,这样可以简洁明了的绑定DOM元素。

<input 
    id="addService" 
    className="input" 
    value={this.state.inputValue} 
    onChange={this.inputChange.bind(this)}
    //关键代码——----------start
    ref={(input)=>{this.input=input}}
    //关键代码------------end
    />

绑定后可以把上边的类改写成如下代码:

inputChange(){
    this.setState({
        inputValue:this.input.value
    })
}

React和Vue的对比

React.js相对于Vue.js,它的灵活性和协作性更好一点,个人认为react是UI框架,而vue是数据驱动框架,因为vue.js有着丰富的API,实现起来更加简单快速。

喵~