基于React18.1.0版本
概念
React是一款用于构建用户界面的 JavaScript 库,它的特点:
- 声明式UI
- 组件化
- 跨平台编写
搭建初始化开发环境
-
使用脚手架创建
执行命令 npx create-react-app react-basic(项目名) 进行创建
-
启动npm start 或 yarn start
src中核心文件
App.js 根组件
index.js 应用入口文件
index.css 全局样式文件
注意把index.js中的严格模式去掉(react.strictMode),会影响useEffect的执行时机
JSX基础
作用在React中创建HTML结构
柔和了HTml和JS
JSX表达式
可用的表达式
1. 字符串、数值、布尔值、null、undefined、object ([]、{})
2. 1+2、‘awd’.split(‘,’),['a','b'] .join('-')
3. fn()
特别注意
if语句/switch-case/变量声明语句/不是表达式,不能在{}中
- JSX列表渲染通过map
条件渲染
1. 三元表达式
2. &&
flase&&‘不会显示’
true&&‘显示’ —前边的条件为true后边才会展示
原则:模板中的逻辑尽量保持精简
复杂的多分支逻辑 收敛为一个函数,模板只负责调用
JSX样式处理
-
行内样式 – 在元素上绑定一个style属性即可 style={{color:red}} //注意驼峰,过长可以收敛为对象
-
类名样式
在元素上绑定ClassName 引入css文件
注意事项
1. JSX中必须有一个根节点,若没有可以使用<></>幽灵节点替代
2. 所有标签必须形成闭合
3. 属性名采用驼峰命名法
4. 支持多行/换行,如果换行需要使用 ()包裹防止BUG
格式化配置
1. 安装VS code prettie
2. 错误提示工具 Error Lens
安装VS code prettie工具后,修改json文件如下—这是结合vue,react,微信小程序
{
"git.enableSmartCommit": true,
// 修改注释颜色
"editor.tokenColorCustomizations": {
"comments": {
"fontStyle": "bold",
"foreground": "#82e0aa"
}
},
// 配置文件类型识别
"files.associations": {
"*.js": "javascript",
"*.json": "jsonc",
"*.cjson": "jsonc",
"*.wxss": "css",
"*.wxs": "javascript"
},
"extensions.ignoreRecommendations": false,
"files.exclude": {
"**/.DS_Store": true,
"**/.git": true,
"**/.hg": true,
"**/.svn": true,
"**/CVS": true,
"**/node_modules": false,
"**/tmp": true
},
// "javascript.implicitProjectConfig.experimentalDecorators": true,
"explorer.confirmDragAndDrop": false,
"typescript.updateImportsOnFileMove.enabled": "prompt",
"git.confirmSync": false,
"editor.tabSize": 2,
"editor.fontWeight": "500",
"[json]": {},
"editor.tabCompletion": "on",
"vsicons.projectDetection.autoReload": true,
"editor.fontFamily": "Monaco, 'Courier New', monospace, Meslo LG M for Powerline",
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features"
},
"editor.fontSize": 16,
"debug.console.fontSize": 14,
"vsicons.dontShowNewVersionMessage": true,
"editor.minimap.enabled": true,
"emmet.extensionsPath": [
""
],
// vue eslint start 保存时自动格式化代码
"editor.formatOnSave": true,
// eslint配置项,保存时自动修复错误
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"vetur.ignoreProjectWarning": true,
// 让vetur使用vs自带的js格式化工具
// uni-app和vue 项目使用
"vetur.format.defaultFormatter.js": "vscode-typescript",
"javascript.format.semicolons": "remove",
// // 指定 *.vue 文件的格式化工具为vetur
"[vue]": {
"editor.defaultFormatter": "octref.vetur"
},
// // 指定 *.js 文件的格式化工具为vscode自带
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
// // 默认使用prettier格式化支持的文件
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.jsxBracketSameLine": true,
// 函数前面加个空格
"javascript.format.insertSpaceBeforeFunctionParenthesis": true,
"prettier.singleQuote": true,
"prettier.semi": false,
// eslint end
// react
// 当按tab键的时候,会自动提示
"emmet.triggerExpansionOnTab": true,
"emmet.showAbbreviationSuggestions": true,
"emmet.includeLanguages": {
// jsx的提示
"javascript": "javascriptreact",
"vue-html": "html",
"vue": "html",
"wxml": "html"
},
// end
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
// @路径提示
"path-intellisense.mappings": {
"@": "${workspaceRoot}/src"
},
"security.workspace.trust.untrustedFiles": "open",
"git.ignoreMissingGitWarning": true,
"window.zoomLevel": 1
}
函数组件
1. 组件首字母大写
2. 必须有返回值
使用JS的函数(或箭头函数)创建的组件,就叫函数组件
类组件
使用ES6创建
1. 首字母大写
2. 继承React.Component父类,从而使用父类提供的方法或属性
3. 必须提供render方法,render必须有返回值,表示该组件的UL结构
事件绑定
1. on+事件名称={事件处理程序}
Class组件放到render()同级,避免this指向问题
注意点: 类组件绑定需要+this
实现对象e
事件回调函数中传入e,就能拿到事件对象e
可以用来阻止默认行为,比如a标签 e.preventDefault()就不会跳走了
传递自定义参数
改造成箭头函数调用执行方式
组件状态
1. 初始化状态
原生使用
1. 添加一个DOM容器到HTML
2. 添加 Script 标签
//这里我是保存到本地引入
<body>
<div id="box"></div>
</body>
<script src="./lib/react.development.js"></script>
<script src="./lib/react-dom.development.js"></script>
<script src="./lib/babel.min.js"></script>
<script type="text/babel">
const box = document.querySelector("#box") //获取盒子
const root = ReactDOM.createRoot(box) //作为根节点
//root.render(<div>123</div>) //1.直接渲染
const e = React.createElement //2.创建一个虚拟dom函数进行渲染
root.render(e('div', {
className: 'children', onClick: () => {
console.log(6);//采用驼峰命名,事件,类名,……
}
}, 'div的内容',
e('p', { className: 'br' }, 'div内还能嵌套')))
</script>
此刻内容已渲染完成,注意如果有多个渲染同一个容器,后面的会将前面的覆盖掉
JSX基础语法
const box = document.querySelector("#box") //获取盒子
const root = ReactDOM.createRoot(box) //作为根节点
let user = {
mise: '相遇',
m: '雨天'
}
function Mise(i) {
return '在' + i.m + i.mise
}
// JSX写法:
// const ele = <h1>hello<small>123</small></h1> //1.嵌入内容
// const ele = <div >{user.mise}</div> //2.可以嵌入表达式like
// const ele = <div >{Mise(user)}</div> //3.可以是一个表达式
// root.render(ele)
// React.createElement写法:
const ele = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
);
root.render(ele)
组件写法
const box = document.querySelector("#box") //获取盒子
const root = ReactDOM.createRoot(box) //作为根节点
//1.函数组件
// function Fn(params) {
// return (<div>{params.content}我爱你</div>)
// }
// root.render(<Fn content='今天星期八' />)
//2.class组件
// class Fn extends React.Component {
// render(props) {
// return <div>我爱你{this.props.content}</div>
// }
// }
// root.render(<Fn content='今天星期八' />)
function Fn(params) { //形参接收传递过来的值
return (<div>{params.content}我爱你</div>)
}
root.render(<Fn content='今天星期八' />) //标签上进行传值
↓ ↓ ↓ 页面也会渲染
组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
组件内部也可以再嵌套组件
state
- 不要直接修改 State
this.setState({name: ‘Hello’});//正确修改姿势
- State 的更新可能是异步的
不要依赖他们的值来更新下一个状态,可能会无法准确更新
要解决这个问题,可以让setState()
接收一个函数而不是一个对象这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
- 单向数据流
生命周期
1. 当组件实例被创建并插入 DOM 中时生命周期顺序如下
constructor
初始化 state 或进行方法绑定时使用static getDerivedStateFromProps()
调用 render 方法之前调用render()
唯一必须实现componentDidMount()
类似于vue中的mounted,在组件挂载后(插入 DOM 树中)立即调用
2.组件的 props 或 state 发生变化时会触发更新时
static getDerivedStateFromProps(props, state)
调用 render 方法之前调用,目的让组件在 props 变化时更新 stateshouldComponentUpdate(nextProps, nextState)
在渲染执行之前被调用render()
唯一必须实现getSnapshotBeforeUpdate()
在最近一次渲染输出(提交到 DOM 节点)之前调用componentDidUpdate
更新后会被立即调用
3. 组件从 DOM 中移除时生命周期顺序如下
componentWillUnmount
组件卸载及销毁之前直接调用
事件绑定
命名采用小驼峰式,使用 JSX 语法时你需要传入一个函数作为事件处理函数
- 元素上直接使用bind绑定
<button onClick={this.handleClick.bind(this)}>{this.state.isToggle ? 'ON' : 'OFF'}</button>
组件内接收 ↓↓↓
handleClick() {
console.log('handleClick!', this)
this.setState(preState => ({
isToggle: !preState.isToggle
}))
}
- 元素上直接绑定+构造器声明
1.绑定方法
<button onClick={this.handleClick}>{this.state.isToggle ? 'ON' : 'OFF'}</button>
2.在constructor构造器中声明 ↓ ↓ ↓
this.handleClick = this.handleClick.bind(this)
3.定义handleClick方法
handleClick() {
console.log('handleClick!', this)
this.setState(preState => ({
isToggle: !preState.isToggle
}))
}
- 元素上使用箭头函数绑定
1.使用箭头函数直接绑定
<button onClick={() => this.handleClick()}>{this.state.isToggle ? 'ON' : 'OFF'}</button>
2.定义事件绑定的回调函数
handleClick() {
console.log('handleClick!', this)
this.setState(preState => ({
isToggle: !preState.isToggle
}))
}
//不建议使用此种方法,会引起性能问题
1. 每次渲染回调函数所在的组件,此回调函数都会调用多次
2. 在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染
事件传参
- 通过bind绑定传参
1.定义事件
<button onClick={this.handleClick.bind(this, 1001)}>{this.state.isToggle ? 'ON' : 'OFF'}</button>
2.接收
handleClick = (id, e) => {
console.log('id:', id)
console.log('e:', e)
this.setState(preState => ({
isToggle: !preState.isToggle
}))
}
- 通过箭头函数绑定传参
1.定义事件
<button onClick={(e) => this.handleClick(e, 1001)}>{this.state.isToggle ? 'ON' : 'OFF'}</button>
2.处理函数
handleClick = (id, e) => {
console.log('id:', id)
console.log('e:', e)
this.setState(preState => ({
isToggle: !preState.isToggle
}))
}
列表渲染&key
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) => <li key={number.toString()}>{number}</li> );
return (
<ul>{listItems}</ul>
);
}
//key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
const numbers = [1, 2, 3, 4, 5];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<NumberList numbers={numbers} />);