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)