react

263 阅读10分钟

vue是单向流,双向绑定数据

vue /vuex/vue-router/axios(fetch) element-ui

react mvc单向,不是双向绑定

react react-dom (react-native) 组件、jsx、DOM DIDD 、事件、组件....

react-router-dom:hash/browser

redux:react-redux、dva、redux-sage、mobx....

axios / fetch

reactui库---antd

使用vue/react我们要告别操作dom的时代,都是基于数据驱动,由数据驱动视图的更新


react比vue少实现了v-model 具体的功能

react是mvc框架(model view controll)

react完成的是:

=》监听数据的更新,当数据更新后,帮助我们去渲染视图 DOM DIFF / 虚拟DOM变为真实DOM


相比较于vue来说

=》视图更新并没有改变数据(MVC是单向的),如果需要数据的更新,则需要自己在controll层中单独的处理

1,view ----视图层,在视图层进行一些操作

2,controll--在控制层里来修改数据

3,model---数据更新会通知视图渲染


react脚手架:create-react-app

全局环境安装脚手架

npm install -g create-react-app

基于脚手架快速构建工程化的项目

create-react-app 项目名称 (如果电脑上安装了yarn,默认基于yarn安装)


不想一步步的安装的话,可以基于npx直接一步到位

npx create-react-app 项目名称


脚手架项目目录

|- node_modules 所有安装的模块

|- public

|- index.html SPA单页面应用中,各组件最后合并渲染完成的结果都会放入到页面的#root盒子中呈现

|- xxx.html MPA/SPA这里存放的是最后编译页面的模板

|- 我们还可能会在此放一些公共资源,把这些资源直接基于SRC/LINK的方式调入到页面模板中,而不是

基于webpack最后合并在一起(不建议,但是项目中可能存在一些模块不支持CommonJs/Es6Module规范,此时我们只能在这里直接引入使用了)

|- src 整个项目的大部分源码都写在这个目录下

|- index.js 项目的入口,webpack从这个文件开始导入打包(MPA中需要创建多入口文件)

|- api 数据处理

|- store Redux公共状态管理的

|- assets 存储公共资源的

|- routes 路由管理的

|- components 公共的组件

。。。

|- package.json 依赖清单

默认的配置清单

  • 生产依赖项
  • react react框架的核心,提供了状态、属性、组件、生命周期等
  • react-dom 把JSX语法渲染成为真实的DOM,最后显示在浏览器中
  • react-scripts 包含了当前工程化项目中webpack配置的东西(嫌弃把webpack放到项目目录中看上去太丑,react脚手架把所有webpack的配置项和信赖都隐藏到node_modules中了),react-scripts这个react脚本执行命令,会通知webpack打包编译
  • scripts 当前项目可执行的脚本命令(yarn xxx)
  • yarn start =>开发环境下启动项目(默认会基于webpack-dev-server创建一个服务,用来随时编译和渲染开发的内容)
  • yarn build =>生产环境下,把编写内容打包编译,放到build文件目录下(服务器部署)
  • yarn eject =>把所有隐藏在node_modules中的webpack配置项都暴露出来(方便自己根据项目需求,二次更改webpack配置 )


yarn eject把webpack暴露出来

  • babel-preset-react-app 解析JSX语法的

|- scripts

|- start.js => yarn start

|- build.js => yarn build

|- config

|- 这里存储的就是webpack的配置项

系统配置默认端口号

在package.json文件里

如果执行yarn start/build 提示少模块,我们则少了谁就安装谁

yarn add @babel/plugin-transform-react-jsx @babel/plugin-transform-react-jsx-source @babel/plugin-transform-react-jsx-selfcls


端口号,PORT
域名 HOST
协议 HTTPS
"scripts": {
    //mac
  "startMac":"PORT=8081 HOST=127.0.0.1 HTTPS=true node scripts/start.js"
  //window
  "startMac":"set PORT=8081&&set HOST=127.0.0.1&&set HTTPS=true&&node scripts/start.js"
  
  "start": "node scripts/start.js",  //默认的
  "build": "node scripts/build.js",
  "test": "node scripts/test.js"
}


修改less的处理配置


自己配置less  https://blog.csdn.net/qq_25520603/article/details/90206399

yarn add less less-loader   安装

config/webpack.config.js文件里修改

const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

