前方高能!!!存在许多代码
React的特点
- 采用组件化模式、声明式编码,提高开发效率及组件复用率
- 使用虚拟DOM+优秀的DIffing算法,减少与真实DOM交互(和ajax原理相似)
- 在React Native中可以使用React语法进行移动端开发
虚拟DOM初体验
<div id="test"></div>
<script type="text/bable"> //声明jsx
const VDOM =<h1>Hello,React</h1> //此处不要写引号,因为不是字符串
ReactDOM.render(VDOM,document.getElementById('test') //渲染虚拟DOM
</script>
// jsx语法糖来减少写某些js繁琐的语法
复制代码
关于虚拟DOM
- 本质是Object类型的对象
- 虚拟DOM属性较少,因为虚拟DOM是React内部在用
- 虚拟DOM最终会被React转换成真实DOM
jsx语法规则
- 定义虚拟DOM,不要写引号
- 标签中混入JS表达式要用{}
- 样式的类名指定不要用class 要用 className=””
- 内联样式,要用style={{key:value}}的形式去写
- 虚拟DOM只有一个根标签
- 标签必须闭合 如
<input type="text" />
- 标签首字母若为小写则转换成同名html标签,大写转换成对应的组件
const data =["1","2","3"];
const VDOM = (
<ul>
{ //{}里面只能放表达式
//如果渲染的是一个列表,每一行必须要有一个key
data.map((item,index)=>{
return <li key = {index}>{item}</li>
})
}
</ul>
)
// 2.渲染,如果有多个渲染同一个容器,后面的会将前面的覆盖掉
ReactDOM.render(VDOM,document.getElementById("test"));
复制代码
JS表达式:返回一个值,可以放在任何一个需要值的地方 a a+b demo(a) arr.map() function text(){}
JS语句:if(){} for(){} while(){} swith(){} 不会返回一个值
模块和组件及三大属性
作用:复用js,提高效率
类组件
class Mycomponent extends React.Component{
render(){
//此处内部自动创建组件实例对象
return <h1>测试下</h1>
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById('test'));
复制代码
三大属性
State
class Weather extends React.Component{
constructor(props){
//constructor调用1次
super(props);
this.state={ishot:false};
this.changeWeather=this.changeWeather.bind(this);
}
render(){
//render调用1+n次
return <h1 onClick={this.changeWeather}>今天天气{this.state.ishot?'炎热':'凉爽'}</h1>;}
changeWeather(){
//render用n次
console.log(this.state.ishot);
const isHot=this.state.ishot;
//不能直接修改state的值,需要用内部的API setState
this.setState({ishot:!isHot});
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'));
复制代码
通过实例属性绑定和箭头函数简化上述代码
class Weather extends React.Component{
state={ishot:false};
render(){
return <h1 onClick={this.changeWeather}>今天天气{this.state.ishot?'炎热':'凉爽'}</h1>;}
changeWeather=()=>{
const isHot=this.state.ishot;
this.setState({ishot:!isHot});
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'));
复制代码
props
类式组件的使用
class Person extends React.Component{
//对标签属性进行类型,必要性的限制
static propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.num,
speak:PropTypes.func
}
//对标签属性进行默认值限定
static defaultProps={
name:"可能是男吧",
age:18
}
//props只读不允许修改
render(){
return (
<ul>
<li>{this.props.name}</li>
<li>{this.props.age}</li>
<li>{this.props.sex}</li>
</ul>
)
}
}
function speak(){
}
//在js中可以使用{...p}浅拷贝复制一个对象
//const p = {name:"张三",age:"18",sex:"女"} {14}就代表的是数值
//ReactDOM.render(<Person {...p}/>,document.getElementById("test"));
ReactDOM.render(<Person name="Tom" age="55" sex="男" speak={speak}/>,document.getElementById('test'));
ReactDOM.render(<Person age="35" sex="女"/>,document.getElementById('test1'));
ReactDOM.render(<Person name="Tim" age="64" sex="男"/>,document.getElementById('test2'));
复制代码
函数式组件的使用
function Person(props){
return (
<ul>
<li>{props.name}</li>
<li>{props.age}</li>
<li>{props.sex}</li>
</ul>
)
}
//只能类外对props的传入进行限制
Person.propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.num,
speak:PropTypes.func
}
复制代码
ref
class Demo extends React.Component{
showinput1=()=>{
const {input1}=this.refs;
alert(input1.value);
}
showinput2=()=>{
const {input2}=this.refs;
alert(input2.value);
}
render(){
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮显示数据"/>
<button onClick={this.showinput1}>点我显示左边输入框的数据</button>
<input onBlur={this.showinput2} ref="input2" type="text" placeholder="失去焦点显示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'));
复制代码
ref三种使用方式
//字符串方式,不推荐,效率较低
<input ref="input1" type="text" placeholder="点击按钮显示数据"/>
//回调函数方式
<input ref={c=>this.input1=c} type="text" placeholder="点击按钮显示数据"/>
//调用createRef此API
Myref=React.createRef();
showinput1=()=>{
alert(this.Myref.current.value)
}
render(){
return (
<div>
<input ref={this.Myref} type="text" placeholder="点击按钮显示数据"/>
<button onClick={this.showinput1}>点我显示左边输入框的数据</button>
</div>
)
}
复制代码
受控式组件使用
//优化版
class Demo extends React.Component{
saveForm=(dataType)=>{
//高阶函数,及函数柯里化来实现数据统一处理
return (event)=>{
this.setState({[dataType]:event.target.value})
//return的值作为回调函数传给onChange
}
}
state={
username:'',
password:''
}
handleSumbit=(event)=>{
event.preventDefault();
const{username,password}=this.state;
alert(username)
}
render()
{
return (
<form action="http://www.baidu.com" onSubmit={this.handleSumbit}>
用户名:<input onChange={this.saveForm('username')} type="text" name="username"/>
密码:<input onChange={this.saveForm('password')} type="password" name="password"/>
<button>提交</button>
</form>
)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'));
复制代码
React生命周期
旧生命周期
新生命周期
重要的三个hook
- render:初始化渲染或更新渲染调用
- componentDidMount:开启监听,发送ajax请求
- componentWillUnmount:做一些收尾工作,如:清理定时器
key的选取和diff算法
开发如何选择key?
最好使用每一条数据的唯一标识作为key 比如id,手机号,身份证号
如果确定只是简单的展示数据,用Index也是可以的
diff算法
Diff算法其实就是react生成的新虚拟DOM和以前的旧虚拟DOM的比较规则:
- 如果旧的虚拟DOM中找到了与新虚拟DOM相同的key并且如果内容没有变化,就直接只用之前旧的真实DOM,如果内容发生了变化,就生成新的真实DOM
- 如果旧的虚拟DOM中没有找到了与新虚拟DOM相同的key,根据数据创建新的真实的DOM,随后渲染到页面上
使用PubSub实现消息订阅和发布
兄弟组件间传统中是通过父组件来实现通信,而现在可以通过PubSub来实现消息订阅和发布
使用方法
import PubSub from 'pubsub-js' //引入PubSub
PubSub.publish('MY TOPIC', 'hello world!'); //消息发布
var token = PubSub.subscribe('MY TOPIC', mySubscriber); //消息订阅
//mySubscriber是个回调函数 msg是消息描述 data是传输的数据
var mySubscriber = function (msg, data) {
console.log( msg, data );
};
PubSub.unsubscribe(token);//订阅取消
复制代码
Promise补充
Promise Api
- Promise.all(promises) —— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成 Promise.all 的 error,所有其他 promise 的结果都会被忽略。
- Promise.allSettled(promises)(ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:
- Promise.race(promises) —— 等待第一个 settle 的 promise,并将其 result/error 作为结果。
- Promise.resolve(value) —— 使用给定 value 创建一个 resolved 的 promise。
- Promise.reject(error) —— 使用给定 error 创建一个 rejected 的 promise
中断Promise链的方法
const p=new Promise((resolve,reject)=>{
resolve('ok');
});
p.then(value=>{
console.log(111);
return new Promise(()=>{}); //返回一个pending对象的Promise!
//否则会返回一个Promise对象 状态由返回值决定
}).then(value=>{
console.log(222);
}).catch(reason=>{
console.warn(reason);
})
复制代码
使用es7中await/async优化网络请求发送
try{
let response =await fetch(url);
let data=await response.json();
console.log(data);
}catch(e){
console.log("Oops, error",e);
}
//外面需要加个 async function
复制代码
路由组件
头文件引入
import {Link,Route} from 'react-router-dom'
将APP组件包括起来
app.jsx
<div className="list-group">
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>//设置路由跳转
<div className="panel-body">
{/* 注册路由,也就是写对应的关系 */}
<Route path="/about"component={About}/>
<Route path="/home"component={Home}/>
</div>
index.jsx:
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root'))
复制代码
路由组件和一般组件区别
1.写法不一样
一般组件:< Demo>
路由组件:< Route path=”/demo” component ={Demo}/>
2.存放的位置一般不同
一般组件:components
路由组件:pages
3.接收的内容【props】
一般组件:写组件标签的时候传递什么,就能收到什么
路由组件:接收到三个固定的属性【history,location,match】
向路由组件传递参数
1.params参数
路由链接(携带参数)
<Link to{'/home/message/detail/${id}/${title}'}>详情 </Link>
注册路由(声明接受):
<Route path="/home/message/detail/:id/:title" component={Detail} />
接受参数:
this.props.match.params
2.search参数
路由链接(携带参数)
<Link to{'/home/message/detail/?id=${id}/&title=${title}'}>详情 </Link>
注册路由(无需声明,正常注册即可):
<Route path="/home/message/detail/" component={Detail} />
接受参数:
this.props.location.search //获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
路由链接(携带参数)
<Link to{{pathname:'/home/message/detail',state:{name:'tom',age:18}}}'}>详情 </Link>
注册路由(无需声明,正常注册即可):
<Route path="/home/message/detail/" component={Detail} />
接受参数:
this.props.location.state
BrowserRouter与HashRouter的区别
1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!
4.备注:HashRouter可以用于解决一些路径错误相关的问题。
复制代码
react-redux
如何优化react-redux
- 容器组件和UI组件整合在一个文件
- 无需自己给容器组件传递store,给包裹一个即可。
- 使用了react-redux后因为使用了connect会自动更新state
4.mapDispatchToProps可以直接传递一个对象,会默认dispatch传递的action
5.1定义好UI组件后不用暴露
5.2接下来引入connect生成容器组件,并暴露,
export defalut connect(
state=>({key:value})
{key:xxxxxAction}
)(UI组件)
复制代码
5.3 在UI组件中通过this.props.xxxxx读取和操作状态
5.4 多个组件之间从redux中读取数据需要先将两个组件使用combineReducers进行合并
5.5 交给store的是总reducer,最后注意在组件中取出状态的时候,要指定好取的数据
react-redux开发者工具的使用
(1).yarn add redux-devtools-extension
(2).store中进行配置
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
复制代码