目录
因为真实DOM频繁操作节点会导致页面重绘和重排,影响性能,所以会用虚拟DOM【应用于前端框架】进行跨平台开发。
一、简单认识
1.1、特点
(1)采用组件化模式、声明式编码,提高开发效率及组件复用率
(2)在React Native中使用它进行移动端开发
(3)使用虚拟DOM+Diffing算法,减少与真实DOM的交互
1.2、JSX语法规则
(1)、定义虚拟DOM时,不要写引号
(2)、标签中混入js表达式时要用{}
(3)、样式的类名指定不要用class,要用className
(4)、内联样式:要用style={{key:value}}形式
(5)、只有一个根标签,所有标签必须闭合
(6)、标签首字母
若小写字母开头,则将该标签转为html中同名元素;若html中没有该标签对应的同名元素,则报错
若大写字母开头,则react去找对应的组件,若找不到,则报错
在Microsoft Edge安装React Developer Tools扩展进行开发辅助,以下语法基于16+版本。
1.3、函数组件和类式组件
前者适用于简单组件(只能使用props属性,除非使用Hooks里的useRef 和 useState ),后者适用于复杂组件
<div id="root"></div>
<script type="text/babel">
// 1.创建函数式组件
function MyComponent() {
return <h1>我是函数式组件</h1>;
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById("root"));
</script>
// 1.创建类式组件
class MyComponent extends React.Component {
render() {
// render()中的this 指向当前组件实例
return <h1>我是类式组件</h1>;
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById("root"));
1.4、类组件三大属性state、props、refs
注意:类组件里的render()会调用1+N次(只要state更新就变)
1.4.1、state
class MyMood extends React.Component {
state = {
isGood: false,
number: 0,
};
render() {
const { isGood, number } = this.state;
return (
<div>
<h1 onClick={this.changeMyMood}>
今天心情很{isGood ? "好" : "糟糕"}
</h1>
<h1 onClick={() => this.setState({ number: 10 })}>
打个{number}分
</h1>
</div>
);
}
changeMyMood = () => {
this.setState({
isGood: !this.state.isGood,
});
};
}
ReactDOM.render(<MyMood />, document.getElementById("root"));
总结:
(1)、状态必须通过setState进行更新,且更新是一种合并,不是替换;
(2)、组件中render()方法中的this为组件实例对象,当this为undefined,如何解决?
a.通过函数对象的bind()强制绑定this;b.箭头函数
1.4.2、props
<div id="root1"></div>
<div id="root2"></div>
<div id="root3"></div>
<script type="text/babel">
class Person extends React.Component {
// 使用 PropTypes 进行类型检查
static propTypes = {
name: PropTypes.string.isRequired,//必传项
sex: PropTypes.string,
age: PropTypes.number,
onSuccess: PropTypes.func
};
// 设置默认属性
static defaultProps = {
age: 18,
sex: "男"
};
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
);
}
}
ReactDOM.render(<Person name="tom" age={18} sex="男" onSuccess={onSuccess}/>, document.getElementById("root1"));
// 批量传递 props 【{...x}】
const backData = { name: '后端返回数据', age: 19, sex: "女" };
ReactDOM.render(<Person {...backData} />, document.getElementById("root2"));
ReactDOM.render(<Person name="jack" age={20}/>, document.getElementById("root3"));
function onSuccess() {
console.log('执行事件');
}
</script>
函数组件Props
function Person(props) {
const { name, age, sex } = props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
);
}
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
};
ReactDOM.render(<Person name='tom' age={18} sex="男" />, document.getElementById("root"));
总结:
(1)、组件标签的所有属性都保存在props中,但是组件内部不要修改props数据;
(2)、批量传递:{...item}、类型检查:PropTypes、默认属性:defaultProps
1.4.3、refs
class MyComponent extends React.Component {
showData = () => {
const { input1 } = this.refs;
alert(input1.value);
};
showData2 = () => {
const { input2 } = this;
alert(input2.value);
};
myRef=React.createRef();
showData3 = () => {
alert(this.myRef.current.value);
};
render() {
return (
<div>
{/* 1、字符串形式 */}
<input ref="input1" type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提示左侧数据</button>
{/* 2、回调函数形式 */}
<input
ref={(c) => (this.input2 = c)}
onBlur={this.showData2}
type="text"
placeholder="失去焦点提示数据"
/>
{/* 3、createRef API:存储被ref标识的节点,“专人专用” */}
<input
ref={this.myRef}
onBlur={this.showData3}
type="text"
placeholder="createRef的使用"
/>
</div>
);
}
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
总结:
(1)、直接访问和操作DOM元素或React组件实例;
(2)、使用
React.createRef()或者回调函数形式
(类组件);使用useRef
(函数组件)
1.5、事件处理
总结:
(1)、通过onXxx属性指定事件处理函数,事件通过事件委托【委托给组件最外层的元素】处理的;
(2)、通过event.target得到发生事件的DOM元素对象【高效】;
(3)、原生Js事件使用onclick,而React事件使用onClick【为了更好的兼容性】。
1.6、收集表单数据—非受控组件和受控组件
特性 | 受控组件 (Controlled) | 非受控组件 (Uncontrolled) |
---|---|---|
值存储位置 | 存储在React组件的state 中 |
存储在DOM中(使用ref 访问) |
表单元素控制 | React通过state 控制表单的值 |
浏览器控制,React不干涉 |
更新方式 | 用户输入时通过onChange 更新state |
使用ref 获取值,无需触发事件 |
适用场景 | 需要表单验证、交互或复杂逻辑时 | 简单表单,且无需与React状态紧密交互 |
代码复杂度 | 稍微复杂,需要维护状态和事件处理 | 简单,直接使用ref 访问DOM |
1.7、高阶函数—函数柯里化
总结:
高阶函数:如果函数的参数或者返回值是函数,那么这个函数就是高阶函数。
常见有:Promise、setTimeout、arr.map()、call、apply、bind、...
函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的一种函数编码形式。·
或者:直接在视图上使用箭头函数,同时传入属性值和对应的event。
1.8、生命周期—新旧对比
1.8.1、旧16+
<script type="text/babel">
class MyComponent extends React.Component {
constructor(props) {
console.log("构造器---constructor");
super(props);
this.state = { count: 0 };
}
componentWillMount() {
console.log("组件将要挂载---componentWillMount");
}
componentDidMount() {
console.log("组件挂载完毕---componentDidMount");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("组件是否需要更新---shouldComponentUpdate");
return true;
}
componentWillUpdate() {
console.log("组件将要更新---componentWillUpdate");
}
componentDidUpdate() {
console.log("组件更新完毕---componentDidUpdate");
}
componentWillUnmount() {
console.log("组件即将卸载---componentWillUnmount");
}
moveIt = () => {
ReactDOM.unmountComponentAtNode(document.getElementById("root")); //卸载
};
addIt = () => {
const { count } = this.state;
this.setState({ count: count + 1 });
};
forceIt = () => {
this.forceUpdate();
};
render() {
console.log("挂载页面---render");
const { count } = this.state;
return (
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.addIt}>点击+1</button>
<button onClick={this.moveIt}>销毁</button>
<button onClick={this.forceIt}>强制更新</button>
</div>
);
}
}
// A和B是父子关系
class A extends React.Component {
state = { carName: '奔驰' };
changeCar = () => {
this.setState({ carName: '宝马' });
};
render() {
return (
<div>
<div>A页面</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName} />
</div>
);
}
}
class B extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('B组件接收到新的属性(第一次不调)', nextProps);
}
render() {
return <div>B组件接收的车是:{this.props.carName}</div>;
}
}
// ReactDOM.render(<MyComponent />, document.getElementById("root"));
ReactDOM.render(<A />, document.getElementById("root"));
</script>
1.8.2、新17+
static getDerivedStateFromProps(props,state) {
console.log("获取新的属性---getDerivedStateFromProps",props,state);
return null;//返回null或者props
}
ReactDOM.render(<MyComponent count={199} />, document.getElementById("root"));
<style>
.list {
width: 200px;
height: 150px;
background-color: skyblue;
overflow: auto;
}
.news {
height: 30px;
</style>
class MyComponent extends React.Component {
state = { newArr: [] }
componentDidMount() {
setInterval(() => {
const { newArr } = this.state
const news = '新闻' + (newArr.length + 1)
this.setState({ newArr: [news, ...newArr] })
}, 1000);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
return this.refs.list.scrollHeight;// 获取更新前的DOM节点
}
componentDidUpdate(prevProps, prevState, snapshot) {
this.refs.list.scrollTop += this.refs.list.scrollHeight - snapshot;
}
render() {
return <div className="list" ref="list">
{
this.state.newArr.map((item, index) => {
return <div className="news" key={index}>{item}</div>
})
}</div>;
}
}
1.8.3、对比
17+在16+的基础上新增了getDerivedStateFromProps、getSnapshotBeforeUpdate;
即将废弃 componentWillMount、componentWillReceiveProps、componentWillUpdate三个钩子。
1.9、DOM的Diffing算法
经典面试题:为啥key不能使用index,而是取id(唯一标识)?
总结:
1、使用 index 作为 key:
在列表项的顺序发生变化、增加或删除时,可能会导致错误渲染或状态丢失。
适用于列表项不变,且不涉及删除、增加或排序操作的简单情况。
2、使用 id 作为 key:
更稳定,能够确保元素在更新时保持一致,特别是在列表顺序发生变化时。
尤其是在动态数据列表(增、删、改、排序等操作)中使用。
二、脚手架搭建
2.1、创建项目
1、全局安装:npm i -g create-react-app【查看是否下载create-react-app --version】
2、切换到想创建项目的目录下:create-react-app react_app【Node版本在14+及以上】
3、进入项目文件夹:cd react_app
4、启动项目:npm start【修改package.json,重新下载
node_modules、
package-lock.json来修改版本
】