1.什么是虚拟DOM
虚拟DOM(VDOM)是真实DOM的内存表示。将整个UI结构保持在内存中并和真正的DOM结构保持同步。在调用渲染函数和元素展现在屏幕上之间有一个处理过程,这个过程叫做reconciliation。
2.虚拟DOM是如何工作的
虚拟DOM的工作过程分为三个阶段:
1)每当任意一个底层数据发生更改时,整个UI都将在虚拟DOM表示中重新渲染
2)对比当然的虚拟DOM和上一份虚拟DOM的不同之处
3)对比结束之后,真是DOM就会根据对比结果重新渲染变化的DOM节点
3.影子(Shadow)DOM和虚拟DOM有什么区别
Shadow DOM 在网络平台中引入作用域样式。 无需工具或命名约定,您即可使用原生 JavaScript 捆绑 CSS 和标记、隐藏实现详情以及编写独立的组件。
Shadow DOM 与普通 DOM 相同,但有两点区别:1) 创建/使用的方式;2) 与页面其他部分有关的行为方式。 通常,您创建 DOM 节点并将其附加至其他元素作为子项。 借助于 shadow DOM,您可以创建作用域 DOM 树,该 DOM 树附加至该元素上,但与其自身真正的子项分离开来。这一作用域子树称为影子树。被附着的元素称为影子宿主。 您在影子中添加的任何项均将成为宿主元素的本地项,包括 <style>。 这就是 shadow DOM 实现 CSS 样式作用域的方式。
所以,Shadow DOM是一种浏览器技术,主要用于在web组件中确定变量和CSS的范围。虚拟DOM是一个由JavaScript中的库在浏览器api之上实现的概念。
4.类组件和方法组件的区别
- 类组件:它允许您使用其他特性,如本地状态和生命周期钩子。此外,还可以使组件直接访问store,从而保持状态。
- 如果你的组件只接收props属性并将它处理之后渲染在页面上,那么这就是个无状态组件,可以使用纯函数来实现。
5.refs在React中用于什么
你可以使用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属性,给它赋值了一个函数方法。这个方法接收了input的真实DOM元素,接着我们将在handleSubmit方法里面访问它。
通常认为只能在类组件中使用refs,但你也可是利用闭包在方法组件中使用。
function CustomForm ({handleSubmit}) {
let inputElement
return (
<form onSubmit={() => handleSubmit(inputElement.value)}>
<input
type='text'
ref={(input) => inputElement = input} />
<button type='submit'>Submit</button>
</form>
)
}
还有另外一种使用refs的方法。你可以用React.createRef()方法创建refs并通过ref属性绑定到元素上。为了在整个组件中使用ref,只需将ref分配给构造函数中的实例属性。
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <div ref={this.myRef} />
}
}
6.什么是forward refs
Ref forwarding 是一个性质,它允许组件接收一个ref属性,并将这个属性传递给子组件。
const ButtonElement = React.forwardRef((props, ref) => (
<button ref={ref} className="CustomButton">
{props.children}
</button>
));
// Create ref to the DOM button:
const ref = React.createRef();
<ButtonElement ref={ref}>{'Forward Ref'}</ButtonElement>
7.描述一下React中事件是怎么处理的
为了解决跨浏览器兼容性问题,React中的事件处理中将传递SyntheticEvent的实例,SyntheticEvent是React中包装了各种浏览器原生属性的跨浏览器包装器。它暴露出统一的事件接口,保证在不同浏览器上都能够正常运行。
稍微有趣的是,React实际上并没有将事件附加到子节点本身。而是使用事件代理的方式,React使用一个事件侦听器在顶层侦听所有事件。这有助于提高性能,也意味着React在更新DOM时不必担心跟踪事件侦听器被移除。
8.state和props之间有什么不同
props和state都是纯JavaScript对象。虽然它们都包含影响渲染输出的信息,但它们在组件方面的功能不同。比如:
- props将数据传递给组件,功能类似于函数中的参数
- state则维护在组件内部,类似于函数中定义的变量
9.什么是高阶组件
高阶组件(HOC)就是一个方法,它接收一个组件并返回一个新的组件。这是一种由React的组成性质衍生出来的模式。
也可以成为“纯组件”,因为它们可以接受任何动态提供的子组件,但不会修改或复制其输入组件中的任何行为。
HOC 可以用在很多情况下,比如:
- 代码重用、逻辑和引导抽象
- 状态抽象与操作
- Props操作
10.使用带有props参数的超级构造函数的目的是什么
一个子类的构造器在调用sper方法之前是不能使用this引用的。就行ES6中的sub-classes。将props参数传递给super()调用的主要原因是访问子构造函数中的this.props。
- 传递
props
class MyComponent extends React.Component {
constructor(props) {
super(props);
console.log(this.props); // Prints { name: 'sudheer',age: 30 }
}
}
- 不传递
props
class MyComponent extends React.Component {
constructor(props) {
super();
console.log(this.props); // Prints undefined
// But Props parameter is still available
console.log(props); // Prints { name: 'sudheer',age: 30 }
}
render() {
// No difference outside constructor
console.log(this.props) // Prints { name: 'sudheer',age: 30 }
}
}
上面的代码片段可以看出,this.props行为仅在构造函数中不同,构造函数之外的表现都是一样的。
11.什么是受控组件
在HTML中,表单元素<input>、<select>以及<textarea>等,都维护自己的状态并根据用户输入进行更新。当用户提交表单时,上述元素的值将随表单一起发送。但在React中,并不是这样的,包含表单的组件将跟踪处于其状态中的<input>的值,并在每次启动回调函数(例如onChange)时,因为状态将被更新而重新渲染该组件。
一个输入表单元素,其值以这种方式由React控制,称为受控组件。
12.下面的表达式使用React.createElement的等价写法
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
Answer:
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
13.怎么理解JSX
当facebook第一次向人们介绍react时,也介绍了一种新的JavaScript写法,也就是 JSX,它将HTML模版写在javascrit代码中。JSX本身是无法被浏览器识别的,它必须借助bable或者webpack编译成传统的javascrit。尽管解一开始许多开发人员下意识的反对这种写法,但JSX(与ES2015一起)已经成为定义React组件的确定方法。
class MyComponent extends React.Component {
render() {
let props = this.props;
return (
<div className="my-component">
<a href={props.url}>{props.name}</a>
</div>
);
}
}
14.给定一段代码,找出其中的两处问题?
代码如下:
class MyComponent extends React.Component {
constructor(props) {
// set the default internal state
this.state = {
clicks: 0
};
}
componentDidMount() {
this.refs.myComponentDiv.addEventListener('click', this.clickHandler);
}
componentWillUnmount() {
this.refs.myComponentDiv.removeEventListener('click', this.clickHandler);
}
clickHandler() {
this.setState({
clicks: this.clicks + 1
});
}
render() {
let children = this.props.children;
return (
<div className="my-component" ref="myComponentDiv">
<h2>My Component ({this.state.clicks} clicks})</h2>
<h3>{this.props.headerText}</h3>
{children}
</div>
);
}
}
Answer:
- 构造器中没有将
props传给supr()
constructor(props) {
super(props);
// ...
}
- 事件侦听器(通过addEventListener()分配时)的作用域不正确。因此,开发人员需要在构造函数中重新分配clickHandler,以包含对此的正确绑定:
constructor(props) {
super(props);
this.clickHandler = this.clickHandler.bind(this);
// ...
}
15.介绍下React组件生命周期的不同阶段
React组件的生命周期有四个不同的阶段:
- 初始化(Initialization):在此阶段,react组件准备设置初始状态和默认props。
- mounting :这个阶段包括componentWillMount和componentDidMount**两个生命周期方法。
- 更新(updating):在这个阶段,组件通过两种方法获取更新:传入新的
props以及更新state。包括shouldComponentUpdate,componentWillUpdate以及componentDidUpdate三个生命周期方法。 - Unmounting: 最后一个阶段,组件不再被需要,就会从DOM节点上移除。包括
componentWillUnmount方法。
16.React中的生命周期方法
- componentWillMount:在渲染之前触发,可以用来在根组件里做APP级别的配置。
- componentDidMount:在渲染之后触发,在这个阶段AJAX请求、DOM和状态更新以及事件监听都可以触发了。
- componentWillReceiveProps:在特定的
prop更新引起state变化时触发。 - shouldComponentUpdate:用来决定组件是否应该被更新。通常默认返回
true。如果你确认组件在state或者prop变化之后不需要更新,那么你可以返回false。因为你可以在组件接收到一个新的prop的时候阻止组件的重新渲染,这对于提高组件性能是一种很好的方式。 - componentWillUpdate:当
props或者state发生变化以及shouldComponentUpdate返回为true,那么在组件重新渲染之前就会触发此方法。 - componentDidUpdate:它主要用于更新DOM以响应prop或state的更改。
- componentWillUnmount:这个方法主要用于取消任何已发出的网络请求,或删除与组件关联的所有事件侦听器。
17.什么是React Hooks
React Hooks时在React 16.8新增的性质。它的的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。
它允许你不需要写一个类,就可以使用react的状态和其它性质。通过Hooks,您可以从组件中提取有状态逻辑,这样就可以独立测试并重用它。Hooks允许您重用有状态逻辑,而无需更改组件层次结构。这使得在许多组件之间钩子变得很容易。
18.使用React Hooks有什么优势
在Hooks出现之前,我们写一个简单的组件都要创建一个类,在大型复杂的项目中往往很复杂。最终导致:
- 大型组件很难拆分和重构,也很难测试。
- 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。
- 组件类引入了复杂的编程模式,比如 render props 和高阶组件。
React Hooks 的设计目的,就是加强版函数组件,完全不使用"类",就能写出一个全功能的组件。
通常, hooks能够提取和重用状态逻辑,这种逻辑在多个组件之间是通用的,而不需要高阶组件或渲染props来实现。hooks能够轻松地操作功能组件的状态,而无需将它们转换为类组件。
Hooks在类组件里面不起作用。通过使用它,我们可以避免使用componentDidMount, componentDidUpdate, componentWillUnmount这些生命周期方法。但需要使用例如useffect这样的内置钩子。
19.React中useState()是什么
...
const [count, setCounter] = useState(0);
const [moreStuff, setMoreStuff] = useState(...);
...
const setCount = () => {
setCounter(count + 1);
setMoreStuff(...);
...
};
useState是react hooks的一个内置方法。useState(0)返回一个元组,其中第一个参数count是计数器的当前状态,setCounter是允许我们更新计数器状态的方法。
我们可以使用setCounter方法在任何地方更新count的状态。这里,我们在setCount函数中使用它,在这里我们可以做更多的事情;hooks的思想是,我们可以使代码更具功能性,并在非必要的情况下避免基于类的组件。
20.描述一下Flux vs MVC
传统的MVC框架很好的将数据 (Model), UI (View) 以及 逻辑 (Controller) 分离开,但它仍然面临两个问题:
- 数据流的弱定义:在视图之间发生的层叠更新常常导致难以调试的复杂事件网络。
- 缺少数据的整体管理:模型数据可以从任何地方变异,从而在整个UI中产生不可预测的结果。
Flux模式使得复杂组件不需要再通过级联更新的方式渲染组件。每个组件都可以通过store提供的数据维护自己的状态。Flux模式还通过限制对共享数据的直接访问来增强数据的统一管理。