{
  test: lessRegex,
  exclude: lessModuleRegex,
  use: getStyleLoaders(
  {
  importLoaders: 2,
  sourceMap: isEnvProduction && shouldUseSourceMap,
  },
  'less-loader'
  ),
  // Don't consider CSS imports dead code even if the
  // containing package claims to have no side effects.
  // Remove this when webpack adds a warning or an error for this.
  // See https://github.com/webpack/webpack/issues/6571
  sideEffects: true,
}


jsx语法 -可以理解为虚拟DOM

基础语法

1,每一个组件的视图只能有一个根元素节点
    ReactDOM.render([jsx],document.getElementById('[id容器]'),[CALLBACK])
    => id容器不建议是HTML或者BODY,指定一个元素容器 #Root
  => CALLBACK把虚拟DOM渲染到浏览页面中触发的回调函数
2,jsx语法中基于{变量}绑定动态数据值或者js表达式
    =》null和undefined代表空元素
    =》在括号中不能直接使用对象或者函数(引用类型(除数组和虚拟DOM)外都不可以,不是合法jsx元素,会把数组变成字符串,数组当中也不可以包括不能识别的引用类型)
3,给jsx元素设置样式
    =》设置样式类使用的是className,而不是class
    =》设置行内样式style不能是字符串,必须是一个对象 style={{color:'red'}}
4,大括号中如果是js表达式,可以返回有效的数据值或者返回一个新的jsx元素
5,jsx要求循环绑定的元素都要设置一个属性key,存储的值是当前循环中的唯一值(key是dom diff时候的重要凭证,key值一般不要设置为循环的索引,而是设置为某一个具体不变的值)


import ReactDOM,{render} from 'react-dom' //把ReactDOM,和render同时导入出来

嵌套式
  let sex=0
  {sex===0?<p>内容</p>:null} 
  


循环数据


data=[{
    id:1,
  name:'a'
},{
    id=2,
  name:'b'
}]
<ul>{data.map((item,index)=>{
    return '<li key={item.id}>
    <span>{item.id}</span>
  <span>{item.name}</span>
  </li>'
})}</ul>


虚拟DOM到真实DOM的渲染原理

执行顺序肯定是先把最里层的create-element执行,执行完依次往外层 ,create-elment

/*
https://www.babeljs.cn/repl
    1,把jsx基于babel-preset-react-app语法解析包变为create-element格式  babeljs.cn
  =>每当遇到一个元素标签都会create-element
  =>react.createElement([标签名],[props|null],...)有几个子节点,从第三个实参开始分别是每一个
  子节点的处理(文本节点直接就是文本内容,元素节点还需要再一次create-clement)
  2,执行React.createElement
    =》返回一个对象
    {
        $$typeof:Symbol(react.element),
      key:
      ref:
      type:标签名/组件
      props:{
        //
        xxx:xxx, 给元素标签上设置的属性(ref/key除外)
        //没有子节点则没有children选项,有子节点才有children,只有一这个字节点它的值是单个值,如果有多个
        子节点,它的值是一个数组
        children:单个值(字符串/对象)或者 数组
      }
    }
  3,基于reactDOM.render把生成的对象变为真实的DOM,最后渲染到浏览器页面指定容器中
*/

<div className="box" id="box" style={{color:'red'}}>你好世界
  <span>aaa</span>
</div>
转成以下:
React.createElement("div", {
  className: "box",
  id: "box",
  style: {
    color: 'red'
  }
}, "\u4F60\u597D\u4E16\u754C", React.createElement("span", null, "aaa"));


react中的组件

在react工程化项目中(vscode),我们会把需要写react组件的js命名为.jsx,这样只有一个目的,让创建的文件识别jsx语法,而在create-react-app脚手架创建的项目中,已经包含了对.jsx文件的处理

react中的组件:每一个组件都是一个单独的个体(数据私有、有自己完整的生命周期、有自己的视图...)

1.函数式组件

只要让函数返回一个jsx元素即可(掌握:props)

2.基于react.component类创建组件

3.react hook


函式组件-特点:简单(开发简单和渲染也快)但是一旦组件被调用,里面的内容渲染完成,当前组件中的信息基本上就不变了(除非:重新调用组件传递不同的属性信息)=》静态组件,没有state状态管控的随时变化,也没有生命周期函数

