一、react 基础
-
react是facebook退出的用于构建用户界面的前端js框架
-
react 使用组件构建用户界面
-
组件就是一个区域,区域内集成了html、css以及js
-
组件开发的优势
-
将一个庞大的项目拆分为多个独立单元
-
组件之间相互独立,有利于程序的维护
-
组件可以重复使用
1.1 搭建react开发环境
- 方法1: 利用webpack搭建相关的配置
- 方法2:利用脚手架进行创建
1.1.1 webpack搭建
- 需要安装的依赖
{
"devDependencies": {
"@babel/core": "^7.13.8",
"@babel/preset-env": "^7.13.9",
"@babel/preset-react": "^7.12.13",
"babel-loader": "^8.2.2",
"html-webpack-plugin": "^4.5.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
}
}
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "development",
entry: './src/index.js',
devtool: 'none',
output: {
filename: 'main.js',
path: path.resolve('dist')
},
devServer: {
port: 3000,
hot: true
},
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
]
}
]
},
plugins: [
new htmlWebpackPlugin({
template: './src/index.html'
})
]
}
1.1.2 脚手架搭建
- 安装
npm install -g create-react-app,运行create-react-app 文件名创建项目 - 跳转到项目中,
npm start运行项目 - 如果发生冲突,配置.env文件,输入
SKIP_PREFLIGHT_check = true保存,再运行
1.2 JSX语法
- 在组建函数中return的部分就是JSX
- 01 JSX可以看作是JS语言的拓展,它既不是一个字符串也不是HTML
- 02 它具备了 js的所有功能,同时还可以转化为HTML,在界面上进行展示
- -动态显示数据 {}
- -调用方法:自定义+内置
- -支持表达式,支持三元表达式,不能使用if表达式
- -模板字符串
const name1 = '123'
const flage = false
const obj = {
name: 'zs',
age: 18
}
function sayHello () {
return '大家好'
}
function App() {
return (
<div>
<div>{name1}</div>
<div>name1</div>
<div>{sayHello()}</div>
<div>{console.log('123')}</div>
<div>{Math.random()}</div>
<div>{1 + 2 + 3}</div>
<div>{flage ? '123' : '456'}</div>
<div>{`hello,${name1}`}</div>
<div>123{/* 这里是注释内容 */}</div>
<div>{JSON.stringify(obj)}</div>
</div>
)
}
export default App;
1.2.1 JSX 语法进阶
- 01 JSX 本身就是一个表达式
- 02 JSX 添加属性
- 字符串属性,双引号包裹
- 动态属性
- 03 JSX 添加子元素
- 04 JSX 只能有一个根元素
- 05 JSX 使用单标签也需要关闭
const name1 = <div>拉勾</div>
const age = 100
function App() {
return (
<div>
{name1}
<p title='自定义标题'>添加属性</p>
<p title={age}>添加动态属性</p>
</div>
)
}
1.2.2 JSX事件操作
-
一、事件绑定
-
-绑定的事件触发形式需要是驼峰命名,on触发的方法={监听事件名称}
-
二、事件传参
-
-利用箭头函数内部调用监听事件传参
-
-利用bind方法返回新的函数事件进行传参
-
三、获取事件对象
-
-直接调用函数,打印参数
-
-箭头函数传参
-
-通过bind,如果没有事件对象,但是传参了,获取的最后一个参数就是对象
// const handler = (a, b) => {
// console.log(a, b);
// console.log('执行力');
// }
const handler = (a, b,ev) => {
console.log(a, b);
console.log(ev);
console.log('执行力');
}
function App() {
return (
<div>
{/* 事件绑定 */}
{/* <button onClick={handler}>点击触发事件</button> */}
{/* 参数传递 */}
{/* <button onClick={() => {handler(1, 2)}}>点击触发事件</button> */}
{/* <button onClick={handler.bind(null, 100, 200)}>点击触发事件</button> */}
{/* 获取事件对象 */}
{/* <button onClick={handler}>点击触发事件</button> */}
{/* <button onClick={(ev) => {handler(ev, 1, 2)}}>点击触发事件</button> */}
<button onClick={handler.bind(this, 1, 2)}>点击触发事件</button>
</div>
)
}
export default App;
1.2.3 循环数据
- jsx中数组可以直接将数组中的数据结构
- 所以可以在在组件内部通过map进行数据的循环
- 循环的item数据根据JSX语法进行操作,最后获得数据的数组
- 直接返回给组件本身,并return
// const arr = [<p>1</p>, <p>22</p>, <p>333</p>]
const arr2 = [
{
id: 1,
name: 'zs',
age: 18,
salary: 2000
},
{
id: 2,
name: 'ls',
age: 19,
salary: 2000
}
]
function App() {
const content = arr2.map(item => {
return (
<li key={item.id}>
<p>{item.name}</p>
<p>{item.age}</p>
<p>{item.salary}</p>
</li>
)
})
console.log(content);
return (
<ul>
{content}
</ul>
)
}
export default App;
1.3 添加样式
1.3.1 添加内联样式
- 需要在行内设置style属性,
<div style={{属性名:属性值}}></div> - 可以在外部声明属性对象,然后再引入到行内
- 由于行内样式本身就不支持伪类操作和媒体查询,所以需要安装radium插件
- 在组件中引入radium,并且导出时要用radium包裹组件
- 如果有媒体查询,则在引入组件的显示界面中引入
import {StyleRoot} from radium - 并且书写StyleRoot标签,然后包裹引入的app组件
- 当想引入多个自定义的样式时,可以给style属性动态绑定一个数组
// 组件 app
/**
* 01 设置样式时应当将键值对放置于{},因为最外围的{}只是表示包裹的区域遵循动态显示数据
* 02 内联默认无法支持伪类以及媒体查询
* 03 radium
* a 导入Radium 函数 将当前需要支持伪类操作的组件进行包裹进行导出
*/
import Radium from 'radium'
const styleObj = {
width: '100px',
height: '50px',
backgroundColor: '#ccc',
':hover': {backgroundColor: 'skyblue'},
'@media (max-width: 1000px)': {backgroundColor: 'red'}
}
function App() {
return (
<div>
{/* <div style={{width: '100px',height: '50px',backgroundColor: '#aaa'}}>内联样式</div> */}
<div style={styleObj}>内联样式</div>
</div>
)
}
export default Radium(App)
// index.js
...
import App from './App';
import {StyleRoot} from 'radium'
ReactDOM.render(
<React.StrictMode>
<StyleRoot><App /></StyleRoot>
</React.StrictMode>,
document.getElementById('root')
);
1.3.2 外联样式
- 添加类名时,通过className属性进行定义
1.3.2.1 全局外联样式
- 在index.js全局中引入样式,并在组件中用className={‘类名’}进行类名创建
- 所有组件均可使用
1.3.2.2 组件级别的外联样式
- 只在某一个组件中生效
- 命名时,组件名.module.css
- 在相应组件中引入样式并用变量接收
- 使用时,变量.属性名即为属性
1.3.2.3 在js中书写样式
- 安装styled-components依赖,在需要内部书写样式的组件中引入依赖中的方法并用变量接收
- 最简单的书写语法格式:变量.标签名
模板字符串中书写属性,返回值即为标签 - 变量.标签名.attrs
模板字符串中书写属性 - 接收的变量首字母一定要大写
import React from 'react'
import test from './test.module.css'
import styled from 'styled-components'
const SectionDiv = styled.div`
width: 100px;
height: 100px;
background-color: hotpink
`
//也可以通过 attrs设置属性后再设置样式
const SectionDi = styled.div.attrs({
className: 'box1 box2'
})`
width: 100px;
height: 100px;
background-color: hotpink;
:hover {
color: red;
width: 200px;
background-color: red;
}
`
function Test () {
return (
<div>
<div className={'box'}>465</div>
<div className={test.item}>789</div>
<SectionDiv />
<SectionDi>123</SectionDi>
</div>
)
}
export default Test
1.4 组件操作
1.4.1 组件创建
- 创建函数组件
- 创建类组件
- 引入React和component
- 类中必须要有render方法
- 必须继承Component render
- 组件名首字母必须大写,在react中可以区分组件和普通的标记
- 最外层要有一个根元素,所以要使用占位符<Fragment>代替组件根元素的DIv,需要引入Fragment
- 也可以简写为<>内容<>
import React, {Component, Fragment} from 'react'
class About extends Component {
render () {
return (
// <Fragment>About 组件</Fragment>
<>About 组件</>
)
}
}
export default About
1.4.2 组件数据传递
- 由于创建组建的方式有两种方法,所以传递数据的方法有两种
- 在组件标签上身上添加属性然后传递数据,这种传递方法会使标签变的特别长,不美观
- 类组件中内部存在props属性储存外部传递的数据
- 函数组建的接收的参数就是传递数据整体对象
- 在外面声明对象存储数据,在组件标签中通过动态解构进行传递
// app.js
const obj = {
name: 'zs',
age: 22
}
function App() {
return (
<div>
{/* <About a={'天道酬勤'} b={100} /> */}
<About {...obj} />
{/* <Header name={'990624'} age={456} /> */}
<Header {...obj} />
</div>
)
}
// header.js
class Header extends Component {
render() {
const {name, age} = this.props
return (
<>
<p>{console.log(this.props)}</p>
<p>{name}</p>
<p>{this.props.age}</p>
<p>{age}</p>
</>
)
}
}
// about.js
// function About (props) {
// console.log(props);
// return (
// <div>
// <p>{props.name}</p>
// <p>{props.age}</p>
// </div>
// )
// }
function About ({age, name}) {
return (
<div>
<p>{name}</p>
<p>{age}</p>
</div>
)
}
1.4.3 默认值以及类型校验
- 针对于函数组件来说,想要设置默认的 props属性值,则直接通过 组件名.defaultProps = {属性名:属性值}直接设置
- 准对于类组件来说可以直接定义 static defaultProps 来管理需要设置的默认属性值即可
// 函数组件
function About ({age, name}) {
return (
<div>
<p>{name}</p>
<p>{age}</p>
</div>
)
}
About.defaultProps = {
name: 'syy',
age: 18
}
// 类组件
class Header extends Component {
static defaultProps = {
name: 'zs',
age: 19
}
render() {
const {name, age} = this.props
return (
<>
<p>{console.log(this.props)}</p>
<p>{name}</p>
<p>{age}</p>
</>
)
}
}
- 数据的校验是必要的,因为js中的数据编写时无法确定数据类型,所以需要设置类型范畴
- 借助
prop-types包进行校验,引入PropTypes - 函数组件中通过组件名.propTypes = {属性名:PropTypes.类型名.isRequired}表示必填
- 类组件也是一样的外部校验
// 函数组件校验
function About ({age, name}) {
return (
<div>
<p>{name}</p>
<p>{age}</p>
</div>
)
}
About.propTypes = {
name: PropTypes.string
}
1.4.4 向组件传递JSX
- 需要双标签进行包裹
- 包裹的内容即为传递的参数
- JSX存放在props中的children中
// app.js
function App() {
return (
<div>
<Header>
<p>header组件中的p标签</p>
<span>header组件中的span</span>
</Header>
<About>
<p>about组件中的p标签</p>
<span>about组件中的s</span>
</About>
</div>
)
}
// header.js
class Header extends Component {
static defaultProps = {
name: '大梦谁先绝',
age: 19
}
render() {
const { name, age } = this.props
return (
<>
<p>{console.log(this.props)}</p>
<p>{name}</p>
<p>{age}</p>
{this.props.children}
</>
)
}
}
// about.js
function About (props) {
return (
<div>
<p>{props.name}</p>
<p>{props.age}</p>
{props.children}
</div>
)
}
1.4.5 组件布局实例
- 可以在组件中进行导入
- 如果是复用的组件可以单独书写到一个组件中,利用JSX传值进行使用,最后将组合好的组件导入App.js中
- 定义全局样式
// app.js
import Home from './components/home'
function App() {
return (
<>
<Home />
</>
)
}
// layout.js
import Header from './Header'
import Footer from './footer'
function Layout (props) {
return (
<>
<Header />
{props.children}
<Footer />
</>
)
}
// home
import Layout from './layout'
function Home() {
return (
<Layout>
<div>home 组件</div>
</Layout>
)
}
1.4.6 组件状态
-
组件状态指的就是数据,因此组件状态指的就是莫格组件自己的数据
-
数据驱动DOM:当修改数据时,界面上的DOM中数据也会更新
-
通过组件状态管理进行实现,将数据交给组件自己进行操作
-
在类组件中存在一个state属性,它是一个对象,目的是存储当前组件的数据
-
之后还可以通过 setState方法来修改数据,最后修改后结合状态自动展示在DOM中
-
通过
this.state.属性名进行访问
import React, { Component } from 'react'
class Header extends Component {
state = {
name: 'zs',
age: 18
}
handler = () => {
// 在 react 中 不能直接修改state中数据的值
// this.setState内部传入的是键值对
this.setState({
name: 'ls'
})
}
render() {
return (
<>
{this.state.name}
{this.state.age}
<button onClick={this.handler}>点击</button>
</>
)
}
}
export default Header
1.4.7 setState使用
-
setState方法默认是异步函数,后面要是使用新数据不行
-
通过async...await进行包裹,事件运行完后往下执行下面的事件
-
也可以通过setState的第二个参数(回调函数)进行操作
-
setState 在使用的时候除了可以传入对象之外还可以传入函数
-
setState修改数据时需要通过对象进行修改,要有state的属性名和修改的数据
-
当setState修改数据
-
通过函数传递时,执行几次操作几次
-
通过对象修改时,会进行合并,后面的会覆盖前面的
import React, { Component } from 'react'
class Header extends Component {
state = {
name: 'zs',
age: 18,
count: 0
}
handler = () => {
// 语法糖执行事件
// await this.setState({
// name: 'ls'
// })
// console.log(this.state.name)
// 第二个参数回调函数执行
// this.setState({
// name: 'ls'
// }, () => {
// console.log(this.state.name, 2222)
// })
// setState传递函数
// 内部的参数是state状态
// this.setState((state) => (
// {
// count: state.count + 1,
// name: 'ls'
// }
// ))
// this.setState((state) => (
// {
// count: state.count + 1,
// name: 'ls'
// }
// ))
this.setState({
count: 5
})
this.setState({
count: 1
})
}
render() {
return (
<>
<div>
{this.state.name}
{this.state.age}
</div>
<hr />
<div>
{this.state.count}
<button onClick={this.handler}>点击</button>
</div>
</>
)
}
}
export default Header
1.4.8 组件 this指向
- 在行内使用方法时,方法是箭头函数时指向是组件;一般函数时this未定义,如果想使用this,那么需要行内用箭头函数包裹或者bind改变this指向
1.5 数据单向流动
- 单项数据流的设计原则
- 要求我们将不同的组件前需要共享的数据定义在上层
- 上层的数据需要以类组件的形式,通过state属性进行存储,然后传递
- 单项数据流动如何修改
- 定义状态的更新方法,定义箭头函数(this指向问题),接收参数target,在想要修改数据的地方进行调用
- 子组件通过调用父组件传递过来的方法可以更改数据
- 当数据发生更改之后,react会重新渲染组件树
// c1.js
import Radium from 'radium'
import C1 from './c1'
import React, {Component} from 'react'
class App extends Component {
state = {
name: 'zs',
age: 18
}
handler = (target) => {
this.setState({ name: target.name, age: target.age })
}
render () {
return (
<div>
<C1 {...this.state} change={this.handler} />
</div>
)
}
}
export default Radium(App)
// App.js
import Radium from 'radium'
import C1 from './c1'
import React, { Component } from 'react'
class App extends Component {
state = {
name: 'zs',
age: 18
}
handler = (target) => {
this.setState({ name: target.name, age: target.age })
}
render() {
return (
<div>
<C1 {...this.state} change={this.handler} />
</div>
)
}
}
export default Radium(App)