目录
视频
【尚硅谷React教程(2022加更,B站超火react教程)】https://www.bilibili.com/video/BV1wy4y1D7JT?vd_source=1b4ebd53bacd613eec97e4a27e6457de
源码文件
链接: https://pan.baidu.com/s/1KCJdanhaslgmHtkp149H5Q?pwd=ymw1 提取码: ymw1 复制这段内容后打开百度网盘手机App,操作更方便哦
React特点
React是一个将数据渲染为 HTML 视图 的 js 库
简单看来,React 框架主要功能体现在前端 UI 页面的渲染,包括性能优化以及操作简化等等方面。站在 mvc 框架的角度来看,React 操作 view 层的功能实现。
采用组件化模式、声明式编码、函数式编程,提高开发效率和组件复用性
它遵循从高阶组件到低阶组件的单向数据流。
在 React Native 中可以用 react 预发进行安卓、ios 移动端开发
使用虚拟 dom 和 diff 算法,尽量减少与真实 dom 的交互,提高性能
React 与 Vue 对比
相同点:
都使用 Virtural DOM
都使用组件化思想,流程基本一致
都是响应式,推崇单向数据流
都有配套框架,Vue 有 Vue-router 和 Vuex,而 React 有 React-router 和 React-Redux
都有成熟的社区,都支持服务端渲染
不同点:
模版语法不同
Vue 推荐编写近似常规 HTML 的模板进行渲染,而 React 推荐 JSX 的书写方式。
核心思想不同
Vue推崇灵活易用(渐进式开发体验),数据可变,双向数据绑定(依赖收集)。
React推崇函数式编程(纯组件),数据不可变以及单向数据流。
组件实现不同
Vue源码实现是把options挂载到Vue核心类上,然后再new Vue({options})拿到实例。
React内部使用了四大组件类包装VNode,不同类型的 VNode 使用相应的组件类处理,职责划分清晰明了。
React 类组件都是继承自 React.Component 类,其 this 指向用户自定义的类,对用户来说是透明的。
响应式原理不同
Vue依赖收集,自动优化,数据可变。递归监听 data 的所有属性,直接修改。当数据改变时,自动找到引用组件重新渲染。
React基于状态机,手动优化,数据不可变,需要 setState 驱动新的 State 替换老的 State。当数据改变时,以组件为根目录,默认全部重新渲染。
diff 算法不同
两者流程思维上是类似的,都是基于两个假设(使得算法复杂度降为 O (n)):
不同的组件产生不同的 DOM 结构。当 type 不相同时,对应 DOM 操作就是直接销毁老的 DOM,创建新的 DOM。
同一层次的一组子节点,可以通过唯一的 key 区分。
但两者源码实现上有区别:
Vue 基于 snabbdom 库,它有较好的速度以及模块机制。Vue Diff使用双向链表,边对比,边更新DOM。
React主要使用 diff 队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。
事件机制不同
Vue原生事件使用标准Web事件。Vue 组件自定义事件机制,是父子组件通信基础。
React原生事件被包装,所有事件都冒泡到顶层 document 监听,然后在这里合成事件下发。基于这套,可以跨端使用事件机制,而不是和 Web DOM 强绑定。
React 组件上无事件,父子组件通信使用 props。
jsx语法规则:
1.定义虚拟DOM时,不要写引号。
2.标签中混入Js表达式时要用{ }
3.样式的类名指定不要用class,要用className
.4.内联样式,要用style={{key : value}}的形式去写。
5.只有一个根标签
6.标签必须闭合
7.标签首字母
(1).若小写字母开头,则将改标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
实质:JSX 通过 babel 编译,而 babel 实际上把 JSX 编译给 React.createElement() 调用
- React.createElement()即 h 函数,返回 vnode
- 第一个参数可能是组件也可能是 html tag ,如果是组件,首字母必须大写
const imgElem = <div> <p>some text</p> <img src={imgUrl} /> </div> // 经过 babel 编译后 var imgElem = React.createElement("div",null, React.createElement("p",null,"some text"),React.createElement("img",{src:imgUrl}) );
state状态
state 是组件实例对象最重要的属性,必须是对象的形式
组件被称为状态机,通过更改 state 的值来达到更新页面显示(重新渲染组件)
组件 render 中的 this 指的是组件实例对象
组件自定义方法中的 this 为 undefined,怎么解决?
①将自定义函数改为表达式 + 箭头函数的形式(推荐)
②在构造器中用 bind()强制绑定 this
状态数据不能直接赋值,需要用 setState()
setState()有同步有异步,基本上都是异步更新,自己定义的DOM事件里setState()是同步的
同步异步原理:看是否能命中 batchUpdate 机制,就是判断 isBatchingUpdates,true 为同步,false 为异步
class ListDemo extends React.component{ constructor(props){...} render(){...} increase = () =>{ // 开始:处于 batchUpdate // isBatchingUpdates = true this.setState({ count : this.state.count + 1 }) // 结束 // isBatchingUpdates = false } } class ListDemo extends React.component{ constructor(props){...} render(){...} increase = () =>{ // 开始:处于 batchUpdate // isBatchingUpdates = true setTimeout(() => { // 由于异步,所以此时 isBatchingUpdates 是 false this.setState({ count : this.state.count + 1 }) }) // 结束 // isBatchingUpdates = false } }
能命中 batchUpdate 机制:生命周期(和它调用的函数)、React 中注册的事件(和它调用的函数),其实就是 React 可以“管理”的入口
不能命中 batchUpdate 机制:setTimeout/setInterval等(和它调用的函数)、自定义 DOM 事件(和它调用的函数),其实就是 React “管不到”的入口,因为不是在 React 中注册的
state 异步更新的话,更新前会被合并:setState()传入对象会被合并(类似于Object.assgin),传入函数不会被合并
// 传入对象会被合并,每次+1 this.setState({ count:this.state.count + 1 }) this.setState({ count:this.state.count + 1 }) this.setState({ count:this.state.count + 1 }) // 传入函数不会被合并,每次+3 this.setState((prevState,proprs) => { return{ count:prevState.count + 1 } }) this.setState((prevState,proprs) => { return{ count:prevState.count + 1 } }) this.setState((prevState,proprs) => { return{ count:prevState.count + 1 } })
组件创建 推荐函数式
函数式 函数名与render内第一个参数可以为直接调用函数 或者同名组件 但尽量使用同名组件 而且名字尽量大写
<div id="example"></div> <script type="text/babel"> function Demo() { return <h2>asdgfasd</h2> } ReactDOM.render(Demo(), document.getElementById('example'))//开发工具无法寻找组件 自定义开头尽量大写 提倡 ReactDOM.render(<Demo/>, document.getElementById('example')) </script>
总结:
1.类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3.类中所定义的方法,都是放在了类的原型对象上,供实例去使用
类式组件
类式组件构造器用来数据初始化以及为事件处理绑定实例,但均有其他方法解决 而自身使用props便需要super(props) 否则会有Bug 所以 构造器尽量不用
一般方法 直接利用箭头函数来定义 函数名=()=>{ }
数据处理 props的基本使用
props 就是在调用组件的时候在组件中添加属性传到组件内部去使用
每个组件都会有 props 属性
组件标签的所有属性都保存在 props
组件内部不能改变外部传进来的 props 属性值
接下来如果想对传入的 props 属性进行一些必传、默认值、类型的校验,就需要用到一个 prop-types 库
下载:npm i prop-types --save
引入:import PropTypes from ‘prop-types’
class Person extends React.Component{
//对标签属性进行类型、必要性的限制
static propTypes = {
name:PropTypes.string.isRequired,//限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数字
speak:PropTypes.func,//限制speak为函数
}
//指定默认标签属性值
static defaultProps = {
sex:'男', //sex默认值为男
age:18 //age默认值为18
}
}
props属性限制 函数式或类式 外添加 记住引入PropTypes 注意使用时prop小写 引入大写Prop
类内添加限制
refs 的使用
字符串形式 refs收集了ref 但不推荐用 可能被淘汰 但写法简单
推荐 回调函数方式
class Demo extends React.Component{
showData = () => {
const {input1} = this
alert(input1.value)
}
render(){
return{
<input ref={c => this.input1 = c} type="text" />
<button onClick={this.showData}>点我展示数据</button>
}
}
}
将ref当前所处的这个节点放在组件实例自身上 并取个名字叫input1 这种直接写的箭头函数 叫内联函数
官方推荐形式
createRef() 方法是 React 中的 API,它会返回一个容器,存放被 ref 标记的节点,但该容器是专人专用的,就是一个容器只能存放一个节点;
当 react 执行到 div 中第一行时,发现 input 节点写了一个 ref 属性,又发线在上面创建了 myRef 容器,所以它就会把当前的节点存到组件实例的 myRef 容器中
注意:如果你只创建了一个 ref 容器,但多个节点使用了同一个 ref 容器,则最后的会覆盖掉前面的节点,所以,你通过 this.ref 容器.current 拿到的那个节点是最后一个节点。
发生事件的元素和正要操作的元素为一个元素 不使用ref