一、v-if 与 v-for 的优先级?

核心答案

v-if在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。v-show会被编译成指令,条件不满足时控制样式将对应节点隐藏 (内部其他指令依旧会继续执行)

扩展回答: 频繁控制显示隐藏尽量不使用 v-if,v-if 和 v-for 尽量不要连用

v-forv-if不要在同一个标签中使用,因为解析时先解析v-for在解析v-if。如果遇到需要同时使用时可以考虑写成计算属性computed的方式。

if (el.staticRoot && !el.staticProcessed) {
  return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
  return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
  return genFor(el, state) // v-for
} else if (el.if && !el.ifProcessed) {
  return genIf(el, state) // v-if
}

源码位置: src/compiler/codegen/index.js:55

let compiler = require('vue-template-compiler')
const ast = compiler.compile('<div v-if="false" v-for="i in 5"></div>')
console.log(ast.render)

with (this) {
  return _l(5, function(i) {
    return false ? _c('div') : _e()
  })
}

二、Vue 中 assets 和 static 的区别?

三、Vue 中用了哪些设计模式?

四、v-bind 是怎么实现的?

五、vue-loader 是什么?

六、Vue 事件修饰符有哪些?

七、函数式组件的优势及原理?

八、keep-alive 平时在哪使用?

九、Vue 路由两种模式的区别?

十、Vue 为什么需要虚拟 DOM?

十一、Vue 组件间传值的方式及之间区别?

十二、Vue.extend 和 Vue.mixin 在项目中的应用场景?

十三、什么时候用 vuex?

  • 谈一下你对 Vuex 的个人理解?
  1. 先表达出单向数据流的概念,以及整个 Vuex 的运行流程;
  2. 状态集中管理,实现多组件状态共享;
  3. Vuex 的原理是通过 new Vue 产生实例;达到响应式数据变化的目的;
流程:
1、先从应用层面来讲解一下对Vuex的理解;(单向数据流的概念以及完整的工作流程;然后说出他解决了哪些问题,比如:我们可以把数据集中进行管理,实现一个多组件共享的状态;说完后还可以说一下,Vuex有哪些缺点:数据无法持久化)
2、从原理的角度来分析一下Vuex的原理(Vuex是怎样去实现一个响应式数据的,其实也是通过一个new
Vue来产生实例,它把所有的状态都放到了实例(对象)上,来实现所有组件中的数据共享;可以说一下模块化的实现,mutaion和action的源码区别?还可以说一下项目实际开发中写过哪些Vuex的插件)

十四、Vue 中 computed 和 watch 的区别?

十五、computed 是如何实现的?

十六、用 vnode 来描述一个 dom 结构?

十七、.sync 修饰符的作用?

  • .sync 的作用?

    1. 实现父子组件之间的数据绑定
    2. 一个组件上可以有多个 .sync 修饰符
  • 工作原理?

    // 正常父传子
    <Com :a="num1" :b="num2"></Com>
    // 加上sync
    <Com :a.sync="num1" :b.sync="num2"></Com>
    
    // 等价于
    <Com :a="num1" @update:a="val => num1 = val" :b="num2" @update:b="val => num2 = val"></Com>
    // 相当于多了一个事件监听,事件名是update:a,回调函数中,会把手动的值赋给属性绑定的数据项中
    

扩展

  • v-model 的工作原理?

    <Com v-model="num"></Com>
    // 等价于
    <Com :value="num" @input="val => num = val"></Com>
    
  • 相同点:

    都是语法糖,都可以实现父子组件中的数据的双向通信

  • 不同点:

    1. 格式不同:v-model="num", :a.sync="num"
    2. v-model: value + @input
    3. :a.sync: @update:a
    4. v-model 只能用一次;.sync 可以有多个

十八、怎样封装一个组件?

十九、v-if,v-model,v-for 的实现原理?

// v-if实现原理
const compiler = require('vue-template-compiler');
const ast1 = compiler.compile('<div v-if="false"></div>')
console.log(ast1.render)

with(this){return (false)?_c('div'):_e()}
实际上是一个三元表达式。true就返回一个div元素;false就返回一个_e空元素

