特点
虚拟dom
ps:真实的dom属性太多了,而且频繁的操作视图重绘回流,而且进行了diff算法之后,将部分真正要改变的dom重绘,避免了大幅度修改。我们的目的是更快!更快!
npx不能用的话,升级nodejs版本
npx create-react-app my-app
npm start //启动项目
npm test //测试
npm run build //打包上线
目录
//src-index.js
//创建小demo
import React from "react"; //jsx 17版本之后默认引入,可不写
import ReactDom from "react-dom";//react渲染到页面
//render方法 将组件渲染指定节点
//参1:用jsx语法写的html
//参2:指定节点
ReactDom.render(1111
, document.getElementById('root'))
然后就报错啦!!!
因为我安装的是18.2.0版本,已经不支持 react-dom
警告:ReactDOM。渲染在React 18中不再被支持。请使用createRoot。在你切换到新的API之前,你的应用程序会像运行React 17一样运行。
然后就去官网尝试新的写法
import { createRoot } from "react-dom/client";
// 清除现有的 HTML 内容
document.body.innerHTML = '';
// 渲染你的 React 组件
const root = createRoot(document.getElementById("app"));
root.render(Hello, world
);
所谓的JSX其实就是Javacript 对象,所以使用 React 和JSX的时候一定要经过编译的过程:
JSX一使用react构造组件,bable进行编译->JavaScript对象 - ReactDom.render中->DOM元素一>插入页面
//将jsx语法转化为react对象
ReactDom.render(React.createElement("div",{
id:"aaa",
class:"bbb",
},"111111111"),document.getElementById("root"))
React 组件必须以大写字母开头
function FunComponent() {
return (
Welcome to my app
);
}
export default FunComponent;
this为undefined (babel翻译时自动开启了严格模式组件
//新建js文件
import React from "react";
class App extends React.Component {
render() {
return hello,react;
}
}
export default App;
引入在index.js,进行渲染
import { createRoot } from "react-dom/client";
import App from './01-base/01-class类组件'
document.body.innerHTML = '';
const root = createRoot(document.getElementById("app"));
root.render( );
执行root.render(<组件/>)之后发生了什么呢?
三大属性一般用在有实例的类组件身上,由于函数可以接受参数,函数组件可以通过传参的方式接受props
存储数据,数据改变,页面随着数据改变。
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
//初始化状态
this.state = {
isHot: false,
wind: "大风",
};
//解决函数this问题
this.changeWeather=this.changeWeather.bind(this)
}
//调用的时候通过原型链查找
changeWeather() {
// const { isHot } = this.state;
console.log(this);//局部函数开启了严格模式 undefined
}
render() {
const { isHot } = this.state;
return (
今天的天气是
);
}
}
export default App;
简写
import React from "react";
class App extends React.Component {
state = {
isHot: false,
wind: "大风",
};
//直接使用父层的this
changeWeather = () => {
const { isHot } = this.state;
this.setState({ isHot: !isHot });
};
render() {
const { isHot } = this.state;
return (
今天的天气是
);
}
}
export default App;
//父组件传值
root.render( );
//简写
//const p = { data: "2023-5-17", data1: "2023-5-18" };
//root.render( );
//子组件接收
render() {
const { data,data1 } = this.props;
return (
今天 {data}
明天 {data1}
);
}
限定props
//父组件
function speak() {
console.log("你好呀");
}
const p = { data: "2023-5-17", data1: "2023-5-18", sun: true };
root.render( );
//子组件
import PropTypes from "prop-types";
//限定标签类型,必要性
static propTypes = {
data: PropTypes.string.isRequired, //isRequired 必传
data1: PropTypes.string,
speak: PropTypes.func, //函数类型
sun: PropTypes.bool, //布尔类型
};
//标签属性的默认值
static defaultProps = {
data1: "明天就是明天啊",
};
函数组件
import PropTypes from "prop-types";
function FunComponent(props) {
const { name, sex, age } = props;
return (
Welcome to my app
name: {name}
sex: {sex ? "女" : "男"}
age: {age}
);
}
//限定标签类型,必要性
FunComponent.propTypes = {
name: PropTypes.string.isRequired, //isRequired 必传
sex: PropTypes.bool,
age: PropTypes.number,
};
//标签属性的默认值
FunComponent.defaultProps = {
name: "明天就是明天啊",
};
export default FunComponent;
字符串形式
获取到的是真实dom元素(虚拟dom转化成真实dom的节点)
//使用refs
//获取refs
changeWeather = () => {
const { but } = this.refs;
console.dir(but.innerText);
};
官网不建议使用字符串形式,可能会影响性能
回调形式(箭头函数)
//箭头函数this指向父元素,指向类的实例
(this.weather = e)}> //内联函数
今天的温度是
changeWeather = () => {
console.log(this.weather);
};
ref回调函数是以内联函数方式定义的,在更新过程中执行两次,第一次传入参数null,第二次传入参数dom元素,因为每次渲染会创建一个新的函数实例,react清空旧的ref并创建新的。通过ref回调函数定义成class绑定函数避免这种问题
//绑定函数
今天的温度是
//绑定节点
saveElement=(e)=>{
this.input1=e
}
//使用节点
console.log(this.input1);
createRef
//本身是个函数,调用后返回一个容器,该容器可以访问存储器ref标识的节点
myRef=React.createRef()
//绑定ref
今天的温度是
//使用
console.log(this.myRef.current);
事件处理
- 通过onXxx属性指定事件处理(注意大小写)
- React使用的是自定义(合成)事件,而不是使用的原生DOM事件
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素),元素会冒泡到父元素身上,用父元素监听多个子元素更加高效
- 通过event.target得到发生事件的DOM元素对象
changeWeather = (e) => {
console.log(e);
}
受控组件&非受控组件
非受控组件
页面中所有输入类的dom,现用现取。
现用现取:比如说点击事件之后,通过回调函数我获取e.属性,获取的是最新的值
class Login extends React.Component {
loginFromSubmit = (e) => {
//表单提交跳到新地址,或者刷新页面
e.preventDefault();
const { username, password } = this;
console.log(username, password);
};
render() {
return (
);
}
}
受控组件
页面中所有输入类的dom,将输入内容直接维护到状态中,取值的话直接到状态里取
不就是v-model嘛~,不过值要自己监听触发onChange事件
import React from "react";
class Login extends React.Component {
state = {
username: "",
password: "",
};
loginClick = (e) => {
e.preventDefault();
let { username, password } = this.state;
console.log(username, password);
};
nameChange = (e) => {
this.setState({ username: e.target.value });
};
passwordChange = (e) => {
this.setState({ password: e.target.value });
};
render() {
return (
);
}
}
export default Login;
高阶函数&函数柯里化
高阶函数
如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
- 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
- 若A函数,调用的返回值是一个函数,那么A就可以称之为高阶函数。
// 相对于受控组件那段代码的高阶函数
formChange = (type) => {
return (e) => {
//返回一个函数
this.setState({ [type]: e.target.value });
};
};
render() {
return (
);
}
函数柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
函数柯里化详解_Boale_H的博客-CSDN博客
js面试高频题:函数柯里化的实现(彻底弄懂)_Coding101的博客-CSDN博客
ps:Array.prototype.slice.call(arguments)将伪数组转为数组
生命周期
组件从 创建到销毁的过程 就是生命周期,过程中的不同阶段会触发不同的钩子函数
- 初始化阶段
- componentWillMount:组件将要初始化;render前最后一次修改状态
- render:渲染组件
- componentDidMount:组件渲染完成真实dom;可以修改dom,进行异步请求
- 运行中阶段
- componentWillReceiveProps:父组件修改属性触发
- shouldComponentUpdate:组件是否更新;若返回false可以阻止render调用
- componentWillUpdate:组件将要更新;不能修改属性和状态
- render:渲染组件;只能访问this.props和this.state,不允许修改状态和dom输出
- componentDidUpdate:组件更新完成;可以修改dom
- 销毁阶段
- componentWillUnmount:组件将要卸载;可以进行清理工作,清除定时器和事件监听