复习2

53 阅读12分钟

九号

1·页面想渲染的几个流程

  1. 解析HTML生成DOM树。

  2. 解析CSS生成CSSOM规则树。

  3. 将DOM树与CSSOM规则树合并在一起生成渲染树。

  4. 遍历渲染树开始布局,计算每个节点的位置大小信息。

  5. 将渲染树每个节点绘制到屏幕。

2· SPA首页加载慢怎么解决

  • 减小入口文件体积,常见的手段就是进行路由的懒加载,动态加载路由
  • 合理利用静态资源本地缓存
  • 按需加载UI组件
  • 合理使用webpack等打包工具进行前端性能优化,常见的包括(treeshaking)等
  • 骨架屏的引用

3· 实现深拷贝的几种方式

  • JSON.stringify();(深拷贝普通对象时推荐使用) 但是属性类型为undefined、null、Date、RegExp、function时,使用该方式进行深拷贝会出问题。在代码里会报错
  • 递归方式(推荐使用)
  • 第三方库lodash的cloneDeep()方法

4·深拷贝与浅拷贝的区别

  • 浅拷贝是创建一个新对象,该对象的内容是原始对象的引用。换句话说,新对象与原始对象共享内存中的某些部分。当对其中一个对象进行更改时,另一个对象也会受到影响。

  • 深拷贝会创建一个新对象,并且递归地复制原始对象及其内容,而不仅仅是引用。深拷贝不共享任何内存地址,因此对其中一个对象的更改不会影响另一个对象。

4·闭包

image.png

示例:

function outerFunction() {
  let count = 0;

  return function innerFunction() {
    count++;
    console.log(count);
  };
}

const counter = outerFunction();
counter(); // 输出 1
counter(); // 输出 2
counter(); // 输出 3

const anotherCounter = outerFunction();
anotherCounter(); // 输出 1

闭包的缺点:

  • 内存泄漏:  如果闭包持有的对外部变量的引用没有被正确释放,可能会导致内存泄漏。尤其是在使用闭包保存DOM元素引用时,需要特别注意在不需要时解除引用,避免循环引用导致的内存泄漏。
  • 调试困难:  闭包的嵌套结构有时会使代码难以理解和调试。由于闭包可以访问外部作用域的变量,追踪变量的变化可能会比较复杂。
  • 过度使用导致代码复杂:  虽然闭包很有用,但过度使用可能会使代码变得难以理解和维护。应该谨慎使用闭包,并在必要时才使用。
  • 性能问题(潜在):  虽然在现代浏览器中闭包的性能问题已经得到很大改善,但在某些情况下,闭包的性能仍然可能比普通函数略低。这主要是因为闭包需要维护其词法环境,这会带来一些额外的开销。

5·前端js中引起内存泄漏的常见原因

1. 意外的全局变量#

由于 js 对未声明变量的处理方式是在全局对象上创建该变量的引用。如果在浏览器中,全局对象就是 window 对象。变量在窗口关闭或重新刷新页面之前都不会被释放,如果未声明的变量缓存大量的数据,就会导致内存泄露。

1.1 未声明变量#
function fn() {
  a = 'hello'
}
fn()
1.2 使用 this 创建的变量(this 的指向是 window)。#
function fn() {
  this.a = 'hello'
}
fn()

解决方法:

  • 避免创建全局变量
  • 使用严格模式,在 JavaScript 文件头部或者函数的顶部加上 use strict

2. 闭包引起的内存泄漏#

由于闭包可以读取函数内部的变量,然后让这些变量始终保存在内存中。如果在使用结束后没有将局部变量清除,就可能导致内存泄露。

3. 没有清理的DOM元素引用#

虽然在某个地方删除了元素,但是对象中还存在对dom的引用。

// 在对象中引用DOM
var elements = {
  btn: document.getElementById('btn'),
}
function doSomeThing() {
  elements.btn.click()
}

function removeBtn() {
  // 将body中的btn移除, 也就是移除 DOM树中的btn
  document.body.removeChild(document.getElementById('btn'))
  // 但是此时全局变量elements还是保留了对btn的引用, btn还是存在于内存中,不能被回收
}

4. 被遗忘的定时器或者回调#

定时器中有 dom 的引用,即使 dom 删除了,但是定时器还在,所以内存中还是有这个 dom。

// 定时器 loadData例为请求数据函数
var serverData = loadData()
setInterval(function () {
  var renderer = document.getElementById('renderer')
  if (renderer) {
    renderer.innerHTML = JSON.stringify(serverData)
  }
}, 5000)

// 观察者模式
var btn = document.getElementById('btn')
function onClick(element) {
  element.innerHTMl = "innerHTML"
}
btn.addEventListener('click', onClick)

6·前端vue中引起内存泄漏的常见原因

1.全局变量造成的内存泄露#

声明的全局变量在切换页面的时候没有清空

<template>
  <div id="home">这里是首页</div>
