React-提上日程:react特性、jsx、三大核心配置、事件处理、生命周期(版本)、类式组件(this指向问题)

185 阅读6分钟

React的特点

1.组件化编程 2.react native可以使用react语法进行移动端开发 3.使用虚拟DOM和diff算法,尽量减少真实dom的操作 diff算法index页面渲染错误(节点存在输入类的dom,破坏原有顺序)的场景,使用id才可以规避(与vue一样,被复用的烦恼)

(<ul /* 在组件标签上的注释 */ >
	list.map((item, index)=>{
		<li>姓名:{item.name},年龄:{item.age} <input type="text"></input></li>
	})
<ul>)
    
{/* 此时在头部或中间插入,则input由于被复用,其中的信息留在原来的位置 */}
react解决哪些传统jquery的问题

jquery命令式编程,不考虑性能,在最初如银行项目只考虑表单展示、提交的项目中,运行良好。但随着前端在项目中对显示要求的提升,需要大量的渲染任务,简单的渲染无法满足需求,或者带来的体验很差。前端框架就引入虚拟dom+diff算法,进行数据的缓存,优化渲染性能。

引入react核心库,到使用jsx编写视图,应用react渲染页面
<body>
  <div id="test"></div>
  <!-- react核心库 ./js/react.development.js 可从bootcdn找一个合适版本 -->
  <script type="text/javascript" src="./js/react.development.js"></script>
  <!-- react周边库,用于操作dom -->
  <script type="text/javascript" src="./js/react-dom.development.js"></script>
  <script type="text/javascript" src="./js/babel.min.js"></script>
  <script type="text/babel">
    // 1.创建dom 
    const vDom = <h1>Hello React</h1> /*此处是jsx*/
    ReactDOM.render(vDom, document.getElementById('test'))
  </script>
</body>

JSX(javascript XML)

为什么要用jsx(简洁描述页面结构)

// const vDom = React.createElement(标签名,标签属性,内容)
// const vDom = React.createElement('h1', {id: 'testH1'}, React.createElement('span', {}, '不使用jsx的烦恼'))
jsx的一点规则:
// 圆节点,花脚本(只能是表达式,判断是否表达式:const x = 预表达式 成立,则为表达式);
// 标签名,首字母大写,会作为react组件进行匹配;首字母不大写,会作为html默认标签进行匹配;
// 根标签只能有一个,所有标签必须闭合;
// 样式使用className,由于class是es6的关键字;
// 内联样式 style={{color: 'orange', fontSize: '20px'}}对象的形式;
// 事件操作为onClick小驼峰
const vDom =(
        <h1 className="container-title">
                <span style={{color: 'orange', fontSize: '20px'}}>层次往往</span>
        </h1>
)

jsx详细描述 t.zoukankan.com/bingquan1-p…

React组件

简述模块化、组件化
模块化:一个js文件,拆分业务代码,使代码结构清晰,易维护;
组件化:局部-body标签体、执行script、css样式的集合
组件(首字母必须大写,与jsx语法呼应)\

函数式组件

简单组件(能使用props-参数,不能使用state、refs,由于函数没有this)

function MyComponent(){
        // this为undefine
        return <h1><span style={{color: orange}}>函数式组件内容</span></h1>
}
类式组件-复杂组件(含有状态-常用)

——关注this指向的是否是组件的对象实例,用于this取得组件的数据和方法;在自定义函数的this无法取得组件对象实例时,采用class的this指向形式去实现

class MyComponent extends React.Component{
    constructor(){
        // 解决this指向问题:使用bind把函数的this指定好,作为组件实例的方法
        this.changeWeather = this.changeWeacther.bind(this) // 后者为原型上的该方法
    }
    render(){ // 初始化和每次setState都会执行render方法
        console.log(this) // this是当前的组件实例对象
    return (
        <div onClick={this.changeWeather}>{data.person.name}</div>
    )
    }
    changeWeather(){
        // react中的状态直接修改,无法在页面生效,需借助react的api:setState
        this.setState( {isHot: !isHot} ) // 另外注意setState是合并,而不是替换
        console.log(this)   // 访问组件实例,通过类的实例访问,this取得到组件内容;但通过函数方式放到onClick的回调中,则丢失this
    }
}

===>简化代码

class MyComponent extends React.Component{
  render(){ // 初始化和每次setState都会执行render方法
    console.log(this) // this是当前的组件实例对象
    return (
      <div onClick={this.changeWeather}>{data.person.name}</div>
    )
  },
  state = {isHot: false, wind: '微风'}  // 相当于组件实例对象,覆盖原型链上的属性和方法,简化写法,不用在constructor中反复操作
  changeWeacher = () => {
        console.log(this) // 此时this获得实例对象,
  }
}

tips: 采用class的this指向形式去实现(复杂形式:手动指定this指向;简化形式:采用实例的属性覆盖原型链属性的形式;);另外条件渲染等作为高阶内容,在后续更新;

React组件对Es6类的内化

class Person{
        constructor(name, age){
                this.name = name
                this.age = age
        }
        
        speak(){
                console.log(`my name is ${this.name}, my age is ${this.Image}`)
        }
        state={state1: 'stateValue'} // 注意直接设置对象属性,可覆盖父类或者说原型链上的方法和属性,就省略了constructor的繁琐传值,this实例可以取到

        mySpeak=()=>{
                console.log(`my name is ${this.name}, my age is ${this.Image}`)
        }
}

