前端面试题(一)

**

一 vue2.0 v-for中的key到底有什么用?

**
2021年前端vue面试题大汇总(附答案)
https://www.php.cn/js-tutorial-457006.html
其实不只是vue,react中在执行列表渲染时也会要求给每个组件添加上key这个属性。
要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了。
我们知道,vue和react都实现了一套虚拟DOM,使我们可以不直接操作DOM元素,只操作数据便可以重新渲染页面。而隐藏在背后的原理便是其高效的Diff算法。
如:
在节点a,b,c列表的第二位插入‘d’
没有key的时候,先把b变成d,把c变成b,然后在在末尾插入c
有key的时候,直接在b之前插入d即可

又如:删除b,vue会认为你做了两件事
1 把b变成c,然后删除c

1、没有key的情况
‘a’, ‘b’, ‘c’, ‘d’, ‘e’ 变成 ‘a’, ‘b’, ‘f’,‘c’, ‘d’, ‘e’ ,会经历’f’,‘c’, ‘d’替换’c’, ‘d’, ‘e’ 的操作。然后将 e 创建添加到最后面。总共经历了5次 patchNode操作,一次 addNode 操作。因为没有key, 所以在 sameNode() 方法中比较 两个节点的key时,会把不同节点认为是同一个节点(因为 key 为 undefined )。因而对不同节点进行 patch 操作。

2、有key的情况
‘a’, ‘b’, ‘c’, ‘d’, ‘e’ 变成 ‘a’, ‘b’, ‘f’,‘c’, ‘d’, ‘e’ ,也会经历5次 patch操作,但是在patch 操作中什么都没干,只会经历 f 插入到 c 之前的操作。因为key 相同,则这两个节点一定是同一个节点。

结论

key 是 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。

1、更准确:因为带 key 就不是原地复用(如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素(),会导致之前节点的状态被保留下来从而产生一些问题)了,在比较是否是同一个节点的 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况,所以会更加准确。同时避免频繁更新不同元素,从而使得整个 patch 过程更加高效,减少 DOM 操作量,提高性能。
2、更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快。
注意事项:

1、如果不设置 key 的话在列表更新时可能会引发一些隐藏的 bug,比如。
2、vue 中使用相同签名元素的过渡切换时,也会使用到 key 属性,其目的也是为了让 vue 可以区分。
3、在渲染时不要用数组的索引去设置 key 的值,不然在对数组做删除操作时,会对索引之后的数组元素做 patch 操作。应该使用其他唯一值去设置 key。

**

二 JS中的同步和异步的理解

**
JS是单线程的,为什么?
如果有一个操作要删除节点,另一个操作要更新该节点,那浏览器就不知道该干什么了。

同步:在主线程上排队的任务,只有前一个任务执行完毕才能执行后一个任务;
异步:不进入主线程,而进入“任务队列”的任务。如:主线程里的异步请求,执行到该异步请求时,发起请求,然后继续执行主线程的任务,直到主线程的任务 执行完毕,检查setTimeout、检查“任务队列”。异步请求返回数据后,在“任务队列”里放置一个事件,然后执行该事件指定的回调函数。
所以,异步过程有两个重要要素:发起函数和回调函数,这两个函数是分开的。

**

三 事件循环机制

**
先来看一个JavaScript代码片段:
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
}, 0);
new Promise((resolve, reject) => {
console.log(4)
resolve(5)
}).then((data) => {
console.log(data);
})
setTimeout(() => {
console.log(6);
}, 0)
console.log(7);
最后输出:1;4;7;5;undefined;2;3;6;

JS的“任务队列”分为宏任务(macrotask)和微任务(microtask)
二者都会被放置于任务队列中,等待某个时机被主线程入栈执行。任务队列分为宏任务队列和微任务队列。
宏任务(macrotask):在浏览器端,可以理解为:该任务执行完后,在下一个macrotask执行前,浏览器可以进行页面渲染
script(整体代码)
setTimeout、setInterval、setImmediate
I/O、UI交互事件
postMessage、MessageChannel
微任务(microtask):可以理解为:在macrotask任务执行后,页面渲染前立即执行的任务
Promise.then
MutationObserver
process.nextTick