// v-for实现原理
const ast2 = compiler.compile('<div v-for="i in 5"></div>')
console.log(ast2.render)

with(this){return _l((5),function(i){return _c('div')})}
实际上就是_l循环5// v-model普通标签
const ast3 = compiler.compile('<input v-model="name"></input>')
console.log(ast3.render)

with(this){return _c('input',{directives:[{name:"model",rawName:"v-model",value:(name),expression:"name"}],domProps:{"value":(name)},on:{"input":function($event){if($event.target.composing)return;name=$event.target.value}}})}

// v-model自定义组件
const ast4 = compiler.compile('<component v-model="name"></component>')
console.log(ast4.render)

with(this){return _c('component',{model:{value:(name),callback:function ($$v) {name=$$v},expression:"name"}})}

自定义组件的时候model被定义为属性

二十、vue 中模板编译原理?

二十一、vuex 模块化是怎么操作的?

二十二、vue 中 diff 的原理?

二十三、vue 的渲染流程?

二十四、vue 中如何检测数组变化?

二十五、为何 vue 采用异步渲染?

二十六、谈一谈你对 Vue 性能优化的理解 ?

二十七、对于修改 data 数据后,组件没有更新,一般你是怎么排查和解决的?

二十八、从多个场景谈谈 vue 和 react 有什么区别?

二十九、vue3.0 在响应式方面对 vue2.0 的主要优化点在哪里?

三十、如何优化单页面首屏加载白屏体验问题 ?

  • Vue 中观察者模式和发布订阅模式的区别和场景?
  1. Vue 中响应式数据变化就是典型的观察者模式
  2. Vue 中自定义事件绑定就是发布订阅模式

总结:观察者模式中观察者和被观察者是存在关联的;发布订阅模式中发布者和订阅者没有关联;所以,观察者模式中包含了发布订阅模式;

三十一、 Vue 组件间传值的方式及区别?

  1. props / emit
Vue是一个单项数据流,可以在父组件中通过属性给子组件进行传递数据,但是子组件是不能直接去更改父组件的状态的;此时,有几种方案:1、可以通过属性的方式,给子组件传递一个方法,然后子组件去调用这个方法,这个方法是属于父组件的,此时父组件可以在这个方法中更新自己的状态。这是通过属性的方式来解决父子之间的通信;2、可以在父组件中申明一个方法,把这个方法绑定到子组件上,子组件再去调用父组件的方法。此时父组件在这个方法中更新自己的状态。【常用props
& emit】
  1. $parent / $children
$parent表示是可以在当前组件中获取它的父组件的实例;
$children表示是可以在当前组件中获取它的子组件的实例;
这两种方法可以快速的拿到父子组件的实例
  1. $attrs / $listeners (跨组件通信)
比如有两个组件,一个组件叫A,
一个组件叫C,希望把A的属性和方法传递给C,原则上来说把属性和方法一个个的传递给B,再由B传递给C;可以通过$attrs和$listeners把属性和方法全部传递给B,B再全部传给C;这种方式相当于不用显示的在B中申明一下我接受到哪些属性和方法。而是直接把这些属性和方法向下传递。
  1. provide / inject
(最好不在业务代码中去使用,会导致数据的来源会混乱不好去控制,一般用于插件或者自己的组件库里)可以再父组件中去提供一些属性,这些属性可以暴露到当前上下文中,这样所有的儿子组件都可以通过inject的方式把我们提供的数据拿过来变成自己的属性去使用。
  1. $refs
在组件上增加一个ref属性,通过this.$refs.xxx来拿到这个组件的实例,去调用组件里的方法和属性。
  1. EventBus (跨级组件)
可以实现跨级或者平级组件间的通信,主要就是通过发布订阅模式。但是EventBus会有问题:我们可以在任何组件中去订阅事件,那事件可能会重名,所有想解决这个问题提供更好的一个状态管理机制就用到了Vue官方提供的Vuex来实现状态管理。
  1. Vuex
Vuex相当于就是把所有的状态都提供到了一个实例上,所有组件都去获取这个实例上的属性和数据,从而实现了一个状态的共享。

