# 一、脚手架搭建项目

craete-react-app是Facebook官方推出的一款react的脚手架。(电脑上必须有node.js)

#使用npm安装全局依赖
npm install -g create-react-app
#使用yarn安装
yarn global add create-react-app
#查看安装的当前脚手架版本
craete-react-app --version
#创建react项目
craete-react-app 文件名

# 二、脚手架组件使用

# 1】项目文件结构

# ①public静态资源文件夹

静态资源文件夹里面的内容不会参与最终的打包。 (与编写的内容是无关的)

webpack会把所有的源码默认打包在"/"根目录下,但是public中的文件是直接放在根目录下的不参与webpack的自动化优化处理

比如:我们npm start之后,然后点开http://localhost:3000就可以访问本地服务器的根目录资源(这里也就是打包好的)。此时访问的是根目录下的所有资源。这里我们可以通过http://localhost:3000/logo192.png访问到这些public下的未参与webpack优化的静态资源。

(webpack打包的文件直接默认放在根目录下,public里面的文件也是直接放在根目录下,是同层级的。如果我们需要放在服务器的非根目录,只需要把build配置里的ouput选项改成相对路径"./"即可

# ②src文件夹写代码的地方

# 2】创建组件

src下创建一个components文件夹然后组件都写在这里面。

可以在vscode安装ES7 React/Redux/GraphQL/React-Native snippets插件使用一些快捷代码补全,比如:输入rcc就可以直接快速创建组件。效果如下:

import React, { Component } from 'react'

export default class Home extends Component {
    render() {
        return (
            <div>
                
            </div>
        )
    }
}

引入组件和使用组件(跟Vue差不多就不多说了)

使用import xx from './xx'(es6的语法)

# 3】render函数返回值需要由一个标签包裹:

# 组件render只能return一个表签包裹

import React, { Component } from 'react'

export default class Home extends Component {
    render() {
        return (
            <div>
                <div>sss</div>
            	<div>sss</div>
            </div>
        )
    }
}
//此时在浏览器上审查元素我们会发现,新增了一个没必要的<div>dom节点。如果不想要页面新增这个dom节点我们可以使用空标签来办到。

# 使用空标签如下:

import React, { Component } from 'react'

export default class Home extends Component {
    render() {
        return (
            <>
                <div>sss</div>
            	<div>ssss</div>
            </>
        )
    }
}

# 使用**<Fragment></Fragment>**标签包裹:

在 vue 里,我们会用 <template></template> 标签来包裹一些不能有父容器的复数同级标签。

例如在 <tbody></tbody>标签中,我们只能放置<tr></tr> 标签,假如我们同时有多个<tr>标签被赋值给一个 JSX 变量,那么在 React 里也有类似的功能:<React.Fragment> 标签。

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

export default class Home extends Component {
    render() {
        return (
            <Fragment>
                <div>sss</div>
                <div>ssss</div>
            </Fragment>
        )
    }
}
//注意这里需要导入React依赖里面的Fragment才能使用,这个效果和上面的空标签是一样的。

# 4】组件内图片的引用问题:

# ①如果图片等资源放在pubilc静态文件夹下可以直接引用

因为public文件夹下的文件webpack不会进行优化处理,是直接放在服务器的根目录下的直接引用即可

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

export default class Home extends Component {
    render() {
        return (
            <Fragment>
                <img src="logo192.png" alt="hhh"/>
            </Fragment>
        )
    }
}

# ②如果图片等资源放在src下的assets文件夹下

# ES6:

我们需要使用ES6的**import xx from '../assets/xxx.png'**导入图片,形成变量jsx插值表达式{}中来引用。

import React, { Component, Fragment } from 'react'
import imgA from '../assets/logo512.png'

export default class Home extends Component {
    render() {
        return (
            <Fragment>
                <img src={imgA} alt="hhh" />
            </Fragment>
        )
    }
}

# ES5:

使用**require('../assets/xxx.png')**直接在jsx插值表达式{}中进行引用。(不需要给路径赋一个变量保存,直接在jsx方便使用)

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

export default class Home extends Component {
    render() {
        return (
            <Fragment>
				<img src={require('../assets/logo512.png')} alt="hhh" />
            </Fragment>
        )
    }
}

# 5】组件propsstate在脚手架中的使用

我们直接输入**快捷代码补全rccp**就可以直接打出有props验证功能的react组件代码:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class News extends Component {
    static propTypes = {
        prop: PropTypes
    }

    render() {
        return (
            <div>
                new组件:{this.props.text}
            </div>
        )
    }
}

也是使用this.setState()方法对state对象里面的状态进行修改。

# 6】组件的传值

react是组件通信是单向数据流,通过props实现,不论是子传父还是父传子都是通过props来实现,区别在于向props中传入的是值还是回调函数,而并没有“双向绑定or单向绑定”的说法,因为是通过this.setState()方法去调用render()函数去渲染view层。

vue也是组件通信也是单向数据流,通过**props子传父**,$emit弹射自定义事件达到父传子的效果,只不过还实现了**view层和model层之间的双向绑定的映射关系**而已。

# 正向传值:父组件向子组件传值(props)

这就是正常的通过props属性进行传值,前面讲的已经够多了,这里就不多讲。

# 逆向传值:子组件向父组件传值

如果想要最好还是从父组件开始下手,然后传递一个回调给props属性(比较好理解),然后在子组件中有一个事件来触发这个回调函数,或者是需要直接调用这个回调函数向里面传入子组件中的状态等值,就可以达到子组件向父组件逆向通信的过程。

class Father extends React.Component {
  fun(name){
    console.log(name)
  }
  render() {
    return (<div>
            		我是父组件
            		<Son transValue={this.fun}></Son>
						</div>)
	}
}
class Son extends React.Component {
  state = {
    name: "我是子组件需要传给父组件的值"
  }
	render() {
    this.props.transValue(this.state.name)
    //会在这步中把值传给父组件,然后执行父组件中的回调函数,
    //打印出"我是子组件需要传给父组件的值"
    return (<div>
          			我是子组件
          	</div>)
  }
}
ReactDOM.render(<Father />, document.getElementById("app"))

# 同级传值:使用pubsub.js消息订阅发布

此方法不限于同级组件通信,不管关系之间的组件都可以进行通信。

# 使用npm进行安装:
npm install --save pubsub-js
# pubsub.js发布设置发布消息和订阅消息进行通信:

首先我们在父组件下面创建两个子组件:

//这里Father是父组件,Child1和Child2是同级的子组件
import React, { Component } from 'react'
import Child1 from './Child1'
import Child2 from './Child2'

export default class Father extends Component {
    render() {
        return (
            <div>
                我是父组件
                <Child1></Child1>
                <Child2></Child2>
            </div>
        )
    }
}

我们需要的是在组件Child1里面的状态,传给和他同级的组件Child2来接收:

import React, { Component } from 'react'
import PubSub from 'pubsub-js';
//Child1组件中引入pubsub-js
export default class Child1 extends Component {
    state={
        text:'我是与Child2同级想传的值'
    }
    pubsub() {
        PubSub.publish('evt',this.state.text)
    }//通过Child1通过点击事件,发布一个‘evt’事件出来,
		//这里的publish第一个参数是发布的事件名称,后面的参数是需要发布出去的值
    render() {
        return (
            <div>
                我是子组件1
                <button onClick={this.pubsub.bind(this)}>点我同级组件传值</button>
            </div>
        )
    }
}

Child2组件中我们需要使用subscribe来订阅事件:

import React, { Component } from 'react'
import PubSub from 'pubsub-js';

export default class Child2 extends Component {
    constructor(props) {
        super(props)
        PubSub.subscribe('evt', (msg, data) => {
            console.log(msg, data);//evt 我是与Child2同级想传的值
        })
      //这里的subscribe接收两个参数,
      //第一个参数是:需要订阅的事件名称
      //第二个参数是:一个回调函数(可以传入第一个是参数是事件名称,第二个参数是需要获取的值)
    }
    render() {
        return (
            <div>
                我是子组件2
            </div>
        )
    }
}

# 非父子组件通信Context使用

# ①Context应用场景

我们来看一个例子:以下这个组件就是一层一层地传递props进行传值,就很复杂。

import React, { Component } from 'react';

function ProfileHeader(props) {
  return (
    <div>
      <h2>用户昵称: {props.nickname}</h2>
      <h2>用户等级: {props.level}</h2>
    </div>
  )
}

class Profile extends Component {
  render() {
    return (
      <div>
        <ProfileHeader nickname={this.props.nickname} level={this.props.level} />
        <ul>
          <li>设置1</li>
          <li>设置2</li>
          <li>设置3</li>
          <li>设置4</li>
          <li>设置5</li>
        </ul>
      </div>
    )
  }
}

export default class App extends Component {
  constructor() {
    super();

    this.state = {
      nickname: "rayhomie",
      level: 99
    }
  }

  render() {
    const { nickname, level } = this.state;

    return (
      <div>
        <Profile nickname={nickname} level={level} />
        <h2>其他内容</h2>
      </div>
    )
//    return (
//      <div>
//        <Profile {...this.state} />
//        <h2>其他内容</h2>
//      </div>
    )
  }
}

