十五、react中有无状态组件间的应用
2、jsx语法中的样式
- 行内样式:
style={ 对象 }
<h1 style={{ color:'red',fontSize:'24px',textAlign:'center' }}>这是列表案例</h1>
也可以抽离出来对象:
const listStyle = { color:'red',fontSize:'26px',textAlign:'center' }
<h1 style={listStyle}>这是列表案例</h1>
甚至抽到另一个`style.js`文件中;
- 样式表之css文件全局使用:(引入正常在标签上用)
- react中在正常html标签上写
className='title'
,就是添加标签属性; - 但在组件标签上写
name='zs'
就是向子组件传数据;
- react中在正常html标签上写
import cssObj from '@/css/CmtItem.css' /这样引入是全局有效,此时cssObj={}
export default function CmtItem(props) {
return <div>
<h1 className='title'>{props.user}</h1> /= 使用css文件中的类名
<p>{props.content}</p>
</div>
}
- css文件私有化(开启模块化,作为引入时对象名的属性来使用)
- 私有化就是让其模块化,哪个组件导入在哪个组件中才能用;
- 但是模块化,只针对css文件中的
类选择器、id选择器有效
,标签选择器还是会全局生效;
module: {
rules: [
{ test: /\.css$/,use: [ 'style-loader', 'css-loader?modules' ] },
]
}/开启模块化
---
import cssObj from '@/css/CmtItem.css' /这样引入是局部有效
/此时cmtItemCss={title: "rXN5vocZCYmokwVvwGP0_"},这串变量值就是最终类名
使用:<h1 className={cssObj.title}>{props.user}</h1>
- css模块化后自定义名称,替换上述一串字符
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[path][name]-[local]-[hash:5]"
}
}
}/= 此时,cssObj={title: "src-css-CmtItem-title-3f30a"}
- css文件模块化后
- 问题:只要是css文件中
类、id选择器都会私有化
,那有的想全局化,有的想私有化怎么办? - 解决:使用
:local(选择器){}、:global(选择器){}
- 问题:只要是css文件中
:global(.test){
background-color: #911;
}/= 这个类就是全局化了,使用方式与全局一样
<h1 className='test'>这是列表案例</h1> == <h1 class="test">这是列表案例</h1>
<h1 className={cssObj.title+' test'}></h1>
== <h1 class="src-css-CmtItem-title-3f30a test">
<h1 className={[cssObj.title,'test'].join(' ')}></h1>
---
:local(){}一般不用
- 第三方css文件的使用
之前js文件导入css文件:
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';/= npm安装这样写,默认去node_modules中找
现在css文件开启模块化了,要像导入js文件一样,接收;
但是因为开启模块化,类名被键值对形式存在了,所以也要通过对象[属性名]的方式使用
<button className={[bootCss.btn,bootCss['btn-primary']].join(' ')}>按钮</button>
对第三方样式库的需求
: 第三方的样式希望像以前一样正常直接写又全局都能用;我们需要改变模块化方式:- 一般第三方样式都是
.css文件
,所以我们不给不给.css文件开启模块化,给scss\less文件开启模块化
- 我们自己的私有样式就写在
scss\less文件中即可
。
- 一般第三方样式都是
不对css文件开启模块化:
import 'bootstrap/dist/css/bootstrap.css'
<button className="btn btn-primary">按钮</button>
3、react中的事件绑定
- 导入jsx文件时,jsx文件的生命周期还没有触发,此时只是单纯的解析了jsx文件,将jsx语法转为
createElement()形式
;当父组件使用子组件标签时才开始子组件的生命周期,此时代码开始执行,创建类实例,执行内部render函数。 - 通过
this.setState({},function(){})
修改state中指定数据是响应的; onClick:绑定点击事件
- 推荐使用箭头函数,来保证函数(同步、异步)中this指向都是点击的元素;
render(){
return <div>
<button onClick={()=>this.myClick2('传参')}>内部按钮</button>
</div>
}
myClick2=(arg)=>{
console.log('内部点击'+ arg);
this.setState({ msg:'setState异步修改' },function(){ console.log('回调函数,修改state是异步,所以修改后的值在这获取'+ this.state.msg)})
}
onChange:绑定文本框输入事件
- 在输入框绑定
value={this.state.msg}
,此时是只读的,需要添加readonly属性
或添加onChange={}
事件设置state中值来响应改变; - 通过事件将输入框的值改变到state中;怎么拿到文本框的值,有两种方法:
事件参数,e.target.value
,通过this.refs.text获取标签元素
- 在输入框绑定
<input type="text" value={this.state.msg} readOnly/> /= 此时文本框不能被选中,只读
绑定输入事件,做双向绑定效果:
<input type="text" value={this.state.msg} onChange={(e)=>this.myInput(e)} ref='text'/>
myInput = (e)=>{
this.setState({msg:e.target.value})
}
myInput = ()=>{
this.setState({msg:this.refs.text.value})
}
- refs被提示弃用,所以修改:
this.textInput = React.createRef() /= 与state同级
<input type="text" value={this.state.msg} onChange={()=>this.myInput()} ref={this.textInput}/>
myInput = ()=>{
this.setState({msg:this.textInput.current.value})
}
十六、react中的组件实例生命周期
三部分:组件创建阶段、组件运行阶段、组件销毁阶段
- 初始化默认props
- 组件创建阶段(只执行一次)
- 初始化:state
componentWillMount
:是组件虚拟DOM将要挂载到页面上时执行;此时虚拟DOM还没创建;render
:渲染函数,此时开始渲染虚拟DOM,当render执行完内存中就有虚拟DOM了,但是还没有挂载到页面;compoentDidMount
:挂载完成函数,这个函数执行就代表虚拟DOM已经完成挂载;
- 组件运行阶段(按需,根据props属性和state状态的改变,有选择地执行0次或多次)
componentWillReceiveProps
:外界传来的props数据改变时触发这个shouldComponentUpdate
:通过props、state改变都能触发触发这个,返回true、falsecomponentWillUpdate
:允许更新后执行,此时组件还没有更新;render
:开始重新渲染内存中的虚拟DOM,当render执行完毕,内存中虚拟DOM与state都是最新的,但页面还是旧的;componentDidUpdate
:组件已经更新到页面;
- 组件销毁阶段(只执行一次)
componentWillUnmount
:关闭页面触发删除组件时,删除前执行,组件还可被使用。
生命周期各阶段介绍
1、 getDefaultProps:初始化props值
- 还可以添加类型校验当外界传来的值不符合内部规定会在控制台提醒:v15版本后校验包需要单独安装:
npm i prop-types -D
;在需要校验的组件中导入并使用; - 校验主要用于开发人员自己用,普通用户也不看控制台,校验库也只在开发模式下其效果。
import React from 'react'
import ReactType from 'prop-types'
...
static defaultProps = {
initCount:5
}
static propTypes={
initCount:ReactType.number
}
2、UNSAFE_componentWillMount:
挂载前钩子(v16.4后改名)
- 在这个函数执行时,组件中props、state和方法已经初始化好了,可以在这个生命周期中使用;
- 此时虚拟DOM没有创建,也没有挂载到页面,所以元素不能获取;
- 等同于
vue中created函数
3、render
:渲染钩子
- 函数执行就是创建虚拟DOM的过程,在这个函数return之前虚拟DOM仍没有创建好;
- 在这个钩子函数中也不能获取DOM元素;
- 与vue中beforeMount不同,后者是生成render函数;
4、componentDidMount:
挂载完成钩子
- 此函数执行说明虚拟DOM已经挂载到页面上;
- 可以获取DOM元素;
- 相当于
vue中Mounted钩子函数
效果;
补充:想修改外界传的值
- 将外界传的值props赋值给state中;
constructor(props){
super();
this.state = {
count:props.initCount;
};
}
组件运行阶段:
5、shouldComponentUpdate:
是否更新钩子函数
- 决定在props、state改变时,是否执行接下来的生命周期,去渲染;
- 函数执行时组件内state、props还没更新那,需要通过
nextState
获取最新的值; - 组件中不写默认为true,但是定义了这个函数,就要自己决定返回值;
true
:正常执行更新生命周期;false
:不进行更新,但页面上不是最新的;- 不管是true、还是false,钩子函数执行完state、props中的值就是被修改的最新值了;
shouldComponentUpdate(nextProps,nextState){
return nextState>1?true:false
/nextState是最新的值,通过this.state取到的是没有修改前的值
}
6、componentWillUpdate:
将要更新前的钩子函数
- 收到
shouldComponentUpdate
是否更新钩子函数的true
后会执行; - 此时内存中虚拟DOM还是旧的,所以获取的DOM节点是旧的;
- state、props仍是旧的,因为上一个钩子返回
true
就执行这个,还没有更新数据; - 下一个
render
钩子执行时就是最新的了;
componentWillUpdate(nextProps,nextState){
/nextState是最新的值,通过this.state取到的是没有修改前的值
}
7、componentWillReceiveProps:
接收props前的钩子函数
- 发生在外界传值改变时,执行时内部props还没改变,也可以通过参数
nextProps
获取最新的值; - 在第一次父子组件创建时,子组件内初始化props,不触发这个钩子;
- 当父组件通过事件修改传给子组件的数据时会触发;
componentWillReceiveProps(nextProps){
/nextProps是最新的值,通过this.props取到的是没有修改前的值
}
8、 componentDidUpdate:
更新完钩子函数
- 此时state、props、虚拟DOM、页面都是一致的最新的;
- 这里想获取旧数据也有参数;
componentDidUpdate(prevProps,prevState){
/preState是更新前的值,通过this.state取到的是最新的值了
}
十七、react中的组件绑定this传参的三种方式
1、利用bind(this,arg1,arg2)
- 在类中定义的普通函数(相当于定义在原型对象上)内部会有this指向问题:
- 在绑定事件时绑定this
- 在构造器中给组件实例定义方法赋值为这个普通函数绑定后的值;
方式一:调用时绑定
<input type="button" value='+1' onClick={this.self.bind(this)}/>
方式二:将绑定后函数赋值到实例的属性上
constructor(props){
super();
this.state = {
count:props.initCount,
msg:'我是state中的msg'
};
this.self = this.self.bind(this)
}
<input type="button" value='+1' onClick={this.self}/>
/相当于调用实例自身的self,之前是调用的原型链上
方式三:
改为箭头函数形式
<input type="button" value='+1' onClick={()=>{this.self()}}/>
/此时函数中this就是实例对象了;定义的普通函数可以是正常形式也可以是箭头函数。
十八、react中的父子组件
- 父组件向子组件传属性、方法都是通过
props
;vue中要想让子组件调用父组件中的方法,需要子组件使用$emit
; - 父组件中方法用箭头函数定义,传给子组件,方法中的this还是指向父组件实例,所以只是让子组件触发父组件的方法。
<CmtBox loadCmts={this.loadCmts}></CmtBox> /=子组件标签
loadCmts=()=>{
let list = JSON.parse(localStorage.getItem('cmts')||'[]');
console.log(this); /=这里this就是父组件实例
this.setState({
CommentList:list
})
}