简介
- 起源于Facebook
- 与传统mvc的关系:轻量级的视图层库,将视图抽象成组件
特点
- 声明式设计
- 高效 通过虚拟dom,减少真实dom交互
- 灵活 适配已知的库或框架
- jsx js语法扩展
- 组件 代码可复用
- 单向响应数据流 减少重复代码,比传统绑定简单
虚拟dom
- 真实dom操作消耗性能,react把真实dom转化为js对象树(就是个对象)
ps:真实的dom属性太多了,而且频繁的操作视图重绘回流,而且进行了diff算法之后,将部分真正要改变的dom重绘,避免了大幅度修改。我们的目的是更快!更快!
安装
npx不能用的话,升级nodejs版本
npx create-react-app my-app
npm start //启动项目npm test //测试npm run build //打包上线
目录
- README.md 项目说明文档
- node_modules 所有的依赖安装的目录
- package-lock.json 锁定安装时的包的版本号,保证团队的依赖能保证一致。
- package.json 配置文件
- public 静态公共目录
- src 开发用的源代码目录
//src-index.js
//创建小demoimport React from "react"; //jsx 17版本之后默认引入,可不写
import ReactDom from "react-dom";//react渲染到页面//render方法 将组件渲染指定节点
//参1:用jsx语法写的html
//参2:指定节点
ReactDom.render(<div>1111
</div>, 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 = '<div id="app"></div>';// 渲染你的 React 组件
const root = createRoot(document.getElementById("app"));
root.render(<h1>Hello, world</h1>);
页面就成功出现想要内容啦
jsx
所谓的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 (<div><h1>Welcome to my app</h1></div>);
}
export default FunComponent;
class类组件
-
this为undefined (babel翻译时自动开启了严格模式组件
//新建js文件
import React from "react";
class App extends React.Component {render() {return <div className="">hello,react</div>;}
}
export default App;
- this为实例对象
- render在类的原型对象上,实例使用
引入在index.js,进行渲染
import { createRoot } from "react-dom/client";
import App from './01-base/01-class类组件'
document.body.innerHTML = '<div id="app"></div>';
const root = createRoot(document.getElementById("app"));
root.render(<App/>);
执行root.render(<组件/>)之后发生了什么呢?
- react解析该组件标签,找到该组件
- 发现组件时类创建,new该实例,调用实例上的render方法
- 将render返回的虚拟dom转为真实dom渲染在页面上
组件实例三大属性
三大属性一般用在有实例的类组件身上,由于函数可以接受参数,函数组件可以通过传参的方式接受props
state
存储数据,数据改变,页面随着数据改变。
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 (<div className="">今天的天气是<button onClick={this.changeWeather}>{isHot ? "炎热" : "凉爽"}</button></div>);}
}export default App;
简写
import React from "react";
class App extends React.Component {state = {isHot: false,wind: "大风",};//直接使用父层的thischangeWeather = () => {const { isHot } = this.state;this.setState({ isHot: !isHot });};render() {const { isHot } = this.state;return (<div className="">今天的天气是<button onClick={this.changeWeather}>{isHot ? "炎热" : "凉爽"}</button></div>);}
}export default App;
props
- 只读
//父组件传值
root.render(<App data="2023-5-17" data1="2023-5-18"/>);
//简写
//const p = { data: "2023-5-17", data1: "2023-5-18" };
//root.render(<App {...p} />);//子组件接收
render() {const { data,data1 } = this.props;return (<div className="">今天 {data} 明天 {data1}</div>);}
限定props
//父组件
function speak() {console.log("你好呀");
}
const p = { data: "2023-5-17", data1: "2023-5-18", sun: true };
root.render(<App {...p} speak={speak} />);//子组件
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 (<div><h1>Welcome to my app</h1><p>name: {name}</p><p>sex: {sex ? "女" : "男"}</p><p>age: {age}</p></div>);
}//限定标签类型,必要性
FunComponent.propTypes = {name: PropTypes.string.isRequired, //isRequired 必传sex: PropTypes.bool,age: PropTypes.number,
};
//标签属性的默认值
FunComponent.defaultProps = {name: "明天就是明天啊",
};
export default FunComponent;
refs
字符串形式
获取到的是真实dom元素(虚拟dom转化成真实dom的节点)
//使用refs
<button onClick={this.changeWeather} ref="but">{isHot ? "炎热" : "凉爽"}</button>//获取refschangeWeather = () => {const { but } = this.refs;console.dir(but.innerText);};
官网不建议使用字符串形式,可能会影响性能
回调形式(箭头函数)
//箭头函数this指向父元素,指向类的实例
<div className="" ref={(e) => (this.weather = e)}> //内联函数今天的温度是<div />changeWeather = () => {console.log(this.weather);};
ref回调函数是以内联函数方式定义的,在更新过程中执行两次,第一次传入参数null,第二次传入参数dom元素,因为每次渲染会创建一个新的函数实例,react清空旧的ref并创建新的。通过ref回调函数定义成class绑定函数避免这种问题
//绑定函数
<div className="" ref={this.saveElement}>今天的温度是<div />
//绑定节点
saveElement=(e)=>{this.input1=e}
//使用节点
console.log(this.input1);
createRef
//本身是个函数,调用后返回一个容器,该容器可以访问存储器ref标识的节点
myRef=React.createRef()//绑定ref
<div className="" ref={this.myRef}>今天的温度是
<div/>
//使用
console.log(this.myRef.current);
事件处理
- 通过onXxx属性指定事件处理(注意大小写)
- React使用的是自定义(合成)事件,而不是使用的原生DOM事件
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素),元素会冒泡到父元素身上,用父元素监听多个子元素更加高效
- 通过event.target得到发生事件的DOM元素对象
<button onClick={this.changeWeather} ref="but">按钮</button>changeWeather = (e) => {console.log(e); }
受控组件&非受控组件
非受控组件
页面中所有输入类的dom,现用现取。 <input/>
现用现取:比如说点击事件之后,通过回调函数我获取e.属性,获取的是最新的值
class Login extends React.Component {loginFromSubmit = (e) => {//表单提交跳到新地址,或者刷新页面e.preventDefault();const { username, password } = this;console.log(username, password);};render() {return (<form action="" onSubmit={this.loginFromSubmit}>username:<input type="text" name="username" ref={(r) => (this.username = r)} /><br />password:<inputtype="password"name="password"ref={(r) => (this.password = r)}/><br /><button>login</button></form>);}
}
受控组件