一、v-if 与 v-for 的优先级?
核心答案:
v-if
在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。v-show
会被编译成指令,条件不满足时控制样式将对应节点隐藏 (内部其他指令依旧会继续执行)
扩展回答: 频繁控制显示隐藏尽量不使用 v-if,v-if 和 v-for 尽量不要连用
v-for
和v-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 的个人理解?
- 先表达出单向数据流的概念,以及整个 Vuex 的运行流程;
- 状态集中管理,实现多组件状态共享;
- Vuex 的原理是通过 new Vue 产生实例;达到响应式数据变化的目的;
流程:
1、先从应用层面来讲解一下对Vuex的理解;(单向数据流的概念以及完整的工作流程;然后说出他解决了哪些问题,比如:我们可以把数据集中进行管理,实现一个多组件共享的状态;说完后还可以说一下,Vuex有哪些缺点:数据无法持久化)
2、从原理的角度来分析一下Vuex的原理(Vuex是怎样去实现一个响应式数据的,其实也是通过一个new
Vue来产生实例,它把所有的状态都放到了实例(对象)上,来实现所有组件中的数据共享;可以说一下模块化的实现,mutaion和action的源码区别?还可以说一下项目实际开发中写过哪些Vuex的插件)
十四、Vue 中 computed 和 watch 的区别?
十五、computed 是如何实现的?
十六、用 vnode 来描述一个 dom 结构?
.sync
修饰符的作用?
十七、.sync
的作用?- 实现父子组件之间的数据绑定
- 一个组件上可以有多个
.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>
相同点:
都是语法糖,都可以实现父子组件中的数据的双向通信
不同点:
- 格式不同:
v-model="num", :a.sync="num"
v-model
:value + @input
:a.sync
:@update:a
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 中观察者模式和发布订阅模式的区别和场景?
- Vue 中响应式数据变化就是典型的观察者模式
- Vue 中自定义事件绑定就是发布订阅模式
总结:观察者模式中观察者和被观察者是存在关联的;发布订阅模式中发布者和订阅者没有关联;所以,观察者模式中包含了发布订阅模式;
三十一、 Vue 组件间传值的方式及区别?
- props / emit
Vue是一个单项数据流,可以在父组件中通过属性给子组件进行传递数据,但是子组件是不能直接去更改父组件的状态的;此时,有几种方案:1、可以通过属性的方式,给子组件传递一个方法,然后子组件去调用这个方法,这个方法是属于父组件的,此时父组件可以在这个方法中更新自己的状态。这是通过属性的方式来解决父子之间的通信;2、可以在父组件中申明一个方法,把这个方法绑定到子组件上,子组件再去调用父组件的方法。此时父组件在这个方法中更新自己的状态。【常用props
& emit】
- $parent / $children
$parent表示是可以在当前组件中获取它的父组件的实例;
$children表示是可以在当前组件中获取它的子组件的实例;
这两种方法可以快速的拿到父子组件的实例
- $attrs / $listeners (跨组件通信)
比如有两个组件,一个组件叫A,
一个组件叫C,希望把A的属性和方法传递给C,原则上来说把属性和方法一个个的传递给B,再由B传递给C;可以通过$attrs和$listeners把属性和方法全部传递给B,B再全部传给C;这种方式相当于不用显示的在B中申明一下我接受到哪些属性和方法。而是直接把这些属性和方法向下传递。
- provide / inject
(最好不在业务代码中去使用,会导致数据的来源会混乱不好去控制,一般用于插件或者自己的组件库里)可以再父组件中去提供一些属性,这些属性可以暴露到当前上下文中,这样所有的儿子组件都可以通过inject的方式把我们提供的数据拿过来变成自己的属性去使用。
- $refs
在组件上增加一个ref属性,通过this.$refs.xxx来拿到这个组件的实例,去调用组件里的方法和属性。
- EventBus (跨级组件)
可以实现跨级或者平级组件间的通信,主要就是通过发布订阅模式。但是EventBus会有问题:我们可以在任何组件中去订阅事件,那事件可能会重名,所有想解决这个问题提供更好的一个状态管理机制就用到了Vue官方提供的Vuex来实现状态管理。
- Vuex
Vuex相当于就是把所有的状态都提供到了一个实例上,所有组件都去获取这个实例上的属性和数据,从而实现了一个状态的共享。
三十二、Vue 页面不刷新的情况?
Vue
无法检测实例被创建时不存在data
中的属性Vue
无法检测对象属性的添加和移除Vue
通过数组下标(索引)修改一个数组项Vue
不能直接修改数组的length
在异步执行之前操作
DOM
循环嵌套层数太深,视图不更新 ->
$forceUpdate()
路由参数变化时,页面不刷新 -> ①、
watch
->'\$route'
②、router-view
加key
三十三、set 和 delete 的实现原理?
三十四、Vue scoped 底层原理?
Vue scoped
是什么?在
vue
文件中的style
标签上,有一个特殊的属性:scoped
。当一个style
标签拥有scoped
属性时,它的CSS
样式就只能作用于当前的组件,也就是说,该样式只能适用于当前组件元素。通过该属性,可以使得组件之间的样式不互相污染。注意: 如果一个项目中的所有style
标签全部加上了scoped
,相当于实现了样式的模块化。Vue scoped
作用?样式隔离、样式模块化
Vue scoped
原理?- 每个
Vue
文件都将对应一个唯一的id
,该id
根据文件路径名
和内容 hash
生成,通过组合形成scopeId
- 编译
template
标签时,会为每个标签添加了当前组件的scopeId
,如:
<div class="demo">test</div> <!-- 会被编译成 --> <div class="demo" data-v-12e4e11e>test</div>
- 编译
style
标签时,会根据当前组件的scopeId
通过属性选择器
和组合选择器
输出样式,如:
.demo { color: red; } // 会被编译成 .demo[data-v-12e4e11e] { color: red; }
总结:这样就相当为我们配置的样式加上了一个唯一表示。
- 问题?
- 渲染的
HTML
标签上的data-v-xxx
属性是如何生成的? 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
生成VNode
,VNode
是描述组件对应的HTML
标签和结构,一个VNode
包含了渲染DOM
节点需要的基本属性,当然这里的基本属性也包含了scopeId
。那这里的scopeId
怎么最后到 DOM 上的了? 在templateLoader.js
中,当scoped=true
的template
的文件会根据单文件唯一的哈希ID
生成一个scopeId
。scopeId
,被解析到VNode
的配置属性。然后在render
函数执行时调用createElement
,作为VNode
的原始属性,渲染成到DOM
节点上。stylePostLoader
- 在
stylePostLoader.js
中生成一个id
,同一个单页面组件中的style
,与templateLoader
中的scopeId
保持一致。 - 然后通过
PostCSS
解析style
标签内容,同时通过scopedPlugin
为每个选择器追加一个[scopeId]
的属性选择器。 - 这里还会对
scoped
有一些特殊处理。对于'>>>' 、 '/deep/'、::v-deep、pseudo
等特殊选择器时,将不会将[scopeId]
的属性选择器追加。 - 通过
selector.insertAfter
为当前styles
下的每一个选择器添加了属性选择器,其值即为传入的[scopeId]
- 在
重点:templateLoader
解决了 id
渲染 DOM
上面的问题,而 stylePostLoader
的作用就是在 Css
中添加属性选择器。
prepare
这一步主要是做一些准备工作。- 生成单文件唯一的哈希
ID
。 - 处理
template
标签,拼接query
参数 - 处理
style
标签,为每个标签拼接type=style
等参数
- 生成单文件唯一的哈希