</template>
<script>
  export default {
    mounted() {
      window.test = {
        // 此处在全局window对象中引用了本页面的dom对象
        name: 'home',
        node: document.getElementById('home'),
      }
    },
  }
</script>

解决方法: 在页面卸载的时候顺便处理掉该引用

destroyed () {
  window.test = null // 页面卸载的时候解除引用
 }

2. 监听在 window/body 等事件没有解绑#

特别注意 window.addEventListener 之类的时间监听

<template>
  <div id="home">这里是首页</div>
</template>

<script>
export default {
  mounted () {
    window.addEventListener('resize', this.func) // window对象引用了home页面的方法
  }
}
</script>

解决方法: 在页面销毁的时候,顺便解除引用,释放内存

beforeDestroy () {
  window.removeEventListener('resize', this.func)
}

3. 绑在 EventBus 的事件没有解绑#

<template>
  <div id="home">这里是首页</div>
</template>

<script>
export default {
  mounted () {
   this.$EventBus.$on('homeTask', res => this.func(res))
  }
}
</script>

解决方法: 在页面卸载的时候也可以考虑解除引用

mounted () {
 this.$EventBus.$on('homeTask', res => this.func(res))
},
destroyed () {
 this.$EventBus.$off()
}

7·js中的宏任务和微任务

JavaScript 是一种单线程的编程语言,这意味着在同一时间只能执行一个任务。为了有效地处理并发操作,JavaScript 引入了事件循环(Event Loop)机制,其中宏任务(Macro Task)和微任务(Micro Task)在其中扮演着关键角色。

1. 什么是宏任务和微任务?
  • 宏任务(Macro Task)  是 JavaScript 中执行的大块任务或代码块,它包括了一些常见的操作,如:

    • setTimeout
    • setInterval
    • setImmediate(仅在 Node.js 中)
    • I/O 操作
    • UI 渲染
    • 事件处理
  • 微任务(Micro Task)  是一个需要在当前宏任务完成后、下一个宏任务开始前立即执行的小任务。常见的微任务有:

    • Promise.thenPromise.catchPromise.finally
    • MutationObserver
    • process.nextTick(仅在 Node.js 中)
宏任务与微任务的执行顺序

了解宏任务与微任务的执行顺序对于掌握 JavaScript 异步操作非常重要。以下是一些关键点:

  • 微任务总是在当前宏任务结束后立即执行,优先级高于下一个宏任务。
  • 如果在微任务中再次添加微任务,这些新添加的微任务会在当前微任务队列完成后立即执行。

示例代码:

复制代码

console.log('Start');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
    console.log('Promise 1');
}).then(() => {
    console.log('Promise 2');
});

console.log('End');

执行顺序解释:

  1. 'Start'  和  'End'  是同步代码,首先执行。
  2. setTimeout 是一个宏任务,它会在宏任务队列中排队,等待当前宏任务完成后执行。
  3. Promise.then 是一个微任务,它会在当前宏任务(即同步代码执行完毕后)立即执行。
  4. 因此,微任务  'Promise 1'  和  'Promise 2'  会先于 setTimeout 的回调执行。
  5. 最后,setTimeout 的回调会被执行。

输出顺序:

Start
End
Promise 1
Promise 2
setTimeout

8·vuerouuter有哪几种导航钩子

  • 全局钩子
  • 某个路由的钩子
  • 组件内钩子

两种函数:

  • Vue.beforeEach(function(to,form,next){}) /*在跳转之前执行*/
  • Vue.afterEach(function(to,form))/*在跳转之后判断*/

代码语言:javascript

**复制

router.beforeEach((to, from, next) => { 
   
    let token = router.app.$storage.fetch("token");
    let needAuth = to.matched.some(item => item.meta.login);
    if(!token && needAuth) return next({ 
   path: "/login"});
    next();
});

beforeEach函数有三个参数:

  • to:router即将进入的路由对象;
  • from:当前导航即将离开的路由;
  • next:Function,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的);否则为false,终止导航。

注:afterEach()不用传next()函数。

image.png

可以在路由组件内直接定义以下路由导航钩子:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave

这里简单说下钩子函数用法:它是和data,methods平级的。

代码语言:javascript

beforeRouteLeave(to, from, next) { 
   
    next()
},
beforeRouteEnter(to, from, next) { 
   
    next()
},
beforeRouteUpdate(to, from, next) { 
   
    next()
},
data:{ 
   },
method: { 
   }

image.png

image.png

9·get和post有什么区别

  1. 参数位置:GET请求的参数是直接放在URL中的,而POST请求的参数则是放在请求体中。

  2. 缓存能力:GET请求可以被浏览器缓存,而POST请求则不能被缓存。

  3. 长度限制:由于GET参数在URL中,所以有长度限制;而POST请求的参数在请求体中,因此没有长度限制。

  4. 安全性:GET请求的安全性相对较差,因为参数明文显示在URL中;而POST请求的安全性较好,参数在请求体中传输。

  5. 访问方式:GET请求可以通过浏览器直接访问,支持刷新和后退操作;而POST请求不能直接通过浏览器访问。

