REACT脚手架:create-react-app
-
全局环境安装脚手架 $ npm install -g create-react-app
-
基于脚手架快速构建工程化的项目 $ create-react-app xxx (如果电脑上安装了yarn,默认基于yarn安装)
-
不想安装在全局,可以基于npx一步到位 $ npx create-react-app xxx
脚手架创建的项目目录
- node_models 所有安装的模块
- public
- index.html SPA单页面应用中,各组件最后合并渲染完成的结果都会放入到页面的#root盒子中呈现
- xxx.html MPA/SPA这里存放的是最后编译页面的模板
- 我们还可能会在此放一些公共资源,把这些资源直接基于SRC/LINK的方式调入到页面模板中,而不是基于webpack最后合并在一起 (不建议:但是项目中,可能存在一些模块不支持CommonJS/ES6Module规范,此时我们只能在这里直接引入使用了)
- src 整个项目的大部分源码都写在这个目录下
- index.js 项目的入口,webpack从这个文件开始导入打包 (MPA中需要创建多入口文件)
- api 数据处理
- store REDUX公共状态管理
- asset 存储公共资源的
- utils 公共的JS模块
- routes 路由管理的
- components 公共的组件
- ...
- package.json
默认的配置清单
- 生产依赖项
- react BEACT框架的核心,提供了状态、属性、组件、生命周期等
- react-dom 把JSX语法渲染成为真实的DOM,最后显示在浏览器中
- react-scripts 包含了当前工程化项目中webpack配置的东西 (嫌弃把webpack放到项目目录中看上去太丑,脚手架把所有的配置项和依赖都隐藏到node_modules中了,react-scripts这个REACT脚本执行命令,会通知webpack打包编译)
- scripts 当前项目课执行的脚本命令 ($ yarn xxx)
- $ yarn start =>开发环境下启动项目 (会默认基于WEBPACK-DEV-SERVER创建一个服务,用来随时编译和渲染开发的内容)
- $ yarn build => 生产环境下,把编写的内容打包编译,放到build文件目录下 (服务器部署)
- $ yarn eject => 把所有隐藏在node_modules中的webpack配置项都暴露出来 (方便自己根据项目需求,二次更改webpack配置)
REACT中的组件
在react工程化项目中 (vscode),我们会把需要写react组件的JS命名为.jsx,这样只有一个目的,让创建的文件识别JSX语法,而在create-react-app脚手架创建的项目中,已经包含了对.jsx文件的处理 每一个组件都是一个单独的个体 (数据私有、有自己完整的生命周期、有自己的视图...) 只要在JS中使用JSX,则必须要引入REACT (因为需要用到CREATE-ELEMENT)
函数式组件 只要让函数返回一个JSX元素即可 (掌握:props)
+ 特点:简单(开发简单和渲染也快),但是一旦组件 被调用,里面的内容渲染完成,当前组件中的信息基本上就不变了 (除非:重新被调用组件传递不同的属性信息) =>”静态组件“:没有state状态管控内容的随时变化,也没有生命周期函数等
- 基于React.Component类创建组件
- REACT HOOK
- Props:在ReactDOM.render进行处理的时候,如果发现TYPE不是标签字符串,而是一个函数(一个类),则会把函数执行(创建类的一个实例),与此同时会把调用组件时候,设置的属性传递给这个函数这个类
- 调取组件的时候,可以单闭合也可以双闭合,双闭合好处:可以继续在里面写子元素,而单闭合不可以,推荐用双闭合。
组件特点:
给组件传递进来的属性是只读的 (只能获取不能修改)
1.不能直接的赋值默认值
=>props.title=props.title || "标题"; 不可以
=>let title=props.title || "标题"; 可以
2.函数式组件不能像类组件一样,基于prop-types给属性设置默认的规则
类组件:
class xxx extends React.Component/React.PureComponent 当REACT-DOM.RENDER渲染的时候,如果发现虚拟DOM中TYPE是一个类组件,会创建这个类的一个实例,并且把解析处理的PROPS传递给这个类:new Clock (props) =>执行CONSTRUCTOR (此时PROPS并未挂载到实例上,基于this.props不能获取到值,但是可以直接少使用形参中的props;解决方法:super(props)这样在CONSTRUCTOR中也可以使用this.props) super执行,相当于把React.Component当做普通函数执行,让方法中的this是当前实例 this=>{props.xxx,context:xxx,refs:{},updater:{...}}
- 当CONSTRUCTOR执行完,REACT会帮我们继续处理
- 把props/context...挂载到实例上 (后期再其它的钩子函数中可以基于THIS.PROPS获取传递的属性值)
- react帮我们把render方法执行
对于每一个类组件来说,只需学会:
- 数据管控
- 属性 PROPS
- 状态 STATE (私有状态,redux公共状态管理)
- 基于第三方插件prop-types设置属性的规则:默认值和其它规则
- $ yarn add prop-types
- 设置默认值
- static defaultProps={ xxx:xxx };
- 设置一些其它规则
- static propTypes={ title:PropTypes.string.isRequired };
- PropTypes.isRequired 必须传递
- PropTypes.string/number/bool/func/object/symbol/node (元素对象) / element (JSX元素) /instanceOf (Xxx)必须是某个类的实例 /oneOf(['news','Photos'])多个中的一个
- 和vue一样,我们设定的规则不会阻碍内容的渲染,不符合规则的在控制台报错
REACT与VUE的区别
视图层 vue:template/jsx react:jsx 语法写法 vue:{{}} react:{} 数据层: vue:data vue有一个vm层 Object.defineProperty/Proxy 给数据添加了以下getter/setter方法监听 getter setter 更新先看setter,setter会通知render执行调用 getter方法
react:state 这里需要我们自己在方法里写来通知render执行 setState/forceUpdate 在这里来通知render来执行渲染
非受控组件
不受状态管控 (直接操作DOM) 我们把基于状态 (或者属性) 的更新来驱动视图渲染:“受控组件” (受状态管控的组件) 属性啥时候能改:设置默认值、让父组件重新调取子组件传递不同的属性、把获取的属性赋值给组件的状态,后期修改状态也可以
react生命周期函数
- getDefaultProps 处理属性 (获取默认值和校验传递属性的类型 )
- getlnitialState 处理状态 (执行CONSTRUCTOR获取初始的状态信息)
- componentWillMount:第一次组件渲染之前 =>从服务器获取数据 (把晦气的数据重新赋值给状态或者存放到REDUX中)
- render:第一次或者重新进行视图的渲染
- componentDidMount:第一次渲染完成
- 此处可以获取到DOM元素了
更新状态后
- shouldComponentUpdate(nextProps , nextState)是否应该更新组件
- 当我们执行setState等操作,首先触发的是当前钩子函数
- this.state当前的状态 (改之前的状态)
- nextState即将要修改的状态信息
- this.props和nextprops的状态一样的意思
- 钩子函数返回TRUE代表允许重新渲染视图,反之FALSE则停止进行渲染视图
- 当我们执行setState等操作,首先触发的是当前钩子函数
- componentWillUpdate
- render
- componentDidUpdate
- 卸载
- componentWillUnmount 卸载组件
当父组件重新渲染,子组件也会重新渲染,首先触发此钩子函数
- componentWillReceiveProps(nextProps)
- shouldComponentUpdatee(nextProps , nextState) 是否应该更新组件
执行SET-STATE必然会触发shouldComponentUpdate这个钩子函数,根据TRUE还是FALSSE决定状态是否重新渲染组件 (无论返回啥,状态本身都已经修改了)
- forceUpdate强制更新 (重新渲染),不会执行shouldComponentUpdate这个钩子函数
第一次渲染TEST视图,会新创建一个CLOCK (执行CLOCK完整生命周期)。当TEST重新渲染的时候,也让CLOCK子组件重新渲染,把最新的属性传递给CLOCK (但不是重新创建),CLOCK中的钩子函数不会从CONSTRUCROR重新执行
REACT中的事件是合成事件
底层源码上是基于事件委托把所有的事件进行代理的 (跨平台、跨终端) =>事件对象也是自己额外单独处理了 SET-STATE在合成事件或者生命周期函数中都是异步操作 + REACT有一个类似于浏览器的渲染队列机制,它会在多次执行SET-STATE的情况下,把其合成一次进行重新渲染 (如果修改的状态一样,以最后一次为主).
SET-STATE同步
把SET-STATE放到一个异步操作中,此时它就没必要异步了,走的是同步处理 + 定时器 + 原生JS事件
REACT中的组件通信
- 基于属性传递props
- 单向数据传递 (父->子)
- 子改父 (子组件想通过一些操作把父组件的信息进行修改)
- 回调函数 (和JSONP或者JSBridge机制类似) :父组件把自身的方法通过属性传递给子组件,子组件在某些情况下,把传递进来的父组件中的方法执行,把子组件中的一些信息传递给方法 (底层:还是基于属性)
- 我们可以自己写一套eventBus (发布订阅) 一般用于:父子之间,当然祖先和后代之间也可以,只不过需要基于属性一层层的传递 (麻烦),也可以用于兄弟之间,但是需要保证,哥俩有同一个父亲 (亲兄弟)
- 执行上下文进行信息传递
- 把后代中需要的都放到祖先元素的上下文中
- 后代需要直接获取使用即可
- redux/react-redux/dva/redux-saga/mobx...
- 实现公共状态的管理
- 本地存储
REACT中的后代调取祖先
//=>我们一般要把挂载到祖先上下文中的数据放置到祖先的状态上 (后期只需要把祖先的状态改变,上下文中的信息也会跟着改变,同时祖先元素重新渲染,后代元素也要重新渲染,获取最新的上下文信息)
static childContextTypes={
//=>指定后代能用的公共状态的类型
}
//生命周期函数:给后代提供能用的上下文信息 (公共状态),上面是指定类型,此处是指定具体的值
getChildContext(){
//=>第一次在getIntialState之后执行,每当祖先组件中的状态改变,重新渲染的时候,此钩子函数也会重新渲染被执行
return{
xxx:xxx
}
}
//哪一个后代组件需要用到,直接注册获取使用即可
static contextTypes={
//想用哪些组件中提供的公共状态,则这块就声明一些它的类型 (要求类型和祖先一致)
}