1、React 事件绑定原理
理解:react中的事件都是合成事件,不是把每一个dom的事件绑定在dom上,而是把事件统一绑定到document中,触发时通过事件冒泡到document进行触发合成事件,因为是合成事件,所以我们无法去使用e.stopPropagation去阻止,而是使用e.preventDefault去阻止。
1.事件注册:组件更新或者装载时,在给dom增加合成事件时,需要将增加的target传入到document进行判断,给document注册原生事件回调为dispatchEvent(统一的事件分发机制)。
2.事件存储:EventPluginHub负责管理React合成事件的callback,它将callback存储到listennerBank中,另外还存储了负责合成事件的Plugin,Event存储到listennerbank中,每一个元素在listennerBank中会有唯一的key。
3.事件触发执行:点击时冒泡到docunment中,触发注册原生事件的回调dispatchEvent,获取到触发这个事件的最深层元素,事件执行利用react的批处理机制。
案例
<div onClick={this.parentClick} ref={ref => this.parent = ref}><div onClick={this.childClick} ref={ref => this.child = ref}>test</div>
</div>
点击test后
1.首先获取到this.child
2.遍历此元素的所有父元素,依次对每一级元素进行处理
3.构成合成事件
4.将每一级的合成事件存储在eventQueen事件队列中
5.遍历,是否组织冒泡,是则停止,否则继续
6.释放已经完成的事件
4.合成事件:循环所有类型的eventPlugin,对应每个事件类型,生成不同的事件池,如果是空,则生成新的,有则用之前的,根据唯一key获取到指定的回调函数,再返回带有参数的回调函数。
5.总流程:组件装载/更新 – 新增/删除事件 – eventplugin添加到ListennerBank中监听事件 – 触发事件 – 生成合成事件 – 通过唯一key获取到指定函数 – 执行指定回调函数 – 执行完毕后释放
2、什么是React?
- React 是 Facebook 在 2011 年开发的前端 JavaScript 库。
- 它遵循基于组件的方法,有助于构建可重用的UI组件。
- 它用于开发复杂和交互式的 Web 和移动 UI。
- 尽管它仅在 2015 年开源,但有一个很大的支持社区。
3、 React有什么特点?
React的主要功能如下:
- 它使用**虚拟DOM **而不是真正的DOM。
- 它可以用服务器端渲染。
- 它遵循单向数据流或数据绑定。
4、类组件和函数组件的区别
定义组件有两个要求:
- 组件名称必须以大写字母开头
- 组件的返回值只能有一个根元素
函数组件
function Welcome (props) {return <h1>Welcome {props.name}</h1>
}
ReactDOM.render(<Welcome name='react' />, document.getElementById('root'));
- 函数组件接收一个单一的
props
对象并返回了一个React元素
类组件
class Welcome extends React.Component {render() {return (<h1>Welcome { this.props.name }</h1>);}
}
ReactDOM.render(<Welcome name='react' />, document.getElementById('root'));
- 无论是使用函数或是类来声明一个组件,它决不能修改它自己的
props
。 - 所有 React 组件都必须是纯函数,并禁止修改其自身
props
。 - React是单项数据流,父组件改变了属性,那么子组件视图会更新。
- 属性
props
是外界传递过来的,状态state
是组件本身的,状态可以在组件中任意修改 - 组件的属性和状态改变都会更新视图。
区别
函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
区别 | 函数组件 | 类组件 |
---|---|---|
是否有 this | 没有 | 有 |
是否有生命周期 | 没有 | 有 |
是否有状态 state | 没有 | 有 |
。
5、请你说说React的路由是什么
1.1、基本用法
单页面得特点:只需要加载一次主页面,通过局部刷新,就可以实现跳转或者切换页面
优点:加载速度快,用户体验比较好
缺点:
- 第一次加载比传统要慢一点
- 不利seo
- 页面相对复杂
- 返回键
1.2安装react-router-dom
cnpm install react-router-dom
下载到生产环境的依赖中。
在组件中通过对象的解构方式去获取到react-router-dom
内置组件,在组件中,按需引入内置组件,在页面中进行使用:
- HashRouter表示一个路由的根容器,将来所有的路由相关的东西,都要包裹在HashRouter里面,而且一个网站中,只需要使用一次HashRouter就好了;
- Route表示一个路由规则,在Route上,有两个比较重要的属性,path,component
- Link表示一个路由的链接
代码示例
render(){`` ``return` `(`` ``<HashRouter>`` ``<div>`` ``<h1>这是网站的根目录</h1>`` ``<hr />`` ``<Link to=``"/home"``>首页</Link> `` ``<Link to=``"/movie/"``>电影</Link> `` ``<Link to=``"/about"``>关于</Link>`` ``<hr />`` ``<Route path=``"/home"` `component={Home} ></Route><hr/>`` ``<Route path=``"/movie"` `component={Movie} exact></Route><hr/>`` ``<Route path=``"/about"` `component={About}></Route><hr/>`` ``</div>`` ``</HashRouter>`` ``);`` ``}
当使用HashRouter把APP根组件的元素包裹起来之后,网站就已经启用路由了,在一个HashRouter中,只能有唯一的一个根元素。 在一个网站中,只需要使用唯一的一次<HashRouter></HashRouter>
就行了。
Route创建的标签,就是路由规则,其中path表示要匹配的路由,component表示要展示的组件。Route具有两种身份:1.它是一个路由匹配规则;2.它是一个占位符,表示将来匹配到的组件都放到这个位置
需要注意的地方
- Route 组件path地址是以/开头 ,配置component属性,是显示的组件,这个属性不可以大写
- Route组件可以单双标签使用,单标签需要/结尾,双标签不可以在中间写入别的东西
- Link to属性的地址也是/开头,Link在页面渲染的是a标签
2.1、路由传值
通过配置路由的地址,在Link跳转时
- Route path路径后面 /:id (key)
- Link to 路径后面 /top/10 (value)
接收传值:
- class类组件,this.props.match.params.属性名
- 函数组件:形参.match.params.属性名
代码示例
render(){`` ``return` `(`` ``<HashRouter>`` ``<div>`` ``<h1>这是网站的根目录</h1>`` ``<hr />`` ``<Link to=``"/movie/top/10"``>电影</Link> `` ``<hr />`` ``<Route path=``"/movie/:type/:id"` `component={Movie} exact></Route>`` ``</div>`` ``</HashRouter>`` ``);`` ``}
在Route内置组件中,配置path地址:
<Route path=``"/movie/:type/:id"` `component={Movie} exact></Route>
在Link内置组件中,配置to属性,进行跳转:
<Link to=``"/movie/top/10"``>电影</Link>
类组件中通过生命周期进行接收,this.props携带路由传递过来的数据:
render(){`` ``console.log(``this``);`` ``return` `(`` ``<div>`` ``电影--{``this``.props.match.``params``.type}--{``this``.props.match.``params``.id}`` ``</div>`` ``);`` ``}
代码优化后:
class` `Movie extends React.Component{` ` ``constructor(props){`` ``super();`` ``this``.state = {`` ``routeParams:props.match.``params`` ``}`` ``}` ` ``render(){`` ``console.log(``this``);`` ``return` `(`` ``<div>`` ``电影--{``this``.state.routeParams.type}--{``this``.state.routeParams.id}`` ``</div>`` ``);`` ``}``}
函数组件中通过形参接收传递过来的值,props形参,函数组件作为路由组件,props就是传递过来的对象,里面携带着路由传递过来的数据
import React ``from` `'react'` `export ``default` `function home(props) {`` ``return` `(`` ``<div>`` ``{props.match.``params``.id}`` ``</div>`` ``)``}
2.2、嵌套路由
嵌套路由:在路由组件中,使用Link, Route,配置子路由,实现跳转,切换;
下面为一级路由,在一级路由Home为路由组件
<Route path=``"/home"` `component={ Home }></Route>
在Home组件中继续使用Link,Route进行路由的嵌套,需要注意的就是路由地址,前部分为一级路由地址,后面接一个二级路由相应的路径
render() {`` ``return` `(`` ``<div>`` ``<ul>`` ``<li><Link to=``"/home/a"``>推荐</Link></li>`` ``<li><Link to=``"/home/b"``>新时代</Link></li>`` ``<li><Link to=``"/home/c"``>动漫</Link></li>`` ``</ul>`` ``<Route path=``"/home/a"` `component={ A }></Route>`` ``<Route path=``"/home/b"` `component={ B }></Route>`` ``<Route path=``"/home/c"` `component={ C }></Route>`` ``</div>`` ``)``}
2.3、JS实现路由跳转
引入BrowserRouter模块
import {BrowserRouter,HashRouter,Route,Link} ``from` `'react-router-dom'
使用BrowserRouter作为根容器
jump(){`` ``window.location.href = ``"/news"``}` `render(){`` ``return` `(`` ``<BrowserRouter>`` ``<div>`` ``<h1>这是网站的根目录</h1>`` ``<hr />`` ``<button onClick={()=>{``this``.jump()}}>新闻</button>`` ``<hr />`` ``<Route path=``"/news"` `component={News}></Route>`` ``</div>`` ``</BrowserRouter>`` ``);``}
在render方法中,写一个按钮,按钮名称为js跳转路由,定义一个onClick方法,箭头函数解决this指向问题,与render同级,定义一个jump方法,在jump方法中执行一句代码进行路由跳转,使用window.location.href = “路由的地址”实现路由跳转。
3、react-router-dom内置组件
首先按需引入,使用什么内置组件,就需要引入
import { BrowserRouter, Link, Route,Redirect,NavLink,Switch } ``from` `'react-router-dom'
3.1、在组件中使用NavLink
NavLink 带有选中activeClassName ,如果路由处于激活状态,显示激活class样式。
在我们之前案例的基础上,找到Link组件,我们已经学到Link组件的作用,可以进行路由的跳转,通过to属性,跳转相应的path地址。
```html``//``<ul>`` ``<li>`` ``<Link to=``"/home"``>首页</Link>`` ``</li>`` ``<li>`` ``<Link to=``"/video"``>好看视频</Link>`` ``</li>``</ul>
将组件中的Link全部换成NavLink组件
```html//
"red"` `to=
“/home”>首页</NavLink>
<li>
<NavLink activeClassName=
“red”` `to="/video"
>好看视频````````我们会发现,之前可以正常进行路由跳转,换成NavLink,还依然可以正常跳转,证明组件得跳转使用NavLink也可以实现,那么问题来了,NavLink有什么用,为什么封装了NavLink,将每一个NavLink加入一个activeClassName属性绑定一个class类样式,这时在触发NavLink时,会触发相应得样式,这样有一个切换效果。
3.2、在组件中使用Redirect内置组件
Redirect 重定向 具备to属性,可以直接跳转到指定路由。
在render方法中,使用内置组件,Redirect内置组件使用to属性,当执行到内置标签是,会进行to跳转路由,to后面接的地址是什么,就可以匹配到相应得路由组件。
```html``//``<Redirect to=``"/home/c"``></Redirect>
6、列出React的一些主要优点。
React的一些主要优点是:
- 它提高了应用的性能
- 可以方便地在客户端和服务器端使用
- 由于 JSX,代码的可读性很好
- React 很容易与 Meteor,Angular 等其他框架集成
- 使用React,编写UI测试用例变得非常容易
7、React hooks用过吗,为什么要用
函数组件比起类组件“少”了很多东西,比如生命周期、对 state 的管理等。这就给函数组件的使用带来了非常多的局限性。
React-Hooks 的出现,就是为了帮助函数组件补齐这些(相对于类组件来说)缺失的能力
8虚拟DOM的优劣如何
- 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
- 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
- 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
- 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢。
9、React有哪些限制?
React的限制如下:
- React 只是一个库,而不是一个完整的框架
- 它的库非常庞大,需要时间来理解
- 新手程序员可能很难理解
- 编码变得复杂,因为它使用内联模板和 JSX
10、什么是JSX?
JSX 是J avaScript XML 的简写。是 React 使用的一种文件,它利用 JavaScript 的表现力和类似 HTML 的模板语法。这使得 HTML 文件非常容易理解。此文件能使应用非常可靠,并能够提高其性能。下面是JSX的一个例子:
render(){return( <div><h1> Hello World from Edureka!!</h1></div>); }
11、react中不同组件之间如何做到数据交互?
- 父组件向子组件通信:使用 props
- 子组件向父组件通信:使用 props 回调
- 跨级组件间通信:使用 context 对象
- 非嵌套组件间通信:使用事件订阅
事实上,在组件间进行通信时,这些通信方式都可以使用,区别只在于使用相应的通信方式的复杂程度和个人喜好,选择最合适的那一个。比如,通过事件订阅模式通信不止可以应用在非嵌套组件间,还可以用于跨级组件间,非嵌套组件间通信也可以使用 context 等。关键是选择最合适的方式。
当然,自己实现组件间的通信还是太难以管理了,因此出现了很多状态管理工具,如 flux、redux 等,使用这些工具使得组件间的通信更容易追踪和管理。12、react中refs的作用是什么?
Refs
是React
提供给我们的安全访问DOM
元素或者某个组件实例的句柄- 可以为元素添加
ref
属性然后在回调函数中接受该元素在DOM
树中的句柄,该值会作为回调函数的第一个参数返回
13、请列举react生命周期函数。
React 生命周期分为三种状态 1. 初始化 2.更新 3.销毁
- 初始化
1、getDefaultProps()
设置默认的props,也可以用dufaultProps设置组件的默认属性.
2、getInitialState()
在使用es6的class语法时是没有这个钩子函数的,可以直接在constructor中定义this.state。此时可以访问this.props
3、componentWillMount()
组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。
在渲染前调用,在客户端也在服务端。
4、 render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。
5、componentDidMount()
组件渲染之后调用,只调用一次。
在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过**this****.getDOMNode()**来进行访问。
如果你想和其他JavaScript框架一起使用,可以在这个方法中调用****setTimeout, setInterval或者发送****AJAX请求等操作(防止异步操作阻塞UI)。
- 更新
6、componentWillReceiveProps(nextProps)
组件初始化时不调用,组件接受新的props时调用。
*使用componentWillReceiveProps的时候,不要去向上分发,调用父组件的相关setState方法,否则会成为死循环*
*在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。*
7、shouldComponentUpdate(nextProps, nextState)
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,
如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,
节省大量性能,尤其是在dom结构复杂的时候
返回一个布尔值。在组件接收到新的props或者state时被调用。
在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。
8、componentWillUpdata(nextProps, nextState)
组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state
9、render()
组件渲染
10、componentDidUpdate()
组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。
- 卸载
11、componentWillUnmount()
组件将要卸载时调用,一些事件监听和定时器需要在此时清除。
二、组件生命周期的执行次数是什么样子的
[](javascript:void(0)😉
只执行一次: constructor、componentWillMount、componentDidMount执行多次:render 、子组件的componentWillReceiveProps、componentWillUpdate、componentDidUpdate有条件的执行:componentWillUnmount(页面离开,组件销毁时)不执行的:根组件(ReactDOM.render在DOM上的组件)的componentWillReceiveProps(因为压根没有父组件给传递props)
[](javascript:void(0)😉
三、React生命周期执行顺序
Mounting中为组件的挂载过程
componentWillMount组件挂载之前
render组件的渲染方法
componentDidMount组件挂载完成执行
Updation中为组件数据发生变化的过程
props独有
componentWillReceiveProps
触发条件 1. 当一个组件从父组件接收了参数。
2.如果这个组件第一次被父组件加载的时候不会被执行。
3.这个组件之前已经存在于父组件中,并且接收的数据发生变动这时此方法才会被触发。
props和states共有
shouldComponentUpdata 是否要更新数据?需要一个返回值true继续执行下面的生命周期,false就会终止当前组件数
componentWillUpdate 组件将要更新
render组件的重新渲染
componentDidUpdata 组件完成更新
Unmounting组件卸载
componentWillUnmount 组件销毁的时候触发14、你了解 Virtual DOM 吗?解释一下它的工作原理。
Virtual DOM 是一个轻量级的 JavaScript 对象,它最初只是 real DOM 的副本。它是一个节点树,它将元素、它们的属性和内容作为对象及其属性。 React 的渲染函数从 React 组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
Virtual DOM 工作过程有三个简单的步骤。
- 每当底层数据发生改变时,整个 UI 都将在 Virtual DOM 描述中重新渲染。
- 然后计算之前 DOM 表示与新表示的之间的差异。
- 完成计算后,将只用实际更改的内容更新 real DOM。
15、为什么浏览器无法读取JSX?
浏览器只能处理 JavaScript 对象,而不能读取常规 JavaScript 对象中的 JSX。所以为了使浏览器能够读取 JSX,首先,需要用像 Babel 这样的 JSX 转换器将 JSX 文件转换为 JavaScript 对象,然后再将其传给浏览器。
16、与 ES5 相比,React 的 ES6 语法有何不同?
以下语法是 ES5 与 ES6 中的区别:
- require 与 import
// ES5 var React = require('react');// ES6 import React from 'react'; 12345
- export 与 exports
// ES5 module.exports = Component;// ES6 export default Component; 12345
- component 和 function
// ES5 var MyComponent = React.createClass({render: function() {return<h3>Hello Edureka!</h3>;} });// ES6 class MyComponent extends React.Component {render() {return<h3>Hello Edureka!</h3>;} } 123456789101112131415
- props
// ES5 var App = React.createClass({propTypes: { name: React.PropTypes.string },render: function() {return<h3>Hello, {this.props.name}!</h3>;} });// ES6 class App extends React.Component {render() {return<h3>Hello, {this.props.name}!</h3>;} } 12345678910111213141516
- state
// ES5 var App = React.createClass({getInitialState: function() {return { name: 'world' };},render: function() {return<h3>Hello, {this.state.name}!</h3>;} });// ES6 class App extends React.Component {constructor() {super();this.state = { name: 'world' };}render() {return<h3>Hello, {this.state.name}!</h3>;} } 12345678910111213141516171819202122
10. React与Angular有何不同?
主题 React Angular 1. 体系结构 只有 MVC 中的 View 完整的 MVC 2. 渲染 可以在服务器端渲染 客户端渲染 3. DOM 使用 virtual DOM 使用 real DOM 4. 数据绑定 单向数据绑定 双向数据绑定 5. 调试 编译时调试 运行时调试 6. 作者 Facebook Google 17、React与Angular有何不同?
主题 React Angular 1. 体系结构 只有 MVC 中的 View 完整的 MVC 2. 渲染 可以在服务器端渲染 客户端渲染 3. DOM 使用 virtual DOM 使用 real DOM 4. 数据绑定 单向数据绑定 双向数据绑定 5. 调试 编译时调试 运行时调试 6. 作者 Facebook Google 18、你理解“在React中,一切都是组件”这句话。
组件是 React 应用 UI 的构建块。这些组件将整个 UI 分成小的独立并可重用的部分。每个组件彼此独立,而不会影响 UI 的其余部分。
19、解释 React 中 render() 的目的
每个React组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内,例如
<form>
、<group>
、<div>
等。此函数必须保持纯净,即必须每次调用时都返回相同的结果。20、如何将两个或多个组件嵌入到一个组件中?
可以通过以下方式将组件嵌入到一个组件中:
class MyComponent extends React.Component{render(){return( <div><h1>Hello</h1><Header/></div>);} } class Header extends React.Component{render(){return<h1>Header Component</h1> }; } ReactDOM.render(<MyComponent/>, document.getElementById('content') );