react起步
需要掌握的知识点: this指向,class类,ES6语法规范、npm包管理器、原型、原型链、数组常用的方法、模块化。
react高效的原因
使用了虚拟dom,不是直接更新真实dom Dom diffing 算法,最小化页面重绘。
react安装
cnpm i create-react-app -g
启动react项目
create-react-app 项目名
react特点
声明化 组件化
启动过程中会安装三个包。
特点
- 组件化
- 声明式(数据驱动)
- 跨平台
- 虚拟dom驱动 (fiber算法)
脚手架 create-react-app 简写 cra
cra项目运行架构
基于webpack 以入口函数 为中心 (src/index.js) 只要安装三个包 react // react核心语法包 react-dom // react组件构建虚拟dom 编译(比较) 挂载到 index.html上
reactDom render方法 虚拟dom为真实dom挂载
// 入口 index.js
`import ReactDom from 'react-dom'
const root = ReactDom.createRoot(document.querySelector('#root'));
root.render(<h1>Hello, world!</h1>);`
jsx
xml in js (可以在js中写标签)
xml可以理解为 自定义标签,一开始主要用于前后端传输数据(现在主要后端java 安卓 项目配置使用)
<student>
<name>小明</name>
<age>18</age>
</student>
洗脑三连 洗脑三连 洗脑三连
jsx目的 是为了 方便开发者写虚拟dom
允许开发者在 js中 直接标签(html标签 还是组件标签)
react自动编译成 虚拟dom对象 来运行
洗脑洗脑洗脑: 在js 你写的所有标签 其实是js对象对象对象
jsx 语法
-
jsx中使用js (一般是表达式) 在jsx中想要使用js 表达式 就加 {}
注意 注释也要加 {/* 这是注释 */}
任意一个jsx必须 包裹在一个闭合标签中
import ReactDom from 'react-dom'
const root = ReactDom.createRoot(document.querySelector('#root'));
const num = 10;
const tpl = (
<div>
<h2>你好世界</h2>
<h3>{num}</h3>
{/* 这是jsx中的注释 */}
</div>
)
root.render(tpl);
特殊属性:避免与关键字重名
<div className="box"></div>
<label htmlFor=""></label>
函数式组件
先天缺陷: 没有内部状态、没有生命周期钩子函数 定义
import React from 'react'
function App(props) {
return (
<div>
<h1>{props.title}</h1>
<h2>{props.subTitle}</h2>
</div>
)
}
// props就是使用时 标签传入的自定义属性
使用
<App title="主标题" subTitle="副标题"/>
class语法复习
属性是自己独有的,但是方法是可以进行公用的,这也就解释:属性在实例上,方法都是在原型上。
定义属性两种方式
1.直接定义 直接挂载到实例上
name='笑你干嘛';
age=132;
2.使用constructor定义 通过this关键字进行定义,实例的属性 new class时自动触发,且new传递参数 ,会传递到constructor
constructor(name,age){
// new class 时自动触发,且new传递参数会传递到constructor的
this.name=name;
this.age=age;
}
定义方法
1.普通函数 (方法在原型链上)
act(){
console.log('555'+this.name)
}
2.箭头函数(方法在实例上)(推荐第二种)
act1=()=>{
console.log('2223'+this.name)
}
属性与方法都需要在创建一个实例进行使用
const p1=new Person('小红',213231)
p1.act1()
静态对象
第一种
static b='长长的路'
第二种, 在实例化对象后面
Person.z='不不知道'
静态方法
第一种
static act3=()=>{
console.log('242432',this.name)
}
第二种,在实例化对象后面
const p1=new Person('小红',213231)
Person.act3=(){
console.log('11111')
}
完整写法
class Person{
// 在constructor通过this 关键字来定义 实例的属性
constructor(name,age){
// new class 时自动触发,且new传递参数会传递到constructor的
this.name=name;
this.age=age;
}
act(){
console.log('555'+Person.b)
}
act1=()=>{
console.log('2223'+this.name)
}
static b='长长的路'
static act3=()=>{
console.log('242432',this.name)
}
}
const p1=new Person('小红',213231)
console.log(p1)
p1.act1()
p1.act()
console.log(Person.b)
Person.z='不不知道'
console.log(Person.z)
Person.act3()
Person.act3=(){
console.log('11111')
}
继承
父类
class Animal {
constructor (kind) {
this.kind = kind;
};
act(){
console.log('这是父类上的方法');
}
}
子类
class Cat extends Animal{
constructor(name, gender, kind){
/*
子类 继承父类,使用 constructor 时,必须在使用this之前调用
super
分析super
在 子类的 constructor super() 相当于父类 constructor
父类constructor this指向实例的
super在子类方法中 super 代表父类在子类方法中调用父类方法
*/
super(kind);
this.name = name;
this.gender= gender;
};
act2(){
console.log('这是子类上的方法');
super.act();
}
}
const kitty = new Cat('tom', '公', '动物');
console.log(kitty);
kitty.act2();
组件中的样式
内联
react将内联样式映射成 js对象
<div style={{width: 200,height: 100+300, background:'red'}}></div>
引入外部的css
注意:注意会影响全局所有组件 cra默认配置的是 sass环境
可以引入外部的css文件 或者 scss文件 注意: jsx class 属性替换成className即可
import './style/a.css'
import './style/a.scss'
<div style={
{width: 200,height: 100+300, background:'red'}
}
></div>
问题: 解析 sass 包 node-sass问题 老vue-cli node-sass 一开始常见问题 node-sass资源 特别难安装(启动项目后,只要写sass/scss)就报错 启动项目 node-sass装不下来 (解决方案 手动安装 cnpm i node-sass)
后期(现在) node-sass 和新node版本 14.x以后版本冲突 使用直接报错 1 package.json中删除 node-sass包记录 2 删除整个node_modules 3 重新安装依赖 npm i 4 安装 sass包 替换node-sass npm i sass -D
styled-components
react插件 用于 做 样式组件
- 安装
npm i styled-components -S
-
基础使用
编译成一个组件 渲染div标签 且具备 以下样式 在style 中创建 ***.js 文件
首先需要引入styled,keyframes主要是用来使用动画的
import styled, { keyframes } from 'styled-components'
接着
const Box=styled.div`
width:300px;
height:300px;
background-color:green;
`
+选择器嵌套 可以通过选择器嵌套修改 样式盒子 内部的标签样式 嵌套规则 同 scss 和less
const Box2= styled.div`
width: 200px;
height: 200px;
background: #dcaa12;
p{
width: 100px;
height: 150px;
background: #aadd44;
&:hover{
color:blue;
}
}
`
- 传递props
const Box3 = styled.div`
width: 200px;
height: 200px;
background: ${props => props.bgc?props.bgc:'red'};
`
//在导入该Box3 的文件中书写
<Box3 bgc='yellow'/>
- 样式的继承
const Box4 = styled(Box3)`
border: 10px solid green;
`
- 定义动画关键帧
const Move = keyframes`
0% {
transform: rotate(-45deg);
}
100% {
transform: rotate(45deg);
}
`
// 动画
const Box5 = styled.div`
width: 30px;
height: 200px;
background: pink;
margin: 30px auto;
//设置中心点
transform-origin: center bottom;
//导入需要使用模板来进行导入
animation: ${Move} 500ms infinite alternate linear;
`
组件数据挂载 (专门针对 class组件)
外部数据 - props属性
- props类型验证 prop-types包 进行 react props 类型验证
npm i prop-types -S
// 在需要验证组件中引入 并定义 class或者(函数式) 静态属性
import PropTypes from 'prop-types'
class MyComponent extends Component{
}
MyComponent.propTypes = {
// 一下是常用类型验证
a: PropTypes.array,
b: PropTypes.bigint,
c: PropTypes.bool,
d: PropTypes.func,
e: PropTypes.number,
f: PropTypes.object,
g: PropTypes.string,
h: PropTypes.symbol,
// react组件 <组件/>
i: PropTypes.element,
// 这也是组件 直接传递 不加 <>
j: PropTypes.elementType,
// k必须是对象 且 必须是 Message实例 (new Message)
k: PropTypes.instanceOf(Message),
// 枚举 l值必须是给定值中的一个
l: PropTypes.oneOf(['News', 'Photos']),
// o值的类型必须是 给定类型中的其中一个
o: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 固定类型数组 必须 number数组 json数组等
p: PropTypes.arrayOf(PropTypes.number),
// 对象 属性 必须是固定数据类型的值
q: PropTypes.objectOf(PropTypes.number),
// 对象 其中两个属性 必须符合给定的验证
r: PropTypes.shape({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),
// 对象精准结构 只能有这两个属性 符合要求
s: PropTypes.exact({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),
t: PropTypes.func.isRequired,
// A value of any data type
u: PropTypes.any.isRequired,
}
prop默认值
定义 静态属性 defaultProps 即可
// 定义静态属性 defaultProps
class Todo extends Component {
Todo.defaultProps = {
a: 50
// 属性名 prop 值默认值
}
}
props.children
类似于 vue slot组件 可以在一个组件标签 双标签嵌套内容 在 组件内部 this.props.children渲染组件内部
- 嵌套内容
- 父组件
<Todo>
<div>
<h3>这是嵌套的内容</h3>
<button>按钮</button>
</div>
</Todo>
- 组件内部渲染
- 子组件
class Todo extends Component{
render(){
return (
<div>
{this.props.children}
</div>
)
}
}
也就是说: 在todo组件内部能够拿到父组件中在写的内容,也就是下面部分代码,并且可以渲染到页面上
<div>
<h3>这是嵌套的内容</h3>
<button>按钮</button>
</div>
+同时也可以不使用props.children,将一个组件中的内容嵌入到另一个组件中去
//主组件
render() {
return (
<div>
<Todo b={A}/>
)
}
// Todo组件
render() {
return (
<div>
<this.props.b/>
</div>
)
}
+也可以直接通过props来获得想在todo中的渲染内容
<Todo a={
<div><h2>这是另一个内容</h2></div>
}>
{this.props.a}
组件内部状态管理 - state
原理: 只需要 给实例定义 state属性即可
定义state
import React, { Component } from 'react'
class Todo extends Component {
// 1. 第一种:直接外部定义
/* state = {
msg: '这是内部的状态'
}; */
// 2. 第二种:内部定义
constructor(){
//必须要写super()因为继承父类的
super();
this.state = {
msg: '这是内部状态',
isBeauty: true
}
};
render() {
return (
<div>
{this.state.msg}
<br />
{ this.state.isBeauty? '哇塞大美女': '你是个好姑娘' }
</div>
)
}
}
注意: react虽然是数据驱动的,但是 并不是 mvvm,state 直接修改 视图不会刷新
修改state 并刷新视图 必须使用 实例调用setState方法 --setState方法
- 参数为对象
<button onClick = {this.clickBtn}>修改msg</button>
//注意必须使用箭头函数,否则this并不是指向实例的,这个方法是一个复合型的函数,需要
修改this的指向,使用bind方法,将this重新指向实例。
clickBtn = () => {
this.setState({
msg: '值'
})
}
// 参数为对象 想要修改哪个state 就 在对象中 定义这个state的值即可
- 参数是函数
// 函数的参数 1 改变state 2 组件的props
this.setState((state, props) => {
return {
msg: '值改变了',
isBeauty: !state.isBeauty
}
})
问题?
setState修改数据时异步的 意味着: setState调用之后,无法立即获取 修改后 最新的数据(新的dom也无法获取)
解决方案? setState的第二个参数 是 异步回调函数 触发时机: setState修改数据后,数据 修改完成,且真实dom 更新完成后触发(触发时机和vue中的$nextTick方法 基本一致) react中:修改完数据 想要立即获取修改后最新的数据 和 最新的dom,都要在 setState的回调中获取
this.setState({
}, () => {
// 在这里获取即可
})
react中的——事件
react 合成 语法: on原生事件(首字母大写) onClick onMouseover ...
绑定 react合成事件
基础语法:
<button onClick={函数}>按钮</button>
要求: 事件函数this指向需要指向组件实例
- 行内定义箭头函数 作为事件函数使用
<button onClick = {
() => {
this.setState({
msg: '值改变了'
})
}
}>修改msg</button>
注意: 造成业务代码 混入 视图结构 不利于 项目 维护 和 可读性降低
- 在原型上定义一个方法 作为事件函数使用 问题: this指向 是undefined 不指向 实例(因为不是 实例调用 合成事件调用) 解决:行内定义bind 改变this指向 (指向实例)
import React, { Component } from 'react'
class Todo extends Component {
state = {
msg: '这是内部的状态'
};
render() {
return (
<div>
<button onClick = {this.clickBtn.bind(this)}>修改msg</button>
{this.state.msg}
</div>
)
};
clickBtn () {
this.setState({
msg:'值改变了'
})
}
}
注意: 也是不推荐, render在初始化 和每一次更新时 都会触发,bind是在render调用的,会导致 多次bind 每一次都返回新的函数
- 挂载到原型上 在 constructor bind改变 this指向 原理: constructor时,给实例上定义一个同名方法 值 为 原型上方法 bind 改变this指向,绑定事件时,作用域关系 绑定的是 实例上的方法 优点:constructor只触发一次 不会多次bind
import React, { Component } from 'react'
class Todo extends Component {
/* state = {
msg: '这是内部的状态'
}; */
constructor(){
super();
this.state = {
msg: '这是内部状态'
}
this.clickBtn = this.clickBtn.bind(this);
};
render() {
return (
<div>
<button onClick = {this.clickBtn}>修改msg</button>
{this.state.msg}
</div>
)
};
clickBtn () {
this.setState({
msg:'值改变了'
})
}
}
- ❤ 直接 k=箭头函数 直接在实例上定义一个方法
import React, { Component } from 'react'
class Todo extends Component {
state = {
msg: '这是内部的状态'
};
render() {
return (
<div>
<button onClick = {this.clickBtn}>修改msg</button>
{this.state.msg}
</div>
)
};
clickBtn = () => {
this.setState({
msg:'值改变了'
})
}
}
注意: 推荐使用
获取事件对象
事件函数的第一个参数即为事件对象
e.stopPropagation() // 取消冒泡
e.preventDefault() // 阻止默认事件
e.target // 获取事件源
事件函数调用传参
原理: 行内新增箭头函数,让它作为事件函数, 实例上方法 在 箭头函数中调用即可传参
class Todo extends Component {
render() {
return (
<div>
<button onClick = {
(e) => {
this.clickBtn(e, 1)
}
}>按钮</button>
</div>
)
};
clickBtn = (e, id) => {
console.log(e);
console.log(id);
}
}
react 内容渲染
条件渲染
使用短路或者三目表达式即可
{/* 单分支 */}
{
this.state.isShow
&&
<div className="box"></div>
}
{/* 双分支 */}
{
this.state.isShow
?
<div className="box1"/>
:
<div className="box2"/>
}
循环
使用数组map方法循环
class Todo extends Component {
state = {
arr: ['a', 'b', 'c', 'd']
};
render() {
return (
<div>
<ul>
{
this.state.arr.map((item, index) => {
return (
<li key={index}>
{ item }
</li>
)
})
}
</ul>
</div>
)
};
}
注意: 循环需要给 循环元素 加独一无二key (fiber比较使用)