{T}

题目

vue

  • v-if 和 v-show 的区别
  • 为何 v-for 中要用 key
  • 描述 vue 组件生命周期(以及有父子组件,两者的生命周期)
  • 组件间如何通讯
  • 响应式原理
  • 组件渲染和更新的过程
  • 双向数据绑定 v-model 的原理

React

  • 组件之间如何通讯
  • JSX 本质是什么
  • context 是什么,如何应用?
  • shouldComponentUpdate 的用途
  • redux 单向数据流过程
  • setState(是同步还是异步) 场景题

框架综合应用

  • 用 React 设计一个 todo-list
  • 说明组件结构
  • 说明 redux state 数据结构
  • 用 vue 设计一个购物车
  • 说明组件结构
  • 说明 vuex state 数据结构

webpack

  • 前端代码为何要进行构架和打包?
  • module chunk bundle 的区别
  • loader 和 plugin 的区别
  • webpack 如何实现懒加载?
  • webpack 性能优化
  • babel-runtime 和 babek-polyfill 的区别,如何应用?

如何应对?

老师看过很多面试题,对于框架面试题,总结出规律

使用

  • 基本用法
  • 高级特性
  • 周边工具

原理

  • 基本理念的理解(组件化, MVVM)
  • 热门技术的深度(vdom, diff 算法)
  • 框架原理的全面性,完整流程

综合应用

  • 场景题,现场做项目设计
  • 组件结构
  • 数据结构

思考一下,为何面试会这样问:

  • 保证候选人能正常干活,不拖后腿 —— 考察:基本用法,周边工具,高级特性
  • 多候选人竞争时,选择有技术追求的 —— 考察:原理
  • 看候选人是否能独立承担项目 —— 考察:综合应用、设计
javascript
class Root extends React.Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
  }
  componentDidMount() {
    this.setState({ count: this.state.count + 1 })
    console.log(this.state.count)    // 打印
    this.setState({ count: this.state.count + 1 })
    console.log(this.state.count)    // 打印
    setTimeout(function(){
      this.setState({ count: this.state.count + 1 })
      console.log(this.state.count)   // 打印
    }, 0)
    setTimeout(function(){
      this.setState({ count: this.state.count + 1 })
      console.log(this.state.count)   // 打印
    }, 0)
  }
  render() {
    return <h1>{this.state.count}</h1>
	}
}

vue原理

目录

  • 组件化
  • 2.x 响应式
  • vdom 和 diff
  • 模板解析
  • 渲染过程(包括异步渲染)
  • 前端路由

题目

  • v-if 和 v-show 的区别
  • 为何 v-for 中要用 key
  • 描述 vue 组件生命周期(以及有父子组件,两者的生命周期)
  • 组件间如何通讯
  • 响应式原理
  • 组件渲染和更新的过程
  • 双向数据绑定 v-model 的原理

针对以上题目:

  • 有些已经了解
  • 有些知道,但不了解原理
  • 学完原理之后,会有大量题目做面试演练

面试为何会考察原理,又用不到?

  • 之前然知其所以然—各行业通用的道理
  • 了解原理,才能应用的更好(竞争激烈,择优录取)
  • 大厂造轮子(有钱有资源,业务定制,技术 KPI)

面试如何考察 Vue 原理?

  • 考察重点,而不是考察细节。掌握好 2/8 原则
  • 和使用相关联的原理,例如 vdom、模板渲染
  • 整体流程是否全面?热门技术是否有深度?

vue 框架原理

使用 Vue 开发应用,实际上是编写若干 Vue 组件,实现模板、data、生命周期钩子等,然后执行 new Vue(),将根组件挂载到指定的DOM节点上面,当编写的组件中生命周期里面的或者在模板的元素事件中改变数据时候,视图会响应地更新。这样就实现了应用

  1. new Vue() 之后,Vue会从根组件开始,遍历整个组件树,对每个组件进行处理
  2. 对于一个Vue组件,Vue首先会进行模板编译,将模板编译为render函数,render函数返回虚拟DOM,如果遇到子组件,也对子组件做同样操作,最终形成一个虚拟DOM树
  3. Vue会把虚拟DOM映射到真实DOM并渲染到指定节点上,这样就实现了视图的渲染
  4. Vue在组件初始化时候还会设置数据为响应式,并将依赖于数据的渲染方法、computed、watch 收集起来
  5. 当数据改变后,Vue会根据初始化时候收集的依赖,更新视图,这时候我们就看到最新的界面了

