常见Vue原理面试题

1. Vue的响应式原理是什么?请详细说明Object.defineProperty()和Proxy的区别和用法。

响应式原理:Vue中采用了数据劫持的方式,通过Object.defineProperty()函数来监听数据变化,并在数据变化时触发对应的更新函数。 Object.defineProperty()与Proxy的区别:前者只能监听属性的读取和修改,后者可以监听数组的变化等更多场景,且性能更高。


// 实现observe函数,对data对象中的所有属性进行数据劫持
function observe(data) {if (!data || typeof data !== 'object') {return}Object.keys(data).forEach(key => {defineReactive(data, key, data[key])})
}// 定义defineReactive函数,通过Object.defineProperty()函数来监听数据变化
function defineReactive(obj, key, val) {observe(val)Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter() {console.log('get value:', val)return val},set: function reactiveSetter(newVal) {console.log('set value:', newVal)val = newVal}})
}// 测试代码
const obj = { name: 'Tom', age: 18 }
observe(obj)
obj.age = 20
console.log(obj.age) // 输出:20

2. Vue的模板编译原理是什么?请说明它的优化策略和实现方式。并手动实现一个简单的模板编译器。

模板编译原理:Vue中将用户写好的模板转换成渲染函数,实际渲染时调用该函数进行渲染。模板编译主要包括三个阶段:解析、优化和生成。

优化策略:Vue在模板编译阶段会对模板进行静态节点标记和静态根节点标记,从而可以避免不必要的重复渲染和提升整体渲染性能。

实现方式:Vue通过将模板解析成抽象语法树(AST),再转换成render函数的方式来实现模板编译。最终生成的render函数就是一个虚拟DOM的描述对象,然后通过虚拟DOM的diff算法进行渲染更新。

// 简化版的 Vue 模板编译器
function compile(template) {var element = document.createElement('div');element.innerHTML = template;Array.from(element.childNodes).forEach(function(node) {if (node.nodeType === Node.TEXT_NODE) {var reg = /\{\{(.+)\}\}/;var match = node.textContent.match(reg);if (match) {var key = match[1].trim();node.textContent = '{{' + key + '}}';node._key = key;}} else if (node.nodeType === Node.ELEMENT_NODE) {Array.from(node.attributes).forEach(function(attr) {if (attr.name.startsWith('v-')) {if (attr.name === 'v-model') {node.value = '';node.addEventListener('input', function(event) {vm[attr.value] = event.target.value;});}node.removeAttribute(attr.name);}});}if (node.childNodes.length > 0) {compile(node.innerHTML);}});return element.innerHTML;
}var vm = new Vue({el: '#app',data: {message: 'Hello, World!'},template: '<div><h1>{{message}}</h1></div>'
});document.getElementById('app').innerHTML = compile(vm.template);

 

3. Vue中的虚拟DOM算法是什么?请说明其原理和优化策略。

虚拟DOM算法:Vue中通过比较新旧虚拟DOM树之间的差异来最小化更新操作,从而提高渲染效率。虚拟DOM算法主要包括两个步骤:Diff算法和更新操作。

原理:Vue将模板编译成虚拟DOM树,并将其与上一个虚拟DOM树进行比较,找出需要更新的节点并进行更新操作。

优化策略:Vue采用了一些策略来减少比较的次数,优化了虚拟DOM树的构建和比较的性能。


