React-基础(一)
React-基础-学习笔记(一)
1 安装
npm i react react-dom -S
react 核心包 创建虚拟 dom
react-dom 展现在界面上
2 主要方法
//创建一个 虚拟DOM
//参数1 标签
//参数2 属性对象
//参数3 子节点名称
//参数4 子节点属性
//参数N···
const myDom = React.createElement('h1', null, '这时一个H1')
// 参数1 虚拟DOM(react virtordom) 对象
// 参数2 容器(container) 节点
// 参数3 callback
ReactDOM.render(myDom, document.getElementById('root'))
// 正常写法 app是react元素
const app= <h1>asdasfsd</h1>
// or
const createApp = (props)=>{
return <h1>asdasdasd{props.title}</h1>
}
const app=createApp({title:"1111"})
ReactDOM.render(app, document.getElementById('root'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3 jsx
在 js 中混合写入 HTML的语法 叫做 jsx 语法 符合 xml 规范
需要通过 babel 转换成 正常的 js React.creatElement()的形式
安装 babel 包
babel-core babel-loader babel-plugin-transform-runtime -D
babel-preset-env babel-preset-stage-0
babel-preset-react
.babelrc 文件内部
{
"presets":["env","stage-0"."react"],
"plugins":["transform-runtime"]
}
2
3
4
在react中有两种括号
- {} 变量引用
- ()返回值域
React的 底层渲染原理
通过 React.createElement('p',{className:'app'}) 来创建DOM 进行比较,而把DOM 解析成JSON 对象。
4 组件
组件中 自定义组件需要大写字母开头,原生 DOM 需要小写字母开头
纯函数,传递进来相同的值得到的额结果是相同的,并且不会修改输入的值。
react 中无论是 class 还是 function 声明的组件都是必须是纯函数。
每个父组件只能调用子组件
4.0.1 创建组件的第一种方式 函数式
function Hello(props){//props 式父组件传递过来的 是只读的属性 read-only
return null //表示什么都不渲染 空组件
//在组件中必须返回一个合法的虚拟DOM
return (<div>
{hello world!}
{props.name}
</div>)
}
const ddog={
name:“xiaobai”,
age:4
}
ReactDOM.render(<Hello {...dog}}/>,document.getElementById('root'))
const ddog=【
‘大黄’,
‘小白’
】
ReactDOM.render(<Hello name={ddog【1】}/>,document.getElementById('root'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4.0.2 使用 class 关键字
import React from 'react'
class World extends React.Component {
render() {
return <div>这是class 创建的组件</div>
}
}
export default World
2
3
4
5
6
7
接受外界 props 参数
在 class 创建的组件不需要接受形参 props 通过 this.props.参数 即可获取到
render() {
return <div>{this.props.name}</div>
}
2
3
在 class 创建的组件 props 也是只读的 不可写
react 官方说 无状态组件由于没有自己的 state 和生命周期函数 所以无状态组件的渲染效率会更高一些
4.0.3 class 创建组件的状态
使用 class 创建的组件有自己的私有数据 有状态组件
但是通过 function 创建的没有私有数据和生命周期函数 无状态组件
class 组件的状态
import React from 'react'
class World extends React.Component {
constructor() {
super()
this.state = {} //这就相当于vue 中的 data 数据对象
}
render() {
return <div>{this.props.name}</div>
}
}
export default World
2
3
4
5
6
7
8
9
10
11
12
13
this.state 是组件自己的状态 是可读可写的
4.0.4 父子组件的基本构造
数据 最好放在父组件中传递到子组件 控制,在数据改变后 基于数据的组件进行销毁重绘
//父组件
import React from 'react'
import Common from './common'
import '../css/Box.css'
class Box extends React.Component {
constructor() {
super()
this.state = {
commons: [
{ username: '王五', userContext: 'hahahahaha' },
{ username: '李四', userContext: 'hahahahaha' },
{ username: '张三', userContext: 'hahahahaha' },
{ username: '人流', userContext: 'hahahahaha' },
{ username: '川流不息', userContext: 'hahahahaha' },
{ username: '金城武', userContext: 'hahahahaha' },
{ username: '王昭君', userContext: 'hahahahaha' },
{ username: '李元芳', userContext: 'hahahahaha' }
]
}
}
render() {
return (
<div className="Box">
{this.state.commons.map((item, index) => (// 数组最好不要更改元数据 而是使用map 等不会改变原数据的方法 生成新数组进程数据渲染
<Common {...item} key={index} />//使用对象解构的方式进行传递值
))}
</div>
)
}
}
export default Box
//子组件
import React from 'react'
import '../css/common.css'
class Common extends React.Component {
render() {
return (
<div className="common">
<h3>评论人:{this.props.username}</h3>
<br />
<div>
<p>评论内容:{this.props.userContext}</p>
</div>
</div>
)
}
}
export default Common
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
4.0.5 默认导入的 Css 样式表是全局生效
修改导入的 CSS 使其在当前生效类似于 Vue 中的 scopted 指令,react 中没有指令的概念,因此在 webpack 配置中给 css-loader 加规则?modules 为样式表启用模块化规则。
text:'/\.css$/', user:['style-loader','css-loader?mudules‘] //然后再具体使用过程中宏 import componentsCss from ‘./componentsCss.css’ const title={componentsCss.title}//这样使用类名绑定 const aside={componentsCss.aside} <h1 className={title}> 标题 </h1>
1
2
3
4
5
6
7
8
9css 模块画只针对类和 ID 选择器 标签选择器不会匹配
对类名进行自定义 在 modules 后面添加
localIdentName=[path][name][local][hash;length]
- [path]代表地址
- name 代表引入文件的名称
- local 代表 类名
- hash 代表 hash 区别 length 代表长度
不想让某一个类名被模块化 使用 :global(类名) 进行包裹,这样该类名就不会被转化。:local()包裹会被模块化(很少用默认就会被模块化)
多类名的书写
{/* 1. 使用多个类名控制样式的时候 类名用jion来添加 */} <p className={['text-green', 'text-size-20'].join(' ')}>P标签内部 font-size 20px</p> {/* 2. 使用classNames 包来动态添加多个不同的类型类名控制样式 */} <p className={classNames('text-red', { 'text-size-25': true, 'text-size-20': false })}> P标签内部 classnames 控制的类名 </p> {/* 3. 使用styled-components 把样式看成是一种组件 在需要的样式处用样式组件把内容包裹起来 */} <TitleStyled>P标签内部使用styled-components 来控制的样式</TitleStyled>
1
2
3
4
5
6
7
8第三方样式表 导入后 可以直接导入使用 或者取消其模块化
行内样式写法
const Comp1 = ()=>{ return ( // 内部分color style 式两个花括号 <h1 style={{color:red}}> h1标签 </h1> ) }
1
2
3
4
5
6
4.0.6 条件渲染
组件的渲染可以根据 JS 的 IF else 进行判断然后选择进行渲染。
function Hello1() {
return <h1>hello 1</h1>
}
function Hello2() {
return <h1>hello 2</h1>
}
function HelloWorld(props) {
if (props.value === 1) {
return <Hello1 />
}
return <Hello2 />
}
2
3
4
5
6
7
8
9
10
11
12
可以通过状态值来存储组件的状态,然后通过状态来有条件的渲染组件。
在条件渲染中 可以使用 && 短路与 或者 || 或者三元运算夫 都可以生效
但是在条件特别复杂的情况下,官方建议单独提取组件
注意:在警告类组件中 可以使用 return null 这样组件默认不渲染,在有警告符号的时候返回渲染组件。
function Logout(props) {
return props.isLogin && <button>退出</button>
//如果登录状态是true 则显示退出按钮 否则不显示
}
2
3
4
4.0.7 组件中的 list 和 key
function Body(props) {
const Array = [1, 2, 3, 4]
const list = Array.map((item, index) => <li>item</li>)
return <ul>{list}</ul>
}
//这样就可以进行渲染了
2
3
4
5
6
但是在 react 中每一这数组
中的==子元素== 需要提供一个==唯一的 KEY== 这个唯一的 KEY 最好使用==数据库中的 ID==
这样的主要原因 : 因为 react 在重新渲染的时候需要进行 diff 算法找出哪个元素进行变动了,在数组中无法找出具体哪个元素有变动了,所以需要吧 List 内的元素进行 Key 编号进行区分。
对于重复渲染的组件使用 key 可以比较元素是否需要进行渲染。
官方不推荐使用用 index 来当最 KEY 因为数组的 index 可能会改变
key 只在数组上下文中有意义
function Users(props){
return (
<ul>
{
props.Users.map(item=>(<User key={props.id} user={item}/>))
//key 只有在这里使用才有意
}
<ul>
)
}
function User(props){
return (<li>
{props.user}
</li>)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
key 只在相邻的兄弟元素有意义,在不同的区域可以使用相同的 key,key 只有标识意义而没有参数意义。
5、react 的事件
5.0.1 在 react 中事件绑定规则
在 react 中有一套自己的绑定事件,这写绑定事件遵循小驼峰命名。具体遵循{}规则,
<button onClick={function(){alert(111)}}></button>
<button onClick={this.hahaha}></button>
function hahaha(){alert(111)}
2
3
5.0.2 事件绑定传参
上面的方法有一点问题使不能传递参数 这个时候需要这样写
<button onclick={function(){alert(111)}}></button>
<button onclick={()=> this.hahaha(参数)}></button> #这样的处理使得this的指向不会处问题
hahaha=(参数)=>{alert(参数)}
2
3
5.0.3 阻止默认事件
需要使用 e.preventDefault()来阻止 而不能使用 return false
6、react 的状态管理
如果在 react 中这个属性不影响界面的渲染那么这个属性不要放在 state 中 直接挂载在 this 上
6.0.1 修改 state 状态
- 推荐使用**this.setState({key:值})**来修改数据,遵守单项数据流规定。只有通过 setState 才能触发组件的重新渲染,每次只会更新变化的地方。如果直接修改 state 不会触发组件更新。
- 在 setState 中只会把对应的 state 状态更新而不会覆盖其他的 state 状态。
- setState 这个方法的执行使异步的,这个要注意,如果在 setstate 调用完成之后又想立即拿到 state 的值 ,需要使用方法 this.setState({},function(){内部拿}),或者通过 async 和 await
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 1
}
}
addCount = () => {
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
//上面count增加了两次但是每次都只加1
//这是因为setState是异步的,每次获取的state都是上一次的值。
}
//使用这种方法进行
addCount = () => {
this.setState((proState//前面一个状态, props//这次的传递进来的参数) => {
return { count: proState.count + 1 }//写法1
})
this.setState((proState, props) => ({count:proState.count + 1}))//写法2
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.addCount}> 增加</button>
</div>
)
}
}
export default App
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
6.0.2 state 的批量更新
上面由于 setState 方法是异步的 所以 其实每次的 setState(方法)这个方法 其实相当于多个 setState 嵌套在内部,只不过是 react 底层把这些方法 放到了异步事件队列里面。把他变成了一个同步类似的操作,但是本质还是异步事件。
当 setState 的多次调用,更改多个独立的变量的时候。setstate 会把多次操作合并一起来操作,提高效率。
相当于使用 Object.assign({},this.state,newState)类似于这个
6.0.3 文本框的 state 改变
当为文本框绑定 value 值之后
要么同时提供一个 readonly 属性 表示这个元素只读 不能被修改
要不然提供一个 onChange 事件 表示可以被修改 要自己定义修改的逻辑
<input type="text" value={this.state.msg} onChange={() => this.changeValue('haha')} />
//这样在数据重新添加后表单框可以继续操作
2
6.0.4 如何返回状态值
每当 state 状态改变 页面上 state 绑定的元素就会自动改变,这样就是单项数据流,而 react 无法把界面的值在同步会 state 中,需要手动监听文本框的 Onchange 事件 拿到在 onChange 事件中文本框的值,调用 this.setState() 来绑定回去。
//在onChange 事件中获取文本狂顶额值由里昂中法安安
1. 通过事件参数E来获取
<input type="text" value={this.state.msg} onChange={(e) => this.changeValue(e)} />
changeValue(e){
console.log(e.target)//代表触发事件的源
}
2 通过ref 属性
<input type="text" value={this.state.msg} onChange={() => this.changeValue()} ref=“txt”/>
changeValue(){
this.ref.tet.value// 通过 this.ref可以获取到原DOM 节点
}
3 通过 document.querySelector().value //等方式来拿
然后通过 setState({})来设置
2
3
4
5
6
7
8
9
10
11
12
13
6.0.5 Form 表单
受控组件 input textarea select 等等保持自己的状态,根据用户输入更新,用户在输入框中输入的值传递给 react 的 state,而通过 stae 控制表单的呈现。把 state 的值作为表单对象值的唯一来源。这样就是受控组件。
input
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
userName: '',
password: ''
}
}
handleSubmit = () => {
console.log(this.state)
}
handelChange = e => {
this.setState({
[e.target.name]: e.target.value
})
console.log('e.target.value :', e.target.value)
}
render() {
return (
<div>
<form action="">
<input type="text" name="userName" value={this.state.userName} onChange={this.handelChange} />
//或者是传递过来一个 onChange={e => this.handleChange('username', e)}
<input type="password" name="password" value={this.state.password} onChange={this.handelChange} />
<input Onsubmit={this.handleSubmit} />
</form>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
select
handelChange = e => {
this.setState({
[e.target.name]: e.target.value
})
console.log('e.target.value :', e.target.value)
}
<select name="fruit" value={this.state.fruit} onChange={this.handelChange}>
<option value="grapeFruit">葡萄</option>
<option value="lime">lime</option>
<option value="coconut">coconut</option>
<option value="mango">mango</option>
</select>
//多选
<select name="fruit"
multiple={true} //可以多选
value={['grapeFruit','lime']} //默认值
onChange={this.handelChange} //选择更改触发的方法
>
<option value="grapeFruit">葡萄</option>
<option value="lime">lime</option>
<option value="coconut">coconut</option>
<option value="mango">mango</option>
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
6.0.6 非受控组件
value 的值于 state 内部的值不保持关联或者统一这种是非受控组件
表单输入框的其他 handlechange 方式 这就是非受控组件的写法
在通过ref 获取值的时候只需要使用
this.state.username 就可以了
而不需要在使用this.refs.
class App extends React.Component {
constructor(props) {
super(props)
}
handleSubmit=()=>{
console.log(this.state)
//1 通过 this.refs.username.value 来获取这个值 但是这种方式已经被抛弃
//2 通过 this.username 就可以拿到了
}
render() {
return (
<div>
<form action="" onSubmit={this.handleSubmit}>
<input type="text" ref=“username” />//1
<br />
//ref可以接受值 也可以接受一个函数,如果接受的是一个函数,那么这个函数会在虚拟DOM 转换成真实DOM 并插入到这个页面的瞬间立刻调用。参数就是真实DOM
<input type="text" ref={input => (this.userName = input)} /> //2
<br />
<input type="text" ref={input => (this.password = input)} />
<br />
<input type=“submit” />
</form>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
受控组件和非受控组件各自的好处和应用场景,如果只是表单 推荐使用受控组件,这样用户的输入收到控制,可以控制值的属性和合法性,
非受控组件和第三方库进行集成,这样就只能使用非受控组件
在渲染的时候如果 value 改成一个 null 那么这个组件会变成一个非受控组件也就是说可以随便改了,
6.0.7 dangerrousSetInnerHTML 富文本编辑器
危险富文本的输入方式
state={
article:<h1>hahahaha<h1> <div>asldjalsjdlaj</div>
}
<div dangerousSetInnerHTML={{__html:this.state.article}}> </div>//两个下划线html
//这样来输入 富文本编辑器中的带标签的渲染
2
3
4
5
6.0.8 {... }扩展传递参数语法
class App extends Component{
constructor(){
super()
}
render(){
return(
<div>
{this.props.students.map(student=>{
return(<p>name:{student.name}</p>
<p>others:{...student}</p>
)
})}
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
7、组件的生命周期
7.0.1 props 值初始化
最开始初始化会调用==static.defaultProps ={}== 接受外面传递进来的参数 ,在这里初始化属性默认值。防止有些 props 属性没传递报错问题
class A extends Component{
static defaultProps={
name:"点击"
}
}
2
3
4
5
在16.8版本中defaultProps 使用方法已经更改
在16.8 版本之后defaultProps 使用方法
class A extends Component{
//TOdo
}
A.defaultProps={
name:'点击'
}
2
3
4
5
6
7.0.2 组件的创建阶段
只在开始的时候运行一次
- constructor 构造函数运行,初始化组件和state
生命周期函数 componentWillMount 组件将要挂载,还没有挂载 此时虚拟 DOM 还没有创建componentWillMount 已经被声明在未来的版本中不支持使用,而使用 getDerivedStateFromProps ,用于将Props 中的实行导出到state 中如果需要的话,虽然react 不推荐这么做。- render,创建虚拟 DOM 但是还没有挂载到页面上。
- 生命周期函数 componentDidMount 已经挂载完成 内存中的虚拟 DOM 和页面 都已经统一了,在这里会发送ajax
7.0.3 组件的运行阶段
state 状态在运行中改变 了 情况下的钩子流程
shouldComponentUpdate() 组件是否需要被更新 会返回true 或者false 此时组件并未更新 但是state 和props 都是最新的 返会false 界面就不会改变,这个时候 数据已经改变了 数据是最新的 ,界面还是旧的 发回true 进入 //componentWillUpdate()//这个时候界面也还没有更新,但是数据时新的 但是已经知道下一步要进行更新了,在16.8 以后官方声明不在支持该函数 已被取消 接下来 render() 然后触发componentDidUpdate() //页面重新渲染完毕
1
2
3
4
5属性 props 改变 首先生命周期函数该函数在16.8 以后已被 声明不支持使用componentWillReceiveProps
只要这个被触发旧说明父组件为子组件传递了新的属性值,然后 触发 state 改变的全套流程。在16.8 以后有一个新函数 getSnapshotBeforeUpdate() 这个生命周期函数在虚拟DOM 渲染之后 DOM 挂载之前,会返回一个快照,并发送给 componentDidUpdate()的第三个参数,这样可以在两个周期中对DOM 进行比较处理 ,getSnapshotBeforeUpdate的触发事件是在更新界面DOM的前一瞬间
7.0.4 组件的销毁阶段
会执行 卸载(Unmount)然后触发 结束前的生命周期事件 componentWillUnmount 还没有卸载(数据和函数还是可以使用的)
import React from 'react'
import ReactDom from 'react-dom'
class World extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
<h1> 计数器</h1>
<hr />
<button onClick={() => this.countAdd('1')}> +1</button>
<h3>当前数量是:{this.props.number}</h3>
<button onClick={() => this.countdelete('1')}>-1</button>
<button onClick={this.destroy}>销毁组件</button>
</div>
)
}
componentWillUnmount(){
//组件将要被卸载
window.clearInterval(this.timer)//第二种(2)
}
componentDidUnmount(){//在16.4 以后已经被 舍弃
//组件已经被卸载完成
}
componentWillMount(){
//把timer挂载到this上
this.timer = setInterval(this.countAdd,1000)
}
countAdd=()=> {}
countdelete=()=> {}
destroy=()=>{
//销毁组件的方法 如果不销毁timer 会发生setInterval 还存在this却被删除的情况,所以在销毁组件之前,需要把定时器清除掉,可以在这个方法内清除 也可以在组件卸载的钩子函数中执行
window.clearInterval(this.timer)//第一种(1)
ReactDom.componentWillUnmountAtNode(document.querySelector('root'))
}
}
export default World
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
在组件的销毁阶段主要是回受定时器或者其他页面的费时资源,减小内存消耗,防止内存泄漏。
7.0.5 props 传递的校验和初始值设定
16.4 版本之前
react 的类型种有专门的类型校验的包,内部有专门用于类型校验的给中属性,对于类型比较负载的使用 function 来进行类型校验,
通过 throw new Errror 来进行类型提醒
import React from 'react'
import PropTypes from 'prop-types'
// 类型校验的包
//关于类型校验 react 由一个专门的第三方包 叫做proptypes 在v-15版本以前没有单独抽离 v-15 以后需要单独安装
class World extends React.Component {
constructor(props) {
super(props)
//在组件封装的额时候 由一些参数是必须的 哪怕用户没有传递一些启动参数 这个时候组件内部 计量给自己传递默认值
this.state = {}
}
static defaultProps = {
//如果外界么有传递props设置默认值
number: 0
}
static propTypes = {
//创建一个静态的props对象 在这个对象中可以把外界传递过来的属性做类型校验
number: PropTypes.number.isRequird,
gender:PropTypes.oneOf(['男','女']),//枚举
name:PropTypes.String.isRequird,//string
hobby:PropTypes.array.isRequird//数组
position:PropTypes.shape({//验证对象类型 内部校验 对对象内部的属性要求
x:PropTypes.number.isRequird,
y:PropTypes.number.isRequird
}),
//对于属性校验比较复杂的
age:function(props,propName,componentName){//接受三个参数 1 参数对象 2 参数名称,3 组件名称
if(props[propName]<0||props[propName]>120){
throw new Error('年龄超标')
}
}
}
render() {
return (
<div>
<h1> 计数器</h1>
<hr />
<button onClick={() => this.countAdd('1')}> +1</button>
<h3>当前数量是:{this.props.number}</h3>
<button onClick={() => this.countdelete('1')}>-1</button>
</div>
)
}
countAdd() {}
countdelete() {}
}
export default World
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
16.4 版本之后
import React from 'react'
import PropTypes from 'prop-types'
class A extends Component{
}
A.propTypes={
num:PropTypes.number
}
2
3
4
5
6
7
8
componentWillmount 钩子函数
7.0.6 16.4版本之后已移除这个钩子运行在 render 之前,在组件即将挂载在页面上之前执行,在这个组件里面没有虚拟 DOM 有最新的数据。 这个函数等同于 vue 中的 created 函数
componentWillMount() {
//在这个钩子函数 不能操作DOm 元素 但是在这里可以获取到 state 和props 数据
console.log('this.state.number', this.props.number)
this.countAdd()
//组件内部的实例方法 自组件周期开始旧可以进行调用
}
2
3
4
5
6
7.0.7 render 函数
render() {
document.querySelecter('.aaa) //null 在这里虚拟DOM 还没有创建好所以是空的
return (
<div>
<h1> 计数器</h1>
<hr />
<button onClick={() => this.countAdd('1')}> +1</button>
<h3>当前数量是:{this.props.number}</h3>
<button onClick={() => this.countdelete('1')}>-1</button>
</div>
)
在这里虚拟DOM 已经创建好了 但是还没有挂载在root 上所以还是获取不到
}
2
3
4
5
6
7
8
9
10
11
12
13
7.0.8 componentDidMount()钩子函数
当组件挂载完毕之后会进入这个生命周期函数,只要进入这个生命周期函数,就说明页面上已经可以看到页面组件了。在这个钩子中可以操作 DOM 元素,想要操作 DOM 元素,最早在这个钩子函数。相当于 Vue 的 mounted 函数,在这里可以给原生 DOM 绑定事件
因为这个生命周期函数只会被执行一次,所有吧 ajax 请求放在这个周期内最合适,
因为 componentWillMount 在 react-native 和服务器同构的时候会右为问题所以不使用 componentWillMount
componentDidMount() {
console.log('document.querySelector()', document.querySelector('h1'))
}
2
3
7.0.9shouldComponentUpdate(nextProps,nextState) 钩子函数
来判断组件是否需要更新,这个函数一定要有 return 一个 boolean 值 true 或者 false
shouldComponentUpdate() {
return false //如果返回False state状态会被修改 也就是实例方法会触发 但是页面不会重新渲染。
}
shouldComponentUpdate(nextProps,nextState) {
// return this.state.count % 2?true:false //注意不能这么写 这里面拿到的是上一次的值
return nextState.count
}
2
3
4
5
6
7
8
componentWillUpdate(nextProps,nextState)钩子函数
7.1.0 16.4版本以后以移除在进入这个生命周期函数的时候,组将还没有更新 将要更新。
注意:在这个组件内部获取到的 DOM 都是界面上原来的旧的,应该慎重操作,因为你可能操作的是旧的 DOM
在这这钩子函数之后的 render 也就是在运行阶段中每当调用 render 函数的时候,内部的 DOM 还是旧的。
7.1.1 componentDidUpdate(prexProps,prevState) 钩子函数
在这个内部可以放心操作 DOM 了 以为你数据和 DOM 已经一致了。
componentWillReceiveProps(nextProps)
7.1.2 16.4版本以后以移除当子组件第一次被渲染在页面上行的时候不会触发这个函数,
只有父组件中通过事件修改了传递到子组件内部的值的时候才会触发这个函数。
注意:这个函数内部的 this。props 获取的属性值是上一次的旧的属性值。如果要获取新的属性值需要通过属性列表中的 nextProps
7.1.3 钩子函数中的参数列表
- 在组件创建阶段和组件的销毁阶段 是没有参数列表属性的
- 在组件的运行阶段==带 will 的== 的内部的 this.state 和 this.props 指向的都是旧的数据,参数列表中的 nextProps 和 nextState 是获取新数据
- 而==DId== 钩子参数列表都是获取前一个阶段的 proState 和 proProps 在组件内部获取==当前==的 this.state 和 this.props
7.1.4 16.4 新增的生命周期函数
getSnapshotBeforeUpdate()
- 这个生命周期函数在虚拟DOM 渲染之后 DOM 挂载之前,会返回一个快照,并发送给 componentDidUpdate()的第三个参数,
- 两个周期中对DOM 进行比较处理 ,
- getSnapshotBeforeUpdate的触发事件是在更新界面DOM的前一瞬间
- 主要应用场景是需要比较生成的虚拟DOM 和 渲染Dom的比较处理
getDerivedStateFromProps ()
当state需要从props初始化时,使用
量不使用,维护俩者状态需要消耗额外资源,增加复杂度
每次组件更新都会触发
典型场景表单获取默认值
props传到到state是有风险的,必须要确定组件的数据是单一数据源,见官方的说明 你可能不需要使用派生 state
8、react 中的函数的 this 指向改变
在 react 中要特别注意 this 的指向问题,需要注意在改变 this 指向的函数,例如定时器中需要对 THIS 指向进行重新绑定
8.0.1 使用箭头函数改变 this 指向
<button style={{ display: 'block', margin: 30 + 'px' }} onClick={this.click1}>
//或者
<button style={{ display: 'block', margin: 30 + 'px' }} onClick={()=>this.click1(参数)}>
//这种方法可能导致子组件的重新渲染,因为每次调用的箭头函数都是新创建的函数
click1 = () => {
this.setState({
msg: 'hahaha'
})
}
2
3
4
5
6
7
8
9
8.0.2 使用 bind 重置 this
<button style={{ display: 'block', margin: 30 + 'px' }} onClick={this.click1.bind(this)}>
click1() {
this.setState({
msg: 'hahaha'
})
}
2
3
4
5
6
当然也可以使用 call 和 apply 改变 但是会立即调用。
传递参数的绑定方法
<button style={{ display: 'block', margin: 30 + 'px' }} onClick={this.click1.bind(this[,...args])}>
click1([...args]) {//传递参数
this.setState({
msg: 'hahaha'
})
}
2
3
4
5
6
8.0.3 在构造函数中绑定 this 并传参
这种方式是官方推荐的,只需要绑定一次,节约效率
在构造器中
constructor(props) {
super(props)
this.state = {
msg: 'aaa'
}
this.click2 = this.click2.bind(this,参数) //bing的返回值是改造函数的拷贝 原函数不变
}
<button style={{ display: 'block', margin: 30 + 'px' }} onClick={this.click2}>
click2(参数) {
console.log('参数', 参数)
console.log('this2', this)
}
2
3
4
5
6
7
8
9
10
11
12
13
9、context 特性(上下文)
9.1 16.8版本 之前的 context 特性
多层组件的嵌套,在后代组件中可以使用 context 特性。
在父组件身上共享一个 context 特性,在后代组件中不需要逐层传递了,可以直接获取这个属性。
在父组件中创建一个实例方法 getChildContext()
import PropTypes from 'prop-types'
getChildContext(){
return {
color:this.state.color
}
}
//使用时互姓校验规定以下传递给子组件的数据类型
static childContextTypes={
color:PropTypes.String
}
#在子组件中
import PropTypes from 'prop-types'
//1 先进行属性校验
static contextTypes ={//接受外部的数据一定要进行数据校验
color:ReactTypes.string//只有在这里进行验证了才说明你要接受使用这个属性
//还可以对方法经行执行验证 也就是夫组件的state 如何在子组件中进行更改
setColor:ReactTypes.func
}
//2 使用
<h5>{this.context.color}</h5>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
9.2 传递方法给子组件 让子组件可以修改父组件的状态
//子组件中
class Conser extends React.Component {
static contextTypes = {
color: PropTypes.string,
setColor: PropTypes.func
//接受父组件的修改颜色的方法
}
render() {
return (
<div style={{ backgroundColor: this.context.color }}>
1111
<button onClick={() => this.context.setColor('red')}>变红</button>
//使用父组件的setCOlor方法
</div>
)
}
}
//子组件中
class Han extends React.Component {
static contextTypes = {
color: PropTypes.string,
setColor: PropTypes.func
}
render() {
return (
<div style={{ backgroundColor: this.context.color }}>
2222222
<button onClick={() => this.context.setColor('black')}>变hei</button>
</div>
)
}
}
//在父组件中
class App extends React.Component {
//定义要传递的属性的类型
static childContextTypes = {
color: PropTypes.string,
setColor: PropTypes.func
}
setColor = color => {
console.log('setColor')
this.setState({
color: color
})
}
constructor(props) {
super(props)
this.state = {
show: true,
color: 'green'
}
}
//要传递的context
getChildContext() {
return { color: this.state.color, setColor: this.setColor }
}
render() {
return (
<div>
{this.state.show ? <Conser /> : <Han />}
<button onClick={this.handleChange}>gabian</button>
</div>
)
}
handleChange = () => {
this.setState({ show: !this.state.show })
}
}
export default App
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
9.3 16.8 版本之后的context使用
在16.8 版本之后context 使用注重组件性 把 context 分为提供者和使用者,通过组件的方式把context属性提供给子组件
import React, { Component, createContext } from 'react' //引入createContext
import ReactDOM from 'react-dom'
const { Provider, Consumer: CountConsumer } = createContext() //解析处提供和使用者
class CountProvider extends Component { //制造提供者组件 而共有属性作为这个组件的属性存在
constructor() {
super()
this.state = {
count: 100
}
}
del = () => {
console.log('del')
this.setState({
count: this.state.count - 1
})
}
add = () => {
console.log('add')
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<Provider //需要引用提供者
value={{//这些式需要被公共引用的属性
count: this.state.count,
del: this.del,
add: this.add
}}
>
{this.props.children}
</Provider>
)
}
}
class Count extends Component {
render() {
return (
<CountConsumer>//使用使用者组件 内部包含一个函数
{arg => {//函数的参数式需要得到的公共参数
console.log(arg)
return (
<>
<Btn handleClick={arg.del} name="-" />//公共参数
{arg.count}
<Btn handleClick={arg.add} name="+" />
</>
)
}}
</CountConsumer>
)
}
}
const Btn = props => {
return <button onClick={props.handleClick}>{props.name}</button>
}
const App = () => {
return (
<CountProvider>//使用自己创造的提供者组件包裹需要公共属性的组件
<Count />
</CountProvider>
)
}
ReactDOM.render(<App />, document.querySelector('#root'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
10、 动画
10.1 className 配合 css 实现动画
10.2 使用了动画过度库'react-transition-group'
10.2.1 内部结构
CSSTransition 会自动在执行动画期间切换三个类名 fade-enter fade-enter-active fade-enter-done
fade-enter 在 show 变成 true 第一时间切入执行
fade-enter-active 第二个瞬间 执行
fade-enter-done 结束的时候执行
同样出场动画与上面类似 fade-exit
const aniDiv=(props)=>{
return (
<div>
<CSSTransition
in={props.show}//执行的触发条件
timeout={1000} //执行时间
classNames=“fade” //这个fade和fade-enter 的前缀fade相同
unmountOnExit //当出场动画结束之后移除对应的DOM 节点
onEntered={(el)=>{//钩子函数 完全进入之后 el是当前动画的动画节点
el.style.color="blue" //对执行动画的节点经行操作
}}
appear={true} //元素第一次出现在DOM 上就会执行动画 这个过长动画的第一帧执行的是fade-appear
>
<h1>
hello world !
</h1>
</CSSTransition>
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
10.2.2 钩子函数
onEntered(完全进入第三针 DONE)onEnter (入场动画执行的第一帧) onEntering(入场动画执行的第二帧) onExit(出去)onExited onExiting
onEntered={(el)=>{//钩子函数 完全进入之后 el是当前动画的动画节点
el.style.color="blue" //对执行动画的节点经行操作
}}
2
3
10.2.3 多个 DOM 元素的动画效果
在 CSSTransition 外部增加一个 TransitionGroup 组件 这个时候 IN 这个属性就不需要使用了
import React from 'react'
import 'bootstrap/dist/css/bootstrap.css'
import { BrowserRouter as Router, Route, Link, Prompt, Redirect, Switch, withRouter, NavLink } from 'react-router-dom'
import './transation.css'
import { TransitionGroup, CSSTransition } from 'react-transition-group'
const AppRouter = () => {
return (
<div>
<Router>
<ul className="nav nav-pills">
<li role="presentation">
<NavLink to="/red">red</NavLink>
</li>
<li role="presentation">
<NavLink to="/green">green</NavLink>
</li>
<li role="presentation">
<NavLink to="/blue">blue</NavLink>
</li>
<li role="presentation">
<NavLink to="/pink">pink</NavLink>
</li>
</ul>
<Route
render={({ location }) => (
<TransitionGroup>
<CSSTransition timeout={3000} classNames="fade" key={location.key}>
<Switch>
<Redirect exact from="/" to="/red" />
<Route path="/red" render={() => <div className="red">red</div>} />
<Route path="/green" render={() => <div className="green">green</div>} />
<Route path="/blue" render={() => <div className="blue">blue</div>} />
<Route path="/pink" render={() => <div className="pink">pink</div>} />
</Switch>
</CSSTransition>
</TransitionGroup>
)}
/>
</Router>
</div>
)
}
export default AppRouter
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
- 在需要经行过度切换的路由上包裹一个 route 不进行匹配这样可以获取{location}对象
- 或者使用 withRoute 高级组件进行包裹也可以
- 使用 TransitionGroup 和 CSSTransition 包裹 route 元素组
- CSSTransition 上要设置 key 才可以使用 key 标识为 location.key
- css 上可设置属性 timeout 动画时间 classNames 动画效果
- 动画过程会触发两个类 .fade-enter 和.fade-enter.fade-enter-active 可以进行自定义动画
11、React 的项目结构
src-
index.js 负责渲染 和 中间件
App.js 负责引用组件 组装 app
components- 内部全是组件
index.js 负责导出所有组件
Header-
index.js 或者 Header.js
index.css 还有其他的文件
Body-
index.js 或者 Body.js
2
3
4
5
6
7
8
9
10