serverPrefetch |
ssr에만 해당, 구성 요소 인스턴스가 서버에서 렌더링되기 전에 호출됨
|
3、Vue 生命周期流程图:
4、结合实践:
beforeCreate:通常用于插件开发中执行一些初始化任务
created:组件初始化完毕,可以访问各种数据,获取接口数据等
mounted:dom已创建,可用于获取访问数据和dom元素;访问子组件等。
beforeUpdate:此时view 层还未更新,可用于获取更新前各种状态
updated:完成view 层的更新,更新后,所有状态已是最新
beforeunmount:实例被销毁前调用,可用于一些定时器或订阅的取消
unmounted:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
可能的追问
知其所以然
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
04-能说一说双向绑定使用和原理吗?
题目分析:
双向绑定是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作为事件。
可能的追问:
知其所以然:
测试代码,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")])
])
05-Vue中如何扩展一个组件
此题属于实践题,考察大家对vue常用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原理:
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
06-子组件可以直接改变父组件的数据么,说明原因
分析
这是一个实践知识点,组件化开发过程中有个单项数据流原则,不在子组件中修改父组件是个常识问题。
参考文档: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,但是我们还是能够直接改内嵌的对象或属性。
07-Vue要做权限管理该怎么做?控制到按钮级别的权限怎么做?
分析
综合实践题目,实际开发中经常需要面临权限管理的需求,考查实际应用能力。
权限管理一般需求是两个:页面权限和按钮权限,从这两个方面论述即可。
思路
权限管理需求分析:页面和按钮权限
权限管理的实现方案:分后端方案和前端方案阐述
说说各自的优缺点
回答范例
权限管理一般需求是页面权限和按钮权限的管理
-
具体实现的时候分后端和前端两种方案:
前端方案会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表。比如我会配置一个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)
08 - 说一说你对vue响应式理解?
分析
这是一道必问题目,但能回答到位的比较少。如果只是看看一些网文,通常没什么底气,经不住面试官推敲,但像我们这样即看过源码还造过轮子的,回答这个问题就会比较有底气啦。
答题思路:
啥是响应式?
为什么vue需要响应式?
它能给我们带来什么好处?
vue的响应式是怎么实现的?有哪些优缺点?
vue3中的响应式的新变化
回答范例:
所谓数据响应式就是能够使数据变化可以被检测并对这种变化做出响应的机制。
MVVM框架中要解决的一个核心问题是连接数据层和视图层,通过数据驱动应用,数据变化,视图更新,要做到这点的就需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出更新处理。
vue를 예로 들면, 데이터 응답성과 가상 DOM 및 패치 알고리즘을 통해 개발자는 지루한 DOM 작업을 전혀 처리할 필요 없이 데이터를 운영하고 비즈니스에만 관심을 가지면 되므로 개발 효율성이 크게 향상되고 개발 난이도가 줄어듭니다. .
vue2의 데이터 응답성은 데이터 유형에 따라 다르게 처리됩니다. 객체인 경우 데이터 가로채기를 정의하는 데 Object.defineProperty()가 사용됩니다. 배열인 경우 배열 객체 프로토타입의 7가지 변경 메서드를 재정의하여 응답하므로 이러한 메서드는 추가 업데이트 알림을 제공할 수 있습니다. 이 메커니즘은 데이터 응답성 문제를 매우 잘 해결하지만 실제 사용에는 몇 가지 단점도 있습니다. 예를 들어 초기화 중 재귀 순회는 성능 손실을 초래하므로 사용자는 Vue.set/delete만 사용해야 합니다. 특수 API가 적용될 수 있습니다. es6에서 생성된 새로운 Map 및 Set 데이터 구조와 같은 문제는 지원되지 않습니다.
이러한 문제를 해결하기 위해 vue3은 구현의 다음 부분을 다시 작성했습니다. ES6의 프록시 프록시를 사용하여 데이터에 응답하면 많은 이점이 있고 프로그래밍 경험이 일관되며 특수 API를 사용할 필요가 없습니다. , 초기화 성능 및 메모리 소비가 크게 향상되었습니다. 또한 반응형 구현 코드가 독립적인 반응성 패키지로 추출되었기 때문에 더 유연하게 사용할 수 있고 타사 확장 프로그램을 더 유연하게 개발할 수 있습니다.
이유를 알아보세요
vue2 반응형:
vue3 반응형:
09 - 가상 DOM에 대한 이해에 대해 알려주세요.
Analytics
거의 모든 기존 프레임워크는 현재 VNode 및 VDOM으로 알려진 실제 DOM을 추상화하기 위해 가상 DOM을 도입합니다. 그러면 왜 가상 DOM을 도입해야 할까요? 이 질문에 답해보세요!
생각
답변 샘플
이름에서 알 수 있듯이 가상 돔은 가상 돔 개체로 그 자체가 JavaScript 개체이지만 다양한 속성을 통해 뷰 구조를 설명합니다.
-
vdom을 도입하면 다음과 같은 이점을 얻을 수 있습니다.
실제 요소 노드를 VNode로 추상화하여 DOM에 대한 직접 작업 수를 효과적으로 줄여 프로그램 성능을 향상시킵니다.
- 직접 작업에는 제한이 있습니다. dom, 예: diff , clone 및 기타 작업 실제 요소에 대해 직접 diff 작업을 수행하면 일부 불필요한 내용이 추가로 diff됩니다. 모든 내용을 복사해야 합니다. 이것도 필요하지 않습니다. 그러나 이러한 작업을 JavaScript 개체로 이동하면 더 간단해집니다.
- dom 작업은 비교적 비용이 많이 드는 작업입니다. 빈번한 dom 작업은 쉽게 페이지 다시 그리기 및 리플로우를 유발할 수 있습니다. 그러나 중간 처리를 위한 추상 VNode를 통해 직접 DOM 작업 횟수를 효과적으로 줄일 수 있으므로 페이지 다시 그리기 및 리플로우가 줄어듭니다.
크로스 플랫폼 구현을 촉진합니다
- 동일한 VNode 노드를 다양한 플랫폼의 해당 콘텐츠로 렌더링할 수 있습니다. 예를 들어 브라우저에서의 렌더링은 DOM 요소 노드이고, 네이티브(iOS, Android)에서의 렌더링은 해당 컨트롤은 SSR을 구현하고 WebGL로 렌더링하는 등의 작업을 수행할 수 있습니다.
- Vue3을 사용하면 개발자는 VNode 기반 사용자 정의 렌더러를 구현하여 다양한 플랫폼에 대한 렌더링을 용이하게 할 수 있습니다.
vdom을 생성하는 방법은 무엇입니까? Vue에서는 종종 구성 요소에 대한 템플릿을 작성합니다. 이 템플릿은 후속 마운트 프로세스에서 렌더링 함수가 호출되고 반환된 개체는 가상 DOM입니다. 하지만 아직은 실제 DOM이 아니므로 후속 패치 프로세스에서 DOM으로 추가 변환될 예정입니다.
이유를 알아보세요
vnode 정의:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/vnode.ts#L127-L128
렌더링 기능 관찰: 21-vdom/test-render-v3.html 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
마운트:
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime - core/src/renderer.ts#L1171-L1172
마운트 프로세스 디버그: mountComponent
21-vdom/test-render-v3.html
10 - diff 알고리즘을 이해하시나요?
Analytics
vue 업데이트 원칙과 관련된 필수 질문, 이해의 깊이를 비교 테스트합니다. ㅋㅋㅋ 구체적인 실행 방법
Raise up: say vue3의 최적화를 살펴보겠습니다
답변 예시
새 하위 노드가 텍스트이고 이전 노드는 하위 노드가 텍스트인 경우 텍스트를 직접 업데이트합니다. 새 하위 노드가 배열이고 이전 하위 노드가 텍스트인 경우 텍스트를 지우고 새 하위 노드 배열에 하위 요소를 만듭니다. 노드는 배열이고 이전 하위 노드도 배열입니다. 그런 다음 두 하위 노드 세트를 비교하고 세부 정보를 업데이트합니다 blabla
5. vue3에 도입된 업데이트 전략: patchFlags, 블록 등의 컴파일 시간 최적화 .
이유를 알아보세요
- 패치 키 코드
- https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/ renderer.ts#L354-L355
-
Debug test-v3.html
- 11 - 당신이 알고 있는 vue3의 새로운 기능은 무엇입니까?
-
-
- Analytic
공식 웹사이트에 나열된 가장 주목할만한 새로운 기능: https:/ /v3-migration.vuejs .org/
즉, 다음과 같습니다: Composition API SFC Composition API 구문 설탕Teleport PortalFragments 조각Emits 옵션
Custom 렌더러 SFC 변수
서스펜스 위 내용은 API와 관련된 내용으로, 빼놓을 수 없는 프레임워크 기능이 많습니다.
답변 예
1 API 수준에서 Vue3의 새로운 기능에는 주로 Composition API, SFC Composition API 구문 설탕, Teleport 포털, 조각, 방출 옵션, 사용자 정의 렌더러, SFC CSS 변수, Suspense가 포함됩니다.
- 2 또한 Vue3.0에는 프레임워크 수준에서 눈길을 끄는 많은 개선 사항이 있습니다.
- 더 빨라짐
- 가상 DOM 재작성
- 컴파일러 최적화: 정적 승격, patchFlags, 블록 등
- 프록시 기반 응답성 시스템
-
- 더 작음: 더 나은 트리 쉐이킹 최적화
- 더 쉬운 유지 관리: TypeScript + Modular
더 쉬운 확장
독립적인 반응형 모듈 사용자 정의 렌더러
이유를 알아보세요
- 컴파일러 최적화를 경험해보세요
반응형 구현
12 - 동적 라우팅을 정의하는 방법은 무엇입니까? 전달된 동적 매개변수를 얻는 방법은 무엇입니까?
Analytics
API 질문, 기본 능력 테스트, 오류의 여지 없음, 최대한 자세히 설명하려고 노력하세요.
생각
답변 예시
주어진 일치 패턴에 대한 경로를 동일한 구성 요소에 매핑해야 하는 경우가 많습니다. 이 경우 동적 라우팅을 정의해야 합니다.
- 예를 들어, 모든 사용자에게 렌더링해야 하지만 사용자 ID가 다른
User 구성 요소가 있을 수 있습니다. Vue Router에서는 이를 달성하기 위해 경로의 동적 필드를 사용할 수 있습니다. 예: { path: '/users/:id', component: User } , 여기서 :id는 경로 매개변수<p><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#响应路由参数的变化
https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#捕获所有路由或-404-not-found-路由
13-如果让你从零开始写一个vue路由,说说你的思路
思路分析:
首先思考vue路由要解决的问题:用户点击跳转链接内容切换,页面不刷新。
- 借助hash或者history api实现url跳转页面不刷新
- 同时监听hashchange事件或者popstate事件处理跳转
- 根据hash值或者state值从routes表中匹配对应component并渲染之
回答范例:
一个SPA应用的路由需要解决的问题是页面跳转内容改变同时不刷新,同时路由还需要以插件形式存在,所以:
-
首先我会定义一个createRouter
경로 매개변수는 콜론 : 로 표시됩니다. 경로가 일치하면 해당 params 값이 각 구성 요소에 this.$route.params 로 노출됩니다. -
-
$route.params , $route 외에도 <code>/users/:username/posts/:postId 와 같은 여러 매개변수가 있을 수 있습니다. 개체는 $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-route
13-처음부터 vue 경로를 작성하라는 요청을 받은 경우 알려주십시오. your ideas
아이디어 분석:
- 먼저 vue 라우팅이 해결해야 하는 문제에 대해 생각해 보세요. 사용자가 콘텐츠를 전환하기 위해 점프 링크를 클릭하면 페이지가 새로 고쳐지지 않습니다.
새로 고침 없이 URL 점프 페이지를 구현하려면 해시 또는 히스토리 API를 사용하세요동시에 hashchange 이벤트 또는 popstate 이벤트를 수신하여 점프를 처리하세요- 해시 값이나 상태에 따라 경로 테이블의 해당 구성 요소를 일치시키세요. value and render it
SPA 애플리케이션의 라우팅에서 해결해야 할 문제는 새로 고침 없이 페이지 점프 내용이 변경됩니다 동시에 라우팅도 필요합니다.
먼저 라우터 인스턴스를 반환하는 createRouter 함수를 정의하겠습니다. 이 함수는 인스턴스 내부에서 여러 가지 작업을 수행합니다. 구성을 저장합니다. 사용자가 전달한 항목 해시 또는 팝스테이트 이벤트 수신 콜백의 경로에 따라 해당 경로 일치 라우터를 Vue 플러그인으로 정의, 즉 설치 방법 구현 및 내부적으로 두 가지 작업을 수행합니다.
- 페이지 이동과 콘텐츠 표시를 각각 구현하는 router-link 및 router-view라는 두 가지 전역 구성 요소를 구현합니다. 두 가지 전역 변수인 $route 및 $router를 정의합니다. 현재 경로와 라우터 인스턴스는 다음과 같습니다. 구성 요소 내에서 액세스
-
이유를 알아보세요:
-
🎜createRouter 인스턴스를 만드는 방법🎜🎜🎜https://github1s.com/vuejs/router/blob/HEAD/src/router.ts #L355-L356🎜🎜🎜이벤트 모니터링🎜🎜🎜https://github1s.com/vuejs/router/blob/HEAD/src/history/html5.ts#L314 -L315🎜🎜RouterView🎜🎜🎜페이지 점프 RouterLink🎜🎜 🎜https://github1s.com/vuejs/router/blob/HEAD/src/RouterLink.ts#L184-L185🎜🎜🎜콘텐츠 표시 RouterView🎜🎜🎜https://github1s.com/vuejs/router/blob/HEAD /src/RouterView.ts#L43-L44🎜🎜🎜🎜14-키의 기능에 대해 알려주실 수 있나요? 🎜🎜🎜🎜🎜분석: 🎜🎜🎜🎜이 질문은 주로 모든 사람의 가상 DOM 숙달도와 패치 세부 사항을 테스트하며 면접관의 이해 수준을 반영할 수 있습니다. 🎜🎜🎜🎜 생각 분석 : KEY의 역할은 패치 성능을 최적화하는 것임을 결론을 내립니다. key key areactual 사용의 필요성 : 소스에서 사용할 수 있습니다. code vue가 두 노드가 동일한지 어떻게 결정하는지 설명하세요🎜
回答范例:
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
15-说说nextTick的使用和原理?
分析
这道题及考察使用,有考察原理,nextTick在开发过程中应用的也较少,原理上和vue异步更新有密切关系,对于面试者考查很有区分度,如果能够很好回答此题,对面试效果有极大帮助。
答题思路
回答范例:
1、nextTick是等待下一次 DOM 更新刷新的工具方法。
2、Vue有个异步更新策略,意思是如果数据变化,Vue不会立刻更新DOM,而是开启一个队列,把组件更新函数保存在队列中,在同一事件循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在DOM上,此时如果想要获取更新后的DOM状态,就需要使用nextTick。
3、开发时,有两个场景我们会用到nextTick:
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
16-watch和computed的区别以及选择?
两个重要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的实现
ComputedRefImpl
缓存性
watch的实现
17-说一下 Vue 子组件和父组件创建和挂载顺序
这题考查大家对创建过程的理解程度。
思路分析
回答范例
知其所以然
观察beforeCreated和created钩子的处理
观察beforeMount和mounted钩子的处理
测试代码,test-v3.html
18-怎么缓存当前的组件?缓存后怎么更新?
缓存组件使用keep-alive组件,这是一个非常常见且有用的优化手段,vue3中keep-alive有比较大的更新,能说的点比较多。
思路
回答范例
-
开发中缓存组件使用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() // 获取数据
},
知其所以然
KeepAlive定义
缓存定义
缓存组件
获取缓存组件
测试缓存特性,test-v3.html
19-从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织
综合实践类题目,考查实战能力。没有什么绝对的正确答案,把平时工作的重点有条理的描述一下即可。
思路
回答范例
从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 :用来放项目的页面文件
20-实际工作中,你总结的vue最佳实践有哪些?
看到这样的题目,可以用以下图片来回答:
思路
查看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
回答范例
我从编码风格、性能、安全等方面说几条:
-
编码风格方面:
- 命名组件时使用“多词”风格避免和HTML元素冲突
- 使用“细节化”方式定义属性而不是只有一个属性名
- 属性名声明时使用“驼峰命名”,模板或jsx中使用“肉串命名”
- 使用v-for时务必加上key,且不要跟v-if写在一起
-
性能方面:
- 路由懒加载减少应用尺寸
- 利用SSR减少首屏加载时间
- 利用v-once渲染那些不需要更新的内容
- 一些长列表可以利用虚拟滚动技术避免内存过度占用
- 对于深层嵌套对象的大数组可以使用shallowRef或shallowReactive降低开销
- 避免不必要的组件抽象
-
安全:
- 不使用不可信模板,例如使用用户输入拼接模板:
template: <div> + userProvidedString + </div>
- html, url, 스타일 등이 삽입되지 않도록 v-html, :url, :style 등을 주의 깊게 사용하세요.
등...
21 - 개요 설명 vuex를 이해하시나요? ㅋㅋㅋ 개인적인 생각, 실제 경험 등
예제
- Vuex는 Vue.js 애플리케이션용으로 특별히 개발된
상태 관리 패턴 + 라이브러리 입니다. 중앙 집중식 저장소를 사용하여 애플리케이션의 모든 구성 요소 상태를 관리하고 해당 규칙을 사용하여 상태가 예측 가능한 방식으로 변경되도록 합니다.
-
간단한 "단방향 데이터 흐름" 방식, 즉 상태 -> 보기 -> 작업의 단방향 루프로 애플리케이션을 관리할 수 있기를 기대합니다. 그러나 애플리케이션이
상태를 공유하는 여러 구성 요소- 를 발견하는 경우, 예를 들어 여러 뷰가 동일한 상태에 의존하거나 여러 뷰의 작업이 동일한 상태를 변경해야 합니다. 단방향 데이터 흐름의 단순성은 이 시점에서 쉽게 깨질 수 있습니다. 따라서 컴포넌트의 공유 상태를 추출하여 글로벌 싱글톤 모드로 관리하는 것이 필요합니다. 상태 관리의 다양한 개념을 정의 및 분리하고 규칙을 적용하여 뷰와 상태 간의 독립성을 유지함으로써 코드가 더욱 구조화되고 유지 관리가 용이해집니다. 이는 vuex의 존재를 위한 필수 요소이며, React 생태계의 redux와 동일한 개념입니다.
- Vuex는 상태 관리를 해결하면서 상태, 돌연변이, 액션 등과 같은 많은 개념을 도입합니다. 도입이 필요한지 여부는 애플리케이션의 실제 상황에 따라 측정해야 합니다. 대신 Vuex를 사용하는 대규모 단일 페이지 애플리케이션은 번거롭고 중복되며 간단한
스토어 모드 이면 충분합니다. 그러나 중대형 단일 페이지 애플리케이션을 구축하려는 경우 기본적으로 Vuex가 표준입니다.
vuex를 사용하면서 뭔가 블라블라 느낌이 나요
- 가능한 질문
- vuex의 단점은 무엇인가요? 개발 과정에서 문제가 발생했나요?
액션과 돌연변이의 차이점은 무엇인가요? 왜 구별합니까?
-
22-템플릿에서 렌더링까지의 과정에 대해 이야기하기
분석
템플릿을 렌더링하는 과정에 대해 묻는 것은 실제로 vue 컴파일러<strong>의 작동 원리에 대해 묻는 것입니다. </strong>
컴파일러의 필요성 설명컴파일러 작업 흐름 설명 답변 예시 编译器
Vue "컴파일러"라는 고유한 컴파일러 모듈이 있습니다. 이 모듈의 주요 기능은 사용자가 작성한 템플릿을 js의 실행 가능한 렌더링 함수로 컴파일하는 것입니다.
이 컴파일 프로세스가 필요한 이유는 프런트 엔드 프로그래머가 뷰 템플릿을 효율적으로 작성할 수 있도록 하기 위해서입니다. 이에 비해 우리는 여전히 직관적이고 효율적인 뷰 작성을 위해 HTML을 선호합니다. 직접 작성한 렌더링 기능은 비효율적일 뿐만 아니라 컴파일 시간 최적화 기능도 상실합니다. Vue에서 컴파일러는 먼저 템플릿을 구문 분석합니다. 이 단계를 구문 분석이라고 합니다. 마지막에 JS 객체가 얻어지고, 그런 다음 심층 처리의 변환 프로세스가 수행됩니다. AST는 변환이 되고, 마지막으로 이전에 얻은 AST를 렌더링 함수인 JS 코드로 생성합니다.
이유를 알아보세요
vue3 컴파일 프로세스 엿보기: https://github1s.com/vuejs/core/blob/HEAD/packages/compiler-core/src/compile.ts# L61 -L62
test, test-v3.html
가능한 후속 질문
Vue의 컴파일러는 언제 실행되나요?
React에 컴파일러가 있나요?
Analytics마운팅 프로세스에서 가장 중요한 두 가지 작업이 완료되었습니다.
이 두 가지만 분명히 하세요!
답변 샘플
마운팅 프로세스는 app.mount() 프로세스를 참조합니다. 이 프로세스는 일반적으로 초기화 및 업데이트 메커니즘 설정
초기화는 구성 요소 인스턴스를 생성하고 다양한 반응형 구성 요소 상태를 초기화합니다. data
업데이트 메커니즘을 설정합니다. 이 단계에서는 구성 요소 업데이트 기능을 즉시 실행하고 동시에 패치를 실행하여 이전에 얻은 vnode를 dom으로 변환합니다. 처음으로 렌더링 기능을 사용하면 내부적으로 생성됩니다. 반응형 데이터와 구성 요소 업데이트 기능 간의 종속성을 통해 향후 데이터가 변경될 때 해당 업데이트 기능이 실행될 수 있습니다.
이유를 알아보세요
-
테스트 코드, test-v3.html
마운트 기능 정의
https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiCreateApp.ts#L277-L278
-
첫 번째 렌더링 프로세스
https://github1s .com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L2303-L2304
가능한 질문
반응형 데이터를 만드는 방법
방법 의존성을 확립하기
원본 주소 : https://juejin.cn/post/7097067108663558151
작성자 : 양촌장
(학습 영상 공유 : vue 영상 튜토리얼)
|