在reactDOM.render进行处理的时候,如果发现type不是标签字符串,而是一个函数(一个类),则会把函数执行(创建类的一个实例),与此同时会把调用组件时候,设置的属性传递给这个函数或者这个类

//只要在js中使用jsx,则必须引入react(因为需要用到create-element)
/*
    简单:开发维护简单,渲染也简单(渲染速度快)
    静态:只要把组件调取渲染完后,组件中的内容将不再修改(函数式组件中没有自己的状态管控,生命周期等)
    组件特点:给组件传递进来的属性是只读的(只能获取不能修改)报错信息:Cannot assign to read only property 'title' of boject
    1.不能直接的赋值默认值
        =》props.title=props.title || '我是默认值' (错误的)
        =》let title=props.title || '我是默认值'   (可以的)
    2.函数式组件不能像类组件一样,基于prop-types给属性设置默认的规则
*/
import React from 'react';
export default function Vote(){
    return <div>
        我是组件
    </div>
}
/*
React.Children.map  //固定的写法
*/
React.Children.map([需要遍历的数组对象],(item,index)=>{
    
})

类组件

类的创建

class Clock{
    constructor(){
  }
  y=200; //=>新增的语法:给实例设置的私有属性和constructor中的this.y=200效果一样
  AAA(){
    //Clock.prototype.AAA
  }
  static CCC=300; //ES7新增语法
  static BBB(){
    //Clock.BBB 私有方法
  }
    
}

/*

    继承
    1.原型继承
    2.Call继承
    3.寄生组合继承 Object.create
    4.ES6中基于class实现继承
*/

class Clock extends Parent{
    constructor(){
    super(); //=>只要用到constructor,第一行就必须要用super,
  }
}


创建类组件

对于第一个类组件来说,只需要学会:

1.数据管控 (model)

属性 props

状态 state(私有状态,redux公共状态管理)

/*
    类组件:
    当react-DOM.render渲染的时候,如果发现虚拟DOM中type是一个类组件,会创建这个类的一实例
    并且把解析出来的props传递给这个类 new Clock(props)
    =>执行 constructor (此时props并未挂载到实例上),基于this.props不能获取到值,但是可以直接使用形参中的props
        解决方案:super(props)这样在constructor中也可以用this.props了
    =>当constructor执行完,react会帮我们继续处理
        =》把props /context...挂载到实例上(后期在其它的钩子函数中可以基于this.props获取传递的属性值)
      =》react帮我们把render方法执行
*/

class Clock extends React.Component{
  //第一种写法,写私有属性
    constructor(props){  //props调取组件传递进来的属性
    
    /*super执行,相当于把react.Component当做普通函数执行,让方法中的this是当前实例 
    this=>{props:xxx,context:xxx,refs:{},updater:{}}
    */
    super(props); //把参数挂载到实例上
    
  } 
  //第二种写法,写么有属性
/*x=100;
    y=200; */
  //必须要有render函数,它返回的内容是我们当前组件要渲染的视图
  render(){ //到这里this.props是有的,因为到这步react帮我们把参数挂载到了实例上
    return <div>
      <h2>{}</h2>
    </div>
  }
}

给属性设置规则 prop-types


安装第三方库 github.com/facebook/prop-types
/*
    基于第三方插件prop-typs设置属性的规则:默认值和其它规则
    =》1,yarn add prop-types
  设置默认值
    =》PropTypes.isRequired 必须传
    =》PropTypes.string/bool/number/func/object/symbol/node(元素对象)/element(jsx元素)/
    instanceOf(xxx)必须是某个类的实例,/oneOf(['news','']),其中的某一个/oneOfTypes([PropTypes.string,PropTypes.number])多个类型中的一个
        和VUE一样,我们设定的规则不会阻碍内容的渲染,不符合规则的在控制台报错
*/
class Clock extends React.Component{
    static defaultProps={
        title:'我是默认值'
    }
    static propTypes={ //给组件里设置默认规则
        title:PropTypes.string.isRequired  //设置规则 isRequired必须传递
    }
    constructor(props){ 
        super(props); 
      
    }
    render(){
        return <div>
        <h2>{this.props.title}</h2>
      </div>
    }
  }

ReactDOM.render(<div>
    <Clock></Clock>
</div>,document.getElementById('root'));