虚拟dom
- vdom是一个js对象;
- vdom比较轻,真实dom比较重;
- vdom最终会被React转化为真实dom,渲染到页面上;
JSX
- javascript xml;
- 语法规则
- 定义虚拟dom时,不需要写引号;
- 标签中混入js表达式时要用{};
- 样式的类名指定不要用class,而要用className;
- 内联样式,要用style={{key: value}}的形式去写;
- 虚拟dom必须只有一个根标签;
- 标签必须闭合;
- 标签首字母:
- 小写字母开头,则将该标签转为html中同名元素,若html中无,则报错;
- 大写字母开头,react就去渲染对应的组件,若没有定义,则报错;
表达式和语句的区别:一个表达式会产生一个值,可以被放在任何一个需要值的地方
模块与组件,模块化与组件化的理解
- 模块:
- 向外提供特定功能的js程序,一般就是一个js文件;
- 为什么要模块化:随着业务逻辑增加,代码越来越多且复杂;
- 作用:复用js,简化js编写,提高js运行效率;
- 组件;
- 用来实现局部功能效果的代码和资源的集合;
- 为什么要组件:一个界面的功能更复杂;
- 作用:复用编码,简化小木编码,提高运行效率;
- 模块化:
当应用的js都以模块化来编写,这个应用就是一个模块化的应用; - 组件化:
当应用时以多组件的方式实现,这个应用就是一个组件化的应用;
函数式组件
- React解析组件标签,找到了MyComponent组件;
- 发现组件式使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,呈现在页面中
类式组件
- 定义
- 必须继承React.Component类;
- 类必须有render方法;
- render必须有返回值;
- 注意
- React解析组件标签,找到了类组件;
- 发现组件是用类定义的,随后new出来这个类的实例,并通过该实例调用原型上的render方法;
- 将render返回的虚拟dom转为真实dom,随后呈现在页面上;
类的继承时,衍生类(derived)写了构造器,则衍生类的构造器必须调用super
简单组件 & 复杂组件
- 有状态的组件,复杂组件;
- 无状态的组件,简单组件;
state
- 组件是状态机,state是状态,state不能直接修改;
- 组件中render方法中的this是组件实例对象;
- 组件自定义方法中的this是undefined;
- 使用bind绑定this;
- 使用箭头函数 ;
复习class相关知识
- 构造器
- 自定义方法
- 赋值语句
- static 赋值语句
复习展开运算符的相关知识
- … 运算符不能展开一个对象;
- {…obj} 是使用字面量的形式复制一个对象;
props
- props是只读的;
- 使用展开运算符+对象的形式直接将对象批量传入组件,注意这是jsx的语法;
- 限制props
Person.propTypes = { name: PropType.string.isRequired, age: PropTypes.number, speak: PropTypes.func } Person.defaultProps = { age: 18 }
- 使用static修饰符将以上两个属性直接放入类中;
- 构造器是否接收props,是否传递给super,取决于你是否希望在构造器中通过this访问props;
refs
- ref三种定义方式
- 字符串,最简单,因为效率问题,官方不推荐;
- 回调方式,如果用函数内联的方式给ref赋值,在更新时可能会出现赋值两次的问题,第一次是null,第二次才是正确的;不过大部分情况无关紧要;
- React.createRef(),官方推荐;
事件
- react中的事件监听使用 onXxxx,注意大小写;
- 事件不是原生事件,而是自定义事件(合成事件);兼容性考虑;
- 事件采用代理方式,代理在组件最外层的元素上;
- 通过event.target获取触发事件的DOM元素;
受控组件 & 非受控组件
- 非受控组件:表单信息现用现取;
- 文件上传
- 富文本编辑器
- 等等,必须操作dom的场景
- 受控组件:表单信息内容维护至state,使用的时候直接用state中去取;
Portals
- 使用方法
render() {
return ReactDOM.createPortal(
<div>{this.props.chidren}</div>,
dcument.body
)
}
- 场景,需要改变组件渲染的层级
- 父组件 overflow:hidden
- 父组件 z-index太小
- fixed需要放在body第一层级渲染
高阶函数
- 函数的参数是一个函数;
- 函数的返回值也是一个函数;
函数柯里化
通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式;
React 生命周期
react lifecycle methods diagram
- 初始化阶段:由ReactDOM.render()触发 — 初次渲染
- constructor()
- componentWillMount()
- render()
- componentDidMount() ====> 页面初始化使用,开定时器,发ajax,订阅消息
- 更新阶段:由组件内部setState()或者父组件render触发
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
- 卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentWillUpdate() ====> 关定时器,取消订阅
- componentWillUpdate() ====> 关定时器,取消订阅
DOM-diffing
- key的作用
CSS Module
- 在定义组件css时,文件名使用 .module.css
- 引入时,import M from 'xxx.module.css
- 在最终生成的文件中,类名会增加模块前缀
组件化编程
- 拆分组件,如果组件拆分完觉得无法命名,那说明组件拆分有问题;
- 实现静态组件
- 实现动态组件(插入数据)
- 实现交互
请求代理配置
- 文件src/setupProxy.js
- 内容
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
'/api/v1/',
proxy({
target : 'https://easymock.spiritling.pub/',
changeOrigin : true, // 设置跨域请求
PathRewrite : {
'^/api/v1' : '' // 将/api/v1 变为 ''
}
})
);
};
组件通讯
- 父子组件,props和方法
- 兄弟组件,发布订阅 pubsubjs
路由的基本使用
- 明确好界面中的导航区,展示区;
- 导航区中的a标签改为Link标签;
- 展示区使用Route标签进行路径匹配;
- App的最外层包裹一个Router标签;
路由组件和一般组件
- 路由组件放在pages中;一般组件放在components;
- props不同;
一般组件,props给什么就能接受到什么;
路由组件,默认给location match history三个重要属性
Switch
可以提升路由的匹配效率(一个路径对应一个组件)
解决刷新之后静态资源丢失的问题
- 静态资源使用绝对路径 /static/css/xxx.css
- 使用%PUBLIC_URL% + /static/css/xxx.css
- 使用hashRouter,基本不会使用这种方式
路由的严格匹配和模糊匹配
- 默认使用的模糊匹配
- 开启方法 exact
- 严格匹配会导致无法匹配二级路由
react 状态管理
react hooks
- React.useRef(),用法与类式组件React.createRef()相同;
- React.useState(),定义状态和操作状态的方法;
- React.useEffect(),使函数式组件具有使用生命周期的能力,第二个参数为空数组时是componentDidMount钩子,第一个参数的返回值是componentWillUnmount钩子;
react 其他概念
- Fragment和空标签,在编译时节点会被忽略,Fragment标签可以带一个key属性;
- Context,可以提供Provider和Consumer两个标签,对后代组件进行传值操作;
- PureComponent,重写了组件componentShouldUpdate钩子,会对props和state进行浅比较,如果不相同触发render,相同就不触发,从而达到优化渲染的目的;