10·什么是渐进增强和优雅降级

image.png

image.png

image.png

11·v-bind和v-model的区别

定义和功能

  1. v-bind

    • 单向绑定:v-bind用于将Vue实例的数据绑定到DOM元素的属性上,数据只能从Vue实例流向DOM元素,不能反向流动。‌12
    • 适用范围:可以绑定任何属性,如class、style等。‌35
  2. v-model

    • 双向绑定:v-model用于在表单控件元素(如input、select、textarea等)和Vue实例之间建立双向数据绑定,数据可以从Vue实例流向DOM元素,也可以从DOM元素流向Vue实例。
    • 适用范围:主要用于表单控件,如input、radio、checkbox等。‌12

使用场景和示例

  • v-bind:适用于需要动态更新DOM属性但不涉及用户输入的场景。例如,可以使用v-bind动态绑定class或style属性,根据数据变化更新元素的样式或类。‌4
  • v-model:适用于需要实时同步用户输入和Vue实例数据的表单控件。例如,在input框中使用v-model绑定一个变量,用户输入的内容会实时更新该变量的值,反之亦然。

总结

  • v-bind主要用于单向数据绑定,适用于需要动态更新DOM属性的场景。
  • v-model主要用于双向数据绑定,适用于需要实时同步用户输入和Vue实例数据的表单控件。

12·原型链

每个对象都有一个原型对象(prototype),通过这个原型对象,可以访问它继承的属性和方法。如果在一个对象上找不到某个属性或方法,就会沿着原型链向上查找,直到找到或者到达原型链的顶端(null)。

13·rem和em的区别

rem和em的主要区别在于它们所依赖的基准不同

  • em:em是一个相对长度单位,其值会继承父级元素的字体大小。例如,如果父元素的字体大小设置为16像素(px),那么1em就等于16px,2em就等于32px。em的优点是可以根据父元素的字体大小进行缩放,具有一定的灵活性,但缺点是如果嵌套层级较多,em的值会根据所有祖先元素的字体大小进行复合计算,容易导致计算复杂和难以预测最终大小。‌12
  • rem:rem是CSS3中新增的一个相对单位,它相对于HTML根元素(即html元素)的字体大小。例如,如果根元素的字体大小设置为16像素(px),那么1rem就等于16px。rem的优点是可以避免em的复合计算问题,只需修改根元素的字体大小即可成比例地调整所有字体大小,具有更好的灵活性和可预测性。缺点是需要设置根元素的字体大小,并且如果用户修改了浏览器的默认字体大小,可能需要通过JavaScript动态调整根元素的字体大小以保持一致性。

14.vue中的插槽

什么是插槽?

插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。插槽显不显示怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制

插槽的分类:

  1. 默认插槽
  2. 具名插槽
  3. 动态插槽

默认插槽的名字:default

15.vue中常见的指令

  1. v-bind 或  :

    • 用于动态地绑定一个或多个属性,或组件 prop 到表达式。
    • 例如:<a v-bind:href="url">Link</a> 或 <a :href="url">Link</a>
  2. v-model

    • 创建一个双向数据绑定在表单输入和应用状态之间。
    • 例如:<input v-model="message">
  3. v-on 或  @

    • 用于绑定事件监听器。
    • 例如:<button v-on:click="doSomething">Click me</button> 或 <button @click="doSomething">Click me</button>
  4. v-if 和 v-else-if / v-else

    • 根据表达式的真假条件来渲染元素。
    • 例如:<p v-if="seen">Now you see me</p>
  5. v-show

    • 根据表达式的真假值切换元素的 CSS 属性 display
    • 例如:<p v-show="ok">Am I visible?</p>
  6. v-for

    • 基于源数据多次渲染元素或模板块。
    • 例如:<li v-for="item in items">{{ item.text }}</li>
  7. v-pre

    • 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。
    • 例如:<span v-pre>{{ This will not be compiled }}</span>
  8. v-cloak

    • 这个指令保持在元素上直到 Vue 实例结束编译。常与 CSS 规则一起使用,可以隐藏未编译的 Mustache 标签直到 Vue 实例准备就绪。
    • 例如:在 CSS 中使用 [v-cloak] { display: none },然后在元素上添加 v-cloak
  9. v-html

    • 更新元素的 innerHTML。注意这样做非常危险,因为它容易导致 XSS 攻击,除非你非常确定内容是安全的。
    • 例如:<div v-html="rawHtml"></div>
  10. v-text

    • 更新元素的 textContent
    • 例如:<span v-text="msg"></span>
  11. v-once

    • 执行一次性地插值,当数据改变时,插值处的内容不会更新。
    • 例如:<span v-once>This will never update</span>