// 旧的虚拟DOM树
const prevVNode = {tag: 'div',props: { id: 'container' },children: [{ tag: 'h1', children: 'Hello, World!' },{ tag: 'p', children: 'Welcome to my website.' }]
}// 新的虚拟DOM树
const nextVNode = {tag: 'div',props: { id: 'container' },children: [{ tag: 'h1', children: 'Hello, Vue!' },{ tag: 'p', children: 'Welcome to the Vue world.' }]
}// 执行diff算法,得到需要更新的节点
function diff(prevVNode, nextVNode) {if (prevVNode.tag === nextVNode.tag) {const patchProps = {}const prevChildren = prevVNode.childrenconst prevProps = prevVNode.propsconst nextChildren = nextVNode.childrenconst nextProps = nextVNode.props// diff propsfor (const key in nextProps) {const prevVal = prevProps[key]const nextVal = nextProps[key]if (prevVal !== nextVal) {patchProps[key] = nextVal}}// diff childrenif (prevChildren.length !== nextChildren.length) {patchProps.children = nextChildren} else {const patchChildren = []for (let i = 0; i < nextChildren.length; i++) {const childPatch = diff(prevChildren[i], nextChildren[i])patchChildren.push(childPatch)}patchProps.children = patchChildren}return { type: 'update', props: patchProps }} else {return { type: 'replace', node: nextVNode }}
}// 执行更新操作,将差异应用到真实DOM树上
function patch(node, patchProps) {switch (patchProps.type) {case 'replace':const newNode = createDOMElement(patchProps.node)node.parentNode.replaceChild(newNode, node)breakcase 'update':updateDOMProps(node, patchProps.props)for (const patchChild of patchProps.children) {patch(node.children[patchChild.index], patchChild.props)}break}
}

 

4. Vue中的组件通信有哪些方式?请分别说明它们的特点和应用场景。

组件通信方式:

  1. 父子组件通信:通过props传递数据和事件监听。 特点:简单易用,适用于父子组件之间的数据交互。 应用场景:父子组件之间的状态传递和操作。

  2. 子父组件通信:通过 $emit 触发自定义事件和 $on 监听事件。 特点:适用于子组件向父组件传递数据和状态变更。 应用场景:子组件向父组件传递用户输入或其他数据。

  3. 兄弟组件通信:通过$emit/ $on或vuex进行数据共享。 特点:适用于兄弟组件之间的状态共享和协同工作。 应用场景:多个组件之间需要共享状态或数据。

  4. 跨级组件通信:通过provide/ inject进行跨级数据传递。 特点:适用于祖先组件向后代组件传递数据。 应用场景:多级嵌套组件之间的状态共享和传递。


// 子组件
<template><div><h1>{{ title }}</h1><button @click="handleClick">点击我</button></div>
</template>
<script>
export default {props: ['title'],methods: {handleClick() {this.$emit('change', '子组件按钮被点击了')}}
}
</script>// 父组件
<template><div><h2>{{ subtitle }}</h2><child :title="title" @change="handleChange"></child></div>
</template>
<script>
import Child from './Child.vue'
export default {components: { Child },data() {return {title: '父子组件通信示例',subtitle: ''}},methods: {handleChange(msg) {this.subtitle = msg}}
}
</script>

 

5. Vue中的v-model指令在表单元素上的使用方式有哪些?请分别说明它们的区别和注意事项。

v-model指令在表单元素上的使用方式:

  1. v-model="value":用于单选框、复选框和选择框的数据绑定,绑定的是选择的值。 区别:单选框和复选框绑定的是选中状态,而选择框绑定的是选中的值。 注意事项:当多个单选框或复选框绑定同一个数据时,需要为每个元素添加不同的value属性。

  2. v-model="value":用于输入框等表单元素的双向数据绑定,绑定的是输入框的值。 区别:v-model与value属性的实现方式不同,v-model实现了双向绑定的效果。 注意事项:需要为输入框添加type属性,并且该元素必须支持input事件或change事件。


// 单选框
<template><div><input type="radio" v-model="gender" value="male">男<input type="radio" v-model="gender" value="female">女</div>
</template>
<script>
export default {data() {return {gender: ''}}
}
</script>// 输入框
<template><div><input type="text" v-model="message"><p>输入的内容是:{{ message }}</p></div>
</template>
<script>
export default {data() {return {message: ''}}
}
</script>

 

6. Vue中的计算属性computed和侦听器watch有什么区别?请举例说明它们的使用场景。

computed和watch的区别:

  1. computed是基于依赖缓存的,只有当依赖的数据发生变化时才会重新计算;watch则是通过监听数据变化来触发回调函数。
  2. computed适用于多个值之间的计算和处理,而watch则适用于单独的数据变化后执行异步或复杂的操作。

// 计算属性
<template><div><p>商品数量:{{ quantity }}</p><p>商品总价:{{ totalPrice }}</p></div>
</template>
<script>
export default {data() {return {price: 10,quantity: 3}},computed: {totalPrice() {return this.price * this.quantity}}
}
</script>// 侦听器
<template><div><input type="text" v-model="message"><p>输入的消息是:{{ message }}</p></div>
</template>
<script>
export default {data() {return {message: ''}},watch: {message(newVal, oldVal) {console.log('输入的消息发生了变化:', newVal, oldVal)// 执行异步操作this.$http.get('/api/messages', { params: { message: newVal } }).then(res => {console.log(res.data)})}}
}
</script>

7. 简述 Vue 中的异步更新队列原理。

Vue 在更新视图时会将所有数据变化的 watcher(观察者)加入到一个异步更新队列中,等到下一个事件循环时才执行更新操作。这样做的好处是避免频繁的更新视图,提高性能。

  1. 在 watcher 对象构造函数中,将当前 watcher 对象 push 到全局的异步更新队列 queue 中;
  2. 在下一个事件循环方式中,通过 flushQueue 函数逐个遍历 queue 数组中的 watcher,调用其 run 方法进行更新;
  3. 清空 queue 数组,以待下一次更新。
// 定义全局异步更新队列
var queue = [];// 异步更新队列处理函数
function flushQueue() {queue.forEach(function(watcher) {watcher.run();});queue = [];
}// 定义 Watcher 类
class Watcher {constructor() {queue.push(this); // 将当前 watcher 加入到异步更新队列中}run() {console.log('更新视图!'); // 执行更新操作}
}// 创建多个 Watcher 对象
var watcher1 = new Watcher();
var watcher2 = new Watcher();// 延迟一定时间,再执行异步更新操作
setTimeout(function() {flushQueue(); // 清空异步更新队列,逐个执行更新
}, 0);

 

8. 简述 Vue 中的事件机制原理,并手动实现一个简单的事件总线。

Vue 中的事件机制是利用事件总线 EventBus 进行封装实现的。事件总线将事件的发送和接收解耦,通过一个中心化的事件分发器(Event Bus)来管理,使得多个组件间的通信变得简单而灵活。

// 定义 EventBus 类
class EventBus {constructor() {this.events = {};}$on(name, callback) {if (!this.events[name]) {this.events[name] = [];}this.events[name].push(callback);}$emit(name, ...args) {if (this.events[name]) {this.events[name].forEach(function(callback) {callback(...args);});}}
}// 创建 EventBus 实例
var bus = new EventBus();// 定义事件处理函数
function handleMessage(message) {console.log(`收到消息:${message}`);
}// 绑定 message 事件处理函数
bus.$on('message', handleMessage);// 触发 message 事件,并传递参数
bus.$emit('message', 'Hello, World!');

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/717370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud负载均衡源码解析 | 带你从表层一步步剖析Ribbon组件如何实现负载均衡功能

目录 1、负载均衡原理 2、源码分析 2.1、LoadBalanced 2.2、LoadBalancerClient 2.3、RibbonAutoConfiguration 2.4、LoadBalancerAutoConfiguration 2.5、LoadBalancerIntercepor⭐ 2.6、再回LoadBalancerClient 2.7、RibbonLoadBalancerClient 2.7.1、DynamicServe…

OpenCV 4基础篇| OpenCV图像的拼接

目录 1. Numpy (np.hstack&#xff0c;np.vstack)1.1 注意事项1.2 代码示例 2. matplotlib2.1 注意事项2.2 代码示例 3. 扩展示例&#xff1a;多张小图合并成一张大图4. 总结 1. Numpy (np.hstack&#xff0c;np.vstack) 语法结构&#xff1a; retval np.hstack(tup) # 水平…

工作日记:JavaScript fill() 方法

定义 fill() 方法用于将一个固定值替换数组的元素。 语法 array.fill(value, start, end) value&#xff1a;必填。要填充的值 start&#xff1a;可选。开始填充位置 end&#xff1a;可选。结束填充位置&#xff08;默认是数组的长度&#xff1a;array.length&#xff09;…

提取拼多多店铺商家电话的爬虫软件

拼多多是中国知名的团购电商平台&#xff0c;许多用户在购物时都希望能够直接联系到店铺商家&#xff0c;以便获得更多的产品信息或解决问题。在这篇文章中&#xff0c;我们将介绍如何使用Python编写一个爬虫软件&#xff0c;来提取拼多多店铺商家电话。 首先&#xff0c;我们…

c++之通讯录管理系统

1&#xff0c;系统需求 通讯录是一个记录亲人&#xff0c;好友信息的工具 系统中需要实现的功能如下&#xff1a; 1&#xff0c;添加联系人&#xff1a;向通讯录中添加新人&#xff0c;信息包括&#xff08;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;联系电话&#…

构建高效的接口自动化测试框架思路

在选择接口测试自动化框架时&#xff0c;需要根据团队的技术栈和项目需求来综合考虑。对于测试团队来说&#xff0c;使用Python相关的测试框架更为便捷。无论选择哪种框架&#xff0c;重要的是确保 框架功能完备&#xff0c;易于维护和扩展&#xff0c;提高测试效率和准确性。今…

IntelliJ IDEA 的常用快捷键

IntelliJ IDEA 的常用快捷键非常多&#xff0c;这些快捷键可以帮助你更高效地编写代码。以下是一些常用的快捷键总结&#xff1a; 基础操作 CtrlN&#xff1a;查找类CtrlShiftN&#xff1a;查找文件CtrlAltL&#xff1a;格式化代码AltInsert&#xff1a;生成代码&#xff08;…

信息安全技术第1章——信息网络安全基本概念

课程介绍 网络信息安全是医学信息工程专业的限选课。主要围绕计算机网络安全所涉及的主要问题进行讲解&#xff0c;内容包括&#xff1a;对称密码与公钥密码的基本原理、相关算法及应用。电子邮件的安全&#xff0c;IP安全&#xff0c;Web安全&#xff0c;恶意软件及防火墙等内…

UI自动化-(web端窗口截图文件上传-实操入门)

1、窗口截图 1. UI自动化中&#xff0c;为什么需要进行窗口截图&#xff1f; 调试和故障排除&#xff1a;截图可以直观地查看界面的状态&#xff0c;快速识别和解决问题。当自动化过程中出现错误或异常时&#xff0c;通过查看截图可以确定是否是界面元素的问题&#xff0c;例…

C++ opencv 学习

文章目录 1、创建窗口2、读取图片3、视频采集4、Mat的使用5、异或操作6、通道分离&#xff0c;通道合并7、色彩空间转换8、最大值、最小值9、绘制图像10、多边形绘制11、随机数12、鼠标实时绘制矩形13、归一化14、resize操作15、旋转翻转16、视频操作17、模糊操作18、高斯模糊操…

SpringBoot整合MyBatis实现增删改查

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容: SpringBoot整合MyBatis实现增删改查 📚个人知识库: Leo知识库,欢迎大家访…

mysql之 case when

1 简单 case 函数&#xff0c;IF函数 格式&#xff1a; CASE input_expression WHEN when_expression THENresult_expression [...n ] [ ELSEelse_result_expression ENDIF(条件,True结果,False结果)2 条件表达式 可嵌套多层&#xff0c;类似于 if … else if … else … end…

Java进阶-IO(1)

进入java IO部分的学习&#xff0c;首先学习IO基础&#xff0c;内容如下。需要了解流的概念、分类还有其他一些如集合与文件的转换&#xff0c;字符编码问题等&#xff0c;这次先学到字节流的读写数据&#xff0c;剩余下次学完。 一、IO基础 1、背景 1.1 数据存储问题 变量…

代码随想录day11(1)字符串:反转字符串中的单词 (leetcode151)

题目要求&#xff1a;给定一个字符串&#xff0c;将其中单词顺序反转&#xff0c;且每个单词之间有且仅有一个空格。 思路&#xff1a;因为本题没有限制空间复杂度&#xff0c;所以首先想到的是用split直接分割单词&#xff0c;然后将单词倒叙相加。 但如果想让空间复杂度为O…

芯来科技发布最新NI系列内核,NI900矢量宽度可达512/1024位

参考&#xff1a;芯来科技发布最新NI系列内核&#xff0c;NI900矢量宽度可达512/1024位 (qq.com) 本土RISC-V CPU IP领军企业——芯来科技正式发布首款针对人工智能应用的专用处理器产品线Nuclei Intelligence(NI)系列&#xff0c;以及NI系列的第一款AI专用RISC-V处理器CPU IP…

反向迭代器

反向迭代器 以list为例,我们完全可以再添加一个__list_reverse_iterator结构体,只需要修改和–的逻辑 template <class T, class Ref, class Ptr> struct __list_reverse_iterator {...self& operator(){_node _node->_prev;return *this;}self operator(int){…

Android 14.0 Launcher3定制化之桌面分页横线改成圆点显示功能实现

1.前言 在14.0的系统rom产品定制化开发中&#xff0c;在进行launcher3的定制化中&#xff0c;在双层改为单层的开发中&#xff0c;在原生的分页 是横线&#xff0c;而为了美观就采用了系统原来的另外一种分页方式&#xff0c;就是圆点比较美观&#xff0c;接下来就来分析下相关…

网络爬虫部分应掌握的重要知识点

目录 一、预备知识1、Web基本工作原理2、网络爬虫的Robots协议 二、爬取网页1、请求服务器并获取网页2、查看服务器端响应的状态码3、输出网页内容 三、使用BeautifulSoup定位网页元素1、首先需要导入BeautifulSoup库2、使用find/find_all函数查找所需的标签元素 四、获取元素的…

基于springboot+vue的健身房管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

●139.单词拆分 ● 关于多重背包,你该了解这些! ●背包问题总结篇!

●139.单词拆分 物品&#xff1a;wordDict里面的单词&#xff1b;背包容量&#xff1a;s.size()。 1.dp[j]含义。dp[j]true表示字符串前j个可以拆分成字典中的单词。dp[s.size()] 就是最后的结果&#xff0c;整个字符串能&#xff08;true&#xff09;不能&#xff08;false…