You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
我坚持在掘金发文章其实有一个原因,就是我也希望中文社区能慢慢发展出类似 medium 那样高质量的前端交流社区(虽然它是付费制的,有难度),而掘金是我前端最开始就接触到的社区,心里也很有感情,看着首页混杂着这种错误百出的低质量文章,我心里真的是百感交集,为什么明明是新手的类似于学习笔记质量的文章也要急着发出来吸引流量呢?
本着对社区的小伙伴们负责的态度,有些文章里应付面试用的一些讲解实在是看不下去。
本文针对 @小明同学哟 的 《前端常见的Vue面试题目汇总》 这篇文章,提出一些错误。
先放一张大图,有兴趣的同学可以点开图片看一下原文,简单来说就是写了很多不知道从哪里收集来的劣质总结,然后底下放个公众号骗粉丝。
且不说原文中每个答案都过于简略,并不能达到面试官的要求,其中还有很多错误的地方会误导读者,接下来我重点指出一下错误的地方。
这里不放原文链接的原因是我希望抵制这样的作者,这个作者的掘力值快要 5000 了,而掘金会对掘力值 5000 以上的作者进行文章首页推荐。如果以后首页都是这样的低质量文章,那真的很让人绝望。
另外比较可笑的是,昨天在这篇文章下提出了一些反驳的观点,今早一看这篇文章的评论区,已经被作者删的一干二净,只留下她的「水军号」的一条评论了。不禁唏嘘,直接删掉文章的反对观点来掩耳盗铃。
准备开始
接下来开始针对作者文章中的观点进行逐条的反驳,注意「引用」 中的文字的即是作者原文,错别字我也原样保留了。
请说一下响应式数据的原理
收集当前组件中的watcher,我进一步问你什么叫当前组件的
watcher
?我面试时经常听到这种模糊的说法,感觉就是看了些造玩具的文章就说熟悉响应式原理了,起码的流程要清晰一些:render
函数是由Watcher
去代理执行的,Watcher
在执行前会把Watcher
自身先赋值给Dep.target
这个全局变量,等待响应式属性去收集它render
函数时访问了响应式属性,响应式属性就会精确的收集到当前全局存在的Dep.target
作为自身的依赖Watcher
去重新调用vm._update(vm._render())
进行组件的视图更新响应式部分,如果你想在简历上写熟悉的话,还是要抽时间好好的去看一下源码中真正的实现,而不是看这种模棱两可的说法就觉得自己熟练掌握了。
为什么Vue采用异步渲染
什么叫本轮数据更新后,再去异步更新数据?
轮指的是什么,在
eventLoop
里的task
和microTask
,他们分别的执行时机是什么样的,为什么优先选用microTask
,这都是值得深思的好问题。建议看看这篇文章: Vue源码详解之nextTick:MutationObserver只是浮云,microtask才是核心!
nextTick实现原理
这句话说的很乱,典型的让面试官忍不住想要深挖一探究竟的回答。(因为一听你就不是真的懂)
正确的流程应该是先去
嗅探环境
,依次去检测Promise的then
->MutationObserver的回调函数
->setImmediate
->setTimeout
是否存在,找到存在的就使用它,以此来确定回调函数队列是以哪个 api 来异步执行。在
nextTick
函数接受到一个callback
函数的时候,先不去调用它,而是把它 push 到一个全局的queue
队列中,等待下一个任务队列的时候再一次性的把这个queue
里的函数依次执行。这个队列可能是
microTask
队列,也可能是macroTask
队列,前两个 api 属于微任务队列,后两个 api 属于宏任务队列。简化实现一个异步合并任务队列:
测试一下:
Vue优点
看起来说的很厉害,其实也没说到点上。关于虚拟 DOM 的优缺点,直接看 Vue 作者尤雨溪本人的知乎回答,你会对它有进一步的理解:
网上都说操作真实 DOM 慢,但测试结果却比 React 更快,为什么?
开发者不操作dom对象,和双向绑定没太大关系。React不提供双向绑定,开发者照样不需要操作dom。双向绑定只是一种语法糖,在表单元素上绑定
value
并且监听onChange
事件去修改value
触发响应式更新。我建议真正想看模板被编译后的原理的同学,可以去尤大开源的vue-template-explorer 网站输入对应的模板,就会展示出对应的 render 函数。
为什么快,快在哪里,什么情况下快,有数据支持吗?事实上在初始化数据量不同的场景是不好比较的,
React
不需要对数据递归的进行响应式定义
。而在更新的场景下
Vue
可能更快一些,因为Vue
的更新粒度是组件级别的,而React
是递归向下的进行reconciler
,React
引入了Fiber
架构和异步更新,目的也是为了让这个工作可以分在不同的时间片
中进行,不要去阻塞用户高优先级的操作。Vue3 没发布不是因为兼容性不好,工作正在有序推进中,新的语法也在不断迭代,并且发布
rfc
征求社区意见。事实上可以,并且尤大说只是为了性能的权衡才不去监听。数组下标本质上也就是对象的一个属性。
React和Vue的比较
比较引用和
diff
有什么关系,难道 Vue 就不diff
了吗。双向绑定是
v-model
吧。Vue 虽然可以传递回调,但是一般来说还是通过
v-on:change
或者@change
的方式去绑定事件吧,这和回调是两套机制。事实上 Vue 是自己实现了一套模板引擎系统,
HTML
可以被利用为模板的而已,你在.vue
文件里写的template
和HTML
本质上没有关系。事实上高阶函数只是社区提出的一种方案被 React 所采纳而已,其他的方案还有
renderProps
和 最近流行的Hook
Vue 也可以利用高阶函数 实现组合和复用。
diff算法的时间复杂度
听这个描述来说,React 没有对
O(n3)
的复杂度进行优化?事实上 React 和 Vue 都只会对tag
相同的同级节点进行diff
,如果不同则直接销毁重建,都是O(n)
的复杂度。谈谈你对作用域插槽的理解
跟 DOM 没关系,是在虚拟节点树的插槽位置替换。
不加 key 也不一定就会复用,关于 diff 和 key 的使用,建议大家还是找一些非造玩具的文章真正深入的看一下原理。
为什么 Vue 中不要用 index 作为 key?(diff 算法详解)
组件中的data为什么是函数
这句话反正我压根没听懂,事实上如果组件里 data 直接写了一个对象的话,那么如果你在模板中多次声明这个组件,组件中的 data 会指向同一个引用。
此时如果在某个组件中对 data 进行修改,会导致其他组件里的 data 也被污染。 而如果使用函数的话,每个组件里的 data 会有单独的引用,这个问题就可以避免了。
这个问题我同样举个例子来方便理解,假设我们有这样的一个组件,其中的 data 直接使用了对象而不是函数:
注意,这里的
Counter.data
是一个引用,也就是它是在当前的运行环境下全局唯一
的,它在堆内存中占用了一部分空间。然后我们在模板中调用两次
Counter
组件:我们从原理出发,先看看它被编译成什么样的
render
函数:每一个
Counter
会被_c
所调用,也就是createElement
,想象一下createElement
内部会发生什么,它会直接拿着Counter
上的data
这个引用去创建一个组件。 也就是所有的Counter
组件实例上的data
都指向同一个引用。此时假如 id 为 a 的 Counter 组件内部调用了
count++
,会去对data
这个引用上的 count 属性赋值,那么此时由于 id 为 b 的 Counter 组件内部也是引用的同一份 data,它也会感觉到变化而更新组件,这就造成了多个组件之间的数据混乱了。computed和watch有什么区别
这也是一个一问就倒的回答,依赖变化是计算属性就重新求值吗?中间经历了什么过程,为什么说
computed
是有缓存值的?随便挑一个点深入问下去就站不住。 事实上computed
会拥有自己的watcher
,它内部有个属性dirty
开关来决定computed
的值是需要重新计算还是直接复用之前的值。以这样的一个例子来说:
首先明确两个关键字:
「dirty」 从字面意义来讲就是
脏
的意思,这个开关开启了,就意味着这个数据是脏数据,需要重新求值了拿到最新值。「求值」 的意思的对用户传入的函数进行执行,也就是执行
return this.count + 1
sum
第一次进行求值的时候会读取响应式属性count
,收集到这个响应式数据作为依赖。并且计算出一个值来保存在自身的value
上,把dirty
设为 false,接下来在模板里再访问sum
就直接返回这个求好的值value
,并不进行重新的求值。count
发生变化了以后会通知sum
所对应的watcher
把自身的dirty
属性设置成 true,这也就相当于把重新求值的开关打开来了。这个很好理解,只有count
变化了,sum
才需要重新去求值。this.sum
的时候,才会真正的去重新调用sum
函数求值,并且再次把dirty
设置为 false,等待下次的开启……后续我会考虑单独出一篇文章进行详细讲解。
Watch中的deep:true是如何实现的
不光是数组类型,对象类型也会对深层属性进行
依赖收集
,比如监听了obj
,假如设置了deep: true
,那么对obj.a.b.c = 5
这样深层次的修改也一样会触发 watch 的回调函数。本质上是因为 Vue 内部对设置了deep
的 watch,会进行递归的访问
(只要此属性也是响应式属性
),而在此过程中也会不断发生依赖收集。在回答这道题的时候,同样也要考虑到
递归收集依赖
对性能上的损耗和权衡,才是一份合格的回答。action和mutation区别
内部并不能检测到是否异步更新,而是实例上有一个开关变量
_committing
,mutation
的调用栈,此时的开关已经是关上的,自然能检测到对 state 的修改并报错。具体可以查看源码中的withCommit
函数。这是一种很经典对于js单线程机制
的利用。关于重复发文章
此外 @小明同学哟 这个作者和 @小梦哟 这两个作者之间有说不清道不明的关系(之前看好像是情侣头像,然后经常互动,并且两个人分别著有《一个湖北女生的总结》、《一个湖北男生的总结》)。
两个作者之间把同一篇低质量文章来回发,都是那种评论区能指出特别多错误的水文。
来波 diff 算法
这是 @小明同学哟 的 《前端面试大厂手写源码系列(上)》:
《前端面试大厂手写源码系列(上)》
这是 @小梦哟 的 《面试时,你被要求手写常见原理了吗?》
面试时,你被要求手写常见原理了吗?
基本上就是顺序调换一下,内容完全重复的文章,阅读量还不低。
关于发课程文章不注明出处
最开始接触到这个作者,是因为她写了一篇 《Vue仿饿了么app项目总结》,我正好在这个项目的作者黄轶老师的群里,群友非常愤慨的来评论区讨公道后她才在评论区里声明这是和慕课网的黄轶老师学习课程后进行的总结。
我可以理解为如果没人说的话,她就想瞒混过去作为自己的项目了,可惜她不了解行情,这门课早就在几年前就家喻户晓,成为 Vue 面试必备的实战项目了。
申请水军号
他们的文章其实挺难获得好评的,毕竟真的挺水的。但是这个用户却时常在他们的文章下抢沙发。点进去仔细一看,只关注了这俩人,点赞的也全是这俩人……
关于知识变现
我一直觉得知识变现不可耻,这是一个「自媒体」流行的时代,认真输出自己观点并且影响他人的人理应获得自己的收益,我并不觉得这有什么丢人的,
我在 写给初中级前端的高级进阶指南 这篇文章里放了几个掘金小册的推广码,是我认真读过以后真心想推荐给大家的,这也是掘金官方提供的一种变现机制。我真心不觉得这有什么不对。知识是有价值的,前提是你不要输出二手错误百出的知识。甚至在大家的公众号上看到广告的时候,我也是会心一笑,因为这个作者曾经或「原创」或「转载」的优质文章给我带来了很大的收益……
而原作者 @小明同学哟 的水平明显还不足以给社区的新人一些启发,甚至我感觉大概相当于某c字开头的论坛上面充斥着的新手学习笔记,这样子为了变现而影响社区环境的吃相我就接受不了了。
再不济,你还可以学习某不愿提及姓名的「闰土大叔」,写些心灵鸡汤做一个教父,也一样可以赚的盆满钵满,毕竟人家没误导人。只是人家是真的不会技术,那就曲线救国而已。
总结
总而言之,我关注了这个作者和她的搭档 @小梦哟 挺久了,不知道这些作者为什么这么拼命的想火起来,不惜重复发文章,不惜借用别人的课程成果而不声明,这对社区的进步来说没有任何好处。
我坚持在掘金发文章其实有一个原因,就是我也希望中文社区能慢慢发展出类似
medium
那样高质量的前端交流社区(虽然它是付费制的,有难度),而掘金是我前端最开始就接触到的社区,心里也很有感情,看着首页混杂着这种错误百出的低质量文章,我心里真的是百感交集,为什么明明是新手的类似于学习笔记质量的文章也要急着发出来吸引流量呢?总之,真心希望掘金能少一些不负责任的水文,一些摘抄搬运官方文档的东西。大家都认真的输出自己去证实过,或者真正理解的总结,慢慢的让掘金、甚至国内的前端氛围能够形成一个良性氛围,前端的明天越来越美好。
The text was updated successfully, but these errors were encountered: