初次邂逅React

前方高能!!!存在许多代码

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="点击按钮显示数据"/>&nbsp;
               <button onClick={this.showinput1}>点我显示左边输入框的数据</button>&nbsp;
               <input onBlur={this.showinput2} ref="input2" type="text" placeholder="失去焦点显示数据"/>&nbsp;
           </div>

       )
      }
   }
   ReactDOM.render(<Demo/>,document.getElementById('test'));
复制代码
ref三种使用方式
//字符串方式,不推荐,效率较低
  <input ref="input1" type="text" placeholder="点击按钮显示数据"/>&nbsp;
//回调函数方式
  <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>&nbsp;
           </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生命周期

旧生命周期

image.png

新生命周期

image.png
重要的三个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

  1. 容器组件和UI组件整合在一个文件
  2. 无需自己给容器组件传递store,给包裹一个即可。
  3. 使用了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)))
    
    
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享