zoukankan      html  css  js  c++  java
  • 临时笔记-react实战

    虚拟DOM

    如果对DOM节点进行修改操作,一般情况下,每次修改都会直接操作DOM树。而React的虚拟DOM技术不一样,它会对比开始状态与最后的状态,当状态一致时,便不会对DOM树进行操作。最后的渲染结果是有diff算法控制的,React只对真正有改变的节点进行渲染。

    搭建开发环境

    方式一:React CDN资源

    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    
    <!--编译JSX-->
    <script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
    

    方式二:npm安装React

    npm install --save react react-dom
    

    方式三:create-react-app脚手架

    npm install -g create-react-app
    create-react-app myapp
    

    端口号

    在node_modules/react-scripts/scripts/start.js文件中可以修改默认端口号3000

    目录说明

    • node_modules:放置项目需要的各种依赖模块
    • package-lock.json:记录实际安装的各个包的来源以及版本号。
    • package.json:定义依赖关系树,以及各个依赖模块的版本范围
    • public:里面的文件用于被index.html引用。
    • src:存放源码等,webpack只识别这个目录

    调试

    安装React Developer Tools的chrome插件

    React事件系统

    React事件系统在原生的DOM事件体系上封装了一个“合成事件”层,事件处理程序通过合成事件进行实例传递。它没有把所有事件绑定到对应的真实DOM上,而是使用委托机制实现统一的事件监听器,把所有的事件绑定到了最外层document上,然后再将事件进行分发。

    React绑定事件的三种方法:

    • 组件上绑定
    • 构造函数中绑定
    • 箭头函数绑定

    React原理

    JSX

    用JSX创建虚拟DOM提高了可读性:

    • JavaScript创建虚拟DOM

    • JSX创建虚拟DOM

    书写JSX时,<script>的type为“text/babel”,把JSX转为浏览器能够理解的js代码

    JSX样式:

    <button style="background-color:red">按钮</button>
    
    class Hello extends React.Component{
        render(){
            var bgColor={backgroundColor:"red"}
            return <button style={bgColor}>按钮</button>
        }
    }
    
    return <button style={{backgroundColor:"red"}}>按钮</button>
    

    dom-diff

    UI进行更新时,React会将当前数据和前一状态的数据进行对比,判断哪些数据发生了变化,接着把发生变化的数据渲染在UI上。Web界面实质上是构建的一棵DOM树,当某一节点发生变化时,React会对当前的DOM树和前一状态的DOM树进行比较,这个比较的算法就是dom-diff算法

    React的diff算法的两个假设:

    • 不同类的元素会产生不同的DOM树
    • 同一层次的一组节点可以通过唯一的key值区分

    在React之前有个标准dom-diff算法,针对任意两棵树找最小变化步骤,这个算法时间复杂度为O(n3)。而基于上述两条假设,React的diff算法可以将算法复杂度减少到O(n)。

    React的同层次比较:

    React对DOM树进行了分层,只会对同一层的节点进行数据比较。如果节点类型发生变化,React会将其删除,然后新建节点到新的DOM树上。如果节点类型相同、属性不同,那么React会进行替换操作。如果遇到同一层级的子节点进行操作时,需要加上key属性来进行唯一区别,否则React会进行告警。key的唯一属性是避免删除、创建等重复操作,减少性能消耗。

    image.png

    用示例解释React的dom-diff算法:

    image.png

    整个变化过程为:C节点从A节点转移到了D节点上。React对整棵DOM树的操作步骤为:

    (1)删除C节点。

    (2)创建C节点。

    (3)更新B节点。

    (4)更新A节点。

    (5)渲染C节点。

    (6)更新D节点。

    (7)更新R节点。

    setState

    React组件可以理解为一个状态机。组件的更新其实就是内部state值的更新,state属性记录着组件的状态。而修改state的方法就setState。(示例)

    class Like extends React.Component{
        constructor(){
            super()
            this.state={
                isLiked:true
            }
            this.changeState=this.changeState.bind(this)
        }
        changeState(){
            console.log(this.state.isLiked)//1.打印出true
            this.setState({
                isLiked:!this.state.isLiked
            })
            console.log(this.state.isLiked)//2.打印出true 可见是异步操作
        }
        render(){
            return(
                <input type="button" onClick={this.changeState} value={this.state.isLiked?"点赞":"取消"}/>
            )
        }
    }
    ReactDOM.render(<Like/>,document.getElementById('root'))
    

    setState()方法为异步操作,它是通过一个队列机制来更新state。在执行changeState()方法中的this.setState时,React先把新state值放到了一个状态队列中,并没有及时更新state值。

    React中的setState异步原理:

    class Like extends React.Component{
        constructor(){
            super()
            this.state={
                count:0
            }
            this.showState=this.showState.bind(this)
        }
        componentDidMount(){
            this.setState({count:this.state.count+1})
            this.setState({count:this.state.count+1})
            this.setState({count:this.state.count+1})
            console.log('setState后:',this.state.count) 
        }
        showState(){
            console.log('showState中的state后:',this.state.count)
        }
        render(){
            return(
                <input type="button" onClick={this.showState} value="显示state"/>
            )
        }
    }
    

    image.png

    结果可以看出,虽然在componentDidMount()方法中执行了三次setState,但是最后就执行了一次,并且没有立即执行。原因是React先把新的state值放到了状态队列里,最后对三次setState进行了批量处理,批处理过程中进行了合并,所以结果就是对setState只执行了一次,这样做的好处是可以避免一些重复渲染setState()可以理解为一个重新渲染的请求,而不是立即更新的一个命令,为了有更好的性能,React可能会推迟执行,然后会批量进行处理。React不会保证在setState操作后立即拿到最新的state值。

    如果想在setState后立即得到最新的state值,可以通过函数来实现:

    this.setState(
        function(state){
            return {
                count:state.count+1
            }
        }
    );
    

    在实际项目中不要用this.state来修改state值,因为获取到的this.state有可能不是最新的,更新组件state时要用setState()方法。

    React组件写法

    • React.Component写法

      class HelloWord extends React.Component{
          state={message:'你好呀!'}
      	render(){
          	return <h1>{this.state.message}</h1>
      	}
      }
      
    • 无状态函数写法(没有state参与,只接受props,以减少耦合度)

      const HelloWord=(props)=>{
      	return <h1>你好啊!{props.name}</h1>
      }
      
      function HelloWord(props) {
          return <h1>你好啊!{props.name}</h1>
      }
      

    React组件分类

    React的组件分类,主要是以两方面为标准的:一方面是呈现,一方面是管理。按这种标准可分为木偶组件和智能组件(对比示例)

    • 引入:获取数据与ui渲染放在一起,显得很臃肿。如果把获取数据和UI渲染分开,分别放在一个独立的组件中,代码的可读性、可维护性、可复用性更好

    • 木偶组件:呈现UI职责,不涉及数据操作

    • 智能组件:更多考虑逻辑操作性问题

    MVC框架

    MVC框架是业界广泛接受的一种前端应用框架类型,这种框架把应用分为三个部分:

    • Model(模型)负责管理数据,大部分业务逻辑也应该放在Model中;
    • View(视图)负责渲染用户界面,应该避免在View中涉及业务逻辑;
    • Controller(控制器)负责接受用户输入,根据用户输入调用对应的Model部分逻辑,把产生的数据结果交给View部分,让View渲染出必要的输出。

    image.png

    MVC框架提出的数据流很理想,用户请求先到达Controller,由Controller调用Model获得数据,然后把数据交给View,但是,在实际框架实现中,总是允许View和Model可以直接通信

    在图中,可以看到Model和View之间缠绕着蜘蛛网一样复杂的依赖关系,根据箭头的方向,我们知道有的是Model调用了View,有的是View调用了Model

    image.png

    Flux框架

    Mobx框架

    Mobx是一个用于状态管理的库,和React是一对很搭的组合。React在View层提供了很好的解决方案,Mobx对状态管理具有极大的简易性和可扩展性,二者搭配,能够很好地管理一些大型前端项目。Mobx的工作原理大概如下图示。在整个数据流中,首先用户的触发事件到达Actions中,然后依据事件属性及事件要求在State中对状态值进行修改,接下来用新的State数据计算所需要的Computed values,最后响应渲染UI视图层:

    image.png

    Redux框架

    Redux生态

    redux middleware

    Redux主要输出createStore、combineReducers、bindActionCreators、applyMiddleware、compose五个接口。

    Redux的核心是控制和管理所有的数据输入与输出,使用纯函数dispatch派发action来更改数据,其功能简单且固定。middleware允许我们dispatch一个action之后,在到达reducer之前先做一些额外的处理,可以理解为每一个middleware都在增强dispatch的功能

    redux-logger

    能够对所有action发生后生成的state进行记录,即日志中间件

    redux-thunk

    用于在redux中处理异步action的中间件,异步action的场景包括需要在action中执行setTimeout,以及需要通过fetch方法调用服务端API等,对于异步action的场景可以使用redux-thunk中间件来优化代码流程

    redux-saga

    与redux-thunk功能类似,也是主要集中管理React应用中的异步操作。redux-saga最大的特点是使用generator(ES6)的形式,采用监听的形式进行工作,可以使用同步的方式编写异步代码,使得项目流程拆分更细,应用的逻辑和视图拆分更加清晰,分工明确。

    React架构

    React不仅仅是一个库,也不是一个框架,而是一个庞大的生态体系。若希望充分使用React尽可能多的特性,整个技术栈都要相应地配合改造,则我们要学习从后端到前端的一整套解决方案。因此,使用React时,合理的选择是采用React的整个技术栈。

    一、文件结构

    采用React+Redux+React-Router+Less+ES6+webpack介绍React实现一个完整应用的文件结构。React的CLI脚手架工具create-react-app屏蔽了和React无关的配置,如Babel、webpack。下面我们使用这个工具快速创建一个项目:

    npm install -g create-react-app
    create-react-app hello-world
    

    image.png

    二、CSS方案

    一些css相关的问题有:全局污染、命名混乱、依赖引入复杂、无法共享变量、代码冗余。

    CSS Modules是一种非常优秀的模块化解决方案。CSS Modules结合了CSS生态和JavaScript模块化能力,使用JavaScript来管理样式依赖,加入了局部作用域和模块依赖。CSS Modules发布时依旧编译出单独的JavaScript和CSS。

    局部样式

    CSS Modules通过使用一个独一无二的class的名字产生局部作用域:

    import React from 'react'
    import style from './App.css'
    
    export default()=>{
        return(
        	<div className={style.wrapper}>
                <h1 className={style.title}>Hello!</h1>
            </div>
        )
    }
    

    其中App.css:

    .wrapper{
        750px;
        margin:0 auto;
    }
    .title{
        color:red;
        font-size:48px;
    }
    

    webpack.config.js中配置css-loader启用CSS Modules:

    module.export={
        entry:__dirname+'/index.js',
        output:{
            publicPath:'/',
            filename:'./bundle.js'
        },
        module:{
            loaders:[
                {
                    test:/.jsx?$/,
                    exclude:/node_modules/,
                    loader:'babel',
                    query:{
                        preset:['es2015','stage-0','react']
                    }
                },
                {
                    test:/.css$/,
                    loader:"style-loader!css-loader?modules&localIdentName=[name]__[local]-[hash:base64:5]"
                }
            ]
        }
    }
    

    localIdentName用于设置生成样式的命名规则,css-loader默认的哈希算法是[hash:base64],这会将.title编译成._3zyde4l1yATCOkgn-DBWEL这样的字符串。本示例中我们自定义的编译规则生成的HTML是,App.css也会同时被编译。CSS Modules对CSS中的class名都做了处理,使用对象来保存原class和混淆后class的对应关系:

    通过这些处理,CSS Modules可以继续使用CSS编写样式,相当于给每个class名外加了一个:local,使得所有样式都是局部样式,以此来实现样式的局部化,解决了命名冲突和全局污染问题。

    全局作用域

    CSS Modules使用:global(.className)的语法,声明一个全局规则。凡是这样声明的class,都不会被编译成哈希字符串:

    .normal{
        color:green
    }
    //等价
    :local(.normal){
        color:green
    }
    
    :global(.btn){
        color:red;
    }
    //定义多个全局样式
    :global{
        .link{
            color:red;
        }
        .box{
            color:yello
        }
    }
    

    组合样式

    在CSS Modules中,一个选择器可以继承另一个选择器的规则,称为“组合”。让.title继承.titleBase:

    .base{
        background-color:blue;
    }
    .title{
        composes:base;
        color:red
    }
    .wrapper{
        composes:base;
        750px
    }
    

    PostCSS

    PostCSS是一款对CSS进行处理的工具,主要依赖插件来进行操作。PostCSS是一个利用JavaScript插件来对CSS进行转换的工具,较常使用的插件有CSS Modules、Autoprefixer、postcss-cssnext等。

    CSS Modules

    npm install --save postcss-loader postcss-modules-values
    

    在webpack.config.js中增加postcss-loader配置:

    var values=require('postcss-modules-values')
    module.export={
        entry:__dirname+'/index.js',
        output:{
            publicPath:'/',
            filename:'./bundle.js'
        },
        module:{
            loaders:[
                {
                    test:/.jsx?$/,
                    exclude:/node_modules/,
                    loader:'babel',
                    query:{
                        preset:['es2015','stage-0','react']
                    }
                },
                {
                    test:/.css$/,
                    loader:"style-loader!css-loader?modules!postcss-loader"
                }
            ]
        },
        postcss:{
            values
        }
    }
    

    定义变量

    @value blue #0c77f8
    @value red #ff0000
    

    引用

    @values colors:"./colors.css";
    @values blue,red from colors;
    .title{
        color:red;
        background-color:blue
    }
    

    Autoprefixer

    针对浏览器兼容的处理可以使用Autoprefixer插件。Autoprefixer是一个根据Can IUse(http://caniuse.com)兼容性解析CSS,然后为其添加浏览器厂商前缀的PostCSS插件。

    ::example{
        display:none;
        position:relative;
        transform:translate(10,10)
    }
    

    Autoprefixer将使用基于当前浏览器支持的特性和属性数据来添加前缀,处理之后生成:

    .example{
        display:none;
        position:relative;
        -webkit-transform:translate(10,10);
        -ms-transform:translate(10,10);
        transform:translate(10,10);
    }
    

    三、状态管理

    四、路由管理

  • 相关阅读:
    8.使用axios实现登录功能
    7.django配置跨域并开发测试接口
    9.Vue组件
    2.初始化项目结构
    1.Django基础
    团队冲刺——第七天
    团队冲刺——第六天
    十天冲刺——第五天
    十天冲刺——第四天
    十天冲刺——第三天
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/13943761.html
Copyright © 2011-2022 走看看