React 介绍
- React 是 facebook 推出的用于构建用户界面的前端 JavaScript 框架
- https://reactjs.org/
- React 使用组件构建用户界面
组件是什么

一块区域,包含了 html、css 及 JavaScript
组件开发的优势
- 将一个庞大复杂的应用程序拆分成多个独立单元
- 组件之间相互独立,有利于应用程序的维护
- 组件可以重用,一次编写多地复用
搭建 React 开发环境
自定义 webpack 搭建 React 开发环境
# 新建项目目录mkdir webpack-react-project# 进入项目目录cd webpack-react-project# 初始化npm init -y
安装开发依赖
"devDependencies": {"@babel/core": "^7.13.14","@babel/preset-env": "^7.13.12","@babel/preset-react": "^7.13.13","babel-loader": "^8.2.2","html-webpack-plugin": "^5.3.1","react": "^17.0.2","react-dom": "^17.0.2","webpack": "^5.30.0","webpack-cli": "^4.6.0","webpack-dev-server": "^3.11.2"}
webpack.config.js ```javascript const path = require(‘path’) const HtmlWebpackPlugin = require(‘html-webpack-plugin’)
module.exports = { mode: ‘development’, // devtool: ‘none’, entry: ‘./src/index.js’, output: { filename: ‘main.js’, path: path.resolve(‘dist’) }, devServer: { port: 2021, hot: true }, module: { rules: [ { test: /.js|jsx$/, exclude: /node_module/, use: [ { loader: ‘babel-loader’, options: { presets: [‘@babel/preset-env’, ‘@babel/preset-react’] } } ] } ] }, plugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’ }) ] }
- src/index.html```html<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><div id="root"></div></body></html>
- src/index.js ```javascript import React from ‘react’ import { render } from ‘react-dom’
// 自定义组件 function App() { return
render(
- 运行 `npx webpack serve`- 通过 [http://localhost:2021/](http://localhost:2021/) 即可访问<a name="2TTbZ"></a>### 脚手架初始化项目```powershell# 全局安装reactCLInpm install create-react-app -g# 创建react 项目create-react-app react-project# 进入项目cd react-project# 启动项目npm start
JSX语法
- JSX 可以看做是JS语言的扩展,它既不是一个字符串也不是一个HTML
- 它具备了 JS 所有的功能,同时还可以被转为 HTML 在界面上进行展示(react react-dom)
动态显示数据
const name= '拉钩教育'function App() {return (<div><p>{name}</p><p>name</p></div>)}export default App;
调用方法:自定义 + 内置
function sayHello () {return 'Hello'}function App() {return (<div><p>{sayHello()}</p><p>{console.log(111)}</p><p>{Math.random()}</p></div>)}export default App;
支持表达式,支持三元表达式
const flag = truefunction App() {return (<div><p>{1 + 2 + 3}</p><p>{flag? '登陆' : '未登陆'}</p></div>)}export default App;
模版字符串
const name= '拉钩教育'function App() {return (<div><p>{`hello, ${name}`}</p></div>)}export default App;
注释
function App() {return (<div><p>124{/*注释*/}</p></div>)}export default App;
页面上不能直接打印对象
const obj = {name: '拉勾教育',age: 100}function App() {return (<div><p>{obj/*会报错*/}</p><p>{JSON.stringify(obj)}</p></div>)}export default App;
JSX 本身就是一个表达式
const name = <h1>拉钩教育</h1>function App() {return (<div>{name}</div>)}export default App;
JSX 添加属性
const num = 100function App() {return (<div><p title='自定义标题'>添加属性</p><p title={num}>添加动态属性</p></div>)}export default App;
JSX 添加子元素
function App() {return (<div><p>子元素</p><p>子元素</p></div>)}export default App;
JSX 只能有一个根节点
function App() {return (<div>根节点</div>)}export default App;
使用单标签时必须正确关闭
function App() {return (<div><img /></div>)}export default App;
事件操作
事件绑定
驼峰命名直接添加={事件监听的名称}
const handler = () => {console.log(1111)}function App () {return (<div><button onClick={handler}>点击触发事件</button></div>)}export default App
事件监听传参
- 利用箭头函数内部调用事件监听的时候传递实参
- 利用 bind 方法返回一个新的函数在事件发生时调用,此时也可以传递参数 ```jsx const handler = (a,b) => { console.log(a,b) console.log(1111) }
function App () { return (
export default App
<a name="Bek8z"></a>### 获取事件对象- 默认情况下不需要接收参数,且直接执行事件监听,此时它的第一个实参默认就是 ev- 利用箭头函数执行事件监听,需要通过箭头函数将 ev 对象 传递给事件监听函数进行使用- 利用 bind 方法执行时,如果有传递参数那么最后一个参数默认就接收到的就是 ev```jsxconst handler = (ev) => {console.log(ev)console.log(1111)}const handler2 = (a,b,ev) => {console.log(ev)console.log(1111)}function App () {return (<div><button onClick={handler}>点击触发事件</button><button onClick={(ev) => {handler(ev)}}>点击触发事件</button><button onClick={handler2.bind(null,10,20)}>点击触发事件</button><button onClick={handler.bind(null)}>点击触发事件</button></div>)}export default App
循环数据
- jsx 可以直接将数组中的数据解构
```jsx
const arr = [
1
,2222
,3333
]
function App () { return(
export default App
- 遍历数据```jsx// const arr = [<p>1</p>, <p>2222</p>, <p>3333</p>]const arr = [{id: 1,name: 'zce',age: 40,salary: 1000000},{id: 2,name: 'zoe',age: 18,salary: 2000000}]function App () {const ret = arr.map(item =>{return (<li key={item.id}><span>{item.name}--</span><span>{item.age}--</span><span>{item.salary}</span></li>)})return <ul>{ret}</ul>}export default App
样式
内联样式
- 设置样式的时候应该将键值对放置于 {} ```jsx const styleObj = { width: 100, height: 100, backgroundColor: ‘pink’, }
function App () { return (
export default App
- 内联样式默认无法支持伪类及媒询样式设置- 导入 Radium 函数将当前需要支持伪类操作的组件包裹之后再导出```jsx//App.jsimport Radium from 'radium'const styleObj = {width: 100,height: 100,backgroundColor: 'pink',":hover": {backgroundColor: 'skyblue'},"@media (max-width: 800px)": {width: 300}}function App () {return (<div style={styleObj}>样式处理</div>)}export default Radium(App)
// index.jsimport React from 'react';import ReactDOM from 'react-dom';import App from './App'import {StyleRoot} from 'radium'ReactDOM.render(<React.StrictMode><StyleRoot><App /></StyleRoot></React.StrictMode>,document.getElementById('root'));
- 多样式 ```jsx import Radium from ‘radium’
const ButtonStyle = { base: { width: 150, height: 40, fontSize: 20, background: ‘#ffff’ }, login: { background: ‘green’ }, logout: { background: ‘orange’ } }
const isLogin = false
function App () { return (
export default Radium(App)
<a name="RCz7t"></a>### 外联样式- 全局外联样式--所有组件当中都可以直接进行使用```css/* style.css */body{background-color: orange;}.box{width: 100px;height: 100px;background-color: seagreen;}
// App.jsfunction App() {return(<div className={'box'}>外联样式</div>)}export default App
// Test.jsfunction Test() {return (<div><div className = {'box'}> Test 中的 div </div></div>)}export default Test
// index.jsimport React from 'react';import ReactDOM from 'react-dom';import App from './App'import test from './Test'import './style.css'ReactDOM.render(<React.StrictMode><App /><Test /></React.StrictMode>,document.getElementById('root'));
- 组件级别的外联样式
- 只有某一个组件可以进行使用
- 组件名.module.css
```jsx // Test.js import style from ‘./Test.module.css’/* Test.module.css */.item{width: 100px;height: 100px;background-color: red;}
function Test() { return (
Test中的P,使用自己的样式
export default Test
- CSS in JS- 安装 `styled-components````jsximport styled from 'styled-components'// 自定义标签const SectionDiv = styled.div.attrs({className: 'box1 box2'})`width: 100px;height: 100px;background-color: hotpink;`function Test () {return (<div><SectionDiv /></div>)}export default Test
创建组件
函数组件
- AboutFunc.js
```jsx
function AboutFunc() {
return (
About Func 组件) }
export default AboutFunc
- App.js```jsximport AboutFunc from './AboutFunc'function App() {return (<div><AboutFunc /></div>)}export default App;
类组件
- 必须继承 Component render
- 组件名称的首字母必须是大写的,在 React 当中可以用于区分组件和普通的标记 ```jsx // AboutClass.js import React, {Component, Fragment} from ‘react’
class AboutClass extends Component { render() { return (
export default AboutClass
```jsximport AboutClass from './AboutClass'function App() {return (<div><AboutClass /></div>)}export default App;
组件传值
- App.js ```jsx import About from ‘./About’ import Header from ‘./Header’
const obj = { name:’zce’, age: 18 } function App() { return (
{/*将数据统一管理,然后利用 ... 操作直接传递给相应的组件*/}<Header {...obj}/><About {...obj} /></div>
) }
export default App;
- 函数组件:在函数组件内可以接上到外部的数据,内部直接访问即可{ name:xxx, age:xxxx }```jsxfunction About ({a,b}) {return (<div><p>{a}</p><p>{b}</p></div>)}export default About
- 类组件: 在类组件的内部存在一个 props 属性,外部传递的数据都放在这里保存,我们可以直接进行使用
```jsx import {Component} from ‘react’
class Header extends Component {
render() { const {name, age} = this.props
return (<div><p>{name}</p><p>{age}</p></div>)
} }
export default Header
<a name="BQqcY"></a>### 默认值及类型校验- 针对于函数组件来说,如果想要设置默认的 props 属性值,则直接通过 组件名称.defaultProps 来设置一个对象- 安装第三方校验插件 `npm install props-type`- [https://github.com/facebook/prop-types](https://github.com/facebook/prop-types)```jsximport PropTypes from 'prop-types'function About ({name,age}) {return (<div><p>{name}</p><p>{age}</p></div>)}About.defaultProps = {name: 'zzz',age: 10}About.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number}export default About
- 针对于类组件来说,我们可以直接定义 static defaultProps 来管理需要设置默认值的属性即可 ```jsx import {Component} from ‘react’
class Header extends Component {
static defaultProps = { name: ‘拉钩教育’, age: 100 } render() { const {name, age} = this.props
return (<div><p>{name}</p><p>{age}</p></div>)
} }
export default Header
<a name="hRumf"></a>### 向组件传递 JSX- App.js```jsximport About from './About'import Header from './Header'const obj = {name:'zce',age: 18}function App() {return (<div><Header><p>Header组中的 P标签</p><span>Header组件中的 span标签</span></Header><About><p>About 中p标签</p></About></div>)}export default App;
- 函数组件 About.js ```jsx import PropTypes from ‘prop-types’
function About (props) { return (
{props.name}
{props.age}
{props.children}
About.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number } export default About
- 类组件 Header.js```jsximport {Component} from 'react'class Header extends Component {static defaultProps = {name: '拉钩教育',age: 100}render() {const {name, age} = this.propsreturn (<div><p>{name}</p><p>{age}</p>{this.props.children}</div>)}}export default Header
组件布局

