# 一、脚手架搭建项目
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】组件props
和state
在脚手架中的使用
我们直接输入**快捷代码补全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"
}
]
}
# ③启动服务
cd
到mock
文件夹下,终端执行以下命令:
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>
)
}
}