三十二、Vue 页面不刷新的情况?

  1. Vue 无法检测实例被创建时不存在 data 中的属性

  2. Vue 无法检测对象属性的添加和移除

  3. Vue 通过数组下标(索引)修改一个数组项

  4. Vue 不能直接修改数组的 length

  5. 在异步执行之前操作 DOM

  6. 循环嵌套层数太深,视图不更新 -> $forceUpdate()

  7. 路由参数变化时,页面不刷新 -> ①、watch -> '\$route' ②、router-viewkey

三十三、set 和 delete 的实现原理?

三十四、Vue scoped 底层原理?

  • Vue scoped 是什么?

    vue 文件中的 style 标签上,有一个特殊的属性:scoped。当一个 style 标签拥有 scoped 属性时,它的 CSS 样式就只能作用于当前的组件,也就是说,该样式只能适用于当前组件元素。通过该属性,可以使得组件之间的样式不互相污染。注意: 如果一个项目中的所有 style 标签全部加上了 scoped,相当于实现了样式的模块化。

  • Vue scoped 作用?

    样式隔离、样式模块化

  • Vue scoped 原理?

    1. 每个 Vue 文件都将对应一个唯一的 id,该 id 根据 文件路径名内容 hash 生成,通过组合形成 scopeId
    2. 编译 template 标签时,会为每个标签添加了当前组件的 scopeId,如:
    <div class="demo">test</div>
    <!-- 会被编译成 -->
    <div class="demo" data-v-12e4e11e>test</div>
    
    1. 编译 style 标签时,会根据当前组件的 scopeId 通过 属性选择器组合选择器 输出样式,如:
    .demo {
      color: red;
    }
    // 会被编译成
    .demo[data-v-12e4e11e] {
      color: red;
    }
    

    总结:这样就相当为我们配置的样式加上了一个唯一表示。

    • 问题?
      1. 渲染的 HTML 标签上的 data-v-xxx 属性是如何生成的?
      2. CSS 代码中添加的属性选择器是如何实现的?

扩展Vue-Loader 的作用?

它可以解析和转换 .vue 文件。提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模板 template,然后再分别把它们交给对应的 loader 去处理。

vue-loader

  • pitcher 根据 query.type 注入处理对应标签的 loader。由于 loader.pitch 会先于 loader 执行 ,在捕获阶段执行,检查 query.type 并直接调用相关的 loader

    • type = style, 执行 stylePostLoader

    • type = template, 执行 templateLoader

    • templateLoader

      Vue 中一个组件最后都会生成 render 方法,然后 render 生成 VNodeVNode 是描述组件对应的 HTML 标签和结构,一个 VNode 包含了渲染 DOM 节点需要的基本属性,当然这里的基本属性也包含了 scopeId那这里的 scopeId 怎么最后到 DOM 上的了?templateLoader.js 中,当 scoped=truetemplate 的文件会根据单文件唯一的哈希 ID 生成一个 scopeIdscopeId,被解析到 VNode 的配置属性。然后在 render 函数执行时调用 createElement ,作为 VNode 的原始属性,渲染成到 DOM 节点上。

    • stylePostLoader

      1. stylePostLoader.js 中生成一个 id ,同一个单页面组件中的 style,与 templateLoader中的 scopeId 保持一致。
      2. 然后通过 PostCSS 解析 style 标签内容,同时通过 scopedPlugin 为每个选择器追加一个 [scopeId] 的属性选择器。
      3. 这里还会对 scoped 有一些特殊处理。对于 '>>>' 、 '/deep/'、::v-deep、pseudo 等特殊选择器时,将不会将 [scopeId] 的属性选择器追加。
      4. 通过 selector.insertAfter 为当前 styles 下的每一个选择器添加了属性选择器,其值即为传入的[scopeId]

重点templateLoader 解决了 id 渲染 DOM 上面的问题,而 stylePostLoader 的作用就是在 Css 中添加属性选择器。

  • prepare 这一步主要是做一些准备工作。
    1. 生成单文件唯一的哈希 ID
    2. 处理 template 标签,拼接 query 参数
    3. 处理 style 标签,为每个标签拼接 type=style 等参数