文章目录
玩转vue面试题系列3 结合源码分析面试题
16. 谈谈对Vue
组件化的理解
组件的优点:
- 组件的复用可以根据数据渲染对应的组件。
- 把组件相关的内容放在一起,方便复用,方便维护。
- 合理的规划组件,可以做到更新视图的时候是组件级别的更新,不会过于消耗性能。
vue中是如何处理组件的?
- 需要使用API,
Vue.extend
。根据用户传入的对象生成一个组件的构造函数。 - 根据组件产生对应的虚拟节点。我们会在生成虚拟节点前,在组件的data中加入一个hook。(data:{hook:{init(){}}})
- 做组件初始化,将虚拟节点转换为真实节点(组件的init方法,就是第二步中增加的)。就是调用init方法(init方法中就是 new Sub().$mount())
当然这个题也是需要结合17题来走一遍流程的。
17. Vue
组件的渲染流程
- 先定义组件的选项
- components -> vm.$options.components[“name”] = {name:{template:””}}
- createComponent:(这个函数是创建vnode的,并插入相关的hook,如init) 渲染组件name的时候,需要创建name对应的虚拟节点
- createComponent创建真实节点 -> vm.data.hook.init() -> new Ctor().$mount() -> 组件的初始化完毕
patch 方法,就是我们的vm.__patch__
方法
18. vue
组件的更新流程
同上一题分析。
组件更新的有几种情况:
- data数据更新,就是依赖收集
- 属性更新,可以给组件传入属性,属性变化后,触发更新
- 插槽变化也要更新
属性props的变化,触发更新,肯定也是来到patch方法。
- 组件更新会触发组件的prepatch方法,会复用组件,并且比较组件的属性,事件,插槽等
- 父组件给子组件传递的属性(props)是响应式的,在模板中使用会做依赖收集,收集自己的组件watcher
- 稍后组件更新了,会重新给props赋值,赋值完成后会触发watcher重新更新
19. vue
中异步组件原理
Vue中异步组件的写法有很多。主要用作大的组件进行异步加载。比如markdown组件,editor组件。
先渲染一个注释标签,等组件加载完毕后,在重新渲染。
-
forceUpdate(类似于图片懒加载),使用异步组件会配合webpack
-
原理就是,异步组件默认不会调用Vue.extend方法,所以Ctor(函数)上不会cid属性,没有cid我们就认为是异步组件了。
- 会先渲染一个占位组件,有loading就会先渲染loading,此时第一轮结束
- 如果用户调用了resolve,会将结果赋给factory.resolved上,强制重新渲染。重新渲染的时候,会再次进入到resolveAsyncComponnet中,会直接拿到factory.resolved的结果来渲染。
20. 函数式组件的优势及其原理
- react中也有两种组件:类组件和函数式组件
- 函数式组件,没有类就没有this,也就没有状态这些,没有生命周期 beforeCreate等。
- 函数式组件的好处:就是性能好,不需要创建watcher等
- 函数式组件就是调用render,拿到返回结果(vnode)所以性能高
21. vue
组件间传值的方式及之间的区别
- props:父传递数据给儿子
- emit :儿子触发组件更新
先看props
<div id="app">
<my a="123" c="我是父给子的数据" />
</div>
<script src="../dist/vue.js"></script>
<script>
const vm = new Vue({
data() {
return {
}
},
el: "#app",
components: {
"my": {
props: ["c"],
// a是普通属性 attrs c是props属性 props->c attrs->a
/*
组件的虚拟节点上 {componentOptions: propsData} propsData -> c
初始化的时候要对propsData做处理
将组件的属性挂载到vm.$options.propsData
声明一个 vm._props 类似于 vm._data
只有根属性会被观测,其他父组件传递给我们的不需要进行观测
将所有的属性定义到 vm._props上
vm.c -> vm._props.c
*/
template: `<h2 :c="c">我是组件</h2>`
}
}
})
</script>
首先要搞明白,我们父组件传递给子组件的数据,最开始全都是attrs,也就是属性。但是,我们在创建组件的虚拟dom的时候,会把属性进行抽离:如果我们在props中定义了该属性,表名这个属性是父组件传递的props属性,如果没有定义,该属性概述作为attrs属性。
更新组件的时候,我们会走updateComponentChildren方法
总结一下:props的原理就是把解析后的props,验证后将属性定义在当前实例上的vm._props
.这个对象上的属性都是通过 defineReactive
方法来定义的。都是响应式的。组件在渲染的过程中,会去vm上的_props
取值,(_props属性也会被代理到vm上)
emit方式:
使用emit进行组件通信,那么用到的就是发布订阅模式。
my.$on("cb",cb)
在创建虚拟节点的时候将所有事件,绑定到了listeners,如果是有修饰符.native
修饰的事件,会绑定在组件上,最后在nativeOn属性上。在ininEvents方法内,将事件通过add(其实就是
o
n
)
方
法
绑
定
事
件
,
通
过
on)方法绑定事件,通过
on)方法绑定事件,通过emit触发事件。
eventBus原理就是发布订阅, $bus = new Vue()
简单的通信可以采用这种方式,但是对于多个组件之间相互通信,会显得有些混乱。
对于 p a r e n t , parent, parent,children,就是在创造子组件的时候,会将父组件的实例传入,在组件本身初始化的时候会构建组件间的父子关系, p a r e n t 获 取 父 组 件 实 例 , 通 过 parent获取父组件实例,通过 parent获取父组件实例,通过children获取所有的子组件实例。开发中也不建议使用。( p a r e n t . parent. parent.parent… 岂不是回到了jquery)
ref原理:
- ref可以获取dom元素和组件的实例,(虚拟dom没有处理ref,这里无法拿到实例,也无法获取组件)
- 创建dom的时候是如何处理ref的
- 会将用户所有的dom操作及属性,都维护到一个cbs属性中,(create,update,insert,destory…)依次调用cbs中的create方法,这里就包含ref相关的操作,会操作ref,并且赋值。
inject,provide
在父组件通将属性暴露出来,在后代属组件中注入属性。(少用)
父级组件提供的数据,在子组件中递归查找,找到就定义在自己的身上。
$attrs, $listeners
- $attrs:所有组件上的属性,不涵盖props
- $listeners:组件上所有的事件
通过$attrs属性,可以快速的把非props的属性传递给子组件
其实还有一个Vue.observal,可以创造一个全局的对象用于通信