let person = new Person('gpf', 100)
let z = person.mySpeak // 箭头函数锁定this,this为该对象的实例
z()
let y = person.speak 
y() // this => undefined << 类中默认开启严格模式,this没绑定对象实例,就得到undefined

React.Component 中的三大要素:state、props、refs

state状态机取值 state={state1: 'stateValue'} 挂在组件对象的实例上(组件对象实例的全局共享数据) props组件传值:(用于组件接受外部数据)

let person = {name: 'gpf', age: 100}
<Person {…person}/>

对props进行限制(必要性、类型、默认值)
PropTypes来源于prop-types.js专门用于类型判断,不再React库中

    MyComponent.propTypes = {
    name: PropTypes.string.isRequired        
       speakFunc: PropTypes.func.isRequired
    }
    MyComponent.defaultTypes = {
    name: '默认值'
    }

refs (使用ref获取dom元素) 使用形式:字符串(性能问题),内联函数(常用,参数为当前节点,可指定挂在组件对象实例的位置),React.createRef()创建容器,挂在组件对象实例上

render中组件部分设置ref属性的都被收集到组件实例的refs属性中
render(){
	return (
		<div>
			<input ref="input1" type="text"/>
			<input ref="input2" type="text" onBlur={this.showData2} placeholder="请输入文本"/>
		</div>
	)
}
====> let {input1, input2}  = this.refs 取得该Dom

不要滥用refs,使用组件实例的实例方法可以获取到引发事件的事件参数,通过event.target获取该节点onBlur={this.handleBlur}

非受控组件、受控组件
非受控组件-数据由React管理-setState
受控组件-数据将交由 DOM 节点来处理

//受控组件
...
  render() {
    return (
      <div>
        <input
          type="text"
          value={this.state.name}
          onChange={this.handleChange}
        />
      </div>
    );
  }
...

//非受控组件
...
  render() {
    return (
      <div>
        {/* ref设置为内联函数input获得该元素 */}
        <input type="text" ref={input => this.$myState = input.value} />
        <button onClick={this.handleChange}>Sign up</button>
      </div>
    );
  }
...

高阶函数
1.接收参数为函数-promise、数组方法等;
2.返回一个函数-函数柯里化-接收多个参数最后统一处理;

函数柯里化,设置参数处理

saveFormData(dataType){
        return (event)=>{
                this.setState({[dataType]: event.target.value})
        }
} //函数柯里化在这里的必要性,调用传参'username',再在事件触发时填入事件参数,分两个阶段最后执行

用户名: <input type="text" onChange={this.saveFormData('username')}></input>
	

不用柯里化的实现

saveFormData(dataType, value){
        this.setState({[dataType]: value})
}
密码: <input type="text" onChange={(e)=>{this.saveFormData('username', e.target.value)}}></input>
	

React生命周期

一个opacity透明度持续改变案例,引出组件生命周期

组件将要挂载
    componentWillMount
组件挂载完毕
    componentDidMount(){
            this.timer = setInterval(()=>{
this.setState({opacity: this.state.opacity -= 0.1})
            if(this.state.opacity <= 0) this.state.opacity = 1
}, 200)
    }

组件将要卸载
    componentWillUnmount(){
            clearInterval(this.timer)
    }

组件在容器节点卸载
    handleClick = ()=>{
            clearInterval(this.timer) // 这里也可以清掉
            ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }

生命周期过程(旧版本16的生命周期)
挂载 constructor==>componentWillMount==>render=>componentDidMount
卸载 componentWillUnmount ==> componentDidUnmount
更新数据完整过程componentWillReceiveProps=>shouldComponentUpdate=>componentWillUpdate=>render=>componentDidUpdate 常用过程: 设置setState=>shouldComponentUpdate(){}
不设置默认为true componentWillUpdate=>render
强制设置 forceUpdate=>componentWillUpdate=>render

父子组件数据更新
    父组件render执行之后,触发执行子组件的完整流程
            需要注意componentWillReceiveProps在接收到props改变时才会调用(第一次不执行)
    render(){
            const {carName} = this.state
            return (
                    <div>
                            <div></div>
                            <B carName={carName}></B>
                    </div>
            )
    }

纵向整理

初始化阶段的生命周期
        constructor、componentWillMount、render、componentDidMount
更新阶段
        shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate
销毁阶段(关闭定时器、取消订阅消息)
        componentWillUnmount
        设置卸载ReactDOM.unmountComponentAtNode(document.getElementById('test'))
        与挂载相对ReactDOM.render(组件, document.getElementById('test'))
		

新版本17生命周期(删掉三个,新增两个) componentWillMount、componentWillUpdate、componentWillReceiveProps在新版本需要加上 UNSAFE_ 前缀,后续的18版本会完全去掉,这三个生命周期可能引发异步渲染的问题 新版本新增 getDerivedStateFromProps、getSnapshotBeforeUpdate 特殊点在于 getDerivedStateFromProps 需要返回状态对象 一般return null getDerivedStateFromProps(props){ return props // 即组件的state,任何时候都只取决于props,不常用 } getSnapshotBeforeUpdate(){ 一般用于记录操作内容区域高度 return this.refs.contentDom.scollHeight // 返回一个快照值,记录数据修改之前的状态 } ===》数据更新之后调用,该快照值不须额外变量存储,使流程更加紧凑 componentDidUpdate(prevProps, prevState, snapShotValue){ // 使得数据列表更新之后,dom维持页面不动 this.refs.contentDom.scollTop += this.refs.contentDom.scollHeight - snapShotValue } 比较重要的钩子(render、componentDidMount、componentWillUnmount)