但是,如果层级更多的话,一层层传递是非常麻烦,并且代码是非常冗余的:

  • React提供了一个API:Context
  • Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props;
  • Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言;
# ②Context相关的API

React.createContext

Context.Provider

Class.contextType

Context.Consumer

Context.displayName

# 三、数据请求与模拟数据

# 1】json-server模拟数据

# ①npm安装:

npm install json-server -g
#或者使用yarn安装
yarn global add json-server

#mock文件夹下创建json格式文件

src文件夹下创建一个mock文件夹,在mock文件夹下创建json文件,来模拟后端发来的数据。

以下文件名是:data.json

{
    "studentList": [
        {
            "name": "小明",
            "age": "15"
        },
        {
            "name": "小刚",
            "age": "11"
        },
        {
            "name": "小红",
            "age": "12"
        },
        {
            "name": "小达",
            "age": "20"
        },
        {
            "name": "小雷",
            "age": "22"
        },
        {
            "name": "小李",
            "age": "19"
        },
        {
            "name": "小张",
            "age": "12"
        },
        {
            "name": "小王",
            "age": "15"
        },
        {
            "name": "小赵",
            "age": "17"
        }
    ]
}

# ③启动服务

cdmock文件夹下,终端执行以下命令:

json-server json文件的名字 --port 4000
#这个mock数据服务器的默认端口是4000

