Vue全家桶+React全家桶面试题及部分原理整理

Vue全家桶+React全家桶面试题及部分原理整理

Vue

v-if和v-show的区别?

v-if表达式为true显示该元素,为false隐藏该元素(直接删除)

v-show根据表达式的真假来显示与隐藏元素,会在标签加入display属性

为何v-for中要用key

vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好的区别各个组件,key的作用主要是为了高效的更新虚拟DOM

注意:v-for和v-if不能同时使用

父子组件之间的通讯

父传子组件标签中,子组件通过props接收

子组件通过$emit()传送事件,父组件需要通过v-on去监听自定义事件

vue生命周期

------------------beforeCreate初始化之前状态(el,data,msg还没有初始化)

el:undefined

data:undefined

msg:undefined

------------------created初始化之后状态(el还没有挂载,data中已经初始化有了数据)

el:undefined

data:[object Object]

msg:hello vue

------------------beforeMount挂载之前状态(el已经挂载,但还没有渲染)

el:

{{msg}}

data:[object Object]

msg:hello vue

------------------mounted挂载之后状态(el挂载并渲染)

el:

hello vue

data:[object Object]

msg:hello vue

------------------beforeUpdate更新前

------------------updated更新后

------------------beforeDestroy销毁前的状态

------------------destroyed销毁后的状态

父子组件生命周期调用顺序

先父组件created,然后子组件created,之后子组件mounted,再父组件mounted

(创建是从外到内,渲染是从内到外,子组件渲染完再父组件)

更新阶段,先父组件update,然后子组件update,然后子组件updated,再父组件updated

何时需要使用beforeDestory

解绑自定义事件event.$off

清除自定义的DOM事件,如window scroll等

Vuex中action和mutation有何区别

action中处理异步,mutation不可以

mutation做原子操作

action可以整合多个mutation

Vue3升级内容

全部用ts重写(响应式、dom、模板编译等)

性能提升,代码量减少,会调整部分API

Proxy实现响应式(基本使用)

Proxy能规避Object.defineProperty的问题,但Proxy无法兼容所有浏览器,无法polyfill

Vue高级特性

自定义v-model

//---------------子组件传值

//绑定 input 事件,$emit 触发父组件的 input 事件

<input type="text" @input="$emit('input', $event.target.value)">

//父组件监听 input 事件,然后将事件携带的 input 输入的值传入到 data 状态中

<my-children @input="value = $event"></my-children>

export default {

data () {

return {

value: ''

}

}

}

//-------------父组件传值

//将value值传递给子组件,子组件再绑定 value值到 input的value属性上

<my-children :value="value" @input="value = $event"></my-children>

//子组件中绑定 input 的 value 属性

<input type="text" @input="$emit('input', $event.target.value)" :value="value">

//props设定value值

