React
一、React - 简介
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框 架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套 东西很好用,就在2013年5月开源了。
二、前端三大框架
框架名 | 出现时间 | 所属 | 一开始特色 |
---|---|---|---|
Angular | 2009年 | 谷歌 | 指令系统、双向数据绑定 |
React | 2013年 | 虚拟DOM、组件化 | |
Vue | 2015年 | 尤玉溪 | 指令系统、双向数据绑定、虚拟DOM、组件化 |
2、react的特点
特点:
- 声明式设计 - React采用声明范式,可以轻松描述应用
- 高效 - React 通过对Dom的模拟(虚拟Dom),最大限度减少与Dom的交互
- 灵活 - React 可以与一直的库或框架很好的配合
- JSX - JSX 是 JavaScript 的语法扩展
- 组件 - 通过React构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中
- 单项响应的数据流 - React 实现了单项响应的数据流,从而减少了重复代码,这也是它为什么比传统的绑定更简单。
3、React 起步
3.1.全局安装 create-react-app
脚手架
$ npm i -g create-react-app
OR
$ npx create-react-app xxxx //xxxx 是项目文件夹的名称
3.2.创建项目文件夹**
$ create-react-app xxxx //xxxx 是项目文件夹的名称
4、React 项目文件夹构成
- react-stack -------------------项目文件夹
- node_modules -------------------模块包
- public -------------------静态资源目录
- src --------------------项目文件
- App.css
- App.js
- App.test.js
- index.js
- logo.svg
- serviceWorker.js
- setupTests.js
- .gitignore
- package.json
- README.MD
5、视图层的开发模式与函数式编程
React 并不是完整的 MVC/MVVM 框架,它专注于提供清晰、简洁的 View(视图)层解决方案。
传统dom更新:
- 真实页面对应一个Dom树。在传统页面的开发模式中,每次需要更新页面时,都需要手动操作Dom来进行更新
虚拟Dom:
- Dom 操作非常昂贵,我们都知道在前端开发中,性能消耗最大的就是DOM操作,而且这部分代码会让整体项目的代码变得难以维护。React 把真实DOM树转换成JavaScript对象树,也就是Virtual DOM
函数式编程好处:
- 代码简洁,开发快速
- 接近自然语言,易于理解
- 更方便的代码管理
- 易于"并发编程“
- 代码的热升级
React把过去不断重复构建UI的过程抽象成了组件,且在给定参数的情况下约定渲染对应的UI界面。React能充分利用很多函数式方法去减少冗余代码。此外,由于它本身就是简单函数,所以易于测试。可以说,函数式编程才是React的精髓。
二、React - JSX语法
1、JSX 简介
JSX 将 HTML 语法直接加入到 JavaScript 代码中,再通过翻译器转换到纯 JavaScript 后由浏览器执行。在实际开发中,JSX 在产品打包阶段都已经编译成纯 JavaScript,不会带来任何副作用,反而会让代码更加直观并易于维护。 编译过程由Babel 的 JSX 编译器实现。
2、JSX 注意事项
- 组件首字母是大写 会被认为是自定义组件,首字母是小写,会被认为是 原生dom 节点
- 组件最外层需要被一个标签包裹,不能有兄弟节点
- return (加上小括号,可以回车)
- 组件可以嵌套
- 函数式写法和class 写法 (无状态组件的编写方式 )
- 注释的写法 {这里面写注释} {//单行} {/多行/}
- 样式
- class ==> className , for ==> htmlFor(label)
- 行内样式(facebook 推荐),注意font-size 的写法
- 事件
- 箭头函数
- bind改变this指向
- ref
- 给标签设置ref=“username”
- 通过这个获取this.refs.username ,ref可以获取到应用的真实dom
- 给组件设置ref=“username”
- 通过这个获取this.refs.username ,ref可以获取到应用的真实组件对象
- 给标签设置ref=“username”
三、React - 组件
1、函数定义组件
function Title() {
return <h1>Hello</h1>;
}
or
const Title = ()=> <h1>Hello</h1>;
2、类定义组件
class Child2 extends Component {
render() {
return <p>我是你家老二啊</p>
}
}
不支持类写法, 函数式写法, React.creatClass
es6普及, class(状态,属性,生命周期) ,函数式(不支持状态、生命周期,支持属性),
16.3 ==> class 生命周期写法升级
16.8 之后, 函数式组件=>支持状态,“生命周期", React Hooks ( Vue3.0 支持)
3、样式
import React, { Component } from 'react'
import './css/index.css' // webpack => css-loader style-loader
export default class App extends Component {
render() {
let myname ="kerwin"
// style改造对象写法
let styleobj = {
background:"red",
fontSize: "30px"
}
return (
<div>
<div style={styleobj}>11111-{10 + 20}</div>
<div style={{background:"yellow"}}>22222-{10 > 20 ? 'aaa' : 'bbb'}</div>
<div class="mybox">{myname + 'myname'}</div>
</div>
)
}
}
16之前 className, 不能class
16之后 className class(警告)
4、定义状态
import React, { Component } from 'react'
export default class App extends Component {
//第一种写法:
// constructor() {
// super()
// // react定义状态的方式
// this.state = {
// myname: "kerwin",
// myage: 100
// }
// this.a=100
// }
a= 100 //不是状态,
//第二种写法:
state = {
myname: "kerwin",
myage: 100
}//状态
render() {
return (
<div>
<div>hello -- {this.state.myname}--{this.state.myage}</div>
<button onClick={this.handleClick}>click</button>
</div>
)
}
handleClick = () => {
// this.state.myname = "xiaoming" //不能直接修改
this.setState({
myname: "tiechui",
myage:18
})//用setState 间接修改
// 虚拟dom创建 ==>对比老的虚拟节点 =>patch=>更新真实dom.
}
}
5、修改state
state不可以直接修改
this.setState({
count: this.state.count + 1
})
- setState 并不保证是同步的 batchUpdate 批处理
- 如果 setState 外部没有被异步处理程序包围,那么 setState 就是异步的
handleClick1 = ()=>{
// setState 状态更新 是同步还是异步???
this.setState({
mytext:"22222222"
},()=>{
console.log("该回调函数,会等待状态更新完, 并且dom更新完,才会被调用",this.state.mytext)
}) // 异步,创建虚拟dom,diff算法对比老的虚拟dom节点,patch ,最小代价更新真实dom
console.log(this.state.mytext)
}
or
<button
onClick={() => {
setTimeout(() => {
this.setState({
count: this.state.count + 10
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 15
})
}, 0)
}}
>
加1
</button>
// 异步,所有执行语句累加
- 如果 setState 外部被异步处理程序包围,那么 setState 就是同步的
handleAdd = ()=>{
this.setState({
count:this.state.count +1
})
this.setState({
count:this.state.count +1
})
this.setState({
count:this.state.count +10
})
//合并一个操作,只做一次虚拟dom 创建,只做一次diff对比
}// 执行最后一个
6、列表循环
{
this.state.list.map((item,index)=>
<li key={item}>
{item}--{index}
<button onClick={()=>{
this.handleDelClick(index)
}}>del</button>
</li>
)
}
7、条件渲染
{
this.state.list.length <= 0 ?
(<li>暂无代办事项</li>)
:
(
this.state.list.map((item, index) => (
<li key={index}>
{item}---{index}
<button onClick={() => this.delHandler(index)}>Del1</button>
</li>
)
)
}
8、组件数据挂载的方式
1.属性(默认属性和属性验证)
-
在组件上通过key=value 写属性,子组件通过this.props获取属性,这样组件的可复用性提高了
-
注意在传参数时候,如果写成isShow="true"那么这是一个字符串,如果写成isShow={true}这个是布尔值
-
默认属性/属性验证:
子组件中
//引入验证模块 import propTypes from 'prop-types' //属性验证: static propTypes = { mytext: propTypes.string, myshow: propTypes.bool } //默认属性: static defaultProps = { mytext: 'Home', myshow: true }
9、组件通信
1.父组件------> 子组件
- 父组件在子组件上设置自定义属性传值,设定自定义事件,
- 子组件通过 this.props.xxxx接收 属性的传值
2.子组件------> 父组件
- 子组件通过 this.props.onEvent(xxx) 通知父组件
- 父组件执行 onEvent 事件处理函数
3.ref 通信
- 子组件上加 ref=“mychild” 属性
- 子组件中设定事件处理函数.handler1
- 父组件用过 this.refs.mychild.handler1(xxxx)
- 子组件执行 setState 修改自身的state状态
4.发布订阅模式
-
先在组件全局定义一个对象 bus
const bus = { list:[], //订阅者回调集合 //订阅方法 subscribe(callback){ this.list.push(callback) }, //发布方法 dispatch(){ this.list.forEach((back)=>{ back() }) } }
-
订阅者,在componentDidMount 声明周期的时候 订阅方法调用
componentDidMount(){ console.log("dom渲染完的生命周期-订阅作用,axios" bus.subscribe((data)=>{ console.log("app组件中定义的回调函数",data) this.setState({ isCreated:!this.state.isCreated }) }) }//订阅,把回调传给bus,放入list
-
发布者,事件触发后,调用 bus.dispatch()
onClick={ ()=>{ bus.dispatch() } }
5.content 状态树 跨级通信
-
全局定义 GlobalContext
import React from'react' const GlobalContext = React.createContext()
-
根组件引入GlobalContext,并使用GlobalContext.Provider(辐射全局通信环境)
export default class App extends Component { state = { isCreated: true, service: "打电话-付费", }; render() { //多次被执行 return ( <GlobalContext.Provider value={{ call: this.state.service, sms: "短信服务", changeCall: (data) => { console.log("改变套餐"); this.setState({ service: "打电话-免费" + data, }); }, changeCreated: () => { this.setState({ isCreated: !this.state.isCreated, }); }, }} > <div style={{ width: "510px", height: "500px", border: "1px solid black", display: "flex", }} > {this.state.isCreated ? <Sidebar /> : null} <Content></Content> </div> </GlobalContext.Provider> ); } }
-
任意组件引入GlobalContext并调用context,使用GlobalContext.Consumer(用户者)
class Header extends Component { render() { return ( <GlobalContext.Consumer> {(context) => ( <div style={{ height: "100px", border: "1px solid #14c145" }}> <button onClick={() => { //发布 // context.call="11111111111" context.changeCall("111111"); context.changeCreated(); }} > 切换 </button> {context.sms} </div> )} </GlobalContext.Consumer> ); } }
四、 React - 生命周期
1.初始化阶段
componentWillMount :render之前最后一次修改状态的机会
render : 只能访问this.props和 this.state,不允许修改状态和Dom 输出
componentDidMount :成功render并渲染完成真实Dom之后触发,可以修改DOM
2.运行中阶段
componentWillReceiveProps :父组件修改属性触发
shouldComponentUpdate :返回false会阻止render调用
componentWillUpdate :不能修改属性和状态
render : 只能访问this.props和 this.state,不允许修改状态和Dom 输出
componentDidUpdate : 可以修改DOM
3.销毁阶段
componentWillUnmount :在删除这个组件前进行清理操作
问题:
- componentWillMount ,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件, 将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
- componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求。
- componetWillupdate, 更新前记录 DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长, 会导致 状态不太信。
解决:
-
getDerivedStateFromProps
第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,返回一个对象作为新的state,返回null则说明不需要在这里更新statestatic getDerivedStateFromProps(nextProps){ if(nextProps.value!==undefined){ return{ current:nextProps.value } } return null }
-
getSnapshotBeforeUpdate
取代了 componetWillUpdate ,触发时间为update发生的时候,在render之后dom渲染之前返回一个值,作为componentDidUpdate的第三个参数
4.react中性能优化的方案
shouldComponentUpdate
控制组件自身或者子组件是否需要更新,尤其在子组件非常多的情况下, 需要进行优化PureComponent
1.PureComponent会帮你 比较新props 跟 旧的props, 新的state和老的state(值相等,或者对象含有相同的属性、且属性值相等 ),决定shouldcomponentUpdate 返回true 或者false, 从而决定要不要呼叫 render function。
2.注意:如果你的 state 或 props 『永远都会变』,那 PureComponent 并不会比较快,因为 shallowEqual 也需要花时间。