2024年最全面试偶尔问的 Vue2 中 slot 和 slot-scope 的原理(1),2024年最新【面试总结

最后

javascript是前端必要掌握的真正算得上是编程语言的语言,学会灵活运用javascript,将对以后学习工作有非常大的帮助。掌握它最重要的首先是学习好基础知识,而后通过不断的实战来提升我们的编程技巧和逻辑思维。这一块学习是持续的,直到我们真正掌握它并且能够灵活运用它。如果最开始学习一两遍之后,发现暂时没有提升的空间,我们可以暂时放一放。继续下面的学习,javascript贯穿我们前端工作中,在之后的学习实现里也会遇到和锻炼到。真正学习起来并不难理解,关键是灵活运用。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

css源码pdf

JavaScript知识点

初始化

对于这样的一个例子来说

Hello

{{prop.msg}}

这段模板会被编译成这样:

with (this) {

return _c(“test”, {

scopedSlots: _u([

{

key: “bar”,

fn: function () {

return [_c(“span”, [_v(“Hello”)])];

},

},

{

key: “foo”,

fn: function (prop) {

return [_c(“span”, [_v(_s(prop.msg))])];

},

},

]),

});

}

然后经过初始化时的一系列处理(resolveScopedSlots, normalizeScopedSlotstest  组件的实例 this.$slotScopes 就可以访问到这两个 foobar 函数。(如果未命名的话,key 会是 default 。)

进入 test 组件内部,假设它是这样定义的:

那么 template 就会被编译为这样的函数:

with (this) {

return _c(“div”, [_t(“bar”), _t(“foo”, null, null, { msg })], 2);

}

已经有那么些端倪了,接下来就研究一下 _t 函数的实现,就可以接近真相了。

_t 也就是 renderSlot的别名,简化后的实现是这样的:

export function renderSlot (

name: string,

fallback: ?Array,

props: ?Object,

bindObject: ?Object

): ?Array {

// 通过 name 拿到函数

const scopedSlotFn = this.$scopedSlots[name]

let nodes

if (scopedSlotFn) { // scoped slot

props = props || {}

// 执行函数返回 vnode

nodes = scopedSlotFn(props) || fallback

}

return nodes

}

其实很简单,

如果是 普通插槽,就直接调用函数生成 vnode,如果是 作用域插槽

就直接带着 props 也就是 { msg } 去调用函数生成 vnode。2.6 版本后统一为函数的插槽降低了很多心智负担。

更新

在上面的 test 组件中, 1s 后我们通过 this.msg = "Changed"; 触发响应式更新,此时编译后的 render 函数:

with (this) {

return _c(“div”, [_t(“bar”), _t(“foo”, null, null, { msg })], 2);

}

重新执行,此时的 msg 已经是更新后的 Changed 了,自然也就实现了更新。

一种特殊情况是,在父组件的作用于里也使用了响应式的属性并更新,比如这样:

Hello

{{prop.msg}}

其实,是因为执行 _t 函数时,全局的组件渲染上下文是 子组件,那么依赖收集自然也就是收集到 子组件的依赖了。所以在 msgInParent 更新后,其实是直接去触发子组件的重新渲染的,对比 2.5 的版本,这是一个优化。

那么还有一些额外的情况,比如说 template 上有 v-ifv-for 这种情况,举个例子来说:

Hello

function render() {

with(this) {

return _c(‘test’, {

scopedSlots: _u([(show) ? {

key: “bar”,

fn: function () {

return [_c(‘span’, [_v(“Hello”)])]

},

proxy: true

} : null], null, true)

})

}

}

注意这里的 _u 内部直接是一个三元表达式,读取 _u 是发生在父组件的 _render 中,那么此时子组件是收集不到这个 show 的依赖的,所以说 show 的更新只会触发父组件的更新,那这种情况下子组件是怎么重新执行 $scopedSlot 函数并重渲染的呢?

我们已经有了一定的前置知识:Vue的更新粒度[2],知道 Vue 的组件不是递归更新的,但是 slotScopes 的函数执行是发生在子组件内的,父组件在更新的时候一定是有某种方式去通知子组件也进行更新。

其实这个过程就发生在父组件的重渲染的 patchVnode中,到了 test 组件的 patch 过程,进入了 updateChildComponent 这个函数后,会去检查它的 slot 是否是稳定的,显然 v-if 控制的 slot 是非常不稳定的。

const newScopedSlots = parentVnode.data.scopedSlots

const oldScopedSlots = vm.$scopedSlots

const hasDynamicScopedSlot = !!(

(newScopedSlots && !newScopedSlots.$stable) ||

(oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||

(newScopedSlots && vm. s c o p e d S l o t s . scopedSlots. scopedSlots.key !== newScopedSlots.$key)

)

// Any static slot children from the parent may have changed during parent’s

// update. Dynamic scoped slots may also have changed. In such cases, a forced

// update is necessary to ensure correctness.

const needsForceUpdate = !!hasDynamicScopedSlot

if (needsForceUpdate) {

// 这里的 vm 对应 test 也就是子组件的实例,相当于触发了子组件强制渲染。

vm.$forceUpdate()

}

这里有一些优化措施,并不是说只要有 slotScope 就会去触发子组件强制更新。

有如下三种情况会强制触发子组件更新:

  1. scopedSlots 上的 $stable 属性为 false

一路追寻这个逻辑,最终发现这个 $stable_u 也就是 resolveScopedSlots 函数的第三个参数决定的,由于这个 _u 是由编译器生成 render 函数时生成的的,那么就到 codegen 的逻辑中去看:

最后:

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

一路追寻这个逻辑,最终发现这个 $stable_u 也就是 resolveScopedSlots 函数的第三个参数决定的,由于这个 _u 是由编译器生成 render 函数时生成的的,那么就到 codegen 的逻辑中去看:

最后:

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-2DDJ8YSY-1715690252859)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值