Cet article résume et partage plus de 20 questions d'entretien classiques Vue (avec explications au niveau du code source) pour vous aider à trier les connaissances de base et à améliorer les réserves de connaissances de Vue. Cela vaut la peine d'être collecté, venez y jeter un œil !
Vue est un framework de développement basé sur des composants, la communication de données entre les composants est donc très importante pour les applications Vue. Cette question teste principalement les compétences de base de Vue et la maîtrise de l'API de base de Vue. D'autres connaissances limites telles que fournir/injecter/$attrs démontrent l'étendue des connaissances de l'intervieweur.
Différentes manières de transmettre des paramètres à travers les composants
Analyse de réflexion :
Aperçu de toutes les méthodes connues
Expliquer les scénarios d'utilisation en fonction des relations entre les composants
Exemple de réponse :
1. Il existe 8 méthodes courantes de communication entre composants :
Faites attention à plusieurs API abandonnées dans vue3
https://v3-migration.vuejs.org/breaking-changes/children.html
https : //v3-migration.vuejs.org/breaking-changes/listeners-removed.html
https://v3-migration.vuejs.org/breaking-changes/events-api.html#overview
2 . Il est plus clair et efficace de discuter de la communication des composants basée sur la relation entre les composants
Composants parents et enfants
props
/$emit
/. $parent
/ref/$attrs
props
/$emit
/$parent
/ref
/$attrs
兄弟组件
$parent
/$root
/eventbus
/vuex
跨层级关系
eventbus
/vuex
/provide
+inject
分析:
此题考查常识,文档中曾有详细说明v2|v3;也是一个很好的实践题目,项目中经常会遇到,能够看出面试者api熟悉程度和应用能力。
思路分析:
先给出结论
为什么是这样的,说出细节
哪些场景可能导致我们这样做,该怎么处理
总结,拔高
回答范例:
实践中不应该把v-for和v-if放一起
在vue2中,v-for的优先级是高于v-if,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常
通常有两种情况下导致我们这样做:
为了过滤列表中的项目 (比如 v-for="user in users" v-if="user.isActive"
)。此时定义一个计算属性 (比如 activeUsers
),让其返回过滤后的列表即可(比如users.filter(u=>u.isActive)
)。
为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers"
)。此时把 v-if
移动至容器元素上 (比如 ul
、ol
)或者外面包一层template
即可。
文档中明确指出永远不要把 v-if
和 v-for
$parent
/$root
/ eventbus
/vuex
🎜🎜🎜🎜🎜Relation inter-niveaux🎜🎜🎜eventbus
/vuex
/provide
+inject
🎜🎜🎜🎜🎜v-for="user in users" v-if= "user.isActive"
). À ce stade, définissez une propriété calculée (telle que activeUsers
) et laissez-la renvoyer la liste filtrée (telle que users.filter(u=>u.isActive)
). . 🎜🎜🎜🎜Pour 🎜 éviter le rendu des listes 🎜 qui doivent être masquées (par exemple v-for="user in users" v-if="shouldShowUsers"
). À ce stade, déplacez v-if
vers l'élément conteneur (tel que ul
, ol
) ou enveloppez-le avec une couche de modèle
C'est tout. 🎜🎜🎜🎜🎜🎜La documentation indique clairement🎜N'utilisez jamais v-if
et v-for
sur le même élément en même temps🎜, c'est évidemment une chose importante à noter. 🎜🎜🎜🎜Dans la partie génération de code du code source, vous pouvez clairement voir si v-if ou v-for est traité en premier. L'ordre de vue2 et vue3 est exactement le contraire, donc certains symptômes sont différents, mais peu importe. quoi, il ne peut pas les écrire ensemble. 🎜
Connaître la raison :
Faire un test Lorsque test.html est du même niveau, la fonction de rendu est la suivante :
ƒ anonymous( ) { with(this){return _c('div',{attrs:{"id":"app"}},_l((items),function(item){return (item.isActive)?_c('div',{key:item.id},[_v("\n "+_s(item.name)+"\n ")]):_e()}),0)} }
Faire un test, test-v3.html
Trouvez la réponse dans le code source
v2 : https://github1s.com/vuejs/vue/blob/HEAD/src/compiler/codegen/index.js#L65-L66
v3 : https : //github1s.com/vuejs /core/blob/HEAD/packages/compiler-core/src/codegen.ts#L586-L587
Questions obligatoires, testez les bases de vue.
Pensée
Donne des concepts
Liste chaque étape du cycle de vie
Explique le processus global
Combiné avec la pratique
Extension : vue 3 changements
Exemple de réponse
1. Chaque instance de composant Vue passera par une série d'étapes d'initialisation après sa création. Par exemple, elle nécessite l'observation des données, la compilation de modèles, le montage de l'instance sur le dom et la mise à jour. le dom lorsque les données changent. Des fonctions appelées hooks de cycle de vie sont exécutées au cours de ce processus pour donner aux utilisateurs la possibilité d'ajouter leur propre code à certaines étapes.
2. Le cycle de vie de Vue peut être divisé en 8 étapes au total : avant et après la création, avant et après le chargement, avant et après la mise à jour, avant et après la destruction, ainsi que le cycle de vie de certaines scènes spéciales. Trois nouvelles scènes pour le débogage et le rendu côté serveur ont été ajoutées à vue3.
Cycle de vie v2 | Cycle de vie v3 | Description |
---|---|---|
beforeCreate | beforeCreate | Lorsque l'instance de composant est créée |
created | créée | L'instance du composant a été complètement créé |
beforeMount | beforeMount | Avant que le composant ne soit monté |
monté | monté | Après que le composant soit monté sur l'instance |
beforeUpdate | beforeUpdate | Modifications des données des composants, avant la mise à jour |
updated | updated | Données mises à jour |
beforeDestroy | beforeUnmount | Avant que l'instance du composant ne soit détruite |
destroyed | démonté | Après la destruction de l'instance du composant |
Lifecycle v2 | Lifecycle v3 | Description |
---|---|---|
activé | activé | garder en vie lorsque le composant mis en cache est activé |
désactivé | désactivé | keep-alive composants mis en cache désactivés Appelé lorsque |
errorCaptured | errorCaptured | est appelé lors de la capture d'une erreur d'un composant descendant |
- | renderTracked | Hook de débogage, appelé lorsque les dépendances réactives sont collectées |
- | renderTriggered | Debug hook, appelé lorsqu'une dépendance réactive est déclenchée |
- | serverPrefetch | ssr uniquement, appelé avant que l'instance du composant ne soit rendue sur le serveur |
3、Vue
生命周期流程图:
4、结合实践:
beforeCreate:通常用于插件开发中执行一些初始化任务
created:组件初始化完毕,可以访问各种数据,获取接口数据等
mounted:dom已创建,可用于获取访问数据和dom元素;访问子组件等。
beforeUpdate:此时view
层还未更新,可用于获取更新前各种状态
updated:完成view
层的更新,更新后,所有状态已是最新
beforeunmount:实例被销毁前调用,可用于一些定时器或订阅的取消
unmounted:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
可能的追问
setup和created谁先执行?
setup中为什么没有beforeCreate和created?
知其所以然
vue3中生命周期的派发时刻:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/componentOptions.ts#L554-L555
vue2中声明周期的派发时刻:
https://github1s.com/vuejs/vue/blob/HEAD/src/core/instance/init.js#L55-L56
题目分析:
双向绑定是vue
的特色之一,开发中必然会用到的知识点,然而此题还问了实现原理,升级为深度考查。
思路分析:
给出双绑定义
双绑带来的好处
在哪使用双绑
使用方式、使用细节、vue3变化
原理实现描述
回答范例:
vue中双向绑定是一个指令v-model
,可以绑定一个响应式数据到视图,同时视图中变化能改变该值。
v-model
是语法糖,默认情况下相当于:value
和@input
。使用v-model
可以减少大量繁琐的事件处理代码,提高开发效率。
通常在表单项上使用v-model
,还可以在自定义组件上使用,表示某个值的输入和输出控制。
通过<input v-model="xxx">
的方式将xxx的值绑定到表单元素value上;对于checkbox,可以使用true-value
和false-value指定特殊的值,对于radio可以使用value指定特殊的值;对于select可以通过options元素的value设置特殊的值;还可以结合.lazy,.number,.trim对v-mode的行为做进一步限定;v-model
用在自定义组件上时又会有很大不同,vue3中它类似于sync
修饰符,最终展开的结果是modelValue属性和update:modelValue事件;vue3中我们甚至可以用参数形式指定多个不同的绑定,例如v-model:foo和v-model:bar,非常强大!
v-model
是一个指令,它的神奇魔法实际上是vue的编译器完成的。我做过测试,包含v-model
的模板,转换为渲染函数之后,实际上还是是value属性的绑定以及input事件监听,事件回调函数中会做相应变量更新操作。编译器根据表单元素的不同会展开不同的DOM属性和事件对,比如text类型的input和textarea会展开为value和input事件;checkbox和radio类型的input会展开为checked和change事件;select用value作为属性,用change作为事件。
可能的追问:
v-model
和sync
修饰符有什么区别
自定义组件使用v-model
如果想要改变事件名或者属性名应该怎么做
知其所以然:
测试代码,test.html
观察输出的渲染函数:
// <input type="text" v-model="foo"> _c('input', { directives: [{ name: "model", rawName: "v-model", value: (foo), expression: "foo" }], attrs: { "type": "text" }, domProps: { "value": (foo) }, on: { "input": function ($event) { if ($event.target.composing) return; foo = $event.target.value } } })
// <input type="checkbox" v-model="bar"> _c('input', { directives: [{ name: "model", rawName: "v-model", value: (bar), expression: "bar" }], attrs: { "type": "checkbox" }, domProps: { "checked": Array.isArray(bar) ? _i(bar, null) > -1 : (bar) }, on: { "change": function ($event) { var $$a = bar, $$el = $event.target, $$c = $$el.checked ? (true) : (false); if (Array.isArray($$a)) { var $$v = null, $$i = _i($$a, $$v); if ($$el.checked) { $$i < 0 && (bar = $$a.concat([$$v])) } else { $$i > -1 && (bar = $$a.slice(0, $$i).concat($$a.slice($$i + 1))) } } else { bar = $$c } } } })
// <select v-model="baz"> // <option value="vue">vue</option> // <option value="react">react</option> // </select> _c('select', { directives: [{ name: "model", rawName: "v-model", value: (baz), expression: "baz" }], on: { "change": function ($event) { var $$selectedVal = Array.prototype.filter.call( $event.target.options, function (o) { return o.selected } ).map( function (o) { var val = "_value" in o ? o._value : o.value; return val } ); baz = $event.target.multiple ? $$selectedVal : $$selectedVal[0] } } }, [ _c('option', { attrs: { "value": "vue" } }, [_v("vue")]), _v(" "), _c('option', { attrs: { "value": "react" } }, [_v("react")]) ])
此题属于实践题,考察大家对vue常用api使用熟练度,答题时不仅要列出这些解决方案,同时最好说出他们异同。
答题思路:
按照逻辑扩展和内容扩展来列举,
逻辑扩展有:mixins、extends、composition api;
内容扩展有slots;
分别说出他们使用方法、场景差异和问题。
作为扩展,还可以说说vue3中新引入的composition api带来的变化
回答范例:
常见的组件扩展方法有:mixins,slots,extends等
混入mixins是分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
// 复用代码:它是一个配置对象,选项和组件里面一样 const mymixin = { methods: { dosomething(){} } } // 全局混入:将混入对象传入 Vue.mixin(mymixin) // 局部混入:做数组项设置到mixins选项,仅作用于当前组件 const Comp = { mixins: [mymixin] }
插槽主要用于vue组件中的内容分发,也可以用于组件扩展。
子组件Child
<div> <slot>这个内容会被父组件传递的内容替换</slot> </div>
父组件Parent
<div> <Child>来自老爹的内容</Child> </div>
如果要精确分发到不同位置可以使用具名插槽,如果要使用子组件中的数据可以使用作用域插槽。
组件选项中还有一个不太常用的选项extends,也可以起到扩展组件的目的
// 扩展对象 const myextends = { methods: { dosomething(){} } } // 组件扩展:做数组项设置到extends选项,仅作用于当前组件 // 跟混入的不同是它只能扩展单个对象 // 另外如果和混入发生冲突,该选项优先级较高,优先起作用 const Comp = { extends: myextends }
混入的数据和方法不能明确判断来源且可能和当前组件内变量产生命名冲突,vue3中引入的composition api,可以很好解决这些问题,利用独立出来的响应式模块可以很方便的编写独立逻辑并提供响应式的数据,然后在setup选项中组合使用,增强代码的可读性和维护性。例如:
// 复用逻辑1 function useXX() {} // 复用逻辑2 function useYY() {} // 逻辑组合 const Comp = { setup() { const {xx} = useXX() const {yy} = useYY() return {xx, yy} } }
可能的追问
Vue.extend方法你用过吗?它能用来做组件扩展吗?
知其所以然
mixins原理:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiCreateApp.ts#L232-L233
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/componentOptions.ts#L545
slots原理:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/componentSlots.ts#L129-L130
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L1373-L1374
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/helpers/renderSlot.ts#L23-L24
分析
这是一个实践知识点,组件化开发过程中有个单项数据流原则,不在子组件中修改父组件是个常识问题。
参考文档:https://staging.vuejs.org/guide/components/props.html#one-way-data-flow
思路
讲讲单项数据流原则,表明为何不能这么做
举几个常见场景的例子说说解决方案
结合实践讲讲如果需要修改父组件状态应该如何做
回答范例
所有的 prop 都使得其父子之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器控制台中发出警告。
const props = defineProps(['foo']) // ❌ 下面行为会被警告, props是只读的! props.foo = 'bar'
实际开发过程中有两个场景会想要修改一个属性:
**这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。**在这种情况下,最好定义一个本地的 data,并将这个 prop 用作其初始值:
const props = defineProps(['initialCounter']) const counter = ref(props.initialCounter)
**这个 prop 以一种原始的值传入且需要进行转换。**在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
const props = defineProps(['size']) // prop变化,计算属性自动更新 const normalizedSize = computed(() => props.size.trim().toLowerCase())
实践中如果确实想要改变父组件属性应该emit一个事件让父组件去做这个变更。注意虽然我们不能直接修改一个传入的对象或者数组类型的prop,但是我们还是能够直接改内嵌的对象或属性。
分析
综合实践题目,实际开发中经常需要面临权限管理的需求,考查实际应用能力。
权限管理一般需求是两个:页面权限和按钮权限,从这两个方面论述即可。
思路
权限管理需求分析:页面和按钮权限
权限管理的实现方案:分后端方案和前端方案阐述
说说各自的优缺点
回答范例
权限管理一般需求是页面权限和按钮权限的管理
具体实现的时候分后端和前端两种方案:
前端方案会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表。比如我会配置一个asyncRoutes
数组,需要认证的页面在其路由的meta
中添加一个roles
字段,等获取用户角色之后取两者的交集,若结果不为空则说明可以访问。此过滤过程结束,剩下的路由就是该用户能访问的页面,最后通过router.addRoutes(accessRoutes)
方式动态添加路由即可。
后端方案会把所有页面路由信息存在数据库中,用户登录的时候根据其角色查询得到其能访问的所有页面路由信息返回给前端,前端再通过addRoutes
动态添加路由信息
按钮权限的控制通常会实现一个指令,例如v-permission
,将按钮要求角色通过值传给v-permission指令,在指令的moutned
钩子中可以判断当前用户角色和按钮是否存在交集,有则保留按钮,无则移除按钮。
纯前端方案的优点是实现简单,不需要额外权限管理页面,但是维护起来问题比较大,有新的页面和角色需求就要修改前端代码重新打包部署;服务端方案就不存在这个问题,通过专门的角色和权限管理页面,配置页面和按钮权限信息到数据库,应用每次登陆时获取的都是最新的路由信息,可谓一劳永逸!
知其所以然
路由守卫
https://github1s.com/PanJiaChen/vue-element-admin/blob/HEAD/src/permission.js#L13-L14
路由生成
https://github1s.com/PanJiaChen/vue-element-admin/blob/HEAD/src/store/modules/permission.js#L50-L51
动态追加路由
https://github1s.com/PanJiaChen/vue-element-admin/blob/HEAD/src/permission.js#L43-L44
可能的追问
类似Tabs
这类组件能不能使用v-permission
指令实现按钮权限控制?
<el-tabs> <el-tab-pane label="⽤户管理" name="first">⽤户管理</el-tab-pane> <el-tab-pane label="⻆⾊管理" name="third">⻆⾊管理</el-tab-pane> </el-tabs>
服务端返回的路由信息如何添加到路由器中?
// 前端组件名和组件映射表 const map = { //xx: require('@/views/xx.vue').default // 同步的⽅式 xx: () => import('@/views/xx.vue') // 异步的⽅式 } // 服务端返回的asyncRoutes const asyncRoutes = [ { path: '/xx', component: 'xx',... } ] // 遍历asyncRoutes,将component替换为map[component] function mapComponent(asyncRoutes) { asyncRoutes.forEach(route => { route.component = map[route.component]; if(route.children) { route.children.map(child => mapComponent(child)) } }) } mapComponent(asyncRoutes)
分析
这是一道必问题目,但能回答到位的比较少。如果只是看看一些网文,通常没什么底气,经不住面试官推敲,但像我们这样即看过源码还造过轮子的,回答这个问题就会比较有底气啦。
答题思路:
啥是响应式?
为什么vue需要响应式?
它能给我们带来什么好处?
vue的响应式是怎么实现的?有哪些优缺点?
vue3中的响应式的新变化
回答范例:
所谓数据响应式就是能够使数据变化可以被检测并对这种变化做出响应的机制。
MVVM框架中要解决的一个核心问题是连接数据层和视图层,通过数据驱动应用,数据变化,视图更新,要做到这点的就需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出更新处理。
Prenons l'exemple de Vue. Grâce à la réactivité des données ainsi qu'au DOM virtuel et aux algorithmes de correctifs, les développeurs n'ont qu'à exploiter les données et à se soucier des affaires, sans avoir à gérer des opérations DOM lourdes, améliorant ainsi considérablement l'efficacité du développement et réduisant les difficultés de développement. . La réactivité des données dans
vue2 sera traitée différemment selon le type de données. S'il s'agit d'un objet , Object.defineProperty() est utilisé pour définir l'interception des données. Lorsque les données sont accédées ou modifiées, nous détectons et. make Response ; s'il s'agit d'un array, il répondra en remplaçant les 7 méthodes de changement du prototype de l'objet tableau afin que ces méthodes puissent fournir des notifications de mise à jour supplémentaires. Ce mécanisme résout très bien le problème de la réactivité des données, mais il présente également quelques défauts dans l'utilisation réelle : par exemple, le parcours récursif lors de l'initialisation entraînera une perte de performances ; lors de l'ajout ou de la suppression d'attributs, les utilisateurs doivent utiliser Vue.set/delete uniquement. des API spéciales peuvent prendre effet ; des problèmes tels que les nouvelles structures de données Map et Set générées dans es6 ne sont pas pris en charge.
Afin de résoudre ces problèmes, vue3 a réécrit cette partie de l'implémentation : en utilisant le proxy Proxy d'ES6 pour répondre aux données, cela présente de nombreux avantages, l'expérience de programmation est cohérente et il n'est pas nécessaire d'utiliser des API spéciales , les performances d'initialisation et la consommation de mémoire ont été considérablement améliorées ; de plus, comme le code d'implémentation réactif est extrait dans un package de réactivité indépendant, nous pouvons l'utiliser de manière plus flexible et les extensions tierces sont plus flexibles à développer.
Sachez pourquoi
vue2 responsive :
https://github1s.com/vuejs/vue/blob/HEAD/src/core/observer/index.js#L135- L136
vue3 responsive :
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/reactive.ts#L89-L90
https:// github1s.com /vuejs/core/blob/HEAD/packages/reactivity/src/ref.ts#L67-L68
Analyse
Presque tous les frameworks existants introduisent le DOM virtuel pour faire abstraction du DOM réel, désormais connu sous le nom de VNode et VDOM. Alors pourquoi devons-nous introduire le DOM virtuel ? Répondez simplement à cette question ! "Réflexion"
Exemple de réponse
Facilite la mise en œuvre multiplateforme
JavaScript
Vue3 permet aux développeurs d'implémenter des moteurs de rendu personnalisés basés sur VNode pour faciliter le rendu pour différentes plates-formes.
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/vnode.ts#L127-L128
Observez la fonction de rendu : 21-vdom/test-render-v3.html
Créer un vnode :
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/vnode.ts#L291-L292
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/vnode.ts#L486-L487
https://github1s.com / vuejs/core/blob/HEAD/packages/runtime-core/src/apiCreateApp.ts#L283-L284
mount:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime - core/src/renderer.ts#L1171-L1172
Déboguer le processus de montage : mountComponent
21-vdom/test-render-v3.html
Analyse
Questions obligatoires, impliquant les principes de mise à jour de la vue, testant comparativement la profondeur de la compréhension.
Réflexion
Que fait l'algorithme de comparaison
Sa nécessité
Quand il est exécuté
La méthode d'exécution spécifique
lift up: disons Jetons un coup d'œil à l'optimisation dans vue3
exemples de réponses
1. L'algorithme de comparaison dans Vue est appelé l'algorithme de patch, qui est modifié à partir de Snabbdom pour convertir le DOM virtuel en. un vrai DOM, vous devez utiliser la méthode patch Convert.
2. Initialement, chaque dépendance dans la vue Vue1.x a une fonction de mise à jour qui lui correspond, qui peut réaliser des mises à jour précises. Par conséquent, la prise en charge du DOM virtuel et de l'algorithme de correctif n'est pas nécessaire. Cependant, une granularité aussi fine rend Vue1.x incapable. pour transporter des applications plus volumineuses ; Afin de réduire la granularité de Watcher dans Vue 2.x, chaque composant n'a qu'un seul Watcher qui lui correspond. À l'heure actuelle, un algorithme de correctif doit être introduit pour trouver avec précision les modifications et les mettre à jour efficacement.
3. Le moment où diff est exécuté dans Vue est celui où la modification des données réactives dans le composant déclenche l'instance pour exécuter sa fonction de mise à jour. La fonction de mise à jour exécutera à nouveau la fonction de rendu pour obtenir le dernier DOM virtuel, puis exécutera le. patch et transmettez les nouvelles et anciennes valeurs virtuelles du DOM, recherchez les modifications en comparant les deux et enfin convertissez-les en opérations DOM correspondantes.
4. Le processus de patch est un processus récursif, suivant la stratégie de comparaison de profondeur d'abord et de même couche ; prenez le patch vue3 comme exemple :
Sachez pourquoicode de la clé de patch
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/ renderer.ts#L354-L355
Debug test-v3.html
AnalyseLes nouvelles fonctionnalités les plus remarquables répertoriées sur le site officiel : https://v3-migration.vuejs .org/
C'est-à-dire ce qui suit :
API de compositionExemples de réponses1. Les nouvelles fonctionnalités de Vue3 au niveau de l'API incluent principalement : l'API de composition, le sucre de syntaxe de l'API de composition SFC, le portail de téléportation, les fragments, les options d'émission, le moteur de rendu personnalisé, les variables CSS SFC, Suspense.
2. De plus, Vue3.0 présente également de nombreuses améliorations accrocheuses au niveau du framework :
Plus rapideSachez pourquoi Faites l'expérience de l'optimisation du compilateur
https://sfc.vuejs.org/
implémentation réactive
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/reactive.ts#L90 -L91
Analyse
Questions API, testez les capacités de base, pas de place à l'erreur, essayez d'être aussi détaillé que possible.
Réflexion
Qu'est-ce que le routage dynamique
Quand utiliser le routage dynamique, comment définir le routage dynamique
Comment obtenir les paramètres
Détails et précautions
Exemple de réponse
Souvent, nous devons mapper des itinéraires pour un modèle correspondant donné au même composant. Dans ce cas, nous devons définir un routage dynamique.
Par exemple, nous pourrions avoir un composant User
qui devrait être rendu à tous les utilisateurs, mais avec des ID utilisateur différents. Dans Vue Router, nous pouvons utiliser un champ dynamique dans le chemin pour y parvenir, par exemple : { chemin : '/users/:id', composant : Utilisateur
, où :id est le paramètre de chemin<code>User
组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,例如:{ path: '/users/:id', component: User }
,其中:id
就是路径参数
路径参数 用冒号 :
表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params
的形式暴露出来。
参数还可以有多个,例如/users/:username/posts/:postId
;除了 $route.params
之外,$route
对象还公开了其他有用的信息,如 $route.query
、$route.hash
等。
如何响应动态路由参数的变化
https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#响应路由参数的变化
我们如何处理404 Not Found路由
https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#捕获所有路由或-404-not-found-路由
思路分析:
首先思考vue路由要解决的问题:用户点击跳转链接内容切换,页面不刷新。
回答范例:
一个SPA应用的路由需要解决的问题是页面跳转内容改变同时不刷新,同时路由还需要以插件形式存在,所以:
首先我会定义一个createRouter
:
. Lorsqu'une route correspond, la valeur de ses params sera exposée dans chaque composant sous la forme this.$route.params
. /users/:username/posts/:postId
en plus de $route.params
, $route ; L'objet
expose également d'autres informations utiles, telles que $route.query
, $route.hash
, etc. https://router.vuejs.org/zh/guide/essentials /dynamic-matching.html #Capturez toutes les routes ou -404-not-found-route
Analyse des idées :
Utilisez l'API de hachage ou d'historique pour réaliser le saut d'URL sans actualiser la page
Je vais d'abord définir une fonction createRouter qui renvoie l'instance du routeur. Elle fait plusieurs choses à l'intérieur de l'instance : Enregistre les éléments de configuration. transmis par l'utilisateur
Écoutez les événements de hachage ou de popstate
Faites correspondre la route correspondante en fonction du chemin dans le rappel
Définissez le routeur en tant que plug-in Vue, c'est-à-dire implémentez la méthode d'installation et faites deux choses en interne :Implémenter deux composants globaux : router-link et router-view, qui implémentent respectivement le saut de page et l'affichage du contenu
key的作用主要是为了更高效的更新虚拟DOM。
vue在patch过程中判断两个节点是否是相同节点是key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能。
实际使用中在渲染一组列表时key必须设置,而且必须是唯一标识,应该避免使用数组索引作为key,这可能导致一些隐蔽的bug;vue中在使用相同标签元素过渡切换时,也会使用key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
从源码中可以知道,vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永远认为这是两个相同节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。
知其所以然
测试代码,test-v3.html
上面案例重现的是以下过程
不使用key
如果使用key
// 首次循环patch A A B C D E A B F C D E // 第2次循环patch B B C D E B F C D E // 第3次循环patch E C D E F C D E // 第4次循环patch D C D F C D // 第5次循环patch C C F C // oldCh全部处理结束,newCh中剩下的F,创建F并插入到C前面
源码中找答案:
判断是否为相同节点
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/vnode.ts#L342-L343
更新时的处理
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L1752-L1753
不使用key
如果使用key
// 首次循环patch A A B C D E A B F C D E // 第2次循环patch B B C D E B F C D E // 第3次循环patch E C D E F C D E // 第4次循环patch D C D F C D // 第5次循环patch C C F C // oldCh全部处理结束,newCh中剩下的F,创建F并插入到C前面
源码中找答案:
判断是否为相同节点
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/vnode.ts#L342-L343
更新时的处理
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L1752-L1753
分析
这道题及考察使用,有考察原理,nextTick在开发过程中应用的也较少,原理上和vue异步更新有密切关系,对于面试者考查很有区分度,如果能够很好回答此题,对面试效果有极大帮助。
答题思路
nextTick是做什么的?
为什么需要它呢?
开发时何时使用它?抓抓头,想想你在平时开发中使用它的地方
下面介绍一下如何使用nextTick
原理解读,结合异步更新和nextTick生效方式,会显得你格外优秀
回答范例:
1、nextTick是等待下一次 DOM 更新刷新的工具方法。
2、Vue有个异步更新策略,意思是如果数据变化,Vue不会立刻更新DOM,而是开启一个队列,把组件更新函数保存在队列中,在同一事件循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在DOM上,此时如果想要获取更新后的DOM状态,就需要使用nextTick。
3、开发时,有两个场景我们会用到nextTick:
created中想要获取DOM时;
响应式数据变化后获取DOM更新后的状态,比如希望获取列表更新后的高度。
4、nextTick签名如下:function nextTick(callback?: () => void): Promise<void></void>
所以我们只需要在传入的回调函数中访问最新DOM状态即可,或者我们可以await nextTick()方法返回的Promise之后做这件事。
5、在Vue内部,nextTick之所以能够让我们看到DOM更新后的结果,是因为我们传入的callback会被添加到队列刷新函数(flushSchedulerQueue)的后面,这样等队列内部的更新函数都执行完毕,所有DOM操作也就结束了,callback自然能够获取到最新的DOM值。
知其所以然:
源码解读:
组件更新函数入队:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L1547-L1548
入队函数:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/scheduler.ts#L84-L85
nextTick定义:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/scheduler.ts#L58-L59
测试案例,test-v3.html
两个重要API,反应应聘者熟练程度。
computed特点:具有响应式的返回值
const count = ref(1) const plusOne = computed(() => count.value + 1)
watch特点:侦测变化,执行回调
const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } )
回答范例
计算属性可以从组件数据派生出新数据,最常见的使用方式是设置一个函数,返回计算之后的结果,computed和methods的差异是它具备缓存性,如果依赖项不变时不会重新计算。侦听器可以侦测某个响应式数据的变化并执行副作用,常见用法是传递一个函数,执行副作用,watch没有返回值,但可以执行异步操作等复杂逻辑。
计算属性常用场景是简化行内模板中的复杂表达式,模板中出现太多逻辑会是模板变得臃肿不易维护。侦听器常用场景是状态变化之后做一些额外的DOM操作或者异步操作。选择采用何用方案时首先看是否需要派生出新值,基本能用计算属性实现的方式首选计算属性。
使用过程中有一些细节,比如计算属性也是可以传递对象,成为既可读又可写的计算属性。watch可以传递对象,设置deep、immediate等选项。
vue3中watch选项发生了一些变化,例如不再能侦测一个点操作符之外的字符串形式的表达式; reactivity API中新出现了watch、watchEffect可以完全替代目前的watch选项,且功能更加强大。
可能追问
watch会不会立即执行?
watch 和 watchEffect有什么差异
知其所以然
computed的实现
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/computed.ts#L79-L80
ComputedRefImpl
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/computed.ts#L26-L27
缓存性
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/computed.ts#L59-L60
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/computed.ts#L45-L46
watch的实现
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiWatch.ts#L158-L159
这题考查大家对创建过程的理解程度。
思路分析
给结论
阐述理由
回答范例
创建过程自上而下,挂载过程自下而上;即:
之所以会这样是因为Vue创建过程是一个递归过程,先创建父组件,有子组件就会创建子组件,因此创建时先有父组件再有子组件;子组件首次创建时会添加mounted钩子到队列,等到patch结束再执行它们,可见子组件的mounted钩子是先进入到队列中的,因此等到patch结束执行这些钩子时也先执行。
知其所以然
观察beforeCreated和created钩子的处理
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/componentOptions.ts#L554-L555
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/componentOptions.ts#L741-L742
观察beforeMount和mounted钩子的处理
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L1310-L1311
测试代码,test-v3.html
缓存组件使用keep-alive组件,这是一个非常常见且有用的优化手段,vue3中keep-alive有比较大的更新,能说的点比较多。
思路
缓存用keep-alive,它的作用与用法
使用细节,例如缓存指定/排除、结合router和transition
组件缓存后更新可以利用activated或者beforeRouteEnter
原理阐述
回答范例
开发中缓存组件使用keep-alive组件,keep-alive是vue内置组件,keep-alive包裹动态组件component时,会缓存不活动的组件实例,而不是销毁它们,这样在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
<keep-alive> <component :is="view"></component> </keep-alive>
结合属性include和exclude可以明确指定缓存哪些组件或排除缓存指定组件。vue3中结合vue-router时变化较大,之前是keep-alive
包裹router-view
,现在需要反过来用router-view
包裹keep-alive
:
<router-view v-slot="{ Component }"> <keep-alive> <component :is="Component"></component> </keep-alive> </router-view>
缓存后如果要获取数据,解决方案可以有以下两种:
beforeRouteEnter:在有vue-router的项目,每次进入路由的时候,都会执行beforeRouteEnter
beforeRouteEnter(to, from, next){ next(vm=>{ console.log(vm) // 每次进入路由执行 vm.getData() // 获取数据 }) },
actived:在keep-alive
缓存的组件被激活的时候,都会执行actived
钩子
activated(){ this.getData() // 获取数据 },
keep-alive是一个通用组件,它内部定义了一个map,缓存创建过的组件实例,它返回的渲染函数内部会查找内嵌的component组件对应组件的vnode,如果该组件在map中存在就直接返回它。由于component的is属性是个响应式数据,因此只要它变化,keep-alive的render函数就会重新执行。
知其所以然
KeepAlive定义
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/components/KeepAlive.ts#L73-L74
缓存定义
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/components/KeepAlive.ts#L102-L103
缓存组件
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/components/KeepAlive.ts#L215-L216
获取缓存组件
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/components/KeepAlive.ts#L241-L242
测试缓存特性,test-v3.html
综合实践类题目,考查实战能力。没有什么绝对的正确答案,把平时工作的重点有条理的描述一下即可。
思路
构建项目,创建项目基本结构
引入必要的插件:
代码规范:prettier,eslint
提交规范:husky,lint-staged
其他常用:svg-loader,vueuse,nprogress
常见目录结构
回答范例
从0创建一个项目我大致会做以下事情:项目构建、引入必要插件、代码规范、提交规范、常用库和组件
目前vue3项目我会用vite或者create-vue创建项目
接下来引入必要插件:路由插件vue-router、状态管理vuex/pinia、ui库我比较喜欢element-plus和antd-vue、http工具我会选axios
其他比较常用的库有vueuse,nprogress,图标可以使用vite-svg-loader
下面是代码规范:结合prettier和eslint即可
最后是提交规范,可以使用husky,lint-staged,commitlint
目录结构我有如下习惯:.vscode
:用来放项目中的 vscode 配置
plugins
:用来放 vite 插件的 plugin 配置
public
:用来放一些诸如 页头icon 之类的公共文件,会被打包到dist根目录下
src
:用来放项目代码文件
api
:用来放http的一些接口配置
assets
:用来放一些 CSS 之类的静态资源
components
:用来放项目通用组件
layout
:用来放项目的布局
router
:用来放项目的路由配置
store
:用来放状态管理Pinia的配置
utils
:用来放项目中的工具方法类
views
:用来放项目的页面文件
看到这样的题目,可以用以下图片来回答:
思路
查看vue官方文档:
风格指南:https://vuejs.org/style-guide/
性能:https://vuejs.org/guide/best-practices/performance.html#overview
安全:https://vuejs.org/guide/best-practices/security.html
访问性:https://vuejs.org/guide/best-practices/accessibility.html
发布:https://vuejs.org/guide/best-practices/production-deployment.html
回答范例
我从编码风格、性能、安全等方面说几条:
编码风格方面:
性能方面:
安全:
template: <div> + userProvidedString + </div>
, etc...
Réflexion
Donner une définition
Élaboration sur la nécessité
Quand utiliser
Développer : quelques réflexions personnelles , expérience pratique, etc.
Exemple
Vuex est un modèle de gestion d'état + bibliothèque spécialement développé pour les applications Vue.js. Il utilise un stockage centralisé pour gérer l'état de tous les composants de l'application et utilise les règles correspondantes pour garantir que l'état change de manière prévisible.
Nous sommes impatients de gérer les applications d'une manière simple "flux de données unidirectionnel", c'est-à-dire une boucle unidirectionnelle état -> vue -> opération. Mais lorsque notre application rencontre plusieurs composants partageant un état, par exemple : plusieurs vues dépendent du même état ou les actions de différentes vues doivent changer le même état. La simplicité du flux de données unidirectionnel peut facilement être rompue à ce stade. Il nous faut donc extraire l’état partagé du composant et le gérer en mode singleton global. En définissant et en isolant les différents concepts de gestion de l'État et en maintenant l'indépendance entre les points de vue et l'État en appliquant les règles, notre code deviendra plus structuré et plus facile à maintenir. C'est la nécessité de l'existence de vuex. C'est le même concept que redux dans l'écosystème React.
Vuex introduit de nombreux concepts lors de la résolution de la gestion d'état : tels que l'état, la mutation, l'action, etc. La nécessité de l'introduire doit être mesurée en fonction de la situation réelle de l'application : si vous ne prévoyez pas de développer un grande application d'une seule page, l'utilisation de Vuex le fera à la place. C'est encombrant et redondant, un simple mode magasin suffit. Cependant, si vous souhaitez créer une application d'une seule page de taille moyenne à grande, Vuex est fondamentalement la norme.
Je ressens un peu de blabla en utilisant vuex
Questions possibles
Quels sont les inconvénients de vuex ? Avez-vous rencontré des problèmes lors du développement ?
Quelle est la différence entre action et mutation ? Pourquoi les distinguer ?
Analyse
Nous poser des questions sur le modèle au processus de rendu, c'est en fait poser des questions sur le principe de fonctionnement du compilateur vue<code>编译器
. "Réflexion"
Vue Il existe un module de compilateur unique appelé "compilateur". Sa fonction principale est de compiler le modèle écrit par l'utilisateur dans une fonction de rendu exécutable en js.
aperçu du processus de compilation de Vue3 :
Quand le compilateur dans Vue sera-t-il exécuté ?
React a-t-il un compilateur ?
Établissez un mécanisme de mise à jour
Expliquez simplement ces deux choses !Le processus de montage fait référence au processus app.mount(). Ce processus fait généralement deux choses : Initialisation et Établissement d'un mécanisme de mise à jour
L'initialisation créera des instances de composants et initialisera l'état des composants. data
Établir un mécanisme de mise à jour.Cette étape exécutera immédiatement une fonction de mise à jour du composant pour la première fois et exécutera le patch pour convertir le vnode obtenu précédemment en dom en même temps ; La fonction de rendu pour la première fois créera son interne. La dépendance entre les données réactives et les fonctions de mise à jour des composants permet à la fonction de mise à jour correspondante d'être exécutée lorsque les données changent à l'avenir.
Sachez pourquoi
Code de test, test-v3.html définition de la fonction de montage
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiCreateApp.ts#L277-L278
Premier processus de rendu
https://github1s .com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L2303-L2304
Questions possibles
Comment créer des données réactives
Comment pour établir des dépendances
Adresse originale : https://juejin.cn/post/7097067108663558151
Auteur : Yang Village Chief
(Partage vidéo d'apprentissage : tutoriel vidéo vue)