在这里的例子就应该是执行以下命令:

json-server data.json --port 4000

此时就可以访问"http://localhost:4000/studentList",访问到json数据。

# 2】axios请求数据

# ①npm安装:

(使用yarn安装的话只需要指定开发运行依赖-D,不需要指定npm安装时的--save,来添加到package.json文件中)

npm install --save axios
#或者使用yarn安装
yarn add axios
#yarn add 包名 = npm i 包名 -S = npm i 包名 --save
#yarn add 包名 -D = npm i 包名 -D = npm i 包名 --save-dev

# ②引入axios请求json-server服务器数据

import React, { Component } from 'react'
import Axios from 'axios'

export default class Father extends Component {
    componentDidMount() {//钩子函数
        Axios.get('http://localhost:4000/studentList').then(({ data }) => {
            console.log(data);//打印出一个数组[{},...,{}]
        })
    }
    render() {
        return (
            <div>
                我是父组件
            </div>
        )
    }
}

# ③把请求的数据渲染到页面

这里我们还可以把这些请求到的数据列表渲染到到我们需要render的模板上:

import React, { Component } from 'react'
import Axios from 'axios'

export default class Father extends Component {
    state = {
        stuList: []
    }
    componentDidMount() {//钩子函数
        Axios.get('http://localhost:4000/studentList').then(({ data }) => {
            this.setState({ stuList: data })
        })
    }
    render() {
        let ListRender = this.state.stuList.map((item, index) => {
            return (<div key={index}>{item.name}{item.age}</div>)
//数组的map方法一定要return,或者箭头函数不换行,自动return
        })
        return (
            <div>
                我是父组件
                <div>{ListRender}</div>
{/*这样我们就把数组使用map方法修饰后返回的数组渲染到页面上*/}
            </div>
        )
    }
}
Last Updated: 8/5/2020, 5:39:29 PM