Vue中的nextTick方法的实现原理:使用microtask,因为microtask具有高优先级,能确保队列中的微任务在一次事件循环前被执行完毕。考虑到兼容问题,vue做了microtask向macrotask的降低方案。

上一个任务 --> macrotask --> 所有的microtask(包括前面的macrotask执行时产生的microtask,当前microtask执行时产生的microtask,就是microtask任务队列中的所有任务) --> 渲染页面 --> 下一个macrotask

可参考:https://www.cnblogs.com/wonyun/p/11510848.html

四 postMessage

postMessage是HTML5引入的API,postMessage方法允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档,多窗口、跨域消息传递,用于多窗口间数据通信,这也使他成为跨域通信的一种有效解决方案。

父窗口(http://ggmm.com)发送消息到子窗口(http://abc.com)
父窗口–发送消息:
window.onload = funciton(){
let data = {name: ‘mm’,color: ‘#666’}
window.frames[0].postMessage(data, 'http://ggmm.html)
}
子窗口–接收消息
window.addEventListener(‘message’, funciton(e){
if(e.source != window.parent) return
console.log(e.data)
let color = container.style.backgroundColor
window.parent.postMessage(color, *)
})
父窗口–接收信息
window.addEventListener(‘message’, function(e){
let color = e.data.color
document.getElementById(‘color’).style.color = color
})

event对象主要属性如下:
event:{
data: ‘父窗口传送过来的数据’,
type: ‘message’, //固定值
origin: ‘http://ggmm.html’, //发送消息的窗口的源
source: ‘’ //发送消息的窗口对象
}

**

五 vue组件data为什么必须是函数

**
因为组件是复用的,JS里的对象是引用关系,如果组件data是一个对象,那么data属性就会互相污染。
而使用函数之后,每个示例维护一份返回对象的独立拷贝。

参考文章:https://www.php.cn/js-tutorial-455048.html
**

六 Vue的渲染过程

**
在创建一个vue实例的时候(var vue = new Vue(options)),Vue的构造函数将自动运行this._init(启动函数),启动函数的最后一步为initRender(vm)
Vue.prototype._init

initLifecycle(vm)
initEvents(vm)
callHook(vm, ‘beforeCreate’)
initState(vm)
callHook(vm, ‘create’)
initReader(vm)

initRender中调用vm.mount(vm.options.el),将实例挂载到dom上,至此启动函数完成

Vue会用纯JavaScript来描述Vue模板
{
tag: ‘div’,
id: ‘app’,
children: [{
tag: ‘div’,
id: ‘span’,
text: ‘123’ ,
children: [{
tag: ‘div’,
id: ‘abc’,
text: ‘123’
}]
}]
}
**

七 前端性能优化

一 后台服务架构
1 使用CDN服务器,使用户就近获取数据、资源
2 使用Nginx服务器作为静态资源服务器,Apache服务器作为动态页面如PHP服务
3 静态资源使用多个域名(PC端浏览器最大并发数一般为6,IE9为10,手机端一般为4-)
二 请求
1 较少HTTP请求
一个完整的请求需要经过DNS寻址、与服务器建立连接(三次握手)、发送数据、等待服务器响应、接收数据这样一个漫长的过程。并且请求具有突发性、瞬时性、慢启动等。因此,对于一些小的请求,耗费时间更多在于除了数据传输之外的其他环节。因此,可以减少HTTP请求。
1.1 合理设置HTTP缓存
1.2 资源合并(如:图标合并)
2 对内容做预加载和懒加载的合理配合
如图片展示,当用户继续往后滚动的时候才加载后续图片
3 对某些请求,不要加cookie
三 渲染方面
1 将CSS放在head中,而将JavaScript放在footer
2 减少DOM操作
四 打包方面
使用webpack打包,使代码分成一个个的chunk
五 vue-router配置路由,使用vue的异步组件技术,可以实现懒加载
const Foo = ()=>Promise.resole({组件定义对象})

当打包构建应用时,JavaScript包会变得非常大,导致页面首次加载会比较慢。
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问时候才加载对应组件,这样就更高效了。
结合Vue的 “异步组件” 和Webpack的 “代码分割” 功能,轻松实现路由组件的懒加载。
我们可以这样修改:
import Vue from ‘vue’
import Router from ‘vue-router’
Vue.use(Router)
funciton loadView(view) {
return ()=>import(/webpackChunkName:“view-[request]”/@/views/${view}.vue)
}
export defual new Router({
routers: [
path: ‘/home’,
name: ‘home’,
component: loadView(‘Home’)
],
[
path: ‘/about’,
name: ‘about’,
component: loadView(‘About’)
]
})
除了上面用到的import引入组件,还可以使用webpack特有的require.ensure()
component: ® => require.ensure([], () => r(require(’./home.vue’)), ‘/home’)
**

**

八 安全

1 xss(Cross Site Scripting)跨站脚本攻击
本质上讲,XSS是代码注入问题,是内容没有过滤导致浏览器将攻击者的输入当成代码执行。
**防御:**按理来说,只要有输入的地方,就有可能存在XSS危险。策略如下:

1.1 httpOnly: 在cookie中设置httpOnly属性后,JS脚本将无法读取到cookie信息
ctx.cookie.set(name, value, {httpOnly: true})
1.2 对输入内容进行前端和后台检查,按照规定的格式输入。
1.3 不执行用户的输入内容
2 CSRF跨站请求伪造
因为浏览器在发送请求时候自动带上cookie,而一般网站的session都存在cookie里面
3 sql脚本注入
当应用程序使用输入内容来构造动态sql语句以访问数据库是,会发生sql注入攻击。
当代码发生存储过程,而这些输入内容作为存储过程的输入参数,这些表单也特别容易受到sql注入攻击。
**防御:**永远不要相信用户的输入;永远不要使用动态拼装sql;不要使用管理员权限的数据库连接
4 上传漏洞

**

**

九 嵌套组件

**
**

十 Vue.extend、Vue.component

**
**

十一 vuex

**
**

十二 vue.router

const router = new VueRouter({})
1 全局前置守卫:
router.beforeEach((to, from, next){
if(to.name !== ‘login’ && !isAuthenticated){
next({name: ‘login’})
}
next()
})
2 全局解析守卫
router.beforeResolve(to, from, next)
3 全局后置路由
router.afterEach(to, from, next)
4 路由独享守卫
const router = VueRouter({
routes: [
path: ‘/foo’,
component: Foo,
beforeEnter: function(to, from, next) {}
]
})
5 组件内守卫
beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave
const Foo = {
template: ‘…’,
beforeRouterEnter: function(to, from, next) {},
beforeRouterUpdate: function(to, from, next) {},
beforeRouterLeave: function(to, from, next) {}
}

路由跳转:
push – 跳转到不同的url,这个方法会向history栈添加一个记录,点击回退后,会返回到上一个页面
router.push(‘home’) – 字符串
router.push({path: ‘home’}) – 对象
router.push({name: ‘home’, params: {userId: ‘123’}}) – 带参数
router.push({path: ‘home’, query: {userId: ‘123’}}) – 带查询参数

replace – 这个操作不会向history添加新的记录,点击返回,会跳转到上上一个页面,上一个记录是不存在的。
router.replace(‘home’)
router.go(n) – 向前 后向后跳转n个页面,类似window.history.go(n)
router.go(-1)

**
**

十三 mixin

**
**

十四 移动端开发

8 为什么移动端click会有300毫秒延迟?
因为移动端浏览器会有默认的行为,比如双击缩放、双击滚动。这些行为尤其是双击缩放,主要是为桌面网站在移动端的浏览体验设计的。而在用户对页面进行操作的时候,移动端浏览器会优先判断用户是否要触发默认行为。

**
**

十五 ES6

**
**

十六 断点续传

**
**

十七 请求提交的所有数据格式

**
**

十八 webpack打包

**
参考地址:https://www.cnblogs.com/gaoht/p/11310365.html

选择合适的devtool
devtool的配置,决定了在构建过程中怎样生成sourceMap文件。
eval – 性能最佳,但不能生成sourceMap
source-map – 性能最差,但可以生成原始版本代码
cheap-module-eval-source-map – 在大多数development场景下的最佳选择

1 webpack与grunt、gulp的不同?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量级的任务还是会使用gulp来处理,比如打包CSS文件
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。

2 有哪些常见的Loader?他们是解决什么问题的?
file-loader – 把文件输出到一个文件夹中,在代码中通过相对URL去引用输出文件
url-loader – 和file-loader相似
source-map-loader – 加载额外的Source Map文件,以方便调试
image-loader – 加载并压缩图片文件
babel-loader – 把ES6转换成ES5
css-loader – 加载CSS,支持模块化、压缩、文件导入等特性
style-loader – 把CSS代码注入到JavaScript中,通过DOM操作去加载CSS
eslint-loader – 通过ESLint检查JavaScript代码

3 有哪些常见的Plugin?他们是解决什么问题?
define-plugin – 定义环境变量
commons-chunk-plugin – 提取公共代码
uglifyjs-webpack-plugin – 通过UglifyJS压缩代码
3.4 copyWebpackPlugin – 用于打包时拷贝文件的插件包
new CopyWebpackPlugin([{
from: path.resole(_dirname, ‘…/static’), // 定义要拷贝的源目录,必须填
to: config.build.assetsSubDirectory // 定义要拷贝到的目录,非必填
}])
new CopyWebpackPlugin([
{ from: ‘public/abc’, to: ‘abc’}, // 从最外层的public的abc文件夹,拷贝到dist文件下的abc文件夹
{ from: ‘public/image’, to: 'image‘’}
])

3.5 htmlWebpackPlugin – 你可以让该插件为你生成一个HTML文件,使用lodash模板,或者使用你自己的模板。
const HTMLWebpackPlugins = require(‘html-webpack-plugin’)
const path = require(‘path’)
module.exports = {
entry: ‘index.js’,
output: {
path: path.resole(_dirname, ‘./dist’),
filename: ‘index_bundle.js’
},
plugins: [
new HTMLWebpackPlugins({
chunks: [‘aboutUS’], // 引入的js,也就是entry中的入口文件
filename: ‘index.html’,
minify: {
collapseWhitespace: true //折叠空白区域,也就是压缩代码
},
hash: true,
title: ‘index’,
template: ‘…/public/index.html’ // 模板地址
})
]
}

4 Loader和Plugin的不同?
loader直译为加载器,webpack只能解析JS文件,如果想将其他文件也打包的话,就会用到loader。所以loader的作用是让webpack拥有加载和解析非JavaScript文件的能力。
plugin直译为插件,可以扩展webpack的功能。在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
5 webpack的构建流程是什么?从读取配置到输出文件这个过程尽量说全
webpack的运行流程是一个串行的过程,如下:
5.1 初始化参数 – 从配置文件和shell语句读取与合并参数,得出最终参数
5.2 开始编译 – 用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法,开始执行编译
5.3 确定入口 – 根据配置中的entry找出所有入口文件
5.4 编译模块 – 从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
5.5 完成模块编译 – 在经过第四步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
5.6 输出资源 – 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
5.7 输出完成 – 在确定好输出内容后,根据配置确定输出路径和文件名,把文件内容写入到文件系统
在以上过程中,Webpack会在特定时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定逻辑,并且插件可以调用Webpack提供的API改变Webpack的运行结果。

**

十九 keep-alive的原理与应用

参考地址:https://www.jb51.net/article/160705.htm
**1 场景:**用户在某个列表页面选择筛选条件过滤出一份数据列表,由列表页面进入数据详情,再返回该列表页面,我们希望:列表页面可以保留用户的筛选(或选中)状态。keep-alive就是用来解决这种场景的。除此之外,它还可以避免组件反复创建和渲染,有效提高系统性能。
2 用法:
2.1在动态组件中的应用:



2.2在路由中的应用:



include定义缓存白名单,keep-alive会缓存命中的组件;exclude定义缓存黑名单,被命中的黑名单将不会被缓存;max定义缓存组件上限,超出上限使用URL的策略置换缓存数据。
**
**

二十 HTTPS

参考地址:https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/78507762

HTTPS原理:
1 客户端发起HTTPS请求,服务器返回证书(证书中包含公钥)
2 客户端验证证书通过后,生成随机数(这个这个加密算法是对称性的),使用证书中的公钥对随机数进行加密传输到服务器
3 服务器接收后,通过私钥解密得到随机数。
4 之后的数据交互通过对称加密算法进行解密

HTTP缺点:
1 使用明文通信,内容可能被窃听
2 不验证通信方身份,可能遭到伪装
3 无法验证报文完整性,可能被篡改
HTTPS就是HTTP加上SSL加密处理+认证+完整性保护
HTTPS缺点
1、HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;
2、HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;
3、SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。
4、SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。
5、HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。

**

二十 深入cookie

Chrome浏览器中的Cookie截图:属性分别有Name、Value、Domain、Path、Expires/Max-age、Size、HTTPOnly、Secure、SameSite和Priority。
在这里插入图片描述
1 Name和Value
Name和Value是一个键值对,一旦创建,名称便不可更改,名称不区分大小写
2 Domain
Domain决定在哪个域名下是有效的,也就是决定在向该域名发送请求时,是否携带此cookie。Domain的设置是对子域生效的。如Domain设置为a.com,则b.a.com和c.a.com均可使用该cookie;如果Domain设置为b.a.com,则a.com和c.a.com不可使用该cookie。Domain必须以点(’.’)开始。
3 Path
Path是cookie的有效路径 ,和Domain类似,也是对子路径生效。比如:cookie1为a.com/a,cookie2为a.com/a/b,那么a.com/a可以使用cookie1,a.com/a/b可以使用cookie1和cookie2。
3 Expires/Max-age
cookie的生命周期:默认情况下,cookie只在浏览器的内存中存活,也就是说你关闭浏览器后,cookie就消失。可以通过设置expires/max-age来设置cookie的存活时间。
1 当时间大于0时,比如1小时,浏览器不仅会把cookie保存在浏览器内存,还会把cookie保存在硬盘上。即使关机,只要没到生存周期,cookie是会 一直存在。
2 当时间设置为-1,cookie的默认时间就是-1,表示只在浏览器内存中存活,一旦浏览器关闭,那么cookie就会消失。
3 当时间设置为0,cookie被作废。表示cookie既不在内存中,也不在硬盘上存活。这样设置的目的只有一个:那就是覆盖客户端原来的哪个cookie,使其作废。
4 Size
size是此cookie的大小。在浏览器中,任何cookie大小超过限制都会被忽略,且永远不会被设置。
浏览器 Cookie最大条数 Cookie最大长度/单位:字节
IE 50 4096
Chrome 150 4096
IFireFox 50 4096
Opera 30 4096
Safari 无限 4096
5 HTTPOnly
httpOnly值为true或false,若设置为true,则不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见,但在发送请求时依旧会携带此Cookie。
6 Secure
secure为cookie的安全属性,若设置为true,则浏览器只会在HTTPS和SSL等安全协议中传输此cookie,不会在不安全的HTTP协议中传输此cookie。
7 SameSite
SameSite用来限制第三方cookie,从而减少安全风险。它有三个属性,分别是:
Strict – 最为严格,完全禁止第三方cookie,跨站点时,任何情况都不会发送cookie。
Lax – 稍微放宽
None –
8 Priority
优先级,Chrome的提案,定义了三种优先级,Low/Hedium/High,当cookie数量超出时,低优先级的cookie会被清除。
**
**

二十一 Vue.$set

V0L3FxXzMwNzQ1ODA3,size_16,color_FFFFFF,t_70)
Vue.$set() -->
target 数据类型检测:
1 基本类型:警告不能设置
2 数组:直接添加或替换value – target.splice(key, 1, value)
3 对象:
3.1 key存在于对象中,直接替换value
3.2 key不存在于对象中:
3.2.1 target为非响应式对象,直接给target的key赋值
3.2.2 target为响应式对象,进行依赖收集,通知订阅者更新数据
**
**

二十二 跨域

1 vue跨域
项目用@vue/cli 4.0.5搭建的,采用proxyTable配置代理跨域。
打开项目根目录下的config/index.js文件,参考以下proxyTable部分的配置:
module.exports = {
dev: {
// Paths
assetsSubDirectory: ‘static’,
assetsPublicPath: ‘/’,
// 代理跨域 主要代码*
proxyTable: {
‘/api’: { //匹配接口路径中的 /api
target: ‘http://********:9001/api’, //目标接口地址,设置调用的接口域名和端口号 别忘了加http、https
changeOrigin: true, //是否跨域
secure: true, // 允许https请求
pathRewrite: {
‘^/api’: ‘’ // 路径重写 将 target 中的目标接口地址重写为 /api
}
}
},
}
**
**

二十三 回流和重绘

render tree:DOM tree和样式树组合后构建render tree
回流:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变,内容改变等,都会引起回流。
重绘:当render-tree中的一些元素需要更新属性,而这些属性不会影响布局,只是影响元素的外观,风格。比如颜色改变,就只会引发重绘
回流一定会引起重绘,而重绘不一定引起回流。

**
**

二十四

Promise.resolve
function foo01(param) {
return new Promise((resolve, reject) => {
if (param > 0) {
resolve(param)
} else {
reject(‘error’)
}
})
}
function foo02(param) {
return new Promise((resolve, reject) => {
if (param > 0) {
resolve(param)
} else {
reject(‘error02’)
}
})
}
Promise.resolve(-3).then(foo01).then(foo02).catch(err => {
if (err === ‘error’) {
console.log(‘hello’)
} else {
console.log(‘abc’)
}
})
**
**

二十五 JS异步函数的方法

1 Promise.then
2 async
3 generater
4 回调函数
**
**

二十六 Vue3.0

一 Vue3.0为什么要用Proxy API代替defineProperty API
Vue2.x 中的响应式实现是 基于defineProperty中的descriptor,对data的属性做了遍历+递归,为每个属性设置了getter、setter。
这也就是为什么Vue只能对data中预定义过的属性做出响应的原因,在Vue中使用下标的方式直接修改数组的值或者添加一个预先不存在的对象属性是无法做到setter监听的,这是defineProperty的局限性。
Proxy API的监听是针对一个对象的,可以理解为在目标对象之前架设了一层拦截,外界对该对象的访问,都必须先通过这层拦截。所以Proxy API不存在defineProperty的局限性。
Vue 2.x中,对深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行defineProperty,把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。
Vue3.0中,使用Proxy API并不能监听到对象内部深层次的属性变化,因此它的处理方式是在setter中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的说是按需实现响应式,减少性能消耗。

二十七 栈和堆

var a={n:1}
var b=a
a. x=a={n:2}
//a. x–> undefined
//b–>{
n:1,
x: {n:2}
}
解释:var a={n:1};var b=a;a和b都指向地址1;a. x=a={n:2},“.”的优先级为19,“=”的优先级为3,所以先执行a.x。连续赋值是同时进行,与顺序无关。
第一步:执行a.x,a.x–>{n:1,x:undefined}
第二步:同时执行a.x={n:2}和a={n:2},a.x指向地址二,a重新指向地址二,由于地址一还被b引用,所以没有被浏览器清空。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值