v-if 和 v-show

行为不同:v-if 指令在满足条件时候才会渲染 DOM,v-show 一开始就渲染 DOM,满足条件时候才设置 CSS 的 display 属性让元素显示出来

应用场景不同:一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好

v-for 和 v-if 放在一起用好吗

v-if 和 v-for 不要同时使用,因为会在每次渲染时候都要遍历列表并判断是否需要渲染,这个遍历操作其实是有一部分冗余或者完全不必要的

应该用以下方式替换 v-if 和 v-for 同时使用的方案:

  1. 如果是为了过滤一个列表中的项目 (v-for 循环,v-if 过滤条件),可以将列表作为计算属性,在 computed 中过滤出需要渲染的列表,再进行渲染。这样避免了每次渲染都计算,同时渲染列表是过滤了的,那么循环的次数也可能减少

  2. 如果是为了控制整个列表的展示和隐藏 (v-for 循环,v-if 判断整个列表是否需要展示) ,可以将判断条件放到父元素上。这样展示和隐藏的判断只需要执行一次(在列表最开始)

Vue3 修改了 v-if 和 v-for 的优先级,v-if 没有权限访问 v-for 的变量,这个需要注意

v-for 为什么要加 key

不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法

key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速

  • 更准确:带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确
  • 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
javascript
// 判断两个vnode的标签和key是否相同 如果相同 就可以认为是同一节点就地复用
function isSameVnode(oldVnode, newVnode) {
  return oldVnode.tag === newVnode.tag && oldVnode.key === newVnode.key;
}

// 根据key来创建老的儿子的index映射表  类似 {'a':0,'b':1} 代表key为'a'的节点在第一个位置 key为'b'的节点在第二个位置
function makeIndexByKey(children) {
  let map = {};
  children.forEach((item, index) => {
    map[item.key] = index;
  });
  return map;
}
// 生成的映射表
let map = makeIndexByKey(oldCh);

v-model 原理

v-model 只是语法糖而已。v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性 和 input 事件;
  • checkbox 和 radio 使用 checked 属性 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

注意:对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新

在普通标签上使用 v-model

javascript
<input v-model="sth"/>//这一行等于下一行
<input v-bind:value="sth" v-on:input="sth = $event.target.value" />

在组件上使用

javascript
<currency-input v-model="price">currentcy-input>

Vue.component('currency-input', {
 template: `<span><input ref="input" :value="value" @input="$emit('input', $event.target.value)">span>`,
 props: ['value'],
})

Vue 生命周期

img

beforeCreate初始化一个空的 Vue 实例,datamethods 等尚未被初始化,无法调用
createdVue 实例初始化完成,datamethods 都已初始化完成,可调用。但尚未开始渲染模板
beforeMount编译模板,调用 render 函数生成 vdom ,但还没有开始渲染 DOM
mounted渲染 DOM 完成,页面更新。组件创建完成,开始进入运行阶段。
beforeUpdate在数据发生改变后,DOM 被更新之前被调用。适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器
updated在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用 <br/>注意,尽量不要在 updated 中继续修改数据,否则可能会触发死循环。
onActivatedkeep-alive 缓存的组件激活时调用
onDeactivatedkeep-alive 缓存的组件停用时调用。
beforeUnmount组件进入销毁阶段。 在这个阶段,实例仍然是完全正常的。 移除、解绑一些全局事件、自定义事件,可以在此时操作
unmounted卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载

正确的操作 DOM

mounted 和 updated 都不会保证所有子组件都挂载完成,如果想等待所有视图都渲染完成,需要使用 $nextTick

javascript
mounted() {
  this.$nextTick(function () {
    // 仅在整个视图都被渲染之后才会运行的代码
  })
}

ajax 在哪个生命周期合适

一般有两个选择:createdmounted ,建议选择后者 mounted

执行速度

  • 从理论上来说,放在 created 确实会快一些
  • 但 ajax 是网络请求,其时间是主要的影响因素。从 createdmounted 是 JS 执行,速度非常快。
  • 所以,两者在执行速度上不会有肉眼可见的差距

代码的阅读和理解

  • 放在 created 却会带来一些沟通和理解成本,从代码的执行上来看,它会一边执行组件渲染,一边触发网络请求,并行
  • 放在 mounted 就是等待 DOM 渲染完成再执行网络请求,串行,好理解

所以,综合来看,更建议选择 mounted