- components/Header.js
```jsx
function Header() {
return (
<>
</> ) }<div className={'header'}>Header组件</div>
export default Header
- components/Footer.js```jsxfunction Footer() {return (<><div className={'footer'}>Footer组件</div></>)}export default Footer
components/Layout.js
- 当前组件的作用就是将 header 与 footer 显示出来,同时中的 main 内容空出来,
- 将来我们传入什么样的 JSX 那么 main 里就显示什么样的 DOM ```jsx import Header from ‘./Header’ import Footer from ‘./Footer’
function Layout(props) { return ( <>
<Header /><div className="main">{props.children}</div><Footer />
</> ) }
export default Layout ```
components/Home.js ```jsx import Layout from ‘./Layout’
function Home() {
return (
当前是home界面
) }export default Home
- components/List.js```jsximport Layout from './Layout'function List() {return (<Layout><p>当前是List界面</p></Layout>)}export default List
- App.js ```jsx import Home from ‘./components/Home’ import List from ‘./components/List’
function App() {
return (
/}
)
}
export default App
<a name="kIi2K"></a>## 组件状态- 组件状态: 状态就数据,因此组件状态指的就是某一个组件自己的数据- 数据驱动 DOM : 当我们修改某一个数据的时候,界面上的DOM中数据展示也会自动更新```jsximport React, { Component } from 'react'class Header extends Component {// 在类组件当中默认就存在一个 state 属性,它是一个对象,可以用于保存当前组件的数据// 之后还可以通过 setState 方法来修改数据的值,最后修改之后结状态会自动展示在 DOM 界面上state = {name: 'zce',age: 40}handler = () => {// 在 react 当中是不能够直接来修改 state 值的// this.state.name = 'zoe'this.setState({name: 'zoe'})}render() {return (<>{this.state.name}{this.state.age}<button onClick={this.handler}>点击</button></>)}}export default Header
setState 使用
setState 是异步函数
可以使用 async 与 await 来解决 setState异步执行的问题
handler = async () => {await this.setState({name: 'Zoe'})console.log(this.state.name, 22222)}
调用 setState 的时候可以传入回调函数,在它里面就可以使用修改之后的数据
handler = () => {this.setState({name: 'zoe'}, () => {console.log(this.state.name, 22222)})}
传入函数
setState 在使用的时候除了可以传入对象之外还能够传入一个函数
state = {count: 0}handler = () => {this.setState((state) => ({count: state.count + 1}))}
传入函数与传入对象的不同点
state = {count: 0}
传入函数
会依次执行 ```jsx handler = () => { this.setState((state) => ({
count: state.count + 1
}))
this.setState((state) => ({ count: state.count + 5 })) }
- 传入对象- 之后执行最后一个 setState```jsxhandler = () => {this.setState({count: this.state.count + 1})this.setState({count: this.state.count + 5})}
组件中的 this
import React, { Component } from 'react'class Header extends Component {handler = function () {console.log(this)}render() {return (<><div>{/*<button onClick={this.handler}>点击</button>*/}{/*<button onClick={() => { this.handler }}>点击</button>*/}<button onClick={this.handler.bind(this)}>点击</button></div></>)}}export default Header
单向数据流动
- 单向数据流的设计原则要求我们将不同组件之前需要共享的数据都定义在上层
//App.jsimport C1 from './C1'import React, { Component } from 'react'class App extends Component {// 在类组件当中就可以使用 state 定义状态state = {name: 'zce',age: 40}// 定义状态的更新方法, 当前外只负责定义,在想要修改数据的地方会进行调用handler = ({ name, age }) => {this.setState({ name, age })}render() {return (<div>单向数据流<C1 {...this.state} change={this.handler} /></div>)}}export default App
// C1.jsimport React from 'react'import C2 from './C2'function C1(props) {console.log(props)return (<div>C1组件<C2 {...props} />{props.name}</div>)}export default C1
// C2.jsimport React from 'react'import C3 from './C3'function C2(props) {console.log(props)return (<div>C2组件<C3 {...props} /></div>)}export default C2
// C3.jsimport React from 'react'function C3(props) {console.log(props)return (<div>C3组件<button onClick={() => { props.change({ name: 'zoe', age: 100 }) }}>点击修改数据</button></div>)}export default C3
特点
- 单向数据流动,自顶向下,从父组件传到子组件
- 基于单向数据流动,要求我们将共享的数据定义在上层组件
- 子组件通过调用父组件传递过来的方法可以更改数据
- 当数据发生更改之后,React 会重新渲染组件树
受控表单
表单元素的值全部由 react 来进行管理,此时表单元素中的值都放在 state 中,所以表单元素里的值需要从 state 当中进行获取
单值绑定与更新
将 state 中的状态与表单的 value 值进行绑定
value = {this.state.xxx}- 更状态值
onChange={方法} ev.target.value ev.target.name === [prop]:this.setState({})
只进行数据展示:
- readOnly
- defaultValue ```jsx import React, {Component} from ‘react’
class App extends Component { // 组件中的状态 state ={ name: ‘zzz’, age: 10, test:’只读’ } // 用于处理状态信息的方法 handler = (ev) => { const prop = ev.target.name this.setState({
[prop]: ev.target.value})
}
render () { return (
<input name='name' value={this.state.name} onChange={this.handler}/><input name='age' value={this.state.age} onChange={this.handler}/>{/* <input type='text' value={this.state.test} readOnly /> */}<input type='text' defaultValue={this.state.test} /></div>)
} }
export default App
<a name="f7bDS"></a>### 下拉菜单```jsximport React, {Component} from 'react'class App extends Component {// 状态state = {subject: 'JavaScript'}render () {return (<div><select value={this.state.subject} onChange={ev => {this.setState({subject: ev.target.value})}}><option value='大前端'>大前端</option><option value='JavaScript'>JavaScript</option><option value='python'>python</option></select><hr /><button onClick={() => {console.log(this.state)}}>点击获取状态</button></div>)}}export default App
单选框
import React, {Component} from 'react'class App extends Component {// 状态state = {sex: '男'}render () {return (<div><inputtype='radio'name='sex'value='男'defaultChecked={this.state.sex === '男'}onChange={ev => this.setState({sex: ev.target.value})} />男<inputtype='radio'name='sex'value='女'defaultChecked={this.state.sex === '女'}onChange={ev => this.setState({sex: ev.target.value})} />女<button onClick={() => {console.log(this.state)}}>Button</button></div>)}}export default App
复选框
import React, {Component} from 'react'class App extends Component {// 模拟数据源hobbies = [{id: 1,title: 'vue',isChecked: true},{id: 2,title: 'React',isChecked: false,},{id: 3,title: 'Angular',isChecked: false}]handler (index, ev) {this.hobbies[index].isChecked = ev.target.checked}render () {return (<div>{this.hobbies.map((hobby,index) => {return (<label key={hobby.id}><inputtype='checkbox'value={hobby.title}defaultChecked={hobby.isChecked}onChange={(ev) => {this.handler(index,ev)}} />{hobby.title}</label>)})}<hr /><button onClick={() => {console.log(this.hobbies)}}>Button</button></div>)}}export default App
提交
import React, {Component} from 'react'class App extends Component {// 状态state = {name: 'zzz',age: 10}// 模拟数据源hobbies = [{id: 1,title: 'vue',isChecked: true},{id: 2,title: 'React',isChecked: false,},{id: 3,title: 'Angular',isChecked: false}]handler (index, ev) {this.hobbies[index].isChecked = ev.target.checked}submit(ev){ev.preventDefault()// 提交时拿到所有选择中的的 id 数组let ret = this.hobbies.filter(hobby => hobby.isChecked).map(hobby => hobby.id)// 合并所有需要提交的数据ret = {...this.state, ret}console.log(ret)}render () {return (<form onSubmit={(ev) => {this.submit(ev)}}>{this.hobbies.map((hobby,index) => {return (<label key={hobby.id}><inputtype='checkbox'value={hobby.title}defaultChecked={hobby.isChecked}onChange={(ev) => {this.handler(index,ev)}} />{hobby.title}</label>)})}<hr /><button onClick={() => {console.log(this.hobbies)}}>Button</button><input type='submit' /></form>)}}export default App
非受控表单
- 不受 react 管理,表单元素的数据由 DOM 元素进行管理,表单元素的值也是存放在 DOM 元素里,获取的时候需要操作 DOM 元素
- 该方法已不被推荐使用 ```jsx import React, {Component} from ‘react’
// 非受控表单 class App extends Component {
submit = (ev) => { ev.preventDefault()
// 获取 DOM 元素console.log(this.refs.username)// 获取具体的值console.log(this.refs.username.value)
}
render () { return (
) } }export default App
```
