文章目录
1. data为什么是一个函数,而不是一个对象?什么情况下可以使用对象?
因为Vue组件可以同时存在多个实例,如果直接使用对象形式的data选项,那么所有的实例将会共享同一个data对象,这样就会造成数据互相干扰的问题。
2. key的作用,为什么不能用Index?
作用:主要用在Vue 的虚拟DOM 算法,在新旧nodes 对比时辨识VNodes,在源码中使用的是 === 来判断的两个DOM元素是否相同,所以不能设置非基本类型。
为什么不能用Index?:
- 性能损耗:用index 作为key 时,在对数据进行,逆序添加,逆序删除等破坏顺序的操作时,会产生没必要的真实DOM更新,从而导致效率低
- 额外问题:例如在一个li列表后面加一个
<input>
输入框,然后在给列表前面加一个元素,会导致patch失败,原因是
源码中:
export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
return n1.type === n2.type && n1.key === n2.key
}
3. render函数,h函数,和template什么关系?
render: h => h(App)
:表示 Vue 实例选项对象的 render 方法作为一个函数, 接受传入的参数 h函数,返回 h(App) 的函数调用结果。
h函数:是 hyperscript 的简称——意思是“能生成 HTML (超文本标记语言) 的 JavaScript”。这个名字来源于许多虚拟 DOM 实现默认形成的约定。一个更准确的名称应该是 createVnode(),类似vue2的createElement()
接受三个参数,返回vNode节点:
- tag:一个 HTML 标签名、一个组件、一个异步组件、或一个函数式组件。(必需的)
- props:与 attribute、prop 和事件相对应的对象。(可选)
- children:子 VNodes,使用
h()
构建,或使用字符串获取 “文本 Vnode” 或者有插槽的对象。(可选)
render和template都是Vue中定义组件的方式,但它们有一些不同之处:
- template是一种类HTML的语法,它定义了组件的结构和展示,包括HTML标签、属性、事件和插值等。Vue将template编译成render函数,最终将render函数渲染成虚拟DOM,并将其渲染到页面上。
- render函数则是一个函数式组件,它的参数是一个createElement函数和上下文对象。render函数用代码方式描述组件的结构和展示,并返回一个虚拟DOM节点,最终也会被渲染到页面上。
相对于template,render函数更加灵活和强大,可以更精细地控制组件的展示。同时,由于render函数是JavaScript代码,因此它可以被更好地集成到其他代码中。template则更加容易编写和理解,但可能会受到HTML标签和属性的限制。
在实际开发中,我们可以选择使用template或render函数来定义组件,具体取决于组件的需求和开发者的习惯。如果组件需要较为复杂的展示逻辑和动态交互,可以使用render函数来实现。如果组件比较简单,可以使用template来编写。
4. vue 是怎么解析template的? template会变成什么?
Vue.js在解析和编译模板时,会经过以下过程:
- 解析模板:Vue.js使用自定义的HTML解析器将模板解析成抽象语法树(AST)。解析器会分析模板中的HTML标记、指令、表达式和事件等内容,并构建出一颗表示模板结构的AST。
- 静态优化:在生成AST的过程中,Vue.js会对静态内容进行优化。静态内容是指在编译过程中不会发生变化的部分,例如纯文本内容。Vue.js会将静态内容标记为静态节点,以在后续更新过程中跳过对其的处理,提高性能。
- 编译为渲染函数:Vue.js将AST编译为渲染函数。渲染函数是一个JavaScript函数,它接收数据作为参数,并返回一个虚拟DOM(VNode)树,用于渲染组件的视图。
- 渲染视图:当组件的数据发生变化时,渲染函数会被调用,生成新的虚拟DOM树。Vue.js会通过比较新旧虚拟DOM树的差异,计算出需要更新的部分,并将其应用到实际的DOM上,从而更新组件的视图。
在上述过程中,模板会被转换成一个渲染函数。渲染函数可以是以下几种形式之一:
使用render方法编写的渲染函数:在组件中定义了一个render方法,该方法返回一个虚拟DOM树。
使用单文件组件(.vue文件):Vue.js提供了单文件组件的支持,其中的部分就是模板,通过编译转换为渲染函数。
使用Vue.js的模板语法:在Vue组件的template选项中使用Vue.js的模板语法,也会被编译为渲染函数。
总之,Vue.js将模板解析为AST,然后根据AST生成渲染函数,最终通过渲染函数来渲染组件的视图。这个过程使得Vue.js能够高效地根据数据动态更新视图。
5. 父子,子孙传值
- 通过 props 传递
- 通过
$emit
触发自定义事件 - 使用 ref
- EventBus
$parent
或$root
- attrs 与 listeners
- Provide 与 Inject
- Vuex
父子关系:选择 props 与 $emit
进行传递,也可选择ref
兄弟关系:选择$bus
,其次可以选择$parent
进行传递
祖先与后代:选择attrs与listeners或者 Provide与 Inject
复杂关系的组件:通过vuex存放共享的变量
6. 子组件为什么不能修改props,有什么办法能让子组件v-model使用props?
官方解释:所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
使用props中的数据方法:
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。通过在data中定义一个数据,然后把props赋值给data数据。
- 这个 prop 以一种原始的值传入且需要进行转换。用这个 prop 的值来定义一个计算属性。通过计算属性进行中转。
7. 服务端渲染nuxt
为什么选择使用nuxt.js?
Nuxt.js 是一个基于 Vue.js 的通用应用框架,一个用于Vue.js 开发SSR应用的一站式解决方案。它的优点是将原来几个配置文件要完成的内容,都整合在了一个nuxt.config.js,封装与扩展性完美的契合。
vue单页面应用渲染是从服务器获取所需js,在客户端将其解析生成html挂载于
id为app的DOM元素上,这样会存在两大问题。
由于资源请求量大,造成网站首屏加载缓慢,不利于用户体验。
8. 重写Vue数组中的push方法,每次push时输出push的值
// 保存原始的Array的原型上的push方法
const arrayPush = Array.prototype.push;
// 重写Array的原型上的push方法
Array.prototype.push = function(...args) {
args.forEach(item => {
console.log('Pushed value:', item);
});
// 调用原始的push方法来实现正常的数组操作
return arrayPush.apply(this, args);
};
// 示例
const arr = [1, 2, 3];
arr.push(4);
9. v-if 和 v-show 有什么区别?应用场景。
v-show 不会导致组件被销毁和渲染,但是 v-if 会导致组件被销毁和重新渲染。
v-show本质是通过设置css中的display属性为none
也就是说:当使用 v-show 时,虽然用户看不到组件,但是当前组件的生命周期都已经执行了。如果在 created 中存在接口的调用,则【接口已经调用完成】。同时,当组件隐藏时,组件并不会销毁。相反:如果是 v-if 的每次点击时都会重新渲染组件,重新执行生命周期,隐藏则销毁组件。
所以说,判断 v-if 和 v-show 的使用条件应该是:当组件需要在指定时机创建,在指定时机销毁时,需要使用 v-if。而 当组件仅需要创建一次时,则可以使用 v-show。
10. provide和inject优缺点和使用场景
官方文档:provide主要用于在祖先组件中,指定我们想要提供给后代组件的数据或方法,在任何其后代组件中,我们都可以用inject来接收来自祖先组件提供的数据或方法。
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
主要使用场景:Vue官方文档提倡用其作为组件开发的方式
类似:
<el-form-item size="mini" label="活动名称">
<el-input /> // 显示为mini尺寸的input就是通过provide API实现
</el-form-item>
但是其因为设计理念导致了3个严重问题
- provide/inject自身并不是响应式需要特殊处理,若为响应式,则会导致子组件可随意变更provide提供的值。
- 较为困难做好作用域隔离
- 耦合度极强
在日常使用时需注意:
- provide提供的值,为单向响应式,即子组件无法修改,但父组件的修改,可响应于子组件中。(provide中提供的值,我们使用computed来包装了一个对象,并将所有的需要管理的状态数据,在ctx中进行管理,最终返回了一个Object.freeze冻结过的对象。这样便能保证,在子组件中,我们一旦进行了对provide提供的值,进行了修改,则会直接报错。)
- 并通过mixins来进行统一管理。
- 数据选择方面,选择一些变动较为小的数据,作为provide管理