React中常被问到的⾯试题
什么是 jsX
要了解 jsX,⾸先先了解什么三个主要问题,什么事 VDOM,差异更新和 JSX 建模:
VDOM,也叫虚拟 DOM,它是仅存于内存中的 DOM,因为还未展⽰到页⾯中,所以称为 VDOM
var vdom = ateElement("div");
上⾯这⼀句就是最简单的虚拟 DOM
var vdom = ateElement("div");
document.body.append(vdom);
上⾯这两句就是把虚拟 DOM 转化为真实 DOM,其实就是把节点 append 到页⾯中
常见 DOM 操作,就三类:增、删、改。对应的 DOM 操作如下:
DOM 操作DOM ⽅法
增加⼀个节点appendChild
删除⼀个节点removeChild
更改⼀个节点replaceChild
以前我们写代码经常会拼接完模板,简单粗暴的⽤$(el).html(template)整块节点替换
这样做最⼤的问题在于性能,如果页⾯⽐较⼩,问题不⼤,但如果页⾯庞⼤,这样会出现卡顿,⽤户体验会很差,所以解决办法就是差量更新
差量更新就是只对局⾯的 html ⽚段进⾏更新。⽐如你加了⼀个节点,那么我就只更新这个节点,我⽆需整个模板替换。这样做效率就会提⾼。但问题在于,不知道哪个节点更新了,哪个节点删除了,哪个节点替换了,所以我们需要对 DOM 建模
DOM 建模,简单点说就是⽤⼀个 JS 对象来表⽰ VDOM。
如果我们可以⽤⼀个 JS 对象来表⽰ VDOM,那么这个对象上多⼀个属性(增加节点),少⼀个属性(删除节点),或者属性值变了(更改节点),就很清醒了
DOM 也叫 DOM 树,是⼀个树形结构,DOM 树上有很多元素节点,要对 VDOM 进⾏建模,本质上就是对⼀个个元素节点进⾏建模,然后再把节点放回 DOM 树的指定位置
JSX 建模,每个节点都是由以下三部分组成:
type : 元素类型
props : 元素属性
children : ⼦元素集合
{type:"div",props: null, children:[
{type:"img",props:{"src":"avatar.png", "className":"profile"},children:[],
{type:"h3",props: null, children:[{[user.firstName, user.lastName].join(' ')}],
]}
上⾯ VDOM 建模是⽤下⾯的 HTML 结构转出来的
var profile = (
<div>
<img src="avatar.png" className="profile" />
<h3>{[user.firstName, user.lastName].join(" ")}</h3>
</div>
);
但这段代码并不是合法的 js 代码,它是⼀种被称为 jsx 的语法扩展,通过它我们就可以很⽅便的在 js 代码中书写 html ⽚段
本质上,jsx 是语法糖,上⾯这段代码会被 babel 转换成如下代码
"div",
null,
src: "avatar.png",
className: "profile"
}),
);
⽽上⾯的这段被转化的代码是将我们的 VDOM 配合ateElement(⼀般应该是createElement函数)转化为真实 DOM
注意,如果是⾃定义组件<App />会转化为ateElement(App, null),因为组件是class App extends React.Component {}这样定义的,所以 App 进⼊createElement函数⾥⾯就会变成是⼀个对象
这⾥我们可以把这个函数放进createElement()⾥⾯⽣成⼀个 VDOM 对象,然后⽤⽣成的 VDOM 对象,配合render()⽣成⼀个 DOM 插⼊页⾯,从⽽转变成真实 DOM 结构
元素和组件有什么区别
React 元素,它是 React 中最⼩基本单位,我们可以使⽤上⾯提到的 JSX 语法轻松地创建⼀个 React 元素:
const element = <div>It is element</div>;
这个元素经过 babel 转化之后会变成带 ateElement 的函数,⽽ateElement() 构建 React 元素的时候。它接受三个参数,第⼀个参数可以是⼀个标签名。如 div、p,或者 React 组件。第⼆个参数为传⼊的属性,如 class,style。第三个以及之后的参数,皆作为组件的⼦组件。
{
"_context": Object,
"_owner": null,
"key": null,
"props": {
"className": null,
"children": "It is element"
},
"ref": null,
"type": "div"
}
⽽ React 中有三种⽅法构建组件:
ES6 类推荐使⽤
⽆状态函数
函数式很简单,就像我们平常写函数⼀个,接受⼀个参数作为输⼊,然后进⾏相应的输出,只不过它输出的 JSX 格式,注意组件只能有⼀个根元素:
function Wscats(props) {
return <h1> {props.name}</h1>;
}
//ES6
const Wscats = ({ props }) => (
<div>
<h1>{props.name}</h1>
</div>
);
类式组件如下,是⼀个纯函数:
import React from 'react';
//推荐这种
class Wscats extends React.Component {
render() {
return <h1> {this.props.name}</h1>
}
}
//or 这种⽅法将要废弃
var Wscats = ateClass({
render() {
return <h1> {this.props.name}</h1>
}
}
类组件和函数组件的区别
类组件有⽣命周期和状态,⽽函数组件则没有。
React 给类组件定义了⾮常完善的⽣命周期函数,类组件渲染到页⾯中叫挂载mounting,所以渲染完成后,叫做componentDidMount,类组件的卸载叫Unmount,所以类组件将要卸载叫做componentWillUnmount。我们想要在什么时候使⽤状态,就可以直接调⽤⽣命周期函
数,把想要做的事情写到函数⾥⾯,⽣命周期函数直接写在类组件内部,类组件在初始化时会触发 5 个钩⼦函数:
id钩⼦函数⽤处
1getDefaultProps()设置默认的 props,也可以⽤ defaultProps 设置组件的默认属性
2getInitialState()在使⽤ es6 的 class 语法时是没有这个钩⼦函数的,可以直接在 constructor 中定义 this.state。此时可以访问this.props
3componentWillMount()组件初始化时只调⽤,以后组件更新不调⽤,整个⽣命周期只调⽤⼀次,此时可以修改 state
es6新特性面试4render()react 最重要的步骤,创建虚拟 dom,进⾏ diff 算法,更新 dom 树都在此进⾏。此时就不能更改 state 了
5componentDidMount()组件渲染之后调⽤,可以通过 DOMNode()获取和操作 dom 节点,只调⽤⼀次
类组件在更新时也会触发 5 个钩⼦函数:
id钩⼦函数⽤处
6componentWillReceivePorps(nextProps)组件初始化时不调⽤,组件接受新的 props 时调⽤
7shouldComponentUpdate(nextProps,
nextState)
react 性能优化⾮常重要的⼀环。组件接受新的 state 或者 props 时调⽤,我们可以设置在此对
⽐前后两个 props 和 state 是否相同,如果相同则返回 false 阻⽌更新,因为相同的属性状态
⼀定会⽣成相同的 dom 树,这样就不需要创造新的 dom 树和旧的 dom 树进⾏ diff 算法对⽐,
节省⼤量性能,尤其是在 dom 结构复杂的时候。不过调⽤ this.forceUpdate 会跳过此步骤
8componentWillUpdate(nextProps,
nextState)组件初始化时不调⽤,只有在组件将要更新时才调⽤,此时可以修改 state
9render()同上
10componentDidUpdate()组件初始化时不调⽤,组件更新完成后调⽤,此时可以获取 dom 节点。还有⼀个卸载钩⼦函
数
11componentWillUnmount()组件将要卸载时调⽤,⼀些事件监听和定时器需要在此时清除
⽐如,页⾯渲染完成后时间⾃动加⼀秒,这时还要涉及到类组件的状态更改。React 不允许直接更改状态, 或者说,我们不能给状态(如: date)进⾏赋值操作, 必须调⽤组件的setState()⽅法去更改状态。这⾥写⼀个函数changeTime来更改状态,详情看 setState 更改状态changeTime函数也可以直接写到组件⾥⾯,根据 ES6 class语法的规定,直接写在类中的函数都会绑定在原型上,所以this.changeTime可以调⽤。但要保证 this 指向的是我们这个组件,⽽不是其他的东西,这也是在 setInterval 中使⽤箭头函数的原因:
//类式组件
class Wscats extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
}; // 给组件添加状态
}
changeTime() {
this.setState({ date: new Date() });
}
// ⽣命周期函数
componentDidMount() {
setInterval(() => {
this.changeTime();
}, 1000);
}
render() {
return (
<div>
<h1>{this.props.name}</h1>
<h2>现在时间是:{this.LocaleTimeString()}</h2>
</div>
);
}
}
/
/组件的组合
function App() {
return (
<div>
<Wscats name="Oaoafly" />
<Wscats name="Eno" />
</div>
);
}
PureComponent 和 Component 的区别
PureCompoent是更具性能的Component的版本。它为你提供了⼀个具有浅⽐较的 shouldComponentU
pdate⽅法,也就是上⾯我们提到的那个类组件的⽣命周期,除此之外PureComponent 和 Component 基本上完全相同。当状态发⽣改变时,PureComponent 将对 props 和state 进⾏浅⽐较。另⼀⽅⾯,⽽Component是不会⽐较的,当 shouldComponentUpdate被调⽤时,组件默认的会重新渲染,所以可以在Component⾥⾯⾃⼰⼿动调⽤shouldComponentUpdate进⾏⽐较来获取更优质的性能。
状态和属性的区别
props(properties 的缩写)和 state 都是普通的 JS 对象。它们都是⽤来保存信息的,这些信息可以控制组件的渲染输出。
⽽它们的⼀个重要的不同点就是:
props 是传递给组件的(类似于函数的形参)
state 是在组件内被组件⾃⼰管理的(类似于在⼀个函数内声明的变量)
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World"
};
}
render() {
return (
<div>
<h1>{ssage} {ssage}</h1>
</div>
);
}
}
// 传递Props给组件,message=”Hi“会被 Test组件⾥⾯的 props接受
所以,状态(State)与属性(Props)很类似,但 state 是组件私有的控制的,除了⾃⾝外部任何组件都⽆法访问它,⽽ props 是组件从外部获取的值,类似形参。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论