export default {

name: 'myChildren',

props: ['value']

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

$nextTick

Vue是异步渲染,data改变之后,DOM不会立即渲染,$nextTick会在DOM渲染之后被触发,以获取最新DOM节点

页面渲染时会将data的修改做整合,多次data修改只会渲染一次

<ul ref="uls">

<li>***</li>

</ul>

this.$nextTick(()=>{

//获取DOM元素

const ulElem =this.$refs.uls

console.log(ulElem.childNodes.length)

})

1

2

3

4

5

6

7

8

9

slot

<div 用法,需要根据数据,动态渲染的场景。即组件类型不确定

<component :is="NextTickName"/>//使用动态组件

<FormDemo v-if="showFormDemo"/>//异步组件

<button @click="showFormDemo = true">show form demo </button>

import NextTick from "./NextTick"//导入外部组件

export default{

components:{

NextTick,

FormDemo:() => import("../BaseUse/FormDemo")//异步加载组件

},

data(){

return {

name:"home",

NextTickName:"NextTickName"

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

keep-alive

缓存组件,频繁切换,不需要重复渲染(被包裹的组件不会被销毁)

mixin

多个组件有相同的逻辑,抽离出来

mixin并不是完美的解决方案,会有一些问题(

变量来源不明确,不利于阅读,多mixin可能造成命名冲突,mixin和组件可能出现多对多的关系,复杂度较高

),vue3提出的composition API旨在解决这些问题

<div>

<p>{{name}}{{age}}{{sex}}</p>

</div>

import myMixin from "./mixin"

export default {

mixins:[myMixin],//可以添加多个,会自动合并起来

data(){

return {

name:"张三",

age:18

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

Vue部分原理(≠源码)

组件化(MVVM,数据驱动视图)

MVVM 即模型-视图-视图模型。【模型(M)】指的是后端传递的数据。【视图(V)】指的是所看到的页面。【视图模型(VM)】mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定

响应式(数据驱动视图第一步)

核心API:Object.defineProperty(Vue3.0之前)

//基本使用

const data ={}

const name="zhangsan"

Object.defineProperty(data,"name",{

get:function(){//get返回内容

console.log("get")

return name

},

set:function(newVal){//set赋值内容

console.log("set")

name=newVal

}

})

//如果监听的类型是对象,需要判断进行深度监听

//如果监听的类型是数组,不污染全局的数组原型情况下,重新定义数组原型

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Object.defineProperty的缺点:深度监听,需要递归到底,一次性计算量大

无法监听新增属性/删除属性(需要Vue.set Vue.delete两个API来使用)

Vue3.0使用Proxy取代,但Proxy有兼容性问题,且无法polyfill,所以Vue2.x还会存在一段时间

vdom(Virtual DOM)和diff(虚拟DOM和diff算法,面试中的热门问题)

vdom----用js模拟DOM结构,计算出最小的变更,操作DOM

vue是参考snabbdom实现的vdom和diff

----------diff算法

diff即对比,是一个广泛的概念,如linux diff命令,git diff等

diff算法过程:patch(elem,vnode)和patch(vnode,newVnode)

patchVnode和addVnodes和removeVnodes

updateChidren(key的重要性)

两课树做diff

模板编译

前置知识点:with语法

改变{}内自由变量的查找规则,当做obj属性来查找,如果找不到匹配的obj属性,就会报错

with要慎用,它打破了作用域规则,易读性变差

模板它不是html,有指令,插值,js表达式,能实现判断,循环

模板编译为render函数,执行render函数返回vnode

基于vnode再执行patch和diff

vue组件中使用render代替template

渲染过程

①初次渲染过程

解析模板为render函数

触发响应式,监听data属性 getter setter

执行render函数,生成vnode,patch(elem,vnode)

②更新过程

修改data,触发setter(此前在getter中已被监听)

重新执行render函数,生成newVnode

path(vnode,newVnode)

③异步渲染

通过$nextTick方法

汇总data的修改,一次性更新视图,减少DOM操作次数,提高性能

前端路由

①hash

hash的特点:hash变化不会触发网页跳转,即浏览器的前进、后退

hash变化不会刷新页面,SPA必需的特点

hash永远不会提交到server端

②H5 history

用url规范的路由,但跳转时不刷新页面

H5 history需要后端支持

to B的系统推荐用hash,简单易用,对url规范不敏感

to C的系统,可以考虑选择H5 history,但需要服务端支持

React

React vs Vue

React和Vue一样重要(特别是大厂面试),力求两者都学会

React和Vue有很多相通之处,而且正在趋于一致

React比Vue学习成本高,尤其对于初学者

React的event

①event是SyntheticEvent,模拟出来DOM事件所有能力

②event.nativeEvent是原生事件对象

③所有的事件,都被挂载到document上

④和DOM事件不一样,和Vue事件也不一样

受控组件和非受控组件

例如等元素都要绑定一个change事件,当表单的状态发生变化,就会触发onChange事件,更新组件的state。这种组件在React中被称为受控组件,在受控组件中,组件渲染出的状态与他的value或checked属性相对应,react通过这种方式消除了组件的局部状态,使整个状态可控。react官方同样推荐使用受控表单组件

如果一个表单组件没有value props(单选和复选按钮对应的是checked props)时,就可以称为非受控组件.

在非受控组件中,我们可以使用一个ref来从DOM获得表单值。而不是为每个状态更新编写一个事件处理程序。

setState(同步还是异步)

setState之前不可变值,可能是异步更新,可能会被合并(传入对象的情况下,多个setState只执行一次,传入函数,不会被合并)

同步还是异步?

setState直接使用是异步的,想获取state中最新的值,在setState加入回调

setTimeout中setState是同步的,自定义的DOM事件中也是同步的

React组件生命周期

1.第一次初始化渲染显示:ReactDOM.render()

construction() 创建对象初始化state

componentWillMount() 组件渲染之前

render() 渲染组件

componentDidMount() 组件渲染之后

2.每次更新this.setState()

componentWillUpdata() 将要更新之前

render() 更新(重新渲染)

componentDidUpdata()已经更新之后

3.移除组件ReactDOM.unmountComponentAtNode(containerDom dom容器)

componentWillUnmount()组件要被移除回调

React高级特性

函数组件

非受控组件

使用场景

必须手动操作DOM元素,setState实现不了

文件上传

showInput = () => {

const input = this.refs.content//之前版本获取ref的标签

console.log(input.value);

console.log(this.input.value);//新方法

}

render(){

<input type="text" ref={input => this.input = input}//ref标识组件内的元素

<button onClick={this.showInput}>获取input值</button>

}

1

2

3

4

5

6

7

8

9

Portals

组件默认会按照既定层次嵌套渲染。如何让组件渲染到父组件以外?

Portals使用场景

overflow:hidden 父组件z-index值太小 fixed需要放在body第一层级

基本使用:

render(){

//使用Portals渲染到body上

return ReactDOM.createPortal(

<div className="modal">{this.props.children}</div>,

document.body

)

}

1

2

3

4

5

6

7

context

公共信息(语言、主题)如何传递给每个组件?用props太繁琐,用redux小题大做

React.createContext:创建一个上下文的容器(组件), defaultValue可以设置共享的默认数据

const {Provider, Consumer} = React.createContext(defaultValue)

1

Provider(生产者): 和他的名字一样。用于生产共享数据的地方。生产什么呢? 那就看value定义的是什么了。value:放置共享的数据。

<Provider value={/*共享的数据*/}>

/*里面可以渲染对应的内容*/

</Provider>

1

2

3

Consumer(消费者):这个可以理解为消费者。 他是专门消费供应商(Provider 上面提到的)产生数据。Consumer需要嵌套在生产者下面。才能通过回调的方式拿到共享的数据源。当然也可以单独使用,那就只能消费到上文提到的defaultValue

<Consumer>

{value => /*根据上下文 进行渲染相应内容*/}

</Consumer>

1

2

3

异步组件

Vue中使用import() React中使用React.lazy React.Suspense

从 React 中引入 lazy 方法和 Suspense 组件,然后用 lazy 方法处理我们的组件,lazy 会返回一个新的React 组件,我们可以直接在 Suspense 标签内使用,这样组件就会在匹配的时候才加载。

lazy 接受一个函数作为参数,函数内部使用 import() 方法异步加载组件,加载的结果返回。

Suspense 组件的 fallback 属性是必填属性,它接受一个组件,在内部的异步组件还未加载完成时显示,所以我们通常传递一个 Loading 组件给它,如果没有传递的话,就会报错。

基本使用:

import React, { lazy, Suspense } from 'react';

const Index = lazy(() => import('components/Index'));

const List = lazy(() => import('components/List'));

class App extends React.Component {

render() {

return <div>

<Suspense fallback={Loading}>//fallback 属性是必填属性,它接受一个组件

<Index></Index>

<List></List>

</Suspense>

</div>

}

}

function Loading() {

return <div>

Loading...

</div>

}

export default App;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

性能优化

shouldComponentUpdate(简称SCU)

_.isEqual方法做对象或者数组的深度比较

SCU默认返回true,即React默认重新渲染所有子组件,必须配合“不可变值”一起使用,可先不用SCU,有性能问题时再考虑使用

PureComponent和React.memo

PureComponent,SCU中实现了浅比较

memo,函数组件中的PuceComponent,浅比较已使用大部分情况(尽量不要做深度比较)

不可变值immutable.js

彻底拥抱“不可变值”,基于共享数据(不是深拷贝),速度好,按需使用

高阶组件HOC

基本用法

Render Props

Redux使用

和Vuex作用相同,但比Vuex学习成本高,不可变值,纯函数

基本概念

store state

action

reducer

import { createStore } from 'redux';

/**

* 这是一个 reducer,形式为 (state, action) => state 的纯函数。

* 描述了 action 如何把 state 转变成下一个 state。

*

* state 的形式取决于你,可以是基本类型、数组、对象、

* 甚至是 Immutable.js 生成的数据结构。惟一的要点是

* 当 state 变化时需要返回全新的对象,而不是修改传入的参数。

*

* 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)

* 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。

*/

function counter(state = 0, action) {

switch (action.type) {

case 'INCREMENT':

return state + 1;

case 'DECREMENT':

return state - 1;

default:

return state;

}

}

// 创建 Redux store 来存放应用的状态。

// API 是 { subscribe, dispatch, getState }。

let store = createStore(counter);

// 可以手动订阅更新,也可以事件绑定到视图层。

store.subscribe(() =>

console.log(store.getState())

);

// 改变内部 state 惟一方法是 dispatch 一个 action。

// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行

store.dispatch({ type: 'INCREMENT' });

// 1

store.dispatch({ type: 'INCREMENT' });

// 2

store.dispatch({ type: 'DECREMENT' });

// 1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

单项数据流

react-redux

异步action

redux中间件

React-router使用

路由模式(hash、H5 history),同vue-router,路由配置(动态路由、懒加载),同vue-router

React部分原理(≠源码)

函数式编程

一种编程范式,概念比较多,纯函数,不可变值

函数式编程:是一种设计思想,尽量用函数组合来进行编程,先声明函数,然后调用函数的每一步都有返回值,将具体的每一步逻辑运算抽象,封装在函数中。再将函数组合来编写程序。

vdom和diff

JSX本质

合并事件

batchUpdate机制

setState

batchUpdate机制

transaction事务机制

组件渲染过程

内容来源bilibili,链接视频