文章目录
- React与Vue的对比
- React 的工作原理
- 使用 React 有何优点
- React生命周期
- react中key的作用
- setState
- 什么是虚拟DOM?
- memo
- 讲讲什么是 JSX ?
- React Hooks
- sass和less的区别
- react中组件传值
- 组件分类
- refs 作用
- 在构造函数调用 super 并将 props 作为参数传入的作用是啥?
- 状态(state)和属性(props)之间有何不同
- 如何 实现React.createElement ?
- SPA、SEO、SSR ,CSR
- React 同构时页面加载流程
- 怎么阻止组件的渲染
- 前端路由
- 事件机制
- react-script
- 描述 Flux 与 MVC?
- 如何避免在React重新绑定实例?
React与Vue的对比
- 组件化
Vue是如何实现组件化的:通过.vue文件
templete 结构,script 行为,style 样式
React是如何实现组件化的:React有组件化的概念,一切都是以js来表现的 - 开发团队不同
- 移动APP开发
Vue,结合Weex技术,提供了迁移到移动端App开发的体验
React,结合ReactNative,提供了无缝迁移到移动端App开发的体验(最流行)
改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState 来改变状态,并且使用这个 API 也有一些坑点。
Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。
语法:React 需要使用 JSX,Vue 使用了模板语法
- 上手
vue - easy 官方做了很多,CSS script
react ,上手偏难, - 数据绑定
vue model双向的
react是单向的:value + onChange,实现双向绑定 - 模板
vue H5模板
react JSX - api
vue 多,计算属性,watch 这种神器
react 少,更多功能留给社区,比如写个函数还有bind以下 - 应用
vue 适合面向用户的,复杂度稍低一些的
react 复杂的 - 测试
react 函数式编程利于测试
React 的工作原理
React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时,
- React 首先会通过 “diff” 算法来标记虚拟 DOM 中的改变
- 第二步是调节(reconciliation),会用 diff 的结果来更新 DOM。
使用 React 有何优点
- JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的
- 支持服务端渲染,可改进SEO和性能
- 易于测试
- React 只关注 View 层,所以可以和其它任何框架(如Backbone.js, Angular.js)一起使用
React生命周期
react生命周期中,最适合与服务端进行数据交互的是哪个函数
componentDidMount
:在这个阶段,实例和dom已经挂载完成,可以进行相关的dom操作
在constructor中绑定事件函数的this指向
把一个对象的方法赋值给一个变量会造成this的丢失,所以需要绑定this
,把绑定放在构造函数中可以保证只绑定一次函数,如果放在render函数中绑定this的话每次渲染都会去绑定一次this,那样是很耗费性能的。
除了在构造函数中绑定 this,还有其它方式吗
你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-react-app 也是默认支持的。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。
应该在 React 组件的何处发起 Ajax 请求
在 React 组件中,应该在 componentDidMount 中发起网络请求。这个方法会在组件第一次“挂载”(被添加到 DOM)时执行,在组件的生命周期中仅会执行一次。
更重要的是,你不能保证在组件挂载之前 Ajax 请求已经完成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用。
react中key的作用
key是React中用于追踪哪些列表中元素被修改、删除或者被添加的辅助标识
。
在diff算法中,key用来判断该元素节点是被移动过来的还是新创建的元素,减少不必要的元素重复渲染。
此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系
例如:在列表循环的时候React会要求每一个列表项有一个独一无二,稳定的key值,它的目的是为了当状态改变时新旧状态的每一个列表项能够对应起来,方便比对。
使用:key 需要写在用数组渲染出来的元素内部,并且需要赋予其一个稳定的值。稳定在这里很重要,因为如果 key 值发生了变更,react 则会触发 UI 的重渲染。
- key 的唯一性
在相邻的元素间,key 值必须是唯一的,如果出现了相同的 key,同样会抛出一个 Warning,告诉相邻组件间有重复的 key 值。并且只会渲染第一个重复 key 值中的元素,因为 react 会认为后续拥有相同 key 的都是同一个组件。 - key 值不可读
虽然我们在组件上定义了 key,但是在其子组件中,我们并没有办法拿到 key 的值,因为 key 仅仅是给 react 内部使用的。如果我们需要使用到 key 值,可以通过其他方式传入,比如将 key 值赋给 id 等。
setState
当你调用setState的时候,发生了什么事?
setState(updater, callback)这个方法是用来告诉react组件数据有更新,有可能需要重新渲染。它是异步的,react通常会集齐一批需要更新的组件,然后一次性更新
来保证渲染的性能;
- 将传递给 setState 的对象
合并到
组件的当前状态,触发所谓的调和过程(Reconciliation) - 然后生成新的DOM树并和旧的DOM树使用Diff算法对比
- 根据对比差异对界面进行最小化重渲染
但在react的生命周期和合成事件中,react仍然处于他的更新机制中,这时更新标识为true。按照上述过程,这时无论调用多少次setState,都会不会执行更新,而是将要更新的state存入_更新队列中,将要更新的组件存入一个数组中
;当上一次更新机制执行完毕后会将更新标识设置为false。这时将一次性执行之前累积的setState。
由执行机制看,setState本身并不是异步的,而是如果在调用setState时,如果react正处于更新过程,当前更新会被暂存,等上一次更新执行后在执行,这个过程给人一种异步的假象;可以理解成在生命周期和合成事件中,setState是异步的,但是在原生事件中,setState是同步的;
setState第二个参数的作用
因为setState是一个异步
的过程,所以说执行完setState之后不能立刻更改state里面的值。如果需要对state数据更改监听,setState提供第二个参数,就是用来监听state里面数据的更改,当数据更改完成,调用回调函数。
为什么建议传递给 setState 的参数是一个 callback 而不是一个对象
setState它是一个异步函数,他会合并多次修改,降低diff算法的比对频率。这样也会提升性能。
因为 this.props 和 this.state 的更新是异步的,不能依赖它们的值去计算下一个 state。
什么是虚拟DOM?
虚拟 DOM (VDOM)是真实 DOM 在内存中的表示。UI 的表示形式保存在内存中,并与实际的 DOM 同步。这是一个发生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为调和
。
为什么虚拟 dom 会提高性能?
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
具体实现步骤如下:
- 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
- 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
- 把树形结构按照层级分解,只比较同级元素。
diff算法
- 给列表结构的每个单元添加唯一的key属性,方便比较。
- React 只会匹配相同 class 的 component(这里面的class指的是组件的名字)
- 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
- 选择性子树渲染。开发人员可以重写shouldComponentUpdate提高diff的性能。。
memo
在类组件中,我们使用shouldComponentUpdate
来避免不需要的渲染
// 使用 shouldComponentUpdate
class Foo {
shouldComponentUpdate(nextProps,nextState) {
if(nextProps.SOME_VALUE === this.props.SOME_VALUE){
return false
}
return true;
}
render () {
return <div>{props.SOME_VALUE}</div>
}
}
// 或者直接继承 PureComponent
class Foo extends PureComponent {
render () {
return <div>{props.SOME_VALUE}</div>
}
}
函数组件使用memo
函数组件用来避免非必要更新
const Foo = memo(function (props) {
return <div>{props.SOME_VALUE}</div>
})
shouldComponentUpdate(nextProps, nextState)
当父组件被重新渲染时即render函数执行时,子组件就会默认被重新渲染,但很多时候是不需要重新渲染每一个子组件的。这时就可以使用 shouldComponentUpdate 来判断是否真的需要重新渲染子组件
。仅仅一个判断,就可以节约很多的消耗。
所以对于父组件发生变化而子组件不变的情况
,使用shouldComponentUpdate会提升性能。
shouldComponentUpdate(nextProps, nextState) {
if(nextProps.content === this.props.content) {
return false;
} else {
return true;
}
}
使用PureComponent
PureComponent
内部帮我们实现了shouldComponentUpdate
的比较,其他和Component一样。但是在shouldComponentUpdate进行的是一个浅比较
浅比较只比较第一层的基本类型和引用类型值是否相同
何时使用Component还是PureComponent?
PureComponent通过prop和state的浅比较来实现shouldComponentUpdate,某些情况下可以用PureComponent提升性能
PureComponent中的判断逻辑是浅比较,如果当状态更新时是一个引用对象内部的更新,那么这个时候是不适用的
浅比较(shallowEqual),即react源码中的一个函数,然后根据下面的方法进行是不是PureComponent的判断,帮我们做了本来应该我们在shouldComponentUpdate中做的事情
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
shouldComponentUpdate(nextProps, nextState) {
return (nextState.person !== this.state.person);
}
讲讲什么是 JSX ?
当 Facebook 第一次发布 React 时,他们还引入了一种新的 JS 语言 JSX,将原始 HTML 模板嵌入到 JS 代码中。JSX 代码本身不能被浏览器读取,必须使用Babel和webpack等工具将其转换为传统的JS。很多开发人员就能无意识使用 JSX,因为它已经与 React 结合在一直了。
class MyComponent extends React.Component {
render() {
let props = this.props;
return (
<div className="my-component">
<a href={props.url}>{props.name}</a>
</div>
);
}
}
这三个点(…)在 React 干嘛用的?
… 在React(使用JSX)代码中做什么?它叫什么?
<Modal {…this.props} title=‘Modal heading’ animation={false}/>
这个叫扩展操作符号或者展开操作符,例如,如果this.props包含a:1和b:2,则
<Modal {…this.props} title=‘Modal heading’ animation={false}>
等价于下面内容:
< Modal a={this.props.a} b={this.props.b}
title=‘Modal heading’ animation={false}>
扩展符号不仅适用于该用例,而且对于创建具有现有对象的大多数(或全部)属性的新对象非常方便,在更新state 咱们就经常这么做:
this.setState(prevState => {
return {foo: {…prevState.foo, a: “updated”}};
});
React Hooks
使用 React Hooks 好处是啥?
首先,Hooks 通常支持提取和重用跨多个组件通用的有状态逻辑,而无需承担高阶组件或渲染 props 的负担。Hooks 可以轻松地操作函数组件的状态,而不需要将它们转换为类组件。
Hooks 在类中不起作用,通过使用它们,可以完全避免使用生命周期方法
,例如 componentDidMount、componentDidUpdate、componentWillUnmount。相反,使用像useEffect这样的内置钩子。
什么是 React Hooks?
Hooks是 React 16.8 中的新添加内容。它们允许在不编写类的情况下使用state和其他 React 特性·
。
使用 Hooks,可以从组件中提取有状态逻辑,这样就可以独立地测试和重用它。
React 中的 useState() 是什么?
useState 是一个内置的 React Hook。
useState(0) 返回一个元组,其中第一个参数count是计数器的当前状态,setCounter 提供更新计数器状态的方法。
可以在任何地方使用setCounter方法更新计数状态。在这种情况下,咱们在setCount函数内部使用它可以做更多的事情,使用 Hooks,能够使咱们的代码保持更多功能,还可以避免过多使用基于类的组件。
sass和less的区别
定义变量的符号不同,less是用@,sass使用$
变量的作用域不同。
- less在全局定义,就作用在全局;在代码块中定义,就作用于整个代码块。
- 而sass只作用域- 全局。
react中组件传值
父传子(组件嵌套):父组件定义一个属性,子组件通过this.props接收。
子传父:父组件定义一个属性,并将一个回调函数赋值给定义的属性,然后子组件调用传过来的函数,并将参数传进去,在父组件的回调函数中即可获得子组件传过来的值。
组件分类
类组件和函数组件之间的区别是啥?
- 类组件可以使用其他特性,如状态 state 和生命周期钩子,也能使组件直接访问 store 并维持状态(Class component)。
- 函数式组件没有自己的state,但有自己的props;没有生命周期;不能连接redux。
当组件只是接收 props 渲染到页面时,就是无状态组件
,就属于函数组件,也被称为哑组件或展示组件。
函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
![](https://i-blog.csdnimg.cn/blog_migrate/1bc8ad7b5881103ed0b0d037926b1d3f.png)
展示组件和容器组件之间有何不同
- 展示组件关心组件看起来是什么。(Presentational component)
展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。 - 容器组件则更关心组件是如何运作的。(Container component)
容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。
无状态组件
无状态组件就是使用定义函数的方式来定义组件
,这种组件相比于使用类的方式
来定义的组件(有状态组件),少了很多初始化过程,更加精简。
可以使用无状态组件应当尽可能的使用无状态组件,会大幅度提升效率
受控组件(controlled component)
- 在 HTML 中,表单元素如 < input >、< textarea >和< select >通常维护自己的状态,并根据用户输入进行更新。当用户提交表单时,来自上述元素的值将随表单一起发送。
- 而 React 的工作方式则不同。包含表单的组件将
跟踪其state中的输入值
,并在每次回调函数(例如onChange)触发时重新渲染组件,因为状态被更新。以这种方式由 React 控制其值的输入表单元素
称为受控组件。
受控组件和非受控组件区别是啥?
- 受控组件是 React 控制中的组件,并且是表单数据真实的唯一来源。
- 非受控组件是由 DOM 处理表单数据的地方,而不是在 React 组件中。
尽管非受控组件通常更易于实现,因为只需使用refs即可从 DOM 中获取值
,但通常建议优先选择受控制的组件,而不是非受控制的组件。
这样做的主要原因是受控组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式。
高阶组件 HOC (higher order component)
高阶组件是一个
以组件为参数并返回一个新组件
的函数。
高阶组件就是一个函数,接收一个组件,经过处理后返回后的新的组件;
高阶组件,其实是一种模式
:称其为纯组件,因为它们可以接受任何动态提供的子组件,但不会修改或复制输入组件中的任何行为。
复用代码
,可以对逻辑代码进行抽离,或者添加某个共用方法;
eg:
- react-redux :connect就是一个高阶组件,接收一个component,并返回一个新的componet,处理了监听store和后续的处理;
- react-router :withrouter 为一个组件注入 history对象;
const EnhancedComponent = higherOrderComponent(WrappedComponent);
HOC 可以用于以下许多用例
- 代码重用、逻辑和引导抽象
- 渲染劫持
- state 抽象和操作
- props 处理
除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。
function add(a, b) {
return a + b
}
现在如果我想给这个 add 函数添加一个输出结果的功能,那么你可能会考虑我直接使用 console.log 不就实现了么。说的没错,但是如果我们想做的更加优雅并且容易复用和扩展,我们可以这样去做:
function withLog (fn) {
function wrapper(a, b) {
const result = fn(a, b)
console.log(result)
return result
}
return wrapper
}
const withLogAdd = withLog(add)
withLogAdd(1, 2)
这个做法在函数式编程里称之为高阶函数,大家都知道 React 的思想中是存在函数式编程的,高阶组件和高阶函数就是同一个东西。
我们实现一个函数,传入一个组件,然后在函数内部再实现一个函数去扩展传入的组件,最后返回一个新的组件,这就是高阶组件的概念,作用就是为了更好的复用代码。
高阶组件和父组件的区别?
高阶组件可以重写传入组件的state,function,props;可以对代码逻辑进行抽离,重写;
父组件只是控制子组件的view层;(UI关系)
refs 作用
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的API
。
在典型的数据流中,props 是父子组件交互的唯一方式,想要修改子组件,需要使用新的pros重新渲染它。凡事有例外,某些情况下咱们需要在典型数据流外,强制修改子代,这个时候可以使用 Refs。
在组件添加一个 ref 属性,该属性的值是一个回调函数
,接收作为其第一个参数的底层 DOM 元素或组件的挂载实例。
class UnControlledForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={(input) => this.input = input} />
<button type='submit'>Submit</button>
</form>
)
}
}
input 元素有一个ref属性,它的值是一个函数。该函数接收输入的实际 DOM 元素,然后将其放在实例上,这样就可以在 handleSubmit 函数内部访问它。
经常被误解的只有在类组件中才能使用 refs,但是refs也可以通过利用 JS 中的闭包与函数组件一起使用。
function CustomForm ({handleSubmit}) {
let inputElement
return (
<form onSubmit={() => handleSubmit(inputElement.value)}>
<input
type='text'
ref={(input) => inputElement = input} />
<button type='submit'>Submit</button>
</form>
)
}
在构造函数调用 super 并将 props 作为参数传入的作用是啥?
在调用 super() 方法之前,子类无法使用this引用,ES6 子类也是如此。
将 props 参数传递给 super() 的主要原因是让子构造函数中能够通过this.props来获取传入的 props。
传递 props
class MyComponent extends React.Component {
constructor(props) {
super(props);
console.log(this.props); // { name: 'sudheer',age: 30 }
}
}
没传递 props
class MyComponent extends React.Component {
constructor(props) {
super();
console.log(this.props); // undefined
// 但是 props 参数仍然可用
console.log(props); // Prints { name: 'sudheer',age: 30 }
}
render() {
// 构造函数外部不受影响
console.log(this.props) // { name: 'sudheer',age: 30 }
}
}
上面示例揭示了一点。props 的行为只有在构造函数中是不同的,在构造函数之外(比如render)也是一样的。
状态(state)和属性(props)之间有何不同
State
是一种数据结构,用于组件挂载时所需的默认值。State可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。
props
是组件的配置。props由父组件传递给子组件,就子组件而言,props是不可变的。组件不能改变自身props,但是可以把其他子组件的props一起管理。
props也不仅仅是数据,回调函数也可以通过props传递。
如何 实现React.createElement ?
![](https://i-blog.csdnimg.cn/blog_migrate/ecefac4ee85ed3d90099b2ff438d1c98.png)
SPA、SEO、SSR ,CSR
-
SPA(single page application) 单页面应用,是前后端分离时提出的一种解决方案。
优点:页面之间切换快;减少了服务器压力;
缺点:首屏打开速度慢,不利于 SEO 搜索引擎优。 -
SEO(search engine optimization)搜索引擎优化,利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。
我们之前说 SPA 单页面应用,通过 ajax 获取数据,这就难保证我们的页面能被搜索引擎收到。并且有一些搜索引擎不支持js 和通过 ajax 获取的数据,那就更不用提 SEO 了,为解决这个问题,SSR 登场了··· -
SSR (server side rendering)服务端渲染
用户请求服务器,服务器上直接生成 HTML 内容并返回给浏览器。页面的内容是由 Server 端生成的。一般来说,服务器端渲染的页面交互能力有限,如果要实现复杂交互,还是要通过引入 JavaScript 文件来辅助实现
优点:
更快的响应时间,不用等待所有 js 都下载完成,浏览器便能显示比较完整的页面;
更好的 SSR,我们可以将 SEO 关键信息直接在后台就渲染成 html,从而保证搜索引擎都能爬取到关键数据。
SSR 的出现一定程度上解决了 SPA 首屏慢的问题,又极大的减少了普通 SPA 对于 SEO 的不利影响。
缺点:
React代码在服务器端执行,占用更多的 CUP 和内存资源,很大的消耗了服务器的性能;
一些常用的浏览器的 api 可能无法正常使用,比如 window,document,alert等,如果使用的话需要对运行环境加以判断。 -
CSR:客户端渲染,即普通的React项目渲染方式。
页面初始加载的 HTML 页面中无网页展示内容,需要加载执行JavaScript 文件中的 React 代码,通过 JavaScript 渲染生成页面,同时JavaScript 代码会完成页面交互事件的绑定,
CSR带来的问题:首屏加载时间过长,SEO 不友好
(因为时间在往返的几次网络请求中就耽搁了,而且因为CSR返回到页面的HTML中没有内容,就只有一个root空元素,页面内容是靠js渲染出来的,爬虫在读取网页时就抓不到信息
,所以SEO不友好)
客户端渲染流程:
- 浏览器发送请求
- 服务器返回HTML
- 浏览器发送bundle.js请求
- 服务器返回bundle.js
- 浏览器执行bundle.js中的React代码
React 同构时页面加载流程
同构:同构这个概念存在于 Vue,React 这些新型的前端框架中,同构实际上是客户端渲染和服务器端渲染的一个整合。
我们把页面的展示内容和交互写在一起,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互
- 服务端运行React代码渲染出HTML
- 浏览器加载这个无交互的HTML代码
- 浏览器接收到内容展示
- 浏览器加载JS文件
- JS中React代码在浏览器中重新执行
怎么阻止组件的渲染
在组件的 render 方法中返回 null 并不会影响触发组件的生命周期方法
前端路由
原理
前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新页面。目前前端使用的路由就只有两种实现方式。
- Hash 模式
- History 模式
Hash 模式
www.test.com/#/
就是 Hash URL,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com。
window.addEventListener('hashchange', () => {
// ... 具体逻辑
})
History 模式
History 模式是 HTML5 新推出的功能,主要使用 history.pushState
和 history.replaceState
改变 URL。
通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。
// 新增历史记录
history.pushState(stateObject, title, URL)
// 替换当前历史记录
history.replaceState(stateObject, title, URL)
当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
window.addEventListener('popstate', e => {
// e.state 就是 pushState(stateObject) 中的 stateObject
console.log(e.state)
})
两种模式对比
- Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的同源 URL
- History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
- Hash 模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置 index.html 页面用于匹配不到静态资源的时候
事件机制
React 其实自己实现了一套事件机制,首先我们考虑一下以下代码:
const Test = ({ list, handleClick }) => ({
list.map((item, index) => (
<span onClick={handleClick} key={index}>{index}</span>
))
})
JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理
的方式,将所有的事件都统一绑定在了document
上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。
另外冒泡到 document 上的事件也不是原生浏览器事件,而是React自己实现的合成事件(SyntheticEvent)。因此我们如果不想要事件冒泡的话,调用 event.stopPropagation 是无效的,而应该调用 event.preventDefault。
那么实现合成事件的目的好处有两点,分别是:
- 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力
- 对于原生浏览器事件来说,浏览器会给监听器创建一个事件对象。如果你有很多的事件监听,那么就需要分配很多的事件对象,造成高额的内存分配问题。但是对于合成事件来说,有一个事件池专门来管理它们的创建和销毁,当事件需要被使用时,就会从池子中复用对象,事件回调结束后,就会销毁事件对象上的属性,从而便于下次复用事件对象。
react-script
使用create-react-app
生成的React项目是使用react-script
来运行的,其package.json如下:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
源代码
命令行相关代码入口位于:/node_modules/react-scripts/bin/react-scripts.js
,其对应的几个脚本都在scripts
文件夹中
eject
将 react-script的所有封装内容释放回项目根目录
描述 Flux 与 MVC?
传统的 MVC 模式在分离数据(Model)、UI(View)和逻辑(Controller)方面工作得很好,但是 MVC 架构经常遇到两个主要问题:
- 数据流不够清晰:跨视图发生的级联更新常常会导致混乱的事件网络,难于调试。
- 缺乏数据完整性:模型数据可以在任何地方发生突变,从而在整个UI中产生不可预测的结果。
使用 Flux 模式的复杂用户界面不再遭受级联更新,任何给定的React 组件都能够根据 store 提供的数据重建其状态。Flux 模式还通过限制对共享数据的直接访问来加强数据完整性。
如何避免在React重新绑定实例?
有几种常用方法可以避免在 React 中绑定方法:
1.将事件处理程序定义为内联箭头函数
class SubmitButton extends React.Component {
constructor(props) {
super(props);
this.state = {
isFormSubmitted: false
};
}
render() {
return (
<button onClick={() => {
this.setState({ isFormSubmitted: true });
}}>Submit</button>
)
}
}
2.使用箭头函数来定义方法:
class SubmitButton extends React.Component {
state = {
isFormSubmitted: false
}
handleSubmit = () => {
this.setState({
isFormSubmitted: true
});
}
render() {
return (
<button onClick={this.handleSubmit}>Submit</button>
)
}
}
3.使用带有 Hooks 的函数组件
const SubmitButton = () => {
const [isFormSubmitted, setIsFormSubmitted] = useState(false);
return (
<button onClick={() => {
setIsFormSubmitted(true);
}}>Submit</button>
)
};