前端面试题【131道】

前端面试题

  • 1、vue2和vue3的主要区别
  • 2、v-if、v-show、v-html 的原理
  • 3、Apply/call/bind的原理是什么?
  • 4、说说你对原型和原型链的理解?
  • 5、说说你对ES6中Generator的理解
  • 6、说说你对Event Loop的理解
  • 7、说说Promise和async/await 的区别?
  • 8、说说浏览器事件循环和nodeJs的事件循环的区别?
  • 9、说说你对浏览器缓存机制的理解
  • 10、说说你对浏览器内核的理解
  • 11、说说你对Vue的响应式原理的理解
  • 12、Methods watch computed区别是什么
  • 13、说说你对Virtual DOM的理解?
  • 14、说说你对nextTick的理解和作用
  • 15、说说你对webpack的理解
  • 16、谈谈GET和POST的区别
  • 17、说说HTTP和HTTPS的区别,HTTPS加密原理是?
  • 18、TCP为什么要三次握手?
  • 19、说说Proxy代理的原理?
  • 20、说说内存泄漏的理解?内存泄漏的情况有哪些?
  • 21、说说你对BOM的理解,常见的BOM对象你了解哪些?
  • 22、说说浏览器的渐进增强和优雅降级的区别?
  • 23、浏览器的内核都有哪些,什么区别?
  • 24、网站性能优化的方案都有哪些?
  • 25、Link和@import之间有什么区别?
  • 26、说说你对BFC的理解,触发条件有哪些?
  • 27、null,undefined 的区别
  • 28、说说css中元素脱离文档流的方式有哪些?定位的方式有哪些以及区别?
  • 29、同步和异步的区别
  • 30、伪类和伪元素的区别有哪些? Css3新增了哪些选择器
  • 31、说说箭头函数和普通函数的区别?
  • 32、SPA首屏加载速度慢怎么解决
  • 33、说说重排和重绘的区别?触发条件有哪些?
  • 34、Javascript如何实现继承?
  • 35、说说什么是严格模式,限制都有哪些?
  • 36、如何快速的让一个打乱一个数组的顺序,比如 var arr = [1,2,3,4,5,6,7,8,9,10];
  • 37、Vue的自定义指令钩子函数有哪些?你用自定义指令做过什么?
  • 38、从A页面跳转到B页面,缓存A组件,从A组件跳转到C组件,取消缓存,如何实现?
  • 39、Vue2和Vue3中响应式原理及区别?
  • 40、Vue是如何实现实现权限管理的,按钮级别权限如何实现?
  • 41、说说webpack中常见的Loader?解决了什么问题?
  • 42、你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢
  • 43、Vue中组件和插件有什么区别?
  • 44、你了解vue的diff算法吗?说说看?
  • 45、Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?
  • 47、说说 React中的setState执行机制?
  • 48、说说对React中类组件和函数组件的理解?有什么区别?
  • 49、说说对React Hooks的理解?解决了什么问题?
  • 50、UseMemo和useCallback如何提升了性能,应用场景有哪些?
  • 51、Vue-router的实现原理是什么?
  • 52、如何封装组件在不同的项目之间使用如何实现?
  • 53、vue、react、angular 区别?
  • 54、说说你对Redux的理解?其工作原理?
  • 55、说说你对递归的理解?封装一个方法用递归实现树形结构封装
  • 56、什么是FOUC? 如何避免?
  • 57、说说你对预编译器的理解?
  • 58、shouldComponentUpdate 的作用
  • 59、概述下 React 中的事务处理逻辑
  • 60、react组件的划分业务组件技术组件?
  • 61、react性能优化是哪个周期函数
  • 62、说说你对Fiber的理解和应用场景
  • 63、react性能优化方案
  • 64、简述flux 思想及Redux的应用
  • 65、说说html和css渲染的过程是什么
  • 66、说一下DOM0、DOM2、DOM3事件处理的区别是什么?
  • 67、如何判断页面滚动到底部,如何判断页面中元素是否进入可视化区域?
  • 68、说一下Vuex的实现原理,commit和dispatch方法如何实现的
  • 69、请简单叙述Vue2和Vue3的区别和变化至少说6点
  • 70、说说对盒子模型的理解?
  • 71、Css的选择器有哪些?优先级?哪些可以继承
  • 72、元素水平垂直居中的方法有哪些?如果元素不定宽高呢?
  • 73、怎么理解回流跟重绘?什么场景下会触发
  • 74、什么是响应式设计?响应式设计的基本原理是什么?如何做?
  • 75、如果要做优化,CSS提高性能的方法有哪些?
  • 76、对前端工程师这个职位是怎么样理解的?它的前景会怎么样
  • 77、说说JavaScript中的数据类型?存储上的差别?
  • 78、请简单叙述js数据类型判断的方法有哪些?
  • 79、说说你对闭包的理解?闭包使用场景
  • 80、bind、call、apply 区别?如何实现一个bind
  • 81、Javascript本地存储的方式有哪些?区别及应用场景
  • 82、请叙述Vue2和Vue3的diff算法的区别?
  • 83、请简单叙述你对作用域链得理解?
  • 84、Vue3中的生命周期函数的变化以及含义
  • 85、Vue3中自定义指令生命周期的变化及含义
  • 86、Vue3中的组合式Api有哪些? 和Vue2的Options Api又什么不同?
  • 87、 什么是跨域?如何解决跨域问题?
  • 88、 什么是浮动?如何清除浮动?
  • 89、 请简述HTML5的新特性。
  • 90、 请简述CSS3的新特性。
  • 91、 请描述CSS中的选择器及其优先级
  • 92、 请描述盒子模型的四个属性:margin、padding、border和content。
  • 93、如何处理移动端的适配问题?
  • 94、 请描述常见的HTTP请求方法。
  • 95、 什么是闭包?请举例说明。
  • 96、 什么是原型链?如何实现继承?
  • 97、 请描述CSS中的position属性及其取值。
  • 98、请描述CSS中的display属性及其取值。
  • 99、请描述CSS中的float属性及其取值。
  • 100、什么是BFC?如何触发BFC?
  • 101、请描述JS中的数据类型及其判断方法。
  • 102、请描述JS中的作用域及其作用域链。
  • 103、什么是异步编程?请举例说明。
  • 104、什么是事件委托?有何优缺点?
  • 105、请描述ES6的箭头函数及其使用方法。
  • 106、请描述ES6的模板字符串及其使用方法。
  • 107、请描述ES6的let和const关键字及其作用。
  • 108、请描述ES6的解构赋值及其使用方法。
  • 109、什么是webpack?如何使用webpack打包项目?
  • 110、Vue中组件和插件有什么区别?
  • 111、什么是防抖和节流,有什么区别?如何实现?
  • 112、什么是事件代理
  • 113、ES5、ES6和ES2015有什么区别?
  • 114、babel是什么,有什么作用?
  • 115、举一些ES6对String字符串类型做的常用升级优化?
  • 116、举一些ES6对Array数组类型做的常用升级优化
  • 117、什么是Vue SSR
  • 118、请列举出3个Vue中常用的生命周期钩子函数?
  • 119、Vue的路由实现:hash模式 和 history模式原理
  • 120、对MVVM的理解
  • 121、Vue组件间的数据传递方式有哪些
  • 122、v-if 和 v-show 区别
  • 123、`$route和$router`的区别
  • 124、如何让CSS只在当前组件中起作用?
  • 125、``的作用是什么
  • 126、在Vue中使用插槽的步骤
  • 127、Proxy 相比于 defineProperty 的优势
  • 128、vuex是什么?怎么使用?哪种功能场景使用它?
  • 129、在vue使用插件的步骤
  • 130、vue路由的钩子函数有哪些
  • 131、vue2的生命周期

1、vue2和vue3的主要区别

1、性能优化:Vue3对虚拟DOM进行了优化,例如使用了更高效的算法,缩减了代码量。此外,Vue3还利用Proxy代理优化了响应式系统,提高了性能。

2、新特性:Vue3引入了Composition API,是一种函数式API。Composition API使得组合逻辑更加容易,能够更好地重用组件逻辑。

3、Tree-shaking:Vue3支持更好的Tree-shaking(摇树优化)能力,使得项目打包后更加精简,加载速度更快。

4、Typescript支持:Vue3对Typescript支持更完善,使用Typescript编写Vue3应用可以获得更好的类型安全和开发体验。

5、小巧的包:Vue3的核心库包大小比Vue2更小,在使用上更加灵活。

#/2、vue3组件通信流程
Vue3中组件通信的流程可以用以下方式表述:

1、父组件向子组件传递数据

  • 父组件通过props属性将需要传递的数据传递给子组件,在子组件中定义props属性接收父组件传递的数据
  • 子组件通过props属性访问父组件传递过来的数据

2、子组件向父组件传递数据

  • 子组件通过 e m i t 方法触发父组件的事件,子组件在 emit方法触发父组件的事件,子组件在 emit方法触发父组件的事件,子组件在emit方法中传递需要传递的数据
  • 父组件中监听子组件的事件,在监听方法中获取子组件传递过来的数据

3、setup中的返回值

  • 在setup函数中定义需要传递的数据或方法返回一个对象或数组
  • 在子组件中使用props属性获取父组件传递过来的数据,子组件通过setup函数提供的数据或方法来使用

4、provide / inject

  • provide在父组件中提供需要传递的数据
  • inject在子组件中获取提供的数据
    可以在组件树中遥远的组件之间进行传递

在Vue3中,父子组件之间的通信需要通过props和$emit方法、setup函数以及provide / inject来进行。这些方式的选择需根据实际情况和项目需求和组织结构而定。需要注意的一点是,过于频繁的组件通信也可能导致应用的耦合度过高,因此,在使用时需要权衡其利弊。

2、v-if、v-show、v-html 的原理

当解析 Vue 模板时,v-ifv-showv-html 指令会被转化为相应的指令对象,然后在组件的渲染过程中进行处理。

  1. v-if 指令的原理:

    • v-if 指令用于根据条件动态地渲染或销毁元素。当条件为真时,元素会被渲染到 DOM 中;当条件为假时,元素会被从 DOM 中移除。
    • 在 Vue 的内部实现中,v-if 指令会调用 addIfCondition 方法,该方法会在组件实例的 _ifConditions 数组中添加一个包含条件和元素描述的对象。
    • 在渲染过程中,当满足 v-if 指令的条件时,Vue 会根据元素描述创建相应的 VNode(虚拟节点),并将其添加到 VNode 树中进行渲染。如果条件不满足,则会跳过对应的元素渲染。
  2. v-show 指令的原理:

    • v-show 指令用于根据条件控制元素的显示与隐藏。被隐藏的元素仍然保留在 DOM 中,只是通过 CSS 控制其显示与隐藏。
    • 在 Vue 的内部实现中,v-show 指令会在元素上设置 CSS 的 display 属性来控制元素的显示与隐藏。当条件为真时,元素的 display 属性会被设置为原始值(如 blockinline),使其显示;当条件为假时,元素的 display 属性会被设置为 none,使其隐藏。
  3. v-html 指令的原理:

    • v-html 指令用于将动态的 HTML 字符串作为元素的内容进行解析和渲染。
    • 在 Vue 的内部实现中,v-html 指令会将指定的 HTML 字符串转换为真实的 DOM 元素,并将其作为元素的内容插入到 DOM 中。Vue 使用浏览器提供的原生 innerHTML 属性来实现这个功能。

需要注意的是,以上是对 v-ifv-showv-html 指令的一般原理进行的简化说明。在具体的 Vue 实现中,可能会有更多的优化和细节处理。

3、Apply/call/bind的原理是什么?

·apply()·, ·call()·, ·bind()· 是 JavaScript 中函数对象的方法,它们的作用是改变函数的执行上下文,即函数内部的 this 指向。

在 JavaScript 中,函数是一类特殊的对象。它们具有 call, apply, bind 等内置方法。这些方法利用了 JavaScript 中函数是对象的性质,在函数运行时临时绑定了一个新的执行上下文,从而改变了函数内部 this 关键字的指向。

  • call() 方法和 apply() 方法都可以改变函数的执行上下文,并且接受的参数也不同。apply() 方法接受的是一个数组参数,而 call() 方法则接受一个或多个单独的参数。这两个方法的本质区别在于参数的传递方式不同,但作用一致。
  • bind() 方法则是将原函数和指定的对象绑定,返回一个新的函数,新函数的执行上下文为绑定对象。bind() 方法只返回对函数的绑定,并不会立即执行函数。

这三个方法的核心机制是通过改变函数的作用域链来改变 this 的指向,进而控制函数的执行上下文。在使用 call(), apply(), bind() 来改变函数执行上下文时,我们需要显式地指定需要绑定的对象,从而避免了因为 this 指向问题而造成的函数执行错误。

4、说说你对原型和原型链的理解?

在JavaScript中,每个对象都有一个关联的原型对象,也称为原型。原型对象是一个普通对象,它具有一组属性和方法,它可以被用来继承这些属性和方法。每当你创建一个新的对象时,它都会自动关联到它的构造函数的原型对象上。

原型链是一种机制,它使得对象之间可以通过它们各自的原型对象相互关联起来。一个对象的原型对象又可以有自己的原型对象,这样就形成了一个原型链。当你试图访问一个对象上的属性或方法时,JavaScript会首先在对象自身上查找,如果没有找到,它就会去该对象的原型对象上查找,如果还没有找到,它就会继续沿着原型链向上查找,直到找到该属性或方法或者到达原型链的顶部,即Object.prototype。如果最终还是没有找到该属性或方法,JavaScript会返回undefined。

这种原型继承的机制在JavaScript中非常强大,因为它使得我们可以轻松地在对象之间共享代码。例如,如果你创建了一个构造函数,它有一些方法和属性,你可以将这些方法和属性添加到它的原型对象上,这样每个从该构造函数创建的对象都可以访问这些方法和属性,这样可以大大节省内存空间。此外,原型链还允许我们实现更高级的继承和多态性,这些是面向对象编程中非常有用的概念。

5、说说你对ES6中Generator的理解

ES6引入了一个新的概念——生成器(Generator)。生成器是一种可以暂停执行的函数,可以用来惰性地生成一系列值。相比普通函数,生成器有以下几个特点:

  • 生成器函数使用function*关键字定义,内部使用yield关键字可以将函数的执行暂停,将控制权交回给调用者。

  • 生成器函数的返回值是一个生成器对象,这个对象同时也是一个可迭代对象,可以使用for…of语句进行迭代。

  • 在每次调用生成器对象的next()方法时,生成器会从上一次暂停的位置开始执行,直到遇到下一个yield表达式或函数结束。

  • 可以使用yield表达式向生成器对象返回一个值,在下一次调用next()方法时可以继续从上一次暂停的位置开始执行,并在yield表达式处暂停。

  • 生成器函数内部可以使用yield*表达式调用另一个生成器函数或可迭代对象的迭代器。

  • 生成器函数内部可以使用return语句返回最终的值,并终止生成器的执行。

通过使用生成器,可以方便地实现异步操作、惰性计算、迭代器等功能,同时避免了回调地狱和复杂的异步操作处理。

6、说说你对Event Loop的理解

当JavaScript代码执行时,它将被放置在执行栈中,按照先进先出的顺序执行。如果当前执行的任务是一个异步任务(例如,定时器、事件监听器、网络请求等),则它将被添加到任务队列(Event Queue)中,等待执行。

当执行栈中的所有任务完成时,事件循环开始工作。它从任务队列中取出一个任务,将其放置在执行栈中执行。执行栈再次为空时,事件循环再次从任务队列中取出一个任务,并将其放置在执行栈中执行。这个过程循环重复,直到任务队列为空。

JavaScript的事件循环采用单线程的方式执行,即每个时刻只有一个任务在执行。由于JavaScript是单线程的,因此在处理长时间运行的任务时,它会阻塞事件循环,导致其他任务不能及时得到执行。因此,JavaScript提供了一些异步编程模式,例如Promise和回调函数,以允许长时间运行的任务在后台执行,不会阻塞事件循环。

总之,事件循环是JavaScript异步编程的基础,它通过任务队列和执行栈来协调任务的执行顺序,保证了JavaScript代码的执行顺序和异步事件的处理。

7、说说Promise和async/await 的区别?

Promise 和 async/await 都是处理异步操作的方式,但它们在语法和使用上有一些区别。

Promise 是一个用于处理异步操作的对象,它代表了一个尚未完成的操作,并提供了一种可以在操作完成时执行回调函数的方式。Promise 提供了 then() 方法,可以在异步操作完成后执行成功和失败的回调函数。

async/await 是在 ES6 中引入的一种处理异步操作的语法糖,它提供了一种更简洁的方式来编写异步代码。使用 async 关键字标记一个函数为异步函数,异步函数中可以使用 await 关键字等待 Promise 对象完成,并返回 Promise 的结果。

下面是 Promise 和 async/await 的一些区别:

  • 语法:Promise 通过链式调用 then() 方法来处理异步操作的结果,而 async/await 利用 async 和 await 关键字来处理异步操作的结果,使得代码更加简洁易懂。

  • 错误处理:在 Promise 中,错误处理通常使用 catch() 方法来捕获 Promise 返回的错误,而在 async/await 中,可以使用 try/catch 语句来捕获异步操作中的错误。

  • 控制流:在 Promise 中,如果有多个异步操作需要依次执行,可以使用链式调用 then() 方法来处理。而在 async/await 中,可以使用 async/await 结合 for…of 循环来处理异步操作的顺序执行。

  • 可读性:由于 async/await 使用了同步代码的风格,所以它通常比 Promise 更加易读和易于理解。但是,当出现多个异步操作需要依次执行时,使用 Promise 可能会更加简洁明了。

总之,Promise 和 async/await 都是处理异步操作的方式,具体使用哪种方式取决于开发者的个人偏好和具体应用场景。

8、说说浏览器事件循环和nodeJs的事件循环的区别?

虽然浏览器和Node.js都使用事件循环来处理异步任务,但它们的事件循环实现方式有所不同。

在浏览器中,事件循环包括主线程、任务队列和微任务队列。当浏览器遇到异步任务时,它将其添加到任务队列中,等待下一次事件循环迭代时执行。当主线程完成当前执行栈中的任务时,它会检查微任务队列是否有任务需要执行,如果有,则按顺序执行微任务队列中的任务。然后,它会从任务队列中取出一个任务,并将其添加到执行栈中执行。这个过程循环重复,直到任务队列和微任务队列都为空。

在Node.js中,事件循环包括主线程、任务队列、微任务队列和观察者(Watchers)。观察者用于监听操作系统的事件,例如文件I/O、网络I/O等。当Node.js遇到异步任务时,它将其添加到任务队列中,等待下一次事件循环迭代时执行。当主线程完成当前执行栈中的任务时,它会检查微任务队列是否有任务需要执行,如果有,则按顺序执行微任务队列中的任务。然后,它会执行观察者中的回调函数,处理相应的事件。这个过程循环重复,直到任务队列和微任务队列都为空,并且没有活动的观察者为止。

因此,浏览器和Node.js的事件循环机制在微任务队列和观察者方面存在一些区别。在浏览器中,微任务队列只包括Promise和MutationObserver回调,而在Node.js中,微任务队列还包括process.nextTick回调。另外,Node.js的事件循环还包括观察者,用于处理底层的操作系统事件。

9、说说你对浏览器缓存机制的理解

浏览器缓存机制是指浏览器在向服务器请求资源时,会先在本地缓存中查找该资源,如果缓存中存在该资源且没有过期,则直接使用缓存中的资源,否则向服务器请求资源。

浏览器缓存机制可以分为两种类型:强制缓存和协商缓存。

  • 强制缓存:强制缓存是指在浏览器向服务器请求资源时,先检查本地缓存中是否存在该资源,如果存在且没有过期,则直接使用缓存中的资源。可以使用 HTTP 头信息中的 Expires 和 Cache-Control 字段来控制缓存的过期时间。缺点是如果资源在缓存期间发生了变化,浏览器无法检测到,依然会使用缓存中的旧资源。

  • 协商缓存:协商缓存是指在浏览器向服务器请求资源时,先向服务器发送一个请求,询问该资源是否有更新。服务器收到请求后,会比较该资源的最后修改时间或者 ETag 值,如果资源有更新,则返回新的资源,否则返回一个状态码 304 Not Modified,并告诉浏览器直接使用缓存中的资源。可以使用 HTTP 头信息中的 Last-Modified 和 ETag 字段来实现协商缓存。

需要注意的是,浏览器缓存机制仅适用于静态资源,对于动态资源或者需要实时更新的内容,缓存机制并不适用。此外,开发者也可以通过设置 HTTP 头信息来控制浏览器缓存,以达到更好的缓存效果。

10、说说你对浏览器内核的理解

浏览器内核是指浏览器用来渲染网页的核心程序。它主要负责将网页的HTML、CSS和JavaScript等前端代码转换成可视化的网页。

常见的浏览器内核包括:

  • Trident内核:是Microsoft开发的浏览器内核,被用于Internet Explorer浏览器。

  • Gecko内核:是Mozilla开发的浏览器内核,被用于Firefox浏览器。

  • WebKit内核:是苹果公司开发的浏览器内核,被用于Safari浏览器和Chrome浏览器的早期版本。

  • Blink内核:是由Google基于WebKit内核开发的浏览器内核,被用于Chrome浏览器和Opera浏览器。

浏览器内核的性能和特性对浏览器的速度、兼容性、安全性等方面都有影响。不同的内核在处理网页的方式、渲染效果、支持的标# 11、说说你对Vue的响应式原理的理解

11、说说你对Vue的响应式原理的理解

Vue的响应式原理是指当Vue监测到数据变化时,它会自动更新与该数据相关联的视图,从而实现数据驱动视图的目的。

具体来说,当我们在Vue中定义一个数据对象时,Vue会将该对象转换成响应式对象。当我们改变响应式对象的属性时,Vue会自动更新与该属性相关联的视图。

实现这个功能的核心是Vue的侦听器和观察者机制。Vue使用侦听器来监测数据的变化,并将变化通知给相关的观察者。观察者会接收到通知后,再执行相应的更新操作。

这种响应式原理的实现,使得我们在使用Vue开发应用时可以更加专注于数据的处理,而不用关心视图的更新。同时,也让我们能够更方便地进行组件化开发,提高了代码的可复用性和可维护性。

12、Methods watch computed区别是什么

在Vue.js中,“methods”,"watch"和"computed"都是用来处理Vue实例中数据和状态的方式,但它们各自有不同的用途和适用场景。

  • Methods(方法): methods是Vue实例中的函数,通常用于响应用户事件或其他触发器。methods可以修改Vue实例的状态,从而影响UI的呈现。例如,一个方法可以在用户输入后切换组件的可见性或更新一个计数器。

  • Watch(观察): Watch是Vue.js的一个功能,它允许您对特定的数据属性进行反应式监视,并在该属性发生更改时执行一些操作。当您在一个属性上定义一个watch时,Vue.js会自动在该属性发生更改时调用一个回调函数,传入该属性的新旧值作为参数。Watches通常用于比简单的computed属性或methods更复杂的逻辑。

  • Computed(计算属性): Computed属性是基于已有的属性(响应式)计算得出的属性,它们可以根据一个或多个已有的属性进行计算,并在依赖的属性发生变化时自动重新计算。这些计算属性可以被看做是对状态的一种派生或衍生,并且它们通常用于根据已有的状态计算出一些衍生的状态或属性,例如计算一个数值或格式化一个字符串。

因此,方法(methods)用于响应用户事件或其他触发器,Watch(观察)用于响应响应式属性的变化,Computed(计算属性)用于基于已有的响应式属性计算得出新的属性。在使用Vue.js时,您可以根据需要选择使用这些功能的组合,以满足您的特定需求。

13、说说你对Virtual DOM的理解?

传统的DOM操作在更新页面时需要重新计算和渲染整个页面,这样会导致页面更新的性能较低,而且会产生大量的性能瓶颈。而Virtual DOM可以通过比较新旧两个虚拟DOM之间的差异,然后只对需要更新的部分进行操作,从而减少了页面的渲染次数和计算量,提高了页面的渲染性能。

Virtual DOM的实现原理可以简单描述为:

  • 每次数据变化后,生成一个新的虚拟DOM树。

  • 将新的虚拟DOM树和旧的虚拟DOM树进行比较,找出两个树之间的差异。

  • 根据差异,只对需要更新的部分进行DOM操作,从而更新页面。

需要注意的是,由于Virtual DOM是通过JavaScript对象来描述页面结构的,因此它的操作速度非常快。同时,由于Virtual DOM只更新需要更新的部分,因此它可以减少不必要的页面渲染和计算,提高了页面的性能和响应速度。

14、说说你对nextTick的理解和作用

在Vue中,nextTick是一个异步执行的方法,它用于在当前DOM更新周期结束之后执行一些操作。

具体来说,当我们在Vue中修改数据时,Vue会立即更新虚拟DOM,并计划在下一个时间片执行DOM更新操作。如果我们需要在DOM更新完成后执行一些操作,比如获取更新后的DOM元素或者调用一些依赖于DOM的方法,那么就可以使用nextTick方法。

nextTick方法的作用是将回调函数推迟到下一个DOM更新周期之后执行。这样可以保证回调函数在DOM更新完成之后执行,从而避免了因为DOM更新尚未完成而导致的操作错误。同时,由于nextTick是异步执行的,所以它也不会阻塞UI线程,保证了页面的流畅性和响应速度。

在使用nextTick方法时,可以通过传入一个回调函数作为参数来执行需要延迟执行的操作。回调函数中可以访问更新后的DOM元素和数据,从而进行一些操作,比如计算DOM元素的尺寸、位置等信息,或者触发一些事件。

总之,nextTick是一个非常实用的工具方法,在Vue的开发中非常常见,可以帮助我们更加灵活地处理DOM更新和操作。

15、说说你对webpack的理解

Webpack是一个现代化的前端打包工具,它可以将多个源文件打包成一个或多个文件,以便于在浏览器中进行加载和执行。Webpack可以处理JavaScript、CSS、HTML和其他各种资源,并提供了丰富的插件和工具来支持各种前端开发场景。

Webpack的核心概念是“模块”,它支持各种模块化规范,包括CommonJS、AMD、ES6模块等。通过模块化的方式,Webpack可以将各种源文件组织成模块,并进行依赖管理和打包。Webpack的主要功能包括:

  • 模块化支持:Webpack支持各种模块化规范,并提供了丰富的加载器和插件来处理各种不同类型的模块。

  • 打包和压缩:Webpack可以将多个源文件打包成一个或多个文件,并且可以对打包后的文件进行压缩和优化,以便于在生产环境中使用。

  • 开发服务器:Webpack提供了一个开发服务器,可以在本地启动一个Web服务器,并支持热更新和自动刷新等功能,方便开发调试。

  • 代码分离:Webpack支持将代码分离成多个文件,以便于在需要的时候进行按需加载,从而提高页面加载速度。

  • 插件系统:Webpack提供了一个强大的插件系统,可以通过各种插件来扩展Webpack的功能,比如添加静态资源、优化代码等。

总之,Webpack是一个非常强大和灵活的前端打包工具,可以大大提高前端开发效率和性能。它在现代化前端开发中已经成为必不可少的工具之一。

16、谈谈GET和POST的区别

GET和POST是HTTP协议中最常见的两种请求方式,它们在传输数据的方式、安全性、适用场景等方面有所不同。

  • 数据传输方式:GET请求将请求参数附加在URL的末尾,以?号分隔,参数之间用&符号连接,比如http://localhost:8080/index?param1=value1¶m2=value2。而POST请求则将请求参数放在请求体中进行传输,不会暴露在URL中。

  • 安全性:GET请求的参数暴露在URL中,容易被其他人看到或者截获,因此不适合传输敏感信息。而POST请求的请求体内容不会暴露在URL中,相对来说更加安全。

  • 适用场景:GET请求通常用于请求数据,比如查询数据、获取资源等,因为它的请求速度快、简单易用,但不适合传输大量数据和敏感信息。而POST请求通常用于提交数据,比如登录、注册、表单提交等,因为它可以传输大量数据,并且更加安全。

  • 缓存:GET请求可以被缓存,从而提高性能,而POST请求无法被缓存,每次提交都会产生新的请求。

总之,GET和POST请求各有优缺点,应根据实际需求进行选择。如果是获取数据或者查询资源,可以使用GET请求,如果是提交数据或者敏感信息,应该使用POST请求。同时,为了保证数据的安全性,不应该将敏感信息放在URL中,而应该使用POST请求。

17、说说HTTP和HTTPS的区别,HTTPS加密原理是?

HTTP和HTTPS都是网络传输协议,主要用于浏览器和服务器之间的数据传输,但它们在数据传输的安全性、加密方式、端口等方面有所不同

  • 数据传输的安全性:HTTP是明文传输,数据不加密,容易被黑客窃听、篡改或者伪造,存在安全隐患。而HTTPS使用了SSL/TLS加密协议对数据进行加密和认证,数据传输更加安全可靠。

  • 加密方式:HTTPS使用SSL/TLS协议对数据进行加密和认证,而HTTP不加密,所以在使用HTTP协议传输数据时,数据很容易被中间人拦截、修改或者伪造。

  • 端口:HTTP使用的默认端口是80,而HTTPS使用的默认端口是443,这是因为HTTPS需要使用SSL/TLS协议进行加密和认证,需要使用一个单独的端口来避免和HTTP协议混淆。

HTTPS加密原理:HTTPS使用了SSL/TLS协议对数据进行加密和认证,主要包括以下几个步骤:

  • 握手阶段:客户端向服务器发送一个加密通信请求,服务器回应一个数字证书。客户端通过数字证书验证服务器的身份,然后生成一个共享的密钥,用于后续的加密通信。

  • 加密阶段:客户端和服务器使用共享密钥进行加密通信,客户端将请求数据加密后发送给服务器,服务器解密后进行处理,然后将响应数据加密后发送给客户端,客户端解密后进行处理。

  • 断开连接:通信完成后,客户端和服务器断开连接。

总之,HTTPS通过加密和认证技术保证数据传输的安全性,是一种更加安全可靠的网络传输协议,比HTTP更适合传输敏感信息和保护用户隐私。

18、TCP为什么要三次握手?

TCP协议采用三次握手(three-way handshake)的方式建立连接,这是为了确保连接的可靠性和安全性。

三次握手的过程如下:

  • 客户端向服务器发送一个SYN(同步)报文,请求建立连接。该报文包括一个随机的序列号x。

  • 服务器收到客户端的SYN报文后,向客户端发送一个SYN+ACK(同步+确认)报文,表示可以建立连接。该报文包括一个随机的序列号y,以及一个确认号x+1。

  • 客户端收到服务器的SYN+ACK报文后,向服务器发送一个ACK(确认)报文,表示连接已经建立。该报文包括一个确认号y+1。

三次握手的目的是为了确保连接的可靠性和安全性:

  • 确保双方都能收到对方的数据包。在第一次握手时,客户端向服务器发送SYN报文,如果服务器没有收到该报文,会认为客户端没有请求建立连接,不会做出响应;在第二次握手时,服务器向客户端发送SYN+ACK报文,如果客户端没有收到该报文,会认为服务器没有响应,不会发送ACK报文,连接不会建立。

  • 防止连接被第三方劫持。在第三次握手时,客户端向服务器发送ACK报文,确认连接已经建立。如果连接被第三方劫持,客户端发送的ACK报文会被第三方拦截,服务器不会收到该报文,连接不会建立。

总之,TCP采用三次握手的方式建立连接,是为了确保连接的可靠性和安全性,防止数据包丢失或者被第三方劫持。

19、说说Proxy代理的原理?

在JavaScript中,Proxy代理可以用来创建一个代理对象,该对象可以代替另一个对象进行一些操作。代理对象可以拦截对另一个对象的访问,对访问进行一些控制和修改,从而实现一些高级的功能。

Proxy代理的原理是利用了JavaScript中的“元编程”能力,即能够对代码进行动态修改和增强的能力。通过创建一个代理对象,我们可以拦截并修改对目标对象的访问,包括读取、赋值、删除等操作。代理对象可以在这些操作发生之前或之后进行一些自定义的处理,从而实现对目标对象的控制和修改。

在使用Proxy代理时,我们需要定义一个“处理器”(handler)对象,该对象包含一些拦截方法,用于拦截对代理对象的操作。处理器对象可以拦截的方法包括get、set、has、deleteProperty、apply等等。通过定义这些方法,我们可以对代理对象的访问进行拦截和修改,从而实现一些高级的功能,比如数据绑定、属性拦截等等。

需要注意的是,Proxy代理只能对ES6标准中定义的对象进行代理,对于一些内置对象如Array、Date等,可以通过使用Reflect对象中的方法来进行操作。同时,由于Proxy代理的性能比直接访问对象要慢一些,因此在一些需要高性能的场景中,需要谨慎使用。

20、说说内存泄漏的理解?内存泄漏的情况有哪些?

内存泄漏指的是程序中申请的内存空间没有被正确释放,从而导致系统的内存资源被浪费或者耗尽的现象。当一个对象没有被程序再次使用时,占用的内存空间应该被立即释放,但是如果程序中存在一些不当的内存使用方式,就会导致内存泄漏。

内存泄漏的情况包括:

  • 循环引用:在程序中存在两个或多个对象之间的相互引用关系,而这些对象中至少有一个不再被程序使用,但是由于相互引用关系,导致这些对象无法被垃圾回收机制回收,从而造成内存泄漏。

  • 内存泄漏的DOM元素:在JavaScript中,DOM元素是非常常见的内存泄漏来源,因为DOM元素的创建和销毁是由浏览器控制的,如果程序中存在对DOM元素的引用,但是没有及时释放,就会导致内存泄漏。

  • 定时器:如果程序中存在一些没有被正确清理的定时器,就会导致内存泄漏。例如,如果一个定时器在页面销毁之前没有被清除,就会一直占用内存空间,从而导致内存泄漏。

  • 闭包:闭包是一种非常强大的编程技巧,但是如果不小心使用会导致内存泄漏。当一个函数返回一个内部函数时,内部函数会保留对外部函数的引用,如果这个内部函数被存储或者传递给其他对象,就会导致外部函数无法被垃圾回收机制回收,从而造成内存泄漏。

  • 资源未释放:在程序中使用一些系统资源,如文件、网络连接、数据库连接等,如果这些资源在使用完毕后没有被正确释放,就会导致内存泄漏。

为避免内存泄漏,我们需要遵循良好的编程习惯,及时释放不再使用的对象和资源,避免循环引用、不合理的定时器使用、合理使用闭包等方式。另外,使用一些工具如内存检测工具、代码分析工具等也可以帮助我们及时发现内存泄漏问题。

21、说说你对BOM的理解,常见的BOM对象你了解哪些?

BOM(Browser Object Model)指的是浏览器对象模型,它提供了浏览器窗口之间交互的接口,使得JavaScript可以操作浏览器窗口以及其中的文档和元素。BOM并不是W3C的标准,而是由浏览器厂商自行定义和实现的。

常见的BOM对象包括:

  • window对象:代表整个浏览器窗口,在JavaScript中,所有的全局变量和函数都是window对象的属性和方法。可以通过window对象获取和设置窗口的大小、位置、URL地址等信息。

  • location对象:代表当前窗口的URL地址,可以通过它获取和设置当前窗口的URL地址,并且可以对URL地址进行解析和操作。

  • history对象:代表当前窗口的浏览历史记录,可以通过它访问浏览器的历史记录,进行前进、后退等操作。

  • navigator对象:代表浏览器本身,可以通过它获取浏览器的一些信息,如浏览器的名称、版本、用户代理字符串等。

  • screen对象:代表用户的屏幕,可以通过它获取屏幕的一些信息,如分辨率、颜色深度等。

  • document对象:代表当前窗口中的文档,可以通过它获取和操作文档中的元素、样式、事件等。

在BOM中,window对象是最基本的对象,其他对象都是它的属性或方法。BOM提供了一些重要的功能,如窗口大小的调整、页面的跳转、浏览器的历史记录、弹出对话框等,可以帮助我们更好地控制浏览器的行为。

22、说说浏览器的渐进增强和优雅降级的区别?

浏览器的渐进增强和优雅降级是两种不同的设计思想,它们都是为了在不同的浏览器环境中提供最佳的用户体验。

渐进增强(Progressive Enhancement)是指以功能为基础的设计方法,即先设计基本功能,再根据用户设备和浏览器的能力逐步增强功能。这种方法从功能的最基本需求出发,确保在所有设备和浏览器环境下都能够正常运行,并且在高端设备和浏览器中添加额外的功能,提升用户的体验。

例如,在设计一个Web页面时,先确保页面的核心功能能够在所有浏览器中正常运行,如HTML的结构和内容,CSS的基本样式等,然后再根据浏览器的支持情况和用户设备的性能逐步增加一些高级的功能,如动画效果、视频播放等。

相反,优雅降级(Graceful Degradation)是指先设计完整的功能,并在更高级别的浏览器环境中实现这些功能,然后在不支持这些功能的较低级别的浏览器环境中以某种方式进行降级处理。这种方法从高级浏览器环境出发,设计完整的功能,再考虑在低端设备和浏览器中适当的降级处理,以确保功能的基本可用性。

例如,在设计一个Web页面时,先考虑在最新的浏览器中实现完整的功能,如动画效果、视频播放等,然后再为低端设备和浏览器提供某种基本的备用方案,如静态图像、文本链接等。

总的来说,渐进增强和优雅降级都是为了提供最佳的用户体验,只是在设计思路上有所不同。渐进增强从基本功能出发,逐步增加高级功能,而优雅降级则是从完整功能出发,逐步降级处理。

23、浏览器的内核都有哪些,什么区别?

浏览器内核是指浏览器用来解析和渲染Web页面的核心组件,它是浏览器的重要组成部分,直接影响着浏览器的性能和兼容性。常见的浏览器内核主要有以下几种:

  • Trident(Trident Engine):Trident是微软开发的浏览器内核,被用于Internet Explorer浏览器。它的特点是渲染速度较快,但对标准的支持较差,不够规范。

  • Gecko(Gecko Engine):Gecko是Mozilla基金会开发的浏览器内核,被用于Firefox浏览器。它的特点是标准支持度高,但对于低端设备性能较差。

  • WebKit(WebKit Engine):WebKit是苹果公司开发的浏览器内核,被用于Safari浏览器、Chrome浏览器的早期版本和一些其他浏览器。它的特点是性能优异,但对标准的支持度较差。

  • Blink(Blink Engine):Blink是Google开发的浏览器内核,是在WebKit基础上开发的,被用于Chrome浏览器和Opera浏览器等。它的特点是性能较好,对标准的支持度也比较高。

  • EdgeHTML(EdgeHTML Engine):EdgeHTML是微软开发的浏览器内核,被用于Edge浏览器。它的特点是对标准的支持度较好,性能也比较出色。

这些浏览器内核之间的区别主要在于它们的开发者和开发宗旨不同,以及在对标准的支持度、性能和兼容性方面有所不同。对于Web开发人员来说,了解不同浏览器内核的特点和差异,可以更好地优化和兼容Web页面,提高页面的性能和用户体验。

24、网站性能优化的方案都有哪些?

网站性能优化是提高网站响应速度和用户体验的关键,它可以从多个方面入手,包括:

  • 前端优化:前端优化是网站性能优化的重要手段,可以从压缩CSS和JavaScript文件、优化图片大小和格式、减少HTTP请求、使用CDN加速等方面入手,从而提高网站的响应速度。

  • 后端优化:后端优化可以通过使用缓存技术、优化数据库查询、减少服务器负载等手段来提高网站的响应速度。

  • 服务器优化:服务器优化包括使用更快速的硬件、优化服务器配置、使用缓存等手段,可以提高服务器的响应速度,进而提高网站的性能。

  • 数据库优化:数据库优化可以通过建立索引、减少查询次数、优化SQL语句等手段来提高数据库查询的速度,从而提高网站的响应速度。

  • 使用CDN:CDN(内容分发网络)可以加速网站的访问速度,将网站的静态资源如图片、CSS、JavaScript等文件缓存到全球各地的CDN节点上,使得用户可以从离自己最近的节点获取资源,从而提高网站的响应速度。

  • 压缩文件:通过压缩CSS、JavaScript、HTML等文件,可以减小文件的大小,从而减少HTTP请求的次数,提高网站的响应速度。

  • 预加载和懒加载:通过预加载可以提高网站的响应速度,而懒加载可以延迟加载图片和其他资源,提高页面的响应速度和用户体验。

  • 使用异步加载:异步加载可以提高网站的响应速度,通过异步加载JavaScript和CSS文件可以使得网站能够更快地响应用户的请求。

综上所述,网站性能优化的方案是多样化的,可以从前端、后端、服务器、数据库、CDN等多个方面入手,需要根据实际情况选择相应的优化方案。

25、Link和@import之间有什么区别?

@import 都可以用来导入外部资源,但它们有以下区别:

加载方式: 是 HTML 标签,而 @import 是 CSS 的一种方式,因此 可以放在 HTML 文档的 中,或者放在文档中任何位置,而 @import 只能出现在 CSS 文件中。

加载时间: 标签会在页面加载时同时加载,并行加载外部资源,而 @import 会等到页面全部加载完毕之后再加载。

兼容性: 标签的兼容性好于 @import,在早期版本的 IE 浏览器中不支持 @import

功能扩展: 标签可以通过 rel 属性指定不同类型的外部资源,比如 rel="stylesheet" 可以加载样式表,rel=“icon” 可以加载网站图标等,而 @import 只能加载 CSS 样式表。

总之, 适合导入各种类型的资源,包括样式表、网站图标、脚本等,而 @import 适合在 CSS 样式表中导入其他样式表。

26、说说你对BFC的理解,触发条件有哪些?

BFC(Block Formatting Context)指的是一个独立的块级渲染区域,其中的元素按照一定的规则进行布局和渲染,不受外部影响。BFC 具有以下特性:

内部的块级元素垂直排列;

  • 块级元素在垂直方向的距离由它们的 margin 决定;
  • BFC 区域不会与浮动元素重叠;
  • BFC 区域的左侧与包含块的左侧相接触,右侧同理;
  • BFC 会阻止垂直方向上的 margin 重叠。

触发 BFC 有以下几种方式:

  • 根元素;
  • 浮动元素(float 不为 none);
  • 绝对定位元素(position 为 absolute 或 fixed);
  • display 为 inline-block、table-cell、table-caption、flex、inline-flex;
  • overflow 不为 visible。

当一个元素触发了 BFC 后,它就成为了一个独立的渲染区域,可以避免和外部元素发生布局和渲染上的冲突,可以用于解决一些布局问题,比如清除浮动、防止 margin 重叠等。

27、null,undefined 的区别

在 JavaScript 中,null 和 undefined 都表示没有值,但它们有以下区别:

  • undefined 表示一个声明了但未被赋值的变量,或者对象没有该属性的值;
  • null 表示一个被赋值为 null 的变量或对象属性。
    简单来说,undefined 表示缺少值,而 null 表示有值但是值为 null。

另外,null 和 undefined 在 JavaScript 中有不同的数据类型,null 是一个表示空值的对象,而 undefined 是一个表示未定义值的原始数据类型。

在使用时,应该尽量避免将变量赋值为 undefined,而使用默认值或者 null 来表示缺少值。同时,在判断变量是否有值时,应该使用严格相等运算符(===)来检查是否为 null 或 undefined,因为它们在非严格相等运算符(==)中可能会发生类型转换。

28、说说css中元素脱离文档流的方式有哪些?定位的方式有哪些以及区别?

元素脱离文档流的方式有以下几种:

  • position: absolute:将元素从文档流中移除,相对于最近的已定位祖先元素定位。
  • position: fixed:将元素从文档流中移除,相对于浏览器窗口定位。
  • float:将元素从文档流中移除,允许文本和行内元素环绕它。

定位的方式有以下几种:

  • position: static:默认值,元素正常的文档流定位方式,不会受到 top、bottom、left、right 等属性的影响。
  • position: relative:相对定位,元素在正常的文档流中,相对于自己原来的位置进行定位,不会影响其他元素的位置。
  • position: absolute:绝对定位,元素脱离文档流,相对于最近的已定位祖先元素定位,如果没有已定位的祖先元素,则相对于 html 元素定位。
  • position: fixed:固定定位,元素脱离文档流,相对于浏览器窗口进行定位。
  • position: sticky:粘性定位,元素在跨越特定阈值前为相对定位,之后为固定定位。

相对定位和绝对定位的区别在于,相对定位是相对于元素原来的位置进行定位,不会影响其他元素的位置,而绝对定位是相对于最近的已定位祖先元素进行定位。

固定定位和绝对定位的区别在于,固定定位是相对于浏览器窗口进行定位,不会随着滚动而移动,而绝对定位是相对于最近的已定位祖先元素进行定位,如果没有已定位的祖先元素,则相对于 html 元素定位。

29、同步和异步的区别

同步和异步是用于描述程序或者系统中的操作方式的。

同步操作是指当一个操作开始执行后,必须等待它执行完成后才能执行下一个操作。也就是说,同步操作是按照顺序依次执行的,每个操作都要等待上一个操作完成后才能执行。同步操作的优点是操作的执行顺序可控,但是如果某个操作执行时间过长,会导致整个程序停顿,用户体验差。

异步操作是指当一个操作开始执行后,不需要等待它执行完成,可以继续执行后续的操作。也就是说,异步操作不是按照顺序依次执行的,某些操作可以在其它操作完成之前就开始执行。异步操作的优点是不会阻塞程序的执行,提高了程序的执行效率,但是操作的执行顺序是不可控的。

在 JavaScript 中,常见的异步操作包括事件回调函数、定时器、Promise 和异步函数等。同步操作包括普通函数调用、for 循环等。可以通过使用异步操作来提高程序的执行效率,避免程序的停顿,提高用户体验。

30、伪类和伪元素的区别有哪些? Css3新增了哪些选择器

伪类和伪元素都是 CSS 中用于选择特定元素的方式,它们的区别如下:

  • 伪类(pseudo-class)是用来描述元素的特殊状态,比如:hover、:active等,通常是以单个冒号(:)表示。
  • 伪元素(pseudo-element)是用来描述元素的特殊部分,比如::before、::after等,通常是以双冒号(::)表示。

CSS3 新增了很多选择器,其中一些比较常用的包括:

  • :nth-child(n)选择器可以选中父元素下的第n个子元素,例如:nth-child(3)可以选中父元素下的第3个子元素。
  • :not(selector)选择器可以排除某些元素,例如:not(.class)可以选中除了具有class类名的元素以外的所有元素。
  • :first-child选择器可以选中父元素下的第一个子元素,例如:p:first-child可以选中父元素下的第一个p元素。
  • :last-child选择器可以选中父元素下的最后一个子元素,例如:p:last-child可以选中父元素下的最后一个p元素。
  • :before伪元素可以在元素内容前插入一些内容,例如:p::before { content: “前缀” }可以在每个p元素前插入一个“前缀”文本。

31、说说箭头函数和普通函数的区别?

箭头函数和普通函数在语法和功能上有一些不同之处:

  • 语法上的区别:箭头函数使用箭头符号(=>)来定义函数,而普通函数使用 function 关键字定义。
  • this 指向不同:箭头函数没有自己的 this,它的 this 指向的是定义时的作用域中的 this;而普通函数的 this 指向的是调用时的上下文对象。
  • 箭头函数没有 arguments 对象,因此不能直接访问函数参数;而普通函数可以使用 arguments 对象来访问函数参数。
  • 箭头函数不能作为构造函数使用,因为它没有自己的 this 对象;而普通函数可以通过 new 关键字来作为构造函数使用,生成一个新的实例对象。

总的来说,箭头函数相对于普通函数来说更加简洁,但是在某些场景下可能会有一些限制,需要根据实际情况进行选择。

32、SPA首屏加载速度慢怎么解决

如果 SPA(Single Page Application)首屏加载速度慢,可以考虑以下一些解决方案:

  • 代码压缩和打包:将代码进行压缩和打包,减少网络传输的时间,提升首屏加载速度。
  • 路由懒加载:采用路由懒加载技术,将不同路由下的组件分别打包成不同的代码块,按需加载,减少首屏加载的数据量,提升加载速度。
  • 图片优化:对于图片资源,可以采用图片懒加载、图片压缩、使用 WebP 格式等方式进行优化,减少网络传输的数据量。
  • 静态资源缓存:使用浏览器缓存等方式对静态资源进行缓存,减少网络请求次数,提升加载速度。
  • CDN 加速:使用 CDN(Content Delivery Network)进行加速,将静态资源分布在不同的 CDN 节点上,减少网络传输距离,提升加载速度。
  • 预渲染技术:使用预渲染技术,在服务器端预先生成页面的 HTML 和 CSS,提高首屏渲染速度。
    综合考虑实际情况,可以采用一些或多个方案,来优化 SPA 首屏加载速度。

33、说说重排和重绘的区别?触发条件有哪些?

重排(Layout)和重绘(Repaint)都是浏览器渲染页面时的关键步骤,它们之间的区别如下:

  • 重排:指对 DOM 结构进行修改后,浏览器需要重新计算元素的布局和位置,以及页面的几何属性,重新构建渲染树,并进行重新布局。重排是相对耗费时间和性能的操作。
  • 重绘:指对 DOM 结构进行修改后,浏览器需要重新绘制元素的外观和样式,但不需要改变其位置和大小,因为在重排时已经计算好了。重绘是比重排更轻量的操作。

在页面渲染过程中,当元素的布局、样式等属性发生变化时,就会触发重排和重绘。常见的触发条件有:

  • 页面初次渲染时;
  • 浏览器窗口大小发生变化;
  • 元素的位置、大小或布局发生变化;
  • 元素的内容发生变化;
  • 元素的样式发生变化;
  • 用户交互事件,如鼠标滚动、点击等。

为了减少页面的重排和重绘,可以采取以下优化策略:

  • 减少 DOM 操作,尽量减少对 DOM 的频繁修改;
  • 避免频繁读取样式值,尽可能合并操作;
  • 使用批量修改样式的方式;
  • 尽量避免使用 table 布局;
  • 避免使用复杂的 CSS 选择器;
  • 使用 CSS3 硬件加速等方式。

通过优化上述策略,可以有效减少页面的重排和重绘,提升页面的性能和响应速度。

34、Javascript如何实现继承?

在JavaScript中,可以通过以下几种方式来实现继承:

  • 原型链继承:将子类的原型对象指向父类的实例。这种方式可以实现简单的继承关系,但是如果父类的属性或方法是引用类型,那么子类实例共享这个引用类型,会导致子类实例之间相互影响。

  • 借用构造函数继承:通过在子类的构造函数中调用父类的构造函数来实现属性的继承。这种方式可以避免子类实例之间共享引用类型的问题,但是父类原型上定义的方法无法被继承。

  • 组合继承:结合原型链和借用构造函数的方式实现继承。子类的原型对象指向父类的实例,而子类的构造函数通过借用父类的构造函数来实现属性的继承。这种方式既可以继承父类的属性,也可以继承父类原型上的方法。

  • 原型式继承:通过使用一个空对象作为中介,将一个对象作为另一个对象的原型来实现继承。这种方式可以实现简单的对象之间的继承关系,但是会存在对象共享的问题。

  • 寄生式继承:在原型式继承的基础上,通过在新对象上增加方法来扩展对象。这种方式可以在不影响原对象的情况下扩展对象,但是也会存在对象共享的问题。

  • 寄生组合式继承:通过借用构造函数来继承属性,通过原型链的方式来继承方法。这种方式可以避免父类构造函数被调用多次的问题,同时也可以继承父类原型上的方法。

35、说说什么是严格模式,限制都有哪些?

严格模式(Strict Mode)是 ECMAScript 5 引入的一种运行模式,它主要对 JavaScript 的一些不安全的或者不合理的语法进行了限制,使代码更加规范、严谨,避免出现一些难以发现的错误。

严格模式的限制包括:

  • 变量必须声明后再使用,否则会抛出错误。
  • 函数的参数不能有同名属性,否则会抛出错误。
  • 不能使用 with 语句。
  • 在严格模式下,对一个对象进行只读属性的赋值会抛出错误。
  • 对象不能有重名的属性,否则会抛出错误。
  • 函数不能有重名的参数,否则会抛出错误。
  • eval 函数的作用域不再是调用它的函数的局部作用域。
  • 在严格模式下,delete 关键字删除变量时,需要用到变量名,否则会抛出错误。

36、如何快速的让一个打乱一个数组的顺序,比如 var arr = [1,2,3,4,5,6,7,8,9,10];

打乱一个数组的顺序,可以使用 Fisher-Yates 洗牌算法,它的基本思路是从数组末尾开始,每次随机生成一个下标,将当前元素与该下标的元素交换位置,然后继续从数组末尾开始,直到数组首位元素都被交换过。这样就可以将数组打乱顺序。

以下是使用 Fisher-Yates 洗牌算法打乱数组顺序的代码实现:

var arr = [1,2,3,4,5,6,7,8,9,10];

function shuffleArray(arr) {
  for (var i = arr.length - 1; i > 0; i--) {
    var j = Math.floor(Math.random() * (i + 1));
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }
  return arr;
}

console.log(shuffleArray(arr)); // 输出打乱顺序后的数组

37、Vue的自定义指令钩子函数有哪些?你用自定义指令做过什么?

Vue的自定义指令提供了一些钩子函数,可以在指令的不同生命周期中执行一些自定义的操作,常用的钩子函数有以下几个:

bind:指令第一次绑定到元素时调用,只调用一次,可以在这个钩子函数中进行初始化操作。
inserted:元素插入到 DOM 中时调用,但是不保证父节点已经插入到 DOM 中。
update:元素的值更新时调用,但是可能在子元素更新之前调用。
componentUpdated:元素和子元素的值都更新后调用。
unbind:指令与元素解绑时调用,只调用一次。

自定义指令可以实现一些特定的需求,比如实现一个点击外部区域关闭弹窗的功能:

Vue.directive('click-outside', {
  bind: function(el, binding, vnode) {
    el.clickOutsideEvent = function(event) {
      // 判断是否点击了 el 元素以外的区域
      if (!(el == event.target || el.contains(event.target))) {
        // 执行指令绑定的方法
        vnode.context[binding.expression](event);
      }
    };
    document.body.addEventListener('click', el.clickOutsideEvent);
  },
  unbind: function(el) {
    document.body.removeEventListener('click', el.clickOutsideEvent);
  },
});

38、从A页面跳转到B页面,缓存A组件,从A组件跳转到C组件,取消缓存,如何实现?

在Vue中,可以通过路由的meta字段来标记需要缓存的组件,在路由配置中设置keepAlive: true。比如:

const router = new VueRouter({
  routes: [
    {
      path: '/A',
      component: A,
      meta: {
        keepAlive: true
      }
    },
    {
      path: '/B',
      component: B
    },
    {
      path: '/C',
      component: C
    }
  ]
})

在需要取消缓存的组件中,可以在activated生命周期钩子中调用$destroy方法来销毁该组件实例。比如:

export default {
  activated() {
    this.$destroy();
  }
}

39、Vue2和Vue3中响应式原理及区别?

Vue2和Vue3中的响应式原理都是通过数据劫持实现的,但是具体实现方式有所不同。

Vue2使用了Object.defineProperty()方法来劫持数据,即将对象的属性转化为getter和setter,通过getter进行依赖收集,当setter被触发时,通知相关的依赖进行更新。这种方式有一些限制,比如无法监听到数组下标的变化以及新增属性的变化。

Vue3则采用了ES6的Proxy来实现数据劫持。通过创建一个代理对象,可以监听到对象属性的访问和修改,可以监听到数组下标的变化以及新增属性的变化。同时,Vue3还使用了WeakMap来进行依赖收集,比Vue2中使用的基于数组的依赖收集方式更加高效。

此外,Vue3中的响应式系统还引入了一些新特性,比如可对多个响应式对象进行操作的组合式API、全局API和应用级别的静态属性、更好的Typescript支持等等。

40、Vue是如何实现实现权限管理的,按钮级别权限如何实现?

Vue 实现权限管理一般需要从前端和后端两个方面考虑,前端主要是 UI 的控制和请求的发送,后端主要是数据的处理和权限校验。

在前端中,Vue 可以通过路由守卫来进行权限控制,比如在 beforeEach 钩子中判断当前用户是否有权限访问该路由,如果没有则跳转到指定的错误页面。另外,也可以使用自定义指令来控制按钮的显示和隐藏,根据用户的权限动态地添加或移除对应的指令。

在后端中,需要对用户的身份进行认证和鉴权,可以通过 session、token 或者 OAuth 等方式实现。一般情况下,用户的权限信息会存储在数据库中,需要在服务器端进行查询和校验。对于按钮级别的权限控制,可以在后端通过权限拦截器对请求进行拦截,根据用户的权限信息来判断是否允许进行操作。

需要注意的是,前端的权限控制只是一种辅助手段,真正的权限控制应该在后端进行,因为前端的代码可以被修改和篡改,容易被绕过。

41、说说webpack中常见的Loader?解决了什么问题?

Webpack中的Loader是用来处理特定类型的文件,将它们转换为Webpack可以处理的模块。常见的Loader有以下几种:

  • babel-loader:将ES6/ES7代码转换成ES5语法,解决了浏览器兼容性问题。
  • style-loader/css-loader:将CSS文件注入到HTML文件中或将CSS文件转换成JS模块,解决了样式打包问题。
  • file-loader/url-loader:将图片、字体等静态资源转换成模块,解决了静态资源打包问题。
  • vue-loader:处理Vue文件,将其转换为JS模块。
  • json-loader:将JSON文件转换为JS模块。

除此之外,还有很多其他的Loader,例如less-loader、sass-loader、postcss-loader等等,都是为了解决Webpack打包过程中的特定问题。

Loader的作用是解决Webpack中不同类型的文件在打包过程中的处理问题,从而使得Webpack能够处理更多类型的文件,让打包更加便捷和高效。

42、你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢

SPA(Single Page Application)即单页面应用,它是一种 Web 应用的架构模式。它通过使用 JavaScript 动态地切换 HTML 内容,从而实现在不刷新整个页面的情况下更新页面的功能。SPA应用通常是基于前端框架(如Vue、React、Angular)实现的,通过路由控制不同的页面展示。

优点:

  • 用户体验好:不用频繁地刷新整个页面,加载速度快,有更好的交互体验;
  • 代码结构清晰:页面结构相对简单,易于维护;
  • 可维护性高:前后端分离,各自负责各自的领域,便于协作开发。

缺点:

  • SEO 问题:由于只有一个 HTML 页面,搜索引擎难以抓取到 SPA 应用中的内容,不利于 SEO 优化;
  • 首屏加载时间长:SPA 应用需要将所有资源加载完毕后才能展示出页面,导致首屏加载时间长;
  • 浏览器兼容性问题:需要兼容多个浏览器,增加了开发难度和工作量。

实现SPA应用通常使用前端框架、路由库、状态管理库等技术来实现。比如使用 Vue 可以使用 Vue Router 来实现路由控制,使用 Vuex 来管理状态。SPA 应用也需要考虑如何优化首屏加载时间,可以采用懒加载等方式来减少首屏需要加载的资源量。同时,需要注意 SEO 优化,可以采用服务端渲染或者预渲染等方式来解决。

43、Vue中组件和插件有什么区别?

Vue中的组件和插件都可以扩展Vue的功能,但它们的使用场景和实现方式不同。

组件是Vue中的一种基本概念,可以将一个页面拆分成多个独立的可重用组件,每个组件可以有自己的数据和方法,通过组合不同的组件来构建复杂的页面。组件的使用非常灵活,可以全局注册,也可以局部注册,可以接收props传递数据,也可以通过事件和vuex实现组件间的通信。

插件则是对Vue进行全局性的功能扩展,可以在Vue实例上添加新的方法、指令、过滤器、混入等,来扩展Vue的能力。插件通常是一些功能性的库,如axios、vue-router、vuex等,需要通过Vue.use()来安装并注册使用。

区别总结如下:

  • 组件是Vue的基本概念,可以拆分页面并重用;插件是对Vue进行全局性功能扩展的库。
  • 组件可以接收props传递数据,也可以通过事件和vuex实现通信;插件可以在Vue实例上添加新的方法、指令、过滤器、混入等。
  • 组件的使用非常灵活,可以全局或局部注册;插件一般需要通过Vue.use()安装并注册。
  • 组件的实现方式是Vue组件选项,插件的实现方式是包含install方法的对象或函数。
    总之,组件和插件都是Vue非常重要的扩展方式,我们可以根据具体的场景和需求来选择使用。

44、你了解vue的diff算法吗?说说看?

Vue的虚拟DOM中实现了一种高效的算法——Diff算法,用于比较新旧虚拟节点之间的差异,只对有变化的节点进行实际的DOM操作,从而避免了不必要的DOM操作,提高了应用的性能。

Vue中的Diff算法实现的大致流程如下:

  • 将旧节点与新节点进行比较,如果它们的标签不同,那么直接删除旧节点,创建并插入新节点;
  • 如果它们的标签相同,那么比较它们的属性差异,并更新节点的属性;
  • 如果它们的子节点不同,那么将子节点逐一进行比较,递归执行Diff算法。

Vue的Diff算法具有以下优点:

  • 减少了不必要的DOM操作,提高了应用的性能;
  • 可以最小化修改DOM的次数,从而减少浏览器的重排和重绘,提高了用户体验;
  • 对比算法的效率较高,能够快速地处理大量的节点。

但是,Vue的Diff算法也存在一些缺点:

  • 对于嵌套层级较深的节点,Diff算法的性能可能会受到影响;
  • 如果在数组中进行插入或删除等操作,Diff算法可能会出现误判的情况,从而导致界面不更新。

总体来说,Vue的Diff算法是一种高效的算法,能够大大提高应用的性能和用户体验,但在使用时需要注意一些问题,以避免出现不必要的问题。

45、Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?

Vue3.0 中的 Composition API 和 Vue2.x 中的 Options API 是两种不同的组件编写方式。下面是它们的一些不同之处:

  • 组合 API 可以将功能逻辑封装到单个函数中,这使得代码更容易重用和组合。相比之下,Options API 是基于定义组件选项对象的方式,其中每个选项对应一个功能。

  • Composition API 更加灵活,可以让开发者更加自由地组织代码。相比之下,Options API 需要严格按照 Vue 的组件选项规范来定义组件。

  • Composition API 更加强大,可以使用 React Hooks 的方式来管理组件状态、生命周期和逻辑。相比之下,Options API 只能在生命周期钩子中处理状态和逻辑。

  • Composition API 支持更好的 TypeScript 支持,因为可以使用泛型来标记状态和逻辑。相比之下,Options API 的类型定义通常需要手动编写。

需要注意的是,Options API 仍然是 Vue3.0 中的一个完全支持的特性,因此如果你习惯了 Options API,你完全可以继续使用它。但是,组合 API 是一个更加现代化和强大的方式来编写 Vue 组件,并且在大型应用中可能会更加适合。# 46、说一下Vue2数据响应式的原理和Vue3数据响应式原理的不同?

47、说说 React中的setState执行机制?

在 React 中,setState 是用于更新组件状态的方法。它是异步执行的,因为 React 希望在进行多次状态更新时,将其批量执行以提高性能。

当我们调用 setState 时,React 会将新的状态合并到当前状态中,然后计划一次更新操作。更新操作将在当前代码执行结束后异步执行。这意味着调用 setState 后,如果我们立即打印状态,可能看到的是当前状态而不是新的状态。

在处理更新时,React 会首先比较新旧状态,检查它们是否相同。如果新旧状态相同,则不会执行任何操作。否则,React 将计划重新渲染组件并将新状态应用于组件。

值得注意的是,在某些情况下,React 可能会在 setState 中同步执行更新,而不是异步执行。这通常发生在使用 refs 或在生命周期方法中调用 setState 时。在这种情况下,我们需要小心处理更新,以避免出现问题。

48、说说对React中类组件和函数组件的理解?有什么区别?

React中的组件分为两种:类组件和函数组件。

类组件是以ES6的class语法为基础,使用extends关键字继承React.Component类来定义的组件。类组件中必须包含一个render()方法,用于返回组件的结构,可以通过this.props来获取父组件传入的属性值,通过this.state来管理组件的状态。

函数组件是以函数的形式定义的组件,是纯函数,接收一个props参数,并返回一个React元素,没有自己的状态和生命周期函数,所以也被称为无状态组件。函数组件的优点是简洁、易于测试、性能更好,所以在React Hooks出现后,函数组件也得到了更多的使用。

类组件和函数组件的区别在于定义方式、组件的状态管理方式、生命周期函数等方面。类组件需要继承React.Component类,而函数组件直接是一个函数。类组件中的状态是通过this.state来管理的,而函数组件中的状态可以通过useState等Hooks来管理。生命周期函数在类组件中有,而在函数组件中有useEffect等Hooks来代替。

在React 16.8之前,函数组件缺少状态和生命周期等特性,只能作为展示性组件。但是React Hooks的出现解决了这个问题,使得函数组件能够管理自己的状态、处理副作用等操作,从而使得函数组件的应用范围更加广泛,性能也更加优秀。

49、说说对React Hooks的理解?解决了什么问题?

React Hooks 是 React 16.8 引入的一项新功能,它可以让函数组件也具有类组件的状态管理和生命周期函数等能力,从而简化 React 的编码方式。

在 React 中,组件的状态是需要通过 class 组件来管理的,而函数组件只能通过 props 来接收父组件传递的值,并且没有生命周期函数等特性。而 React Hooks 则打破了这种限制,可以让函数组件也能够具有管理状态和生命周期函数的能力。

React Hooks 解决了一些问题,比如:

  • 在 class 组件中,管理状态需要使用 this.state 和 this.setState,而使用 Hooks 可以通过 useState 简化状态管理的方式,更加直观易懂。
  • 在 class 组件中,生命周期函数的使用比较繁琐,而使用 Hooks 可以通过 useEffect 简化生命周期函数的使用。
  • 在 class 组件中,代码量较多,使用 Hooks 可以使代码更加简洁,更加易于维护。

React Hooks 的常用函数包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useImperativeHandle、useLayoutEffect 等。通过使用这些函数,可以在函数组件中管理状态、获取上下文、优化性能等。

50、UseMemo和useCallback如何提升了性能,应用场景有哪些?

useMemouseCallback 都是 React 中用于性能优化的 Hooks。

useMemo 可以缓存计算结果,避免每次重新计算,从而提升组件的渲染性能。它接收两个参数,第一个参数为计算函数,第二个参数为依赖数组,只有依赖数组中的值发生变化时,计算函数才会重新执行。应用场景包括:

  • 计算量较大的数据处理
  • 避免重复渲染子组件

举个例子,在一个表格组件中,如果每次渲染时都需要重新计算表格行数,会导致性能问题。可以使用 useMemo 来缓存计算结果:

const rowNumber = useMemo(() => {
  // 计算表格行数
  return data.length;
}, [data]);

useCallback 可以缓存函数,避免每次重新创建函数实例,从而提升组件的渲染性能。它接收两个参数,第一个参数为函数,第二个参数为依赖数组,只有依赖数组中的值发生变化时,函数才会重新创建。应用场景包括:

  • 传递给子组件的回调函数
  • 事件处理函数

举个例子,在一个列表组件中,如果每次渲染时都需要重新创建点击事件处理函数,会导致性能问题。可以使用 useCallback 来缓存函数:

const handleClick = useCallback((item) => {
  // 处理点击事件
}, []);

需要注意的是,使用 useMemouseCallback 并不是一定能提升性能,需要结合实际场景进行优化。同时,过度使用 useMemouseCallback 也可能会导致代码可读性下降,需要在性能和可读性之间进行权衡。

51、Vue-router的实现原理是什么?

Vue-router是Vue.js官方提供的路由管理库,可以在Vue.js应用程序中实现客户端路由。

Vue-router的实现原理可以简单概括为以下几个步骤:

  • 定义路由规则:在Vue.js应用程序中定义路由规则,即指定哪些URL路径应该由哪些组件渲染。

  • 创建路由实例:使用VueRouter类创建一个路由实例,将路由规则作为参数传递给该实例。

  • 将路由实例挂载到Vue实例上:在Vue.js应用程序中将路由实例挂载到Vue实例上,使得所有的组件都可以访问路由实例。

  • 监听URL变化:当URL发生变化时,路由实例会根据定义的路由规则匹配相应的组件,并将组件渲染到应用程序的页面上。

  • 实现导航:Vue-router提供了一些API,可以通过编程方式实现导航,例如:push()、replace()、go()、back()等方法。

在实现路由的过程中,Vue-router利用了Vue.js的组件系统,将路由组件定义为Vue组件,实现了路由和组件的无缝衔接。同时,Vue-router还利用了浏览器的History API实现了前端路由的实现。

总的来说,Vue-router的实现原理是通过监听URL的变化,根据定义的路由规则匹配相应的组件,并将组件渲染到应用程序的页面上,实现了前端路由的实现。

52、如何封装组件在不同的项目之间使用如何实现?

封装组件使其可以在不同的项目之间使用的一种常见的方式是通过将组件打包为可复用的npm包。具体步骤如下:

  • 创建一个独立的npm包,可以使用工具如npm init来初始化包的基本信息,或者使用脚手架工具如create-react-library或vue-cli-plugin-library来创建基础的npm包结构。

  • 将要封装的组件放在npm包的src目录下,并编写组件的代码。

  • 使用工具如rollup或webpack将组件代码打包成umd、cjs、esm等不同格式的包,并输出到npm包的dist目录下。同时在package.json文件中定义main、module、browser等字段,指定输出的不同包格式和入口文件。

  • 在组件代码中引用依赖的第三方库,使用peerDependencies字段将第三方库的版本号声明到npm包中,表示组件需要依赖这些第三方库。

  • 发布npm包到npm仓库,可以使用npm publish命令将包发布到公共仓库,或使用私有仓库来管理npm包。

在不同的项目中使用封装的组件时,可以通过npm install安装该组件包,然后在项目中引入组件。对于React项目,可以使用import语句导入组件,并在JSX中使用;对于Vue项目,可以使用Vue.use()方法全局注册组件,或在单个组件中通过import语句导入并使用。需要注意的是,引用组件时需要根据组件包中定义的不同包格式,选择合适的导入方式。

53、vue、react、angular 区别?

Vue、React和Angular都是当前流行的前端框架,它们有一些不同的特点和用法。

Vue是一个轻量级的框架,具有较为简单易懂的API和良好的文档,适合用于构建中小型应用。Vue使用了双向数据绑定和虚拟DOM等技术,以及一些方便的工具,如Vuex、Vue-router和Vue-cli等,使得开发者可以快速构建高效且易于维护的应用。

React是由Facebook开发的一个灵活且高效的框架,它采用了虚拟DOM、单向数据流和组件化等概念。React具有很好的可重用性和可组合性,以及很好的性能,但相比Vue有较为复杂的API和一些陡峭的学习曲线。

Angular是由Google开发的一个全面的框架,它采用了强类型、依赖注入和模块化等概念。Angular提供了很多强大的工具和功能,如路由、表单验证、HTTP服务和可复用的组件等,但相比Vue和React有较大的体积和较为复杂的API。

总的来说,Vue、React和Angular都有其适用场景和特点,开发者可以根据项目需求和个人偏好来选择合适的框架。

54、说说你对Redux的理解?其工作原理?

Redux是一个状态管理库,用于在JavaScript应用程序中管理应用程序的状态。它是一个单向数据流的模式,它将应用程序状态存储在单个store中,而不是分散在多个组件中。Redux的三个核心原则是单一的数据源、状态不可变和纯函数。

  • Redux的工作原理可以概括为以下几个步骤:

  • Action:使用Action来描述发生的事件。它是一个JavaScript对象,它必须包含一个type属性,表示事件类型,并且可以包含其他任意数据。

  • Reducer:Reducer负责根据Action更新状态。它是一个纯函数,接受当前状态和Action作为输入,返回新的状态。

  • Store:Store是一个包含整个应用程序状态的对象。它是由Redux的createStore函数创建的,包含三个方法:getState,dispatch和subscribe。

  • View:View使用store.getState()来获取应用程序状态,并根据状态来渲染UI。

通过这样的方式,Redux可以实现状态的集中管理,让应用程序的状态变得可预测和可控。

相比于其他状态管理库,Redux的优势在于它的设计思想非常简单,易于理解和学习,并且可以与各种UI库和框架结合使用。同时,Redux的严格规定也让它能够在大型复杂应用程序中得到更好的应用。

55、说说你对递归的理解?封装一个方法用递归实现树形结构封装

递归是一种在函数内部调用自身的方法,通常用来解决需要反复执行同一任务的问题,其思想是将大问题分解成小问题进行解决。

在Vue中,递归可以用于展示树形结构的数据,它通过不断递归调用相同的组件,使得数据能够依次按照树形结构展开。

下面是一个用递归实现树形结构的代码示例:

<template>
  <div>
    <li v-for="item in list" :key="item.id">
      {{ item.label }}
      <tree :list="item.children"></tree>
    </li>
  </div>
</template>

<script>
export default {
  name: "tree",
  props: {
    list: {
      type: Array,
      default: () => []
    }
  },
  components: {
    Tree: () => import('./Tree.vue') // 注册为异步组件,避免无限递归
  }
};
</script>

在这里,我们通过使用Vue的组件机制,定义了一个Tree组件,它接收一个list参数作为树形结构的数据。在组件的模板中,我们通过v-for指令遍历list数组,并递归调用自身(注:要把子组件注册为异步组件,避免无限递归)。这样,就可以实现树形结构的展示了。

通过以上示例,我们可以发现,递归非常适合处理树形结构这类重复性工作,它可以大大减少代码的复杂度和冗余度,提高代码的可维护性和可读性。

56、什么是FOUC? 如何避免?

FOUC是指页面在加载时,由于CSS文件的加载顺序导致页面的样式先后变化,从而出现页面闪烁的现象。FOUC的全称是“Flash of Unstyled Content”。

要避免FOUC,我们可以采取以下几种方式:

  • 将CSS样式表放在HTML文档头部,这样浏览器加载HTML时就可以同时加载CSS文件,避免了样式变化的闪烁。

  • 使用媒体查询,对不同的设备采取不同的CSS样式,以避免因页面元素的尺寸改变导致的样式变化。

  • 使用JavaScript将CSS样式表动态地插入到页面中,可以避免页面的样式变化。

  • 使用CSS样式表中的特定属性,如visibility: hidden或者opacity: 0,避免在页面加载时元素的显示。

在HTML标签上加上style="display:none"的方式,避免页面的样式变化。在JS中,我们可以使用window.onload事件,在页面元素加载完毕后再显示页面。

57、说说你对预编译器的理解?

预编译器是一种将代码预处理成标准化代码的工具。它们通过增加特性和语法来扩展普通的CSS、HTML、JS,并将这些扩展内容转换成浏览器可识别的CSS、HTML、JS代码。常见的预编译器包括Sass、Less、Stylus、Pug等。

预编译器的优点包括:

  • 增加特性:预编译器能够增加CSS、HTML、JS的特性,比如在CSS中添加变量、嵌套等语法;在HTML中添加变量、条件语句等语法;在JS中添加类似于Typescript的类型检查、ES6的语法等。
  • 提高可读性:预编译器能够更加语义化和清晰地书写代码,让代码更易读、易维护。
  • 提高开发效率:使用预编译器能够极大地提高开发效率,减少代码量和重复劳动。
  • 支持自定义扩展:预编译器支持自定义扩展,能够满足特定需求或组织特定的业务逻辑。

缺点则包括:

  • 需要学习新的语法和工具:使用预编译器需要熟悉其特定的语法和工具,增加了学习成本。
  • 不易调试:使用预编译器需要将其转换成标准的代码,当出现问题时,调试起来比较困难。
  • 增加编译时间:使用预编译器需要编译成标准代码,增加了编译时间。

总之,预编译器在前端开发中被广泛使用,能够提高开发效率和代码质量,减少重复劳动。但也需要注意其对开发成本和调试难度的影响。

58、shouldComponentUpdate 的作用

shouldComponentUpdate 是 React 生命周期中的一个方法,用于判断组件是否需要更新,可以在组件渲染前被调用,根据返回值来决定是否执行更新操作。该方法可以用来优化 React 应用的性能,避免不必要的渲染。

默认情况下,当父组件的状态或属性发生变化时,所有的子组件都会重新渲染,但是在某些情况下,某些子组件其实并不需要重新渲染,这时可以通过在子组件中实现 shouldComponentUpdate 方法来控制是否需要更新。

shouldComponentUpdate 接收两个参数:nextPropsnextState,分别表示下一个状态和属性。该方法返回一个布尔值,如果返回 true,则表示需要更新组件,反之则表示不需要更新组件。可以通过比较前后两个状态和属性的值,来判断是否需要更新组件。

当组件状态或属性改变时,如果使用了 shouldComponentUpdate 方法,React 会先调用该方法,然后再根据方法的返回值来决定是否执行更新操作。如果返回 true,则继续执行更新操作,如果返回 false,则停止更新操作,直接使用之前的状态和属性进行渲染,从而避免了不必要的渲染,提高了应用的性能。

59、概述下 React 中的事务处理逻辑

React中的事务处理逻辑与浏览器中的事件机制很相似,都是把一系列的回调函数封装起来,然后进行统一的调度和执行。

在React中,每一个组件的rerender都可以理解为一个事务,每个事务都包括三个阶段:

开始阶段 (transaction start):在这个阶段,React会记录当前要进行rerender的组件,并维护一个执行列表,用于记录当前事务中的所有回调函数。
执行阶段 (transaction perform):在这个阶段,React会依次执行事务中所有的回调函数,包括组件的生命周期函数、组件的setState函数等。
提交阶段 (transaction complete):在这个阶段,React会根据事务的执行结果进行状态更新和DOM操作等,最终完成真正的渲染。
React通过ReactDOM.unstable_batchedUpdates()函数将这些事务进行了批处理,可以把多个事务合并为一个,从而减少了不必要的性能消耗。

总之,React中的事务处理逻辑是React高效渲染的重要保证,它通过将多个更新操作批量处理来提高性能,并保证了在事务执行期间的更新操作能够稳定地进行。

60、react组件的划分业务组件技术组件?

在 React 中,组件的划分一般可以分为业务组件和技术组件。

业务组件(Presentational Components)是指与业务相关的组件,主要负责展示数据和用户交互,通常称为展示组件或 dumb 组件,因为它们没有自己的状态,只接收父组件传递的属性(props),并根据属性渲染 UI。

技术组件(Container Components)是指与技术相关的组件,主要负责处理数据和业务逻辑,通常称为容器组件或 smart 组件,因为它们拥有自己的状态,可以直接操作数据,并将数据以属性(props)的形式传递给业务组件。

在实际开发中,我们通常会将复杂的页面或功能拆分成多个小组件,然后将这些小组件组合在一起形成一个完整的业务组件。而技术组件则负责控制数据流动和处理业务逻辑,将数据传递给业务组件进行展示。

业务组件和技术组件的划分可以帮助我们更好地组织代码,提高代码的可复用性和可维护性。

61、react性能优化是哪个周期函数

React性能优化的周期函数主要是shouldComponentUpdate()。在React应用中,每当props或state更新时,React会自动调用render()函数重新生成组件的虚拟DOM树,并将其与旧的虚拟DOM树进行Diff算法的比较,从而找出需要更新的部分进行局部渲染。

其中,shouldComponentUpdate()函数就是在进行Diff算法比较的时候调用的,我们可以在这个函数中控制是否需要更新组件的state和props,从而避免不必要的更新和渲染,提高应用的性能。

当shouldComponentUpdate()函数返回false时,React就认为组件不需要更新,从而直接使用旧的虚拟DOM树进行渲染,避免了不必要的操作。

需要注意的是,对于Pure Component (纯组件),即纯函数组件和继承自React.PureComponent的类组件,React会自动实现一个浅比较的shouldComponentUpdate()函数来避免不必要的重新渲染。

总之,React性能优化中的shouldComponentUpdate()函数可以帮助我们控制组件是否需要更新和渲染,从而提高应用的性能。但这个函数的实现需要注意保证正确性并避免过度优化导致的维护成本增加。

62、说说你对Fiber的理解和应用场景

Fiber是React中的一种新的协调机制,它的目的是解决在React应用中由于大量计算和渲染导致的卡顿问题。在旧的协调机制中,React是通过递归遍历组件树来计算并更新组件,如果组件树层级过深,递归过程中的计算和渲染会占用大量时间,从而导致页面卡顿。而Fiber则采用了一种基于时间片的调度算法,将大量的计算和渲染任务划分为多个小的任务单元,并且在每个任务单元执行完成后进行一次中断和恢复,以确保页面的响应速度。

Fiber的应用场景包括但不限于:

大型React应用,如社交网络、电商等,这些应用通常包含大量的组件,使用Fiber可以提高应用的性能和用户体验。
高并发场景,如在线游戏等,使用Fiber可以确保应用的实时性和响应速度,避免因大量计算和渲染导致的卡顿问题。
移动端应用,移动端设备的计算和渲染能力有限,使用Fiber可以减少计算和渲染的负担,提高应用的性能和用户体验。

63、react性能优化方案

React性能优化有多种方案,以下列举了一些常见的方法:

  • 使用shouldComponentUpdate()或React.PureComponent来控制组件的更新;
  • 使用React的React.memo()函数或第三方库reselect进行数据的缓存和避免重复渲染;
  • 对长列表或树形结构等组件进行虚拟化,例如使用react-virtualized;
  • 对动画组件使用transform代替top、left等属性进行渲染,避免触发浏览器的重排和重绘;
  • 对复杂的组件进行分割和代码的懒加载,例如使用React.lazy和React.Suspense;
  • 对组件的代码进行优化,避免不必要的渲染和操作,例如对事件绑定进行正确的销毁和管理、合理使用生命周期函数等。

需要注意的是,优化方案需要根据项目的具体情况进行选择,不能随意追求性能而影响代码的可读性和可维护性。在实现优化方案时,也需要注意对组件的正确性进行保证,避免引入新的问题。

64、简述flux 思想及Redux的应用

Flux是一种架构思想,用于管理Web应用中的数据流。它强调单向数据流,即数据只能从store(数据层)向视图层(view)流动,视图层不能直接修改数据,而必须通过action来触发数据的改变。这种模式下,数据的流动更加可控和可预测,提高了代码的可维护性和可测试性。

Redux是一个基于Flux思想的状态管理库,它的核心思想是将应用中的所有状态保存在一个store中,视图层从store中读取状态,并通过派发action来触发store中的状态改变。Redux通过提供统一的API和严格的规则来管理应用中的状态,使得状态管理更加可控和可维护。

Redux的应用场景包括:

复杂的数据流管理:当应用中数据流较为复杂,需要管理多个相互依赖的状态时,Redux可以提供统一的数据管理方案,使得状态的变化更加可预测和可控。

跨组件状态共享:当多个组件需要共享同一状态时,Redux可以提供一个全局的状态容器,并通过订阅和派发机制来实现状态的同步。

服务端渲染:当应用需要进行服务端渲染时,由于服务端没有DOM环境,因此需要将数据和组件状态提前序列化并传递给客户端。Redux可以通过提供统一的数据容器和数据序列化方案来简化服务端渲染的实现。

65、说说html和css渲染的过程是什么

HTML和CSS渲染的过程一般分为三个阶段:解析、渲染和合成。下面是具体的流程:

  • 解析HTML:浏览器首先会解析HTML,生成一颗DOM树。DOM树是由一些个体(HTML标签、CSS样式)构成的树形结构,表示整个页面的结构和层级关系。

  • 解析CSS:浏览器接着解析CSS文件,生成一份CSSOM树。CSSOM树也是由一些个体(CSS样式)构成的树形结构,表示整个页面的样式信息。

  • 合成:在完成DOM树和CSSOM树的解析后,浏览器就可以开始将它们合成为一颗渲染树(Render Tree),这个过程就是合成。渲染树只包含渲染网页所必须的节点,例如可见的节点,所有的CSS信息和计算后的样式属性,不可见的节点和一些不需要渲染的节点就不在渲染树中。

  • 布局:渲染树生成后,浏览器会根据每个节点的内容、样式等信息计算其在页面中的位置和大小,这个阶段称为布局(Layout)。

  • 绘制:最后是绘制(Painting)阶段,浏览器遍历渲染树,并依据树中节点的几何信息将所有的节点绘制出来,呈现在屏幕上。

需要注意的是,HTML和CSS渲染的过程是一个复杂的过程,可以受到很多因素的影响,并且在实际渲染中会涉及到很多细节和优化,了解渲染的基本流程可以帮助我们更好的理解页面渲染的过程,从而更好地进行前端的开发和调试。

66、说一下DOM0、DOM2、DOM3事件处理的区别是什么?

DOM0、DOM2和DOM3都是JavaScript中用于处理事件的三种不同的API版本,它们之间有以下区别:

  • DOM0事件处理:是最早出现的一种事件处理方式,通过在DOM节点上直接指定事件处理函数来实现。例如:
button.onclick = function() {
  alert('button clicked');
}
  • DOM2事件处理:DOM2级事件定义了两种方法,addEventListener和removeEventListener,它们可以动态地添加和移除事件处理函数。DOM2事件处理可以添加多个事件处理函数,它们按照添加的顺序依次执行。例如:
button.addEventListener('click', function() {
  alert('first click');
}, false);

button.addEventListener('click', function() {
  alert('second click');
}, false);
  • DOM3事件处理:DOM3级事件增加了更多的事件类型,例如鼠标滚轮事件、键盘按键事件、文本输入事件等。除此之外,DOM3事件处理还增加了更多的方法,例如preventDefault()、stopPropagation()、stopImmediatePropagation()等,这些方法可以更加精确地控制事件的传播和处理。

总体来说,DOM2事件处理相比DOM0事件处理有更多的优势,而DOM3事件处理进一步扩展了事件的类型和方法。但是,由于DOM0事件处理在一些场景下的兼容性更好,所以在实际开发中也需要根据具体情况来选择不同的事件处理方式。

67、如何判断页面滚动到底部,如何判断页面中元素是否进入可视化区域?

1、判断页面滚动到底部

在浏览器中判断页面是否滚动到底部,一般有以下两种方法:

(1)通过监听浏览器的scroll事件。当滚动条的位置与文档的高度之差等于可视区域的高度时,就可以认为页面已经滚动到了底部。

window.addEventListener('scroll', function() {
  const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 当前滚动条的位置
  const scrollHeight = document.documentElement.scrollHeight; // 文档高度
  const clientHeight = document.documentElement.clientHeight; // 可视区域的高度
  if (scrollTop + clientHeight >= scrollHeight) { // 如果滚动到底部 
    // do something
  }
});

(2)通过 Intersection Observer API。这是一种新的浏览器API,可以观察一个元素是否进入/退出浏览器的视窗。

const observer = new IntersectionObserver(function(entries) {
  if (entries[0].intersectionRatio <= 0) return; // 元素未进入视窗
  if (entries[0].intersectionRatio === 1) {
     // 元素完全进入视窗
  } else {
     // 元素部分进入视窗
  }
});

observer.observe(targetElement); // 监听目标元素

2、判断页面中元素是否进入可视化区域

同样,我们可以借助上面的 Intersection Observer API 来实现元素是否进入可视化区域的判断。只需要创建一个IntersectionObserver对象,并绑定回调函数即可。

const observer = new IntersectionObserver(function(entries) {
  if (entries[0].intersectionRatio <= 0) return; // 元素未进入视窗
  if (entries[0].intersectionRatio === 1) {
     // 元素完全进入视窗
  } else {
     // 元素部分进入视窗
  }
});

observer.observe(targetElement); // 监听目标元素

在回调函数中,第一个参数 entries 是被观测的目标元素,它是一个数组,每个元素都包含目标元素的一些信息:

isIntersecting:目标元素是否进入/退出视窗
intersectionRatio:目标元素进入视窗的比例(完全进入 = 1,完全退出 = 0)
target:目标元素本身
需要注意的是,Intersection Observer API 的兼容性存在一定的局限性,在使用前需要按照兼容性表格检查目标浏览器是否支持该API。

68、说一下Vuex的实现原理,commit和dispatch方法如何实现的

Vuex是一个专为Vue.js设计的状态管理库,它借鉴了Flux、Redux等框架的设计思想,提供了一种集中式管理应用状态的方案。Vuex的实现原理是基于Vue.js的响应式系统和发布/订阅模式实现的。

Vuex中包含了五个模块:state、mutations、actions、getters和modules。其中,state是存储应用状态的地方;mutations是修改应用状态的地方;actions是处理异步操作的地方;getters是计算状态的地方;modules是将状态进行模块化管理的地方。

Vuex中提供了两个方法:commit和dispatch。其中,commit用于提交mutation,而mutation是唯一修改状态的方式;dispatch用于触发action,而action则可以进行异步操作,最终通过提交mutation来修改状态。

commit方法的实现原理比较简单,它只是简单地调用mutation的方法来修改状态。

dispatch方法的实现原理则比较复杂,它需要先处理异步操作,然后再通过commit方法提交mutation来修改状态。在处理异步操作时,dispatch方法会根据action的类型分别执行不同的操作,可以是异步请求、定时器等等。当异步操作完成后,它会再次调用commit方法来提交mutation,从而修改状态。

总之,Vuex通过响应式系统和发布/订阅模式,实现了集中式管理应用状态的方案,提供了commit和dispatch方法来实现状态的修改和异步操作,从而简化了应用状态管理的过程。

69、请简单叙述Vue2和Vue3的区别和变化至少说6点

Vue2和Vue3之间有许多变化和区别,以下是其中的六点:

  • 性能提升:Vue3在内部使用了Proxy来代替Vue2中的Object.defineProperty,可以更高效地跟踪数据变化,从而提高性能。

  • 组合式 API:Vue3引入了组合式API,可以更好地组织组件的逻辑,使代码更具可读性和可维护性。

  • Composition API:Vue3的组合式API将组件的逻辑代码按照功能划分成多个小函数,而非Vue2中的选项式API,使得代码更加模块化。

  • V-model指令:Vue3中的v-model指令可以直接用于自定义组件,而Vue2则需要在组件中手动处理v-model。

  • Teleport组件:Vue3中引入了Teleport组件,可以轻松实现Portal的功能,将子组件渲染到父组件之外的DOM节点中。

更好的TypeScript支持:Vue3对TypeScript的支持更加完善,通过TypeScript可以更好地实现类型检查和自动补全。

70、说说对盒子模型的理解?

盒子模型是指我们网页中每个 HTML 元素都被看作是一个矩形的盒子,它由四条边界(外边距、边框、内边距和内容)组成,它们之间的关系可以用下面的公式来表示:

内容区宽度 = 元素宽度 - 左内边距 - 右内边距 - 左边框宽度 - 右边框宽度

内容区高度 = 元素高度 - 上内边距 - 下内边距 - 上边框宽度 - 下边框宽度

在这些边界中:

  • 内边距(padding):包裹在内容周围,用来增加元素内容与其边框的间距。
    边框(border):包裹在内边距周围,用来定义元素的边框。
  • 外边距(margin):包裹在边框周围,用来增加元素与其他元素之间的距离。

盒子模型对于网页布局来说非常重要,因为当我们设置元素的宽度、高度、内边距、边框、外边距等属性时,浏览器会根据盒子模型进行解析和渲染,如果不理解盒子模型的机制,就很容易出现一些布局上的问题。

理解盒子模型可以帮助我们更好的控制网页布局和样式,例如能够更好地调整文本与其周围元素的间距、更好地处理元素之间的空隙、更好地管理网页内的空间和排版等。

71、Css的选择器有哪些?优先级?哪些可以继承

CSS选择器有以下几种:

  • 标签选择器:选择指定 HTML 标签的元素,如 div、p、a 等。
  • 类选择器:选择指定类名的元素,以 . 开头,如 .box。
  • ID选择器:选择指定ID的元素,以 # 开头,如 #header。
  • 属性选择器:选择包含指定属性的元素,如 [title] 选择所有有 title 属性的元素。
  • 伪类选择器:选择元素的特殊状态,如 :hover 表示当鼠标悬停在元素上时的状态。
  • 伪元素选择器:选择元素的特殊部分,如 ::before 表示元素前面的内容。

CSS选择器的优先级如下:

  • !important声明的样式优先级最高;
  • 行内样式(内联样式);
  • ID选择器;
  • 类选择器、属性选择器、伪类选择器;
  • 标签选择器、伪元素选择器。

CSS中可继承的属性有以下几种:

  • font系列属性(font-family、font-size、font-weight、font-style、font-variant等)
  • text系列属性(text-indent、text-align、line-height、word-spacing、letter-spacing等)
  • color、visibility、cursor
  • border-collapse、white-space、background、list-style等。

72、元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

在CSS中实现元素的水平垂直居中方式常常使用以下几种方法。

使用flex布局:将元素的父容器设置成display: flex,并设置子元素的align-items: center和justify-content: center,这样子元素就可以实现水平垂直居中。

使用绝对定位:将元素的父容器设置成相对定位position: relative,将元素设置成绝对定位position: absolute,并使用left: 50%和top: 50%将元素的中心点移到父容器的中央,最后再使用transform: translate(-50%, -50%)将元素向左、向上移动自身宽高的一半。

使用网格布局:将元素的父容器设置成display: grid,并使用align-items: center和justify-content: center使其水平垂直居中。

当元素不定宽高时,可以使用所有方法中的绝对定位方式,使用上述方法在将元素的中心点移到父容器的中央后,再使用transform: translate(-50%, -50%)将元素向左、向上移动自身宽高的一半。

例如,如果我们的HTML代码如下所示:

<div class="wrapper">
  <div class="box">
    <p>这是一段文本。p>
  div>
div>

可以采用绝对定位实现元素的水平垂直居中,可以将CSS样式设置为:

.wrapper {
  position: relative;
  width: 100%;
  height: 500px;
  background-color: #ccc;
}

.box {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

这个样式可以让.box元素垂直居中于.wrapper容器中,即使它不定宽高。

73、怎么理解回流跟重绘?什么场景下会触发

回流(reflow)和重绘(repaint)是网页渲染时经常涉及到的两个概念,它们之间的关系是重绘是回流的一个子集。具体解释如下:

  • 回流:指元素的布局或几何属性发生变化时,浏览器重新计算元素的几何属性和页面布局,这个过程称为回流。回流会使其他的元素的布局也受到影响,可能会引起其他元素的重新布局和回流,导致性能问题。
  • 重绘:指元素的绘制属性发生变化时,浏览器会重新绘制元素的外观,但不会重新布局,这个过程称为重绘。例如,修改元素的颜色、背景颜色、边框颜色等属性都只会引起重绘。
    回流和重绘的出现,会对页面的性能产生负面影响。因此,需要尽量减少页面的回流和重绘,从而提高页面的性能。

以下是常见引起回流和重绘的场景:

  • 添加、删除、修改 DOM 元素。
  • 改变窗口大小,或者滚动页面。
  • 修改元素的样式,如改变元素的颜色、大小、位置等。
  • 读取某些属性,如 offsetWidth、offsetHeight、clientWidth、clientHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight 等。

有些属性值的变化,只会引起重绘而不会引起回流,例如改变元素的颜色、背景色等,而有些属性值的变化会同时引起回流和重绘,例如改变元素的宽度、高度等。

为了减少回流和重绘的次数,可以采取以下策略:

  • 尽量避免频繁读取计算样式属性和布局属性,避免多次触发回流和重绘。
  • 尽量批量修改 DOM 元素,减少对 DOM 的频繁操作。
  • 使用文档碎片(DocumentFragment)来操作 DOM 元素,减少 DOM 的访问次数。
  • 对于频繁重绘的元素,可以使用绝对定位来让它脱离文档流,从而避免其他元素的回流。
  • 避免在元素频繁改变大小、位置等属性时,使用 table 布局,因为 table 布局的元素变化时,需要触发整个表格的重新布局。
  • 对于需要读取元素尺寸和位置等信息的场景,可以使用 getBoundingClientRect() 方法,它可以返回一个矩形对象,包含元素的位置和尺寸信息,而且不会触发回流和重绘。

74、什么是响应式设计?响应式设计的基本原理是什么?如何做?

响应式设计是指能够根据不同设备的屏幕尺寸和分辨率自动调整页面布局和内容的设计方法。它的基本原理是使用弹性布局、媒体查询、图片自适应等技术实现页面的自适应性,从而使得页面在不同设备上都能够呈现出最佳的用户体验。

具体来说,响应式设计需要考虑以下几个方面:

  • 1.弹性布局:使用相对单位(如百分比、em、rem等)来设置元素的尺寸和位置,使得页面能够根据不同设备的尺寸自适应调整。

  • 2.媒体查询:根据不同设备的屏幕尺寸和分辨率,使用CSS3的媒体查询技术来加载不同的CSS样式文件,从而实现页面布局的自适应性。

  • 3.图片自适应:使用响应式图片技术(如srcset、picture等)来根据不同设备的屏幕尺寸加载适合的图片,从而提高页面加载速度和用户体验。

  • 4.内容筛选:根据不同设备的屏幕尺寸和分辨率,显示或隐藏某些内容,从而提高页面的可读性和可用性。

通过以上技术的组合应用,可以实现页面的自适应性,并在不同设备上呈现出最佳的用户体验。

75、如果要做优化,CSS提高性能的方法有哪些?

以下是几种提高CSS性能的方法:

  • 合并和压缩样式表:合并多个CSS样式表为一个,这样可以减少HTTP请求,从而提高页面的加载速度。同时,压缩CSS文件可以删除样式表中的注释和空格,以减少文件大小。

  • 避免使用通配符和浅层次选择器:通配符和浅层次选择器会影响渲染速度,避免使用它们可以提高页面的性能。

  • 减少使用选择器:在CSS中,选择器的使用越多,渲染速度就越慢。因此,尽量减少选择器的数量和使用范围。

  • 避免使用过于复杂的选择器:过于复杂的选择器也会降低渲染速度,例如在选择器中使用多个后代选择器或层次选择器。

  • 避免使用不必要的样式:尽量避免为元素指定不必要的样式,例如将font-family属性指定为系统默认字体Arial,而不是使用默认值inherit。

  • 使用CSS预处理器:CSS预处理器可以提高CSS的可维护性和可扩展性,同时还可以使用变量、函数、混合等高级特性,以提高CSS的性能和开发效率。

  • 使用GPU加速:使用CSS3中的GPU加速特性,例如transform和opacity属性,可以提高元素的渲染速度和性能。但是,过度使用GPU加速也会降低性能,因此要谨慎使用。

综上所述,我们可以通过合并、压缩和优化CSS样式表、避免使用通配符和选择器、精简样式、使用预处理器和GPU加速等方法,来提高CSS性能。

76、对前端工程师这个职位是怎么样理解的?它的前景会怎么样

作为一名前端工程师,主要负责开发和维护Web应用程序的前端部分,包括网站的布局、样式、交互逻辑和数据展示等。前端工程师需要熟练掌握HTML、CSS、JavaScript等技术,能够实现页面的响应式设计和良好的用户交互体验,并且需要与后端开发人员进行协作,实现前后端的数据交互。

随着Web技术的不断发展,前端工程师的前景非常广阔。现在越来越多的企业和组织意识到Web应用程序的重要性,因此需要越来越多的前端工程师来开发和维护这些应用程序。同时,随着移动设备和平板电脑的普及,对于响应式设计和移动端开发的需求也越来越大,这为前端工程师提供了更多的机会和挑战。

77、说说JavaScript中的数据类型?存储上的差别?

JavaScript中有七种基本数据类型和一种复杂数据类型,分别为:

  • number(数字):用于表示数值,包括整数、浮点数和NaN(表示非数字)。

  • string(字符串):用于表示文本数据,使用一对单引号(')或双引号(")将文本括起来。

  • boolean(布尔值):用于表示真或假,只有两个值,即true和false。

  • null(空值):用于表示一个空值或不存在的对象。

  • undefined(未定义):用于表示变量未初始化或不存在。

  • symbol(符号):用于表示对象的唯一标识符(ES6引入)。

  • BigInt(大整数):用于表示任意长度的整数,以n作为后缀创建(ES10引入)。

除基本数据类型外,JavaScript还有一种复杂数据类型,即Object(对象),用于存储键值对。对象是一组无序的属性的集合,每个属性都有一个字符串类型的名称(键)和对应的值,可以是基本数据类型和其他对象。

在JavaScript中,基本数据类型的值存储在栈内存中,而对象类型的值存储在堆内存中,变量保存的是该对象在堆内存中的地址引用。
举个例子:

let a = 10; // 存储在栈内存中
let b = 'hello'; // 存储在栈内存中
let c = true; // 存储在栈内存中

let obj = { name: 'Tom', age: 18 }; // 存储在堆内存中
let arr = [1, 2, 3]; // 存储在堆内存中

基本数据类型的值在赋值时是直接传递,而对象类型的值在赋值时是传递引用。也就是说,当我们将一个对象赋值给一个变量时,实际上该变量保存的是该对象在内存中的地址,而不是对象本身。因此,当我们修改对象时,其它引用该对象的变量也会受到影响。

值类型的数据在栈内存中比较简单,读取速度快,但容量小,无法存储大量数据。而使用对象存储数据,可以保存大量数据,但在读取时需要从堆内存中寻址,速度较慢。

至此,JavaScript中的数据类型和存储上的差别就介绍完毕了。

78、请简单叙述js数据类型判断的方法有哪些?

JavaScript数据类型判断的方法有以下几种:

  • typeof 操作符:用于判断一个变量的数据类型,返回一个字符串表示该变量的数据类型,包括 “undefined”、“boolean”、“number”、“string”、“bigint”、“symbol” 和 “object”。

  • instanceof 操作符:用于判断一个对象是否属于某个构造函数的实例,返回一个布尔值。

  • Object.prototype.toString() 方法:返回对象的数据类型,包括 “[object Object]”、“[object Array]”、“[object Function]” 等,需要注意的是该方法返回的都是字符串,需要进行截取。

  • constructor 属性:每个实例对象都有 constructor 属性,该属性指向创建该实例对象的构造函数。

  • Array.isArray() 方法:用于判断一个变量是否为数组类型,返回一个布尔值。

综上,不同的判断方法适用于不同的场景,需要根据具体情况选择。

79、说说你对闭包的理解?闭包使用场景

闭包是指在一个函数内部定义的函数,这个函数可以访问外层函数作用域中的变量和参数,即使外层函数已经执行完毕,也可以继续访问这些变量和参数。闭包可以保存函数所在的作用域,使其在函数执行完之后仍然可以被访问。

闭包有以下几个特点:

  • 函数内部定义函数:闭包就是一个函数,它在另一个函数内部定义。

  • 可以访问外部函数作用域中的变量:由于作用域链的特性,闭包可以访问外部函数作用域中的变量和参数。

  • 变量和作用域不会被回收:当函数执行完毕后,其内部定义的函数仍然可以访问其作用域中的变量和参数,因此这些变量和作用域不会被回收。

闭包的使用场景有很多,以下是一些常见的场景:

1、实现函数柯里化:函数柯里化是指将多个参数的函数转换成单个参数的函数的过程。通过闭包可以实现函数柯里化,例如下面这个例子:

function add(x) {
  return function(y) {
    return x + y;
  };
}

let add5 = add(5);
console.log(add5(3)); // 8
console.log(add5(7)); // 12

2、封装私有变量:通过闭包可以封装私有变量,保护数据不被外界访问。例如下面这个例子:

function Counter() {
  let count = 0;

  return function() {
    return ++count;
  };
}

let increment = Counter();
console.log(increment()); // 1
console.log(increment()); // 2

3、解决循环中的异步问题:在循环中使用异步函数时,由于变量作用域的问题,往往得不到期望的结果。通过使用闭包可以解决循环中的异步问题。例如下面这个例子:

for (let i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

4、上述代码中,由于回调函数是异步执行的,输出结果可能不是期望的,这时候可以使用闭包来保证每个回调函数中的变量i都是独立的。例如:

for (let i = 0; i < 5; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j);
    }, 1000);
  })(i);
}

闭包可以用于许多场景,但需要谨慎使用,因为它会占用内存并且可能会导致变量泄漏等问题。

80、bind、call、apply 区别?如何实现一个bind

bind、call、apply 都是 JavaScript 中用于改变函数执行上下文的方法,它们的主要区别在于参数传递方式和函数调用的时机:

  • call 和 apply:改变函数执行时的上下文,并立即执行该函数。它们的第一个参数都是指定的函数执行上下文,区别在于传递给函数的参数的方式不同。call 会将参数逐个传递,而 apply 则会将参数放在数组中传递。
  • bind:返回一个新函数,并将原函数的执行上下文改变为绑定的对象。不同于 call 和 apply,bind 不会立即执行原函数,而是返回一个新的函数,需要手动调用这个新的函数才能执行。

实现一个 bind 方法,可以通过以下步骤:

  • 在函数原型上扩展一个新的方法 bind,该方法接受一个上下文对象作为第一个参数,返回一个新的函数。

  • 在新函数内部使用 apply 方法,将函数执行的上下文设置为上下文对象,同时将调用 bind 方法传入的参数列表也传递给原函数。

代码示例如下:

Function.prototype.bind = function (context) {
  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);
  return function () {
    var bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(context, args.concat(bindArgs));
  };
};

这样实现的 bind 方法可以将原函数绑定到指定的上下文对象上,并返回一个新的函数,调用这个新函数时,新函数内部的 this 指向绑定的上下文对象。

81、Javascript本地存储的方式有哪些?区别及应用场景

JavaScript本地存储技术有以下几种方式:

  • Cookie:通过在客户端存储数据来跟踪和识别用户,以便使网站的服务更加个性化。Cookie可以通过设置过期时间来控制其生命周期。

  • sessionStorage:在用户关闭浏览器之前存储数据,存储的数据仅在当前会话中可用,关闭标签页或者浏览器窗口后数据将失效。

  • localStorage:与sessionStorage类似,但存储的数据没有过期时间,持久保存在客户端,直到被手动删除。

  • IndexedDB:在客户端浏览器中使用的数据库系统,可以存储大量的结构化数据,支持事务操作和索引查询,但使用起来相对复杂。

这四种存储方式的区别在于:Cookie的大小限制为4KB,而sessionStorage和localStorage的大小限制为5MB以上,IndexedDB的大小限制相对较大;Cookie存储数据会发送到服务器端,需要带宽和时间成本,而其他三种存储方式存储在客户端浏览器中,访问速度快;sessionStorage只在当前会话中保存,而localStorage可以被跨会话访问,IndexedDB则是一个独立的数据库系统,使用起来相对复杂。不同的存储方式适用于不同的应用场景。

  • Cookie 适用于存储少量简单信息,并且需要发送到服务器端使用
  • sessionStorage 适用于需要在同一标签页或窗口中保留状态数据,比如表单或用户登录状态等
  • localStorage 适用于需要在不同标签页或浏览器会话都可以访问的数据,比如网站的配置信息或用户设置
  • IndexedDB 适用于需要离线可操作的、复杂的数据结构,比如离线时的内容上传及编辑、富媒体(音频、视频)等的存储和管理等。

总之,选择本地存储方式需要根据具体业务和应用场景来考虑。

82、请叙述Vue2和Vue3的diff算法的区别?

Vue 2.x 的 Virtual DOM 中采用的是基于"全量比对"的 diff 算法。即每次更新都对着 Virtual DOM 进行一次递归式的对比,比较一下旧的 Virtual DOM 和新的 Virtual DOM 之间的差别。然后再将差别更新到真实 DOM 中。

这种算法有一个缺陷是,每次更新之后,会产生一份全新的 Virtual DOM 树,这就会导致大量的重绘和回流,从而影响性能。

Vue 3.x 引入了新的 diff 算法,采用了类似于 React 中Fiber架构的算法,即根据组件本身的特点以及组件之间的关系建立一棵"虚拟链表",这样在更新时只需要更新与之相关的部分,不会再遍历整棵树,减少了不必要的性能浪费。

Vue 3.x 的 diff 算法还在更新了多个组件时使用了异步更新,将多个更新任务一次性提交到队列中,只需要一次性的更新 DOM,减少了界面的重绘次数。

此外,Vue 3.x 引入了新的编译器,支持静态提升和静态节点提取,以及更好的 TypeScript 支持,这些改进都使得 Vue 3.x 的性能和开发效率得到了很大的提升。

83、请简单叙述你对作用域链得理解?

JavaScript 的作用域规则指的是确定变量和函数在什么位置可以访问的规则,其中作用域链是一个非常重要的概念。

作用域链是用来描述一个函数执行时变量的查找路径,其由当前环境记录的一连串对象组成,它的最前端是当前执行的函数的活动对象,最后一个对象是全局执行环境的变量对象。当访问一个变量时,JavaScript 引擎会先从当前函数自己的活动对象中查找,如果找不到就向它的外层作用域链中的上一个对象(外部函数的活动对象)继续查找,直到找到或者抵达全局执行环境的变量对象位置为止。

作用域链决定了当前执行环境对包含变量和函数的访问权限,它是在函数创建的时候就已经形成的。JavaScript中的每个函数都会形成一个新的作用域,在函数内部定义的变量和函数只能在该函数内部访问,而在函数外部定义的变量和函数在整个文件范围内都是可见的。一个函数可以访问在它被创建时处于它父级作用域的变量,这种机制就被称为"闭包"。

理解作用域链的规则有助于我们编写更加精简高效的代码,避免出现变量和函数重名、避免滥用全局变量等问题。同时,在前端开发中,对作用域链的理解也帮助我们更好的理解JavaScript框架和库的底层实现。

84、Vue3中的生命周期函数的变化以及含义

Vue 3.x 中的生命周期函数与 Vue 2.x 有所不同,主要有以下变化:

  • beforeCreate 和 created 合并为一个新的生命周期函数 - setup。setup 函数是组件内仅次于 props 解析的第二个调用的函数,该函数接收 props 对象及相关的上下文对象 (包括 attrs, slots, emit 等) 作为参数,并且它是一个普通函数而非响应式的函数。通过 setup 函数,我们可以完成类似于 beforeCreate 和 created 生命周期的操作,例如初始 data 和组件方法等。

  • deactivated 和 activated 已经被废弃,而 Suspense 和 Error Boundaries 组件则提供了一种新的处理异步操作和错误的方案,使得我们可以更加灵活地控制组件的渲染和行为。

  • beforeMount 和 mounted 生命周期函数的含义与Vue 2.x 中保持一致。它们在组件挂载前后会分别触发,可以在 beforeMount 钩子内进行一些组件挂载前的准备工作,比如获取初始数据和计算属性等;在 mounted 钩子内可以进行与 DOM 操作相关的逻辑,例如渲染 canvas 或添加事件监听器等工作。

  • beforeUpdate 和 updated 生命周期也保持了与 Vue 2.x 相同的含义,分别表示数据更新前和数据更新后的时刻。在这两个生命周期函数内可以实现与数据更新相关的操作,比如手动调用异步操作。

  • beforeUnmount 和 unmounted 表示组件卸载前和卸载后的时刻。在 beforeUnmount 钩子内可以进行与组件自身相关的清理工作,比如禁用定时器、取消事件监听器等,而在 unmounted 钩子内可以执行与组件自身无关但需要关闭的工作,例如释放占用的内存和其他资源等。

在Vue 3.x 中,生命周期函数的变化主要是为了更好地支持组合和异步操作,使得开发者可以更加灵活地在不同的时刻引入和执行代码。

85、Vue3中自定义指令生命周期的变化及含义

Vue3中自定义指令的生命周期相比于Vue2有所变化,主要包括以下几个:

  • bind 生命周期被重命名为 beforeMount
    在 Vue3 中,bind 生命周期被重命名为 beforeMount,表示指令和组件实例的挂载准备工作已经完成,但是尚未挂载到页面上。

  • update 生命周期被重命名为 mounted
    在 Vue3 中,update 生命周期被重命名为 mounted,表示指令和组件已经挂载到页面上,此时可以访问 DOM 节点。

  • componentUpdated 生命周期被重命名为 updated
    在 Vue3 中,componentUpdated 生命周期被重命名为 updated,表示组件已经更新完毕,可以访问 DOM 节点。

  • unbind 生命周期被重命名为 beforeUnmount
    在 Vue3 中,unbind 生命周期被重命名为 beforeUnmount,表示指令和组件即将被卸载。

这些生命周期的变化主要是为了和组件生命周期保持一致,方便开发者理解和使用自定义指令。

在使用自定义指令时,我们可以根据具体的场景选择不同的生命周期钩子函数来处理指令的功能。比如,在 mounted 钩子函数中可以访问 DOM 节点,可以用来操作 DOM,而在 updated 钩子函数中可以对组件进行更新操作等。

需要注意的是,Vue3 中的自定义指令也可以通过使用 watchEffect 和 watch 等函数来实现响应式数据的更新,这也是 Vue3 中响应式系统的一大特点。

86、Vue3中的组合式Api有哪些? 和Vue2的Options Api又什么不同?

Vue 3.x 中的组合式 API 对比 Vue 2.x 中的 Options API,在语法和使用方式上都有很大的不同。Vue 3.x 中的组合 API 主要包括以下几个部分:

  • reactive 和 ref:通过 reactive 或者 ref 函数对数据进行响应式处理。其中 reactive 主要用于处理复杂对象,而 ref 则用于处理基本数据类型。
  • computed 和 watchEffect:通过 computed 函数对数据进行计算,从而生成新的响应式数据,而 watchEffect 则用于监听数据的变化,并且在数据变化时执行一些操作,比如长链接 WebSocket 通信等。
  • provide 和 inject:通过 provide 和 inject 函数实现跨越组件树的组件通信。父组件通过 provide 函数提供数据,子组件则通过 inject 函数获取数据。
  • 生命周期钩子函数:Vue 3.x 中的组合 API 仍然包含了 beforeMount、mounted、beforeUpdate、updated、beforeUnmount 和 unmounted 等生命周期钩子函数。

相比之下,Vue 2.x 中的 Options API 则通过大量的选项对象实现组件的配置和管理。而 Vue 3.x 中的组合 API 则更加关注组件中动态数据的处理和组件之间的通信,同时其 API 使用起来更加直观和简洁,可以帮助开发者更加容易地编写高效的代码。其最大的优势,恰恰在于使得不再需要关注 runtime-core 的 api 来构建渲染逻辑。

87、 什么是跨域?如何解决跨域问题?

跨域是指在同源策略下,一个网页无法向另一个网页发送请求。同源策略是一种浏览器安全策略,主要限制一个源加载的文档或脚本如何与另一个源进行交互。如果一个源包含的文档或脚本,试图向另一个源的资源发起请求,那么浏览器就会拦截这个请求。

解决跨域问题有多种方式,常见的有以下几种:

  • CORS(跨域资源共享):在服务端设置相应的头部信息,允许跨域访问。需要后端支持。

  • JSONP:利用 script 标签可以跨域请求的特性,将请求封装成 script 请求,服务器返回 JavaScript 脚本,通过回调函数的方式将数据返回到前端。只支持 get 请求。

  • 反向代理:通过 nginx、Apache 等反向代理服务器,将前端请求转发到目标服务器,再将目标服务器返回的数据返回给前端,使得前端代码和目标服务器属于同一域名和端口。需要服务器的配置和管理。

  • WebSocket:建立双向通信通道,通过特定的 API 实现浏览器和服务器之间的实时数据传输。需要服务器的支持。

88、 什么是浮动?如何清除浮动?

浮动(float)是 CSS 中的一种布局方式,其作用是将元素从正常的文档流中移除,并使其向左或向右浮动。通常用来实现文字环绕效果,以及实现多列布局等效果。

浮动元素在文档流中不占据空间,而是从文档流中脱离出来,这也就造成了在某些情况下可能会影响其他元素的布局,比如撑不开父元素、位置异常等问题。为了解决这些问题,我们需要清除浮动。

清除浮动可以使用以下几种方法:

1、Clearfix:在父元素中使用 clearfix 类或者伪元素进行清除浮动。这种方法可以解决标准文档流中很多让人困扰的布局问题。

.clearfix::after {
  content: "";
  display: block;
  clear: both;
} 

2、BFC (块级格式化上下文):当父元素被设置为 BFC 后,就可以自动清除子元素的浮动。这种方法需要额外注意一些细节问题,但是不会像 Clearfix 一样会产生额外内容。

.parent {
  overflow: auto;  /* 或者使用其他触发 BFC 的方法 */
}

3、Grid 布局和 Flex 布局:使用 Grid 布局或 Flex 布局可以不需要考虑 clearfix 和 BFC 的技巧,而是使得正确的布局变得更加容易。

清除浮动的方法都是通过一些额外的元素或属性,来使得当前元素变成一个干净的盒子,可以避免它对后续元素的影响。

89、 请简述HTML5的新特性。

HTML5是HTML的第五个版本,相比于之前的版本,它引入了许多新的特性。以下是HTML5的一些主要特性:

  • 新的语义化元素:HTML5提供了一些新的语义化元素,如

    ,
    ,

  • 多媒体支持:HTML5新增了多媒体相关的元素和属性,如, , , 等,同时还提供了更好的音频和视频控制、跨浏览器支持等功能。

  • 表单增强:HTML5增强了表单元素的功能,新增了一些新的表单元素,如, , 等,同时也增强了表单验证和表单控制的功能。

  • Canvas绘图:HTML5引入了Canvas元素,可以通过JavaScript动态绘制2D和3D图形。

  • 地理定位:HTML5提供了Geolocation API,可以获取用户的地理位置信息。

  • Web存储:HTML5提供了Web Storage API,可以通过JavaScript在客户端本地存储数据。

  • WebSocket通信:HTML5提供了WebSocket API,可以实现基于TCP协议的全双工通信。

  • Web Worker:HTML5引入了Web Worker,可以在后台执行JavaScript任务,避免阻塞页面渲染。

  • 拖放功能:HTML5提供了Drag and Drop API,可以实现拖拽操作。

总的来说,HTML5引入了许多新的特性,使得Web应用开发更加方便和灵活。

90、 请简述CSS3的新特性。

CSS3 是CSS的第三个版本,是CSS技术的重大升级,包含了大量新增的特性和模块,可以更加丰富、灵活地实现网站的样式和布局效果。具体的 CSS3 新特性如下:

  • 新选择器:包括属性选择器、伪类选择器、伪元素选择器等,提供了更加灵活的元素选择方式;
  • 多列布局:可以通过 CSS3 实现多列布局,使得页面的布局更加灵活、美观;
  • 盒模型:CSS3 引入了新的盒模型,使得计算盒子大小的规则更加直观和简洁;
  • 媒体查询:可以针对不同的设备和屏幕大小使用不同的样式;
  • 2D/3D 转换:CSS3 提供了丰富的 2D/3D 变换方式,使得更加丰富的效果可以通过 CSS 实现;
  • 动画效果:CSS3 可以通过 transition 和 animation 属性实现各种动画效果;
  • Flexbox 布局:CSS3 引入了弹性盒子布局,为实现复杂布局提供了更好的支持;
  • Grid 布局:CSS3 提供了基于网格的布局方式,使得多列、多行布局更加高效;
  • 文字阴影和高亮渲染、边框的颜色渐变、背景动画和多重背景图等效果。

总之,CSS3 的新特性大大提高了开发者实现网站样式和布局效果的效率和灵活性,同时也使得页面的视觉效果更加优美和高端。

91、 请描述CSS中的选择器及其优先级

CSS选择器是一种用于选取HTML或XML文档中指定元素的方式。常见的CSS选择器有:

  • 标签选择器(tag selector):通过HTML或XML元素的名称选取元素,如 div、p、a 等。
  • 类选择器(class selector):通过元素的 class 属性选取元素,以点号(.)开头,如 .header、.text 等。
  • ID选择器(ID selector):通过元素的 id 属性选取元素,以井号(#)开头,如 #logo、#nav 等。
  • 属性选择器(attribute selector):通过元素的属性值选取元素,用方括号包含属性名称和可选的值,如 [type=“submit”]、[name=“email”] 等。
  • 伪类选择器(pseudo-class selector):通过元素的状态或位置选取元素,以冒号(:)开头,如 :hover、:nth-child()、:checked 等。
  • 组合选择器(combinator selector):通过不同的选择器组合选取元素,如后代选择器(空格)、直接子元素选择器(>)、相邻兄弟选择器(+)等。

在CSS中,选择器的优先级是由其特定性(specificity)决定的。特定性通常由选择器中包含的各种不同类型的选择器组成,其计算规则为:

  • 对于每个ID属性值,将0、1、0、0添加到特定性中。
  • 对于每个class属性值、属性选择器或伪类,将0、0、1、0添加到特定性中。
  • 对于每个元素和伪元素,将0、0、0、1添加到特定性中。
  • 将计算结果相加以得到选择器的特定性。

如果两个选择器具有相同的特定性,则最后一个选择器的规则将优先应用。此外,通过使用 !important 标记,可以将特定规则提升到最高优先级。

总体来说,选择器的优先级和其在CSS中的位置是密切相关的。通常,更具体和更详细的选择器将具有更高的优先级,因此在编写CSS样式时应该尽可能地使用具体和明确的选择器。

92、 请描述盒子模型的四个属性:margin、padding、border和content。

盒子模型是网页布局和元素定位的重要概念之一,它由四个属性组成:margin、padding、border 和 content。

  • Margin(外边距):指元素与元素之间的距离,它是指定元素与其他元素或者元素外部(父元素或者边框外)之间的空间。Margin 可以通过 margin-top、margin-right、margin-bottom 和 margin-left 四个属性分别定义。

  • Padding(内边距):指元素的内容区域与元素边界之间的距离。Padding 可以通过 padding-top、padding-right、padding-bottom 和 padding-left 四个属性分别定义。

  • Border(边框):指元素的边框大小和边框样式。可以通过 border-width、border-style 和 border-color 这三个属性来分别定义边框的宽度、样式和颜色。也可以直接使用 border 来同时设置三个属性。

  • Content(内容区域):指元素包含的文本或图片等内容,但不包括元素的内边距、外边距和边框。Content 的大小和位置可以通过 width、height 和 text-indent 属性来设置。

这四个属性共同构成了一个盒子,盒子的大小和位置受到它们的影响。在计算盒子的总大小时,浏览器默认将 content 大小、padding 大小、border 大小和 margin 大小相加。这个默认的盒子模型也叫传统盒子模型(box-sizing: content-box)。

除了传统盒子模型,CSS3 还定义了另一种盒子模型:border-box。这种盒子模型将 border 和 padding 包含在盒子内部,而不是在盒子外部计算。这样,content 的大小就不会受到其他属性的影响了,可以更加方便地进行布局计算。

93、如何处理移动端的适配问题?

处理移动端适配问题是前端开发中一个比较重要的问题,主要是针对不同的设备和屏幕大小,使得网页能够适配所有的移动设备。下面是几种处理移动端适配问题的方法:

  • 使用响应式布局:即利用 CSS3 中的 Media Query 和 百分比 布局、弹性布局(flexbox)等技术,在不同设备屏幕大小下选择不同的样式。使用响应式布局可以适应各种屏幕大小,但对网页的整体结构和内容重新排版成本较高。

  • 使用 viewport:viewport 是指浏览器显示网页内容的区域,使用 viewport 可以根据不同设备的屏幕大小动态调整网页的显示比例。可以通过设置 viewport 的 meta 标签来控制网页在不同设备上的显示比例,例如:

  • 使用 rem 布局:rem 是相对于根元素(即 html 标签)的字体大小来计算的,使用 rem 布局可以根据设备屏幕大小动态计算字体、行高、模块大小等属性,实现移动端自适应。可以通过设置根元素的字体大小来调整整个页面的大小。

  • 使用 flexible.js 或者 postcss-px-to-viewport 等第三方库:这些库可以将 px 单位自动转换成 vw、vh、rem 等单位,方便在移动端上使用,同时支持更多的细节处理,提高了开发效率。

以上是常见的移动端适配方法,可以根据不同的需求和情况选择合适的方案。

94、 请描述常见的HTTP请求方法。

常见的HTTP请求方法包括:

  • GET:获取指定资源或者文档,可以附带请求参数在URL中,请求参数会受到浏览器缓存的影响;
  • POST:向指定资源或者文档提交数据,比如表单提交、文件上传等,请求参数在请求体中,不会受到浏览器缓存的影响;
  • PUT:更新指定资源或者文档,请求参数在请求体中,请求时要携带更新后的完整数据,如果只更新部分数据,则需要使用 PATCH 方法;
  • DELETE:删除指定资源或者文档;
  • HEAD:获取资源的元数据,类似于 GET 方法,但是只返回响应头部信息,用于获取资源的基本信息,比如资源大小、类型、修改时间等,而不需要获取完整的响应体;
  • OPTIONS:获取目标资源所支持的通信选项,比如支持的请求方法、请求头部等。

这些HTTP请求方法可以通过 AJAX、Fetch、XMLHttpRequest等工具实现发送和接收HTTP请求。

95、 什么是闭包?请举例说明。

闭包(Closure)是指一个函数访问了它外部的变量,即使在这个外部变量已经销毁的情况下,该函数仍然可以访问这个变量。闭包在 JavaScript 中非常常见,几乎所有的 JavaScript 开发者都需要理解和使用它。

以下是一个简单的闭包示例:

function outer() {
  var a = 10;
  function inner() {
    console.log(a);
  }
  return inner;
}

var innerFn = outer();
innerFn(); // 输出 10

在这个例子中,inner 函数引用了 outer 函数中定义的变量 a。在 outer 函数执行完毕以后,变量 a 和函数 inner 都应该已经被销毁了,但是 inner 函数仍然可以访问变量 a,因为 inner 函数形成了一个闭包,保留了对变量 a 的引用。这就是闭包的基本概念。

闭包在 JavaScript 开发中有很多用途,比如可以用来实现私有变量和函数、实现函数式编程、事件处理等等。在实际开发中需要注意闭包的内存泄漏问题,由于闭包会引用外部变量,如果不注意适当释放资源,可能会导致内存泄漏和性能问题。

96、 什么是原型链?如何实现继承?

原型链是JavaScript中对象之间的一种关系,每个对象都有一个指向其原型的引用,通过这个引用可以访问原型对象的属性和方法。如果在对象上访问一个属性或方法时,它先在自身属性中查找,如果没有找到,则沿着原型链向上查找直到找到为止。

实现继承的方式有很多种,其中比较常用的有以下几种:

  • 1、原型链继承:通过将子类的原型指向父类的实例来实现继承。缺点是父类中的引用类型属性会被所有子类实例共享,容易造成数据混乱。
function Parent() {
  this.name = 'parent';
}
Parent.prototype.sayHello = function() {
  console.log('hello');
}

function Child() {}
Child.prototype = new Parent();

  • 2、构造函数继承:通过在子类构造函数中调用父类构造函数来实现继承,可以避免引用类型属性被共享的问题。缺点是父类中的方法无法复用,每个子类都会创建一份副本。
function Parent() {
  this.name = 'parent';
}

function Child() {
  Parent.call(this);
}
  • 3、组合继承:通过将原型链继承和构造函数继承结合起来,既可以实现方法的复用,又可以避免引用类型属性被共享的问题。
function Parent() {
  this.name = 'parent';
}
Parent.prototype.sayHello = function() {
  console.log('hello');
}

function Child() {
  Parent.call(this);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

  • 4、原型式继承:通过Object.create()方法来实现继承。该方法会创建一个新对象,并将其原型指向传入的对象。缺点是无法传递参数。
var parent = {
  name: 'parent',
  sayHello: function() {
    console.log('hello');
  }
};

var child = Object.create(parent);

  • 5、寄生式继承:通过在原型式继承的基础上增强新对象的属性和方法,然后返回新对象。
function createChild(parent) {
  var child = Object.create(parent);
  child.sayHi = function() {
    console.log('hi');
  };
  return child;
}

var parent = {
  name: 'parent',
  sayHello: function() {
    console.log('hello');
  }
};

var child = createChild(parent);

97、 请描述CSS中的position属性及其取值。

CSS 中的 position 属性用来确定一个元素在页面中的定位方式,可以控制元素的布局和渲染位置。position 常用的取值有以下几种:

  • static:默认值,元素在正常文档流中的位置,不受 top、bottom、left、right 属性控制。

  • relative:相对定位,元素相对于原来的位置通过 top、bottom、left、right 属性进行偏移,不会改变元素的布局位置,周围元素不会重新排布。

  • absolute:绝对定位,元素相对于父元素(如果父元素没有设置 position 属性,则相对于 body 元素)进行定位,通过 top、bottom、left、right 属性进行偏移。元素完全脱离文档流,周围元素会重新排布。

  • fixed:固定定位,元素相对于浏览器窗口进行定位,通过 top、bottom、left、right 属性进行偏移。元素完全脱离文档流,不随页面滚动而滚动,并且不会改变元素的布局位置。

  • sticky:粘性定位,在元素在容器中的位置不超出指定区域时,表现为相对定位,超出指定区域时表现为固定定位。

需要注意的是,使用 position 属性定位的元素,如果不设置宽和高,则其大小会根据内容撑开,如果宽和高都设置为 0,则在页面中不会显示该元素。同时,设置一个元素的 position 为 absolute 或 fixed 后,该元素的 z-index 属性会变为相对于其容器的 z-index 实现层叠效果,而不再是相对于父元素的 z-index 属性。

98、请描述CSS中的display属性及其取值。

CSS 中的 display 属性用于控制元素的布局和渲染方式,常用的取值有以下几种:

  • block:块级元素,元素会独占一行显示,上下都有一定的间距。宽度默认为 100%,高度默认由内容撑开。

  • inline:行内元素,元素不会独占一行,只占据内容所需的宽度空间。不可设置宽高,margin-top、margin-bottom、padding-top、padding-bottom 属性也不会生效。

  • inline-block:行内块级元素,元素既可以像块级元素一样设置宽高,也可以像行内元素一样设置上下、左右间距,常用于实现多列布局。

  • none:元素不会在页面中显示,并且不占据空间。

  • flex:弹性盒子布局,常用于实现分布对齐、自适应等效果。

  • grid:网格布局,常用于实现复杂的多列布局和对齐效果。

display 属性还有很多其他取值,比如 table、table-cell 等等,值得注意的是,不同的取值会影响元素的布局、渲染和盒模型属性,需要根据具体场景选择合适的取值。

99、请描述CSS中的float属性及其取值。

CSS 中的 float 属性可以设置一个元素浮动在其父元素的左侧或右侧,并在文本流中腾出相应的空间,常用于实现多列布局、图文混排、清除浮动等效果。float 常用的取值有以下几种:

  • left:元素向左浮动,其他内容根据其位置重新排布,如有需要可以设置 clear 属性清除浮动。

  • right:元素向右浮动,其他内容根据其位置重新排布,如有需要可以设置 clear 属性清除浮动。

  • none:默认值,元素不浮动,不影响其他元素的排列。

  • inherit:继承父元素的 float 值。

需要注意的是,设置 float 属性后,该元素会脱离文档流,周围元素重新排布。同时,float 属性只对块级元素和部分其他元素(如 img 元素)生效,行内元素应用 float 属性会自动变为行内块元素。另外,多个浮动元素在一起时,可能会出现问题,需要进行清除浮动(如设置 clear 属性)才能实现预期效果。

100、什么是BFC?如何触发BFC?

BFC(块级格式化上下文)是一个独立的渲染区域,具有一定的特性,如可以包含浮动元素、防止外边距合并等。BFC中的元素按照一定的规则进行排列布局。

触发BFC的方式有以下几种:

  • 根元素或包含根元素的元素
  • 浮动元素(元素的 float 不为 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed)
  • 行内块元素(元素的 display 为 inline-block)
  • 表格单元格(元素的 display 为 table-cell,HTML表格单元格默认为该值)
  • 表格标题(元素的 display 为 table-caption,HTML表格标题默认为该值)
  • overflow 值不为 visible 的块元素
  • display 值为 flow-root 的元素
  • contain 值为 layout、content或 paint 的元素
  • 弹性元素(display为flex或inline-flex的元素的直接子元素)

BFC的作用有:

  • 清除浮动:父级元素设置 BFC 后,可以包含浮动元素,不会影响到外部元素。
  • 防止边距重叠:同一BFC内,两个相邻元素的 margin 会发生重叠,可以通过触发BFC来解决。
  • 解决宽度塌陷问题:当父元素没有设置宽度或者为 auto,子元素使用 float 时,父元素会发生宽度塌陷。可以通过触发BFC来解决。

101、请描述JS中的数据类型及其判断方法。

JavaScript 中的数据类型包括基本数据类型和引用数据类型,其中基本数据类型有 5 种,分别是:

  • number:表示数字类型,包括整数和浮点数。

  • string:表示字符串类型,一般用单引号或双引号括起来。

  • boolean:表示布尔类型,只有两个值,true 和 false。

  • undefined:表示未定义的值,表示变量声明但未赋值时的默认值。

  • null:表示空值,表示变量赋值为 null 时的特殊值。

而引用数据类型包括对象、数组、函数等。

在 JavaScript 中,可以使用 typeof 运算符来判断变量的类型。其使用方法为:typeof 变量名,返回一个表示数据类型的字符串,常用的有以下几种:

  • “number”:表示数字类型。

  • “string”:表示字符串类型。

  • “boolean”:表示布尔类型

  • “undefined”:表示未定义类型。

  • “object”:表示引用数据类型,包括对象、数组等。

  • “function”:表示函数类型。

需要注意的是,typeof 运算符通常不能准确地判断一个变量是否为 null,因为它会将 null 错误的判断为 “object” 类型。

另外,还有一些判断数据类型的方法,如 instanceof、Object.prototype.toString.call() 等,这些方法更加准确,可以处理 null、Date、正则表达式等情况,但基本数据类型的判断不如 typeof 运算符简洁明了。

102、请描述JS中的作用域及其作用域链。

JavaScript中的作用域是指变量、函数等在程序中被访问的范围。JS采用的是词法作用域,即代码的作用域在定义时就已经确定了。作用域链是指变量在代码中查找的过程,JavaScript引擎在查找变量时会先在当前作用域内查找,如果没有找到,就会逐级向上查找直到全局作用域。

JavaScript中的作用域有全局作用域和局部作用域。在函数内部定义的变量,只在该函数内部可见,称为局部变量;在函数外定义的变量,称为全局变量,在整个程序中都可以访问。

作用域链是由当前执行环境的变量对象和上一级执行环境的变量对象构成,这些变量对象有序地排列在一个链表结构中。当执行环境需要访问一个变量时,会首先在当前的变量对象中查找,如果没有找到,则会到上一级的变量对象中查找,直到找到该变量或者到达全局作用域。如果在全局作用域中也没有找到该变量,则会抛出“未定义”错误。

作用域和作用域链的概念在理解JavaScript中的变量、函数、闭包等重要概念时非常重要。

103、什么是异步编程?请举例说明。

异步编程是一种编程方式,通过使用异步操作,实现程序在等待长时间的操作(如网络请求、文件读写、计算等)时不会发生阻塞,仍可以执行其他操作,避免程序长时间等待造成用户体验不佳的问题。

异步编程的主要方式是利用回调函数、Promise 和 async/await 等机制,例如在 JavaScript 中,可以使用 setTimeout/setInterval 来实现异步操作,以 setTimeout 为例,可以通过以下方式实现异步编程:

console.log("start");

setTimeout(function () {
   console.log("delayed");
}, 2000);

console.log("end");

在上面的例子中,setTimeout 会在 2 秒后执行回调函数,但它不会阻塞程序的执行,而是在执行 setTimeout 的同时,继续执行后面的代码,输出的结果是:

start
end
delayed

可以看到,在执行 setTimeout 的 2 秒等待期间,程序继续执行后面的代码,直到等待时间结束后再执行回调函数。这样就实现了异步编程,提高了程序的效率和用户的体验。

104、什么是事件委托?有何优缺点?

事件委托(Event delegation)是一种常见的JavaScript编程技巧,它利用了事件冒泡的特性,将事件处理器绑定到父元素而不是子元素上,从而实现对子元素的事件响应。

事件委托的优点有:

减少DOM操作次数,提高性能。因为事件委托只需要绑定一个事件处理器到父元素上,不需要为每个子元素都绑定事件处理器,减少了DOM操作次数,提高了性能。

动态添加的元素也能响应事件。由于事件处理器绑定在父元素上,所以子元素动态添加时也能响应事件,而不需要为其单独绑定事件处理器。

简化代码。如果一个父元素下有多个子元素需要响应相同的事件,使用事件委托可以简化代码,避免代码重复。

事件委托的缺点有:

事件处理器可能会被误触发。由于事件委托的机制是利用事件冒泡,所以当子元素的事件处理器返回false时,可能会阻止父元素的其他事件处理器的执行,从而产生误触发的情况。

事件委托对事件对象的处理不方便。由于事件处理器是绑定在父元素上的,所以当需要获取事件对象时,需要从事件的target属性中获取,不太方便。

总的来说,事件委托是一种优秀的编程技巧,可以提高性能、简化代码,但是需要注意误触发和事件对象处理的问题。

105、请描述ES6的箭头函数及其使用方法。

ES6 引入的箭头函数是一种新的函数表达式,它的语法更加简洁,可以使 JavaScript 代码更加精简可读。下面来详细介绍箭头函数及其使用方法。

箭头函数的语法格式为:

(参数1,参数2,…,参数n) => { 函数体 }

箭头函数的参数部分和函数体部分分别由“()”和“{}”包围,中间用“=>”连接,其参数可以有一个或多个,也可以没有参数。如果只有一个参数时,可以省略“()”,如果函数体只有一条语句,则可以省略“{}”和返回值。

下面是一些使用箭头函数的例子:

1、声明一个接受两个参数并返回它们的和的函数:

let add = (a, b) => { return a + b; };
console.log(add(2, 3)); // 输出 5

2、声明一个接受一个参数的函数,返回该参数的平方值:

let square = x => x * x;
console.log(square(5)); // 输出 25

3、在 Array.prototype.map() 方法中使用箭头函数:

let arr = [1, 2, 3, 4];
let squareArr = arr.map(x => x * x);
console.log(squareArr); // 输出 [1, 4, 9, 16]

需要注意的是,在箭头函数中,this 绑定的是定义时所在的对象,而不是使用时所在的对象,这个特性使得箭头函数更加方便。但同时,箭头函数也有一些限制,比如不能作为构造函数使用,不能使用 arguments 对象等。

106、请描述ES6的模板字符串及其使用方法。

ES6中的模板字符串是一种特殊的字符串类型,用反引号(`)括起来,可以在其中嵌入变量或表达式,而无需使用字符串拼接或转义字符。模板字符串支持多行字符串,并且可以包含变量、表达式和函数调用,使得字符串的拼接和格式化更加方便和直观。

模板字符串的使用方法如下:

  • 使用反引号()括起来字符串,可以包含变量和表达式,变量使用${}`括起来。
  • 支持多行字符串,不需要使用\n换行符。
  • 支持嵌套使用,可以方便地拼接字符串。
  • 可以调用函数,实现字符串的格式化输出。

例如,下面的代码演示了如何使用模板字符串:

let name = 'Tom';
let age = 18;
let str = `My name is ${name}, I'm ${age} years old.`;
console.log(str);  // 输出:My name is Tom, I'm 18 years old.

模板字符串的优点包括:

  • 方便的字符串拼接和格式化。
  • 支持多行字符串,可读性更好。
  • 更加直观和易于理解。

模板字符串的缺点包括:

由于需要使用反引号,可能会与一些编辑器和IDE的语法高亮产生冲突。
模板字符串相对于普通字符串稍微有些慢,但这个性能损失通常可以忽略不计。

107、请描述ES6的let和const关键字及其作用。

ES6 中新增了两个关键字 let 和 const,用来声明变量和常量。它们相对于传统的 var 声明变量的方式,有一些特殊的使用规则和作用。

let 关键字:

声明的变量具有块级作用域,可以限制变量的作用范围,避免变量污染全局作用域;

不允许重复声明同一个变量。

const 关键字:

声明的是常量,一旦被赋值就不能再改变;

声明的变量也具有块级作用域;

不允许重复声明同一个常量
使用 let 和 const 的示例:

{
  let a = 1;
  const b = 2;
}
console.log(a); // 报错:a is not defined
console.log(b); // 报错:b is not defined

let c = 3;
console.log(c); // 输出 3

c = 4;
console.log(c); // 输出 4

let c = 5;     // 报错:Identifier 'c' has already been declared

const d = 6;
console.log(d); // 输出 6

d = 7;          // 报错:Assignment to constant variable.

const d = 8;    // 报错:Identifier 'd' has already been declared

需要注意的是,const 声明的常量并不是绝对不能变,如果常量是对象类型,其指向的地址不可以变,但是属性值可以改变。 大多数情况下,建议优先使用 const 声明常量,只有在确实需要修改变量值时才使用 let。在开发过程中,利用块级作用域可以更好地管理和维护代码。

108、请描述ES6的解构赋值及其使用方法。

ES6中的解构赋值是一种方便的语法,可以从数组或对象中快速提取数据并赋值给变量。其基本语法为使用花括号包裹的变量名来表示要提取的属性或元素。

数组解构赋值的语法如下:


let [a, b, c] = [1, 2, 3];

对象解构赋值的语法如下:

let {name, age} = {name: 'Alice', age: 25};

使用解构赋值可以快速地将多个变量赋值给多个变量,或者将对象的属性赋值给变量,使代码更简洁易读。

在使用解构赋值时,可以使用默认值来为变量赋初值。例如:

let [a=1, b=2, c=3] = [4];

在上面的例子中,由于数组中只有一个元素,变量a会被赋值为4,而变量b和c则会使用默认值2和3。

解构赋值还可以用来交换变量的值。例如:

let a = 1, b = 2;
[a, b] = [b, a];

上面的代码将变量a和b的值交换了。

109、什么是webpack?如何使用webpack打包项目?

Webpack 是一款开放源代码的前端资源模块化管理和打包工具,它可以将各种不同文件类型(图片、样式、脚本等)按照特定的规则打包成最终的可用于浏览器加载的静态资源文件。Webpack 是目前比较流行的构建工具之一,其主要特点有:

  • 支持模块化,可以将多个模块打包成单个文件,以便在浏览器中使用;

  • 支持打包 ES6、JSX、CSS、图片等多种文件类型;

  • 支持开发和生产环境两种不同的构建方式;

  • 支持插件扩展,可以根据不同的需求进行自定义配置。

下面以一个简单的项目为例,介绍如何使用 Webpack 打包:

1、安装 webpack 和 webpack-cli:

npm install webpack webpack-cli --save-dev

2、配置 webpack.config.js 文件:

module.exports = {
  entry: './src/index.js',  // 入口文件
  output: {                 // 输出文件
    filename: 'bundle.js',
    path: __dirname + '/dist',
  },
  module: {                 // 处理不同类型的资源
    rules: [
      // 处理 CSS 文件
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      // 处理图片文件
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: ['file-loader'],
      },
    ],
  },
};

3、编写项目代码,并在代码中引入样式或图片等资源:

// index.js 文件
import './style.css';
import logo from './logo.png';

// 在代码中使用图片
const img = new Image();
img.src = logo;
document.body.appendChild(img);'

打包项目:

webpack --config webpack.config.js

查看生成的 bundle.js 文件,并在浏览器中加载打包后的文件,从而完成项目的构建和打包。
以上是基本的 webpack 使用方式的介绍,具体使用方法还需针对实际项目进行具体的配置和使用。

110、Vue中组件和插件有什么区别?

在Vue中,组件和插件是不同的概念。

组件是Vue应用中的基本构建块,它将一个页面分解为多个独立的、可复用的模块。组件可以有自己的状态、方法、事件和生命周期钩子。在Vue中,组件使用单文件组件(.vue文件)进行定义和编写。

而插件则是一些可以扩展Vue功能的库,它们通常为Vue提供额外的功能、工具或者全局的组件。插件可以是第三方库,也可以是自己编写的代码。Vue中的插件通过Vue.use()方法进行安装和使用。

总的来说,组件和插件都可以扩展Vue的功能和功能模块,但它们的使用方式和使用场景不同。组件主要用于构建页面,插件主要用于提供额外的功能和工具

111、什么是防抖和节流,有什么区别?如何实现?

防抖和节流都是前端开发中常用的性能优化技巧,用于减少一些高频率触发的事件处理函数的执行次数,从而提高页面的响应速度和性能。

防抖(Debounce)是指在一定时间内连续触发某个事件时,只执行最后一次触发的事件。例如在搜索框中连续输入文字时,我们希望只在用户停止输入一段时间后才触发搜索操作,这就需要使用防抖技术。防抖的实现方式通常是使用 setTimeout 函数延迟执行事件处理函数,每次触发事件时清除之前设置的延时,重新设置一个新的延时。

节流(Throttle)是指在一定时间内最多只能触发一次某个事件。例如在页面滚动时,我们希望只在用户停止滚动一段时间后才触发处理函数,而不是每次滚动都触发处理函数,这就需要使用节流技术。节流的实现方式通常是使用一个时间戳记录上次触发事件的时间,每次触发事件时判断是否超过设定的时间间隔,如果超过就执行事件处理函数,否则忽略本次事件。

区别:防抖和节流都可以控制函数被执行的频率,但防抖是在连续触发事件后只执行最后一次事件,而节流是在一定时间内最多只执行一次事件。因此,防抖适用于连续触发频率高的事件(如搜索框输入),而节流适用于高频触发但只需要执行一次的事件(如页面滚动)。

以下是防抖和节流的示例实现代码:

// 防抖实现
function debounce(func, delay) {
  let timeoutId;
  return function() {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(func, delay);
  };
}

// 节流实现
function throttle(func, delay) {
  let lastExecTime = 0;
  return function() {
    const now = Date.now();
    if (now - lastExecTime > delay) {
      func();
      lastExecTime = now;
    }
  };
}

112、什么是事件代理

事件代理(Event Delegation)是一种优化事件处理程序的技术,在处理大量相似元素的事件时特别有用。它的基本思想是将事件处理程序绑定到它们的祖先元素(如父元素或更高级别的元素),而不是绑定到每个子元素上。然后,当子元素触发事件时,事件会冒泡到祖先元素,并且由祖先元素上绑定的事件处理程序来处理事件。在处理大量相似元素的事件时,这样可以减少事件处理程序的数量,并且避免在每个元素上绑定事件处理程序的麻烦。

例如,我们有一个列表,其中每个元素都有一个点击事件处理程序。如果我们在每个元素上绑定事件处理程序,当列表中的元素数量变得很多时,就会产生大量事件处理程序,这可能会影响性能。相反,我们可以将事件处理程序绑定到列表的父元素上,并根据目标元素(即点击的元素)来确定执行何种操作。这样,无论列表中有多少元素,我们都只需要一个事件处理程序来处理它们的点击事件,从而提高了性能和可维护性。

以下是一个使用事件代理的示例代码:

// HTML 代码
<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
  <li>Item 5</li>
</ul>

// JavaScript 代码
const list = document.getElementById('list');

list.addEventListener('click', function(event) {
  if (event.target.nodeName === 'LI') {
    console.log(`Clicked on ${event.target.textContent}`);
  }
});

在上面的示例中,我们将 click 事件处理程序绑定到了列表的父元素上,然后根据目标元素(即 event.target)来判断点击事件的来源是不是列表项元素(即 LI 元素)。如果是,则输出被点击的列表项的文本内容。这样,无论列表中有多少元素,我们都只需要一个事件处理程序来处理它们的点击事件。

113、ES5、ES6和ES2015有什么区别?

ES5 是 ECMAScript 的第5个版本,发布于2009年。它引入了许多新功能,如严格模式、数组迭代方法、JSON 对象和函数绑定等。ES5 是当前广泛支持的 JavaScript 版本之一,因为它具有良好的兼容性。

ES6 是 ECMAScript 的第6个版本,发布于2015年。它引入了大量新功能,如箭头函数、解构赋值、类、模板字面量、Promise 等。ES6 是一种革命性的 JavaScript 版本,引入了许多被广泛使用的特性,大大提高了 JavaScript 开发的效率。

ES2015 实际上就是 ES6 的另一个名称,因为它在 2015 年发布。ES2015 与 ES6 是同一个版本,它引入了 ES6 的所有新功能和语法。

因此,ES5 是一个相对较旧的版本,而 ES6/ES2015 是一个更现代、更功能丰富的版本。虽然 ES6/ES2015 的新特性在许多浏览器中仍不完全支持,但在现代前端开发中,ES6/ES2015 已经成为事实上的标准。

114、babel是什么,有什么作用?

Babel是一个广泛使用的 JavaScript 编译器,它的主要作用是将现代的 JavaScript 代码转换为向后兼容的代码,以便在旧版的浏览器或环境中运行。Babel 可以将 ECMAScript 2015+ (ES6+) 的代码转换为 ES5 代码,包括一些新的语法、API、和内置函数等。在转换的过程中,Babel 会识别出哪些语法特性需要转换,然后根据一系列插件和预设将代码转换成低版本的 JavaScript 代码。

除了语法转换外,Babel 还有一些插件可以优化代码,如去除冗余代码、缩短变量名、静态代码分析等。这些优化可以提高代码的性能和可读性。

Babel 可以在 Node.js 环境中使用,也可以在浏览器中使用。在开发过程中,Babel 经常和其他工具一起使用,如 webpack、Rollup 等打包工具,以便将转换后的代码打包到单个文件中,减少文件大小和网络传输时间。

总之,Babel 可以让我们使用最新的 JavaScript 语法和特性来编写代码,同时保持代码的向后兼容性,让我们的代码可以在更广泛的环境中运行。

115、举一些ES6对String字符串类型做的常用升级优化?

ES6 对 String 字符串类型进行了许多升级和优化,下面是一些常用的:

  • 模板字符串:使用反引号(`)和 ${} 符号,可以方便地拼接字符串和变量,可以替代传统的字符串拼接方式,让代码更加简洁易读。

  • 字符串扩展方法:ES6 为字符串类型添加了一些常用的方法,如 startsWith()、endsWith()、includes()、repeat() 等,这些方法可以方便地操作字符串,提高了开发效率。

  • Unicode 字符支持:ES6 支持 Unicode 字符集,可以使用 \u{…} 的语法来表示一个 Unicode 字符,可以支持更多的字符集。

  • 新的正则表达式功能:ES6 为正则表达式添加了一些新的功能,如 s 标志(可以让 . 匹配任意字符,包括换行符)、y 标志(可以匹配从上一次匹配成功的位置开始匹配)、u 标志(支持 Unicode 匹配)等。

  • 字符串解构赋值:ES6 允许我们对字符串进行解构赋值,这样可以方便地从字符串中提取出需要的部分,如 const [a, b, c] = ‘abc’。

这些升级和优化都让 String 字符串类型更加强大和方便,让我们在开发中更加高效地操作字符串。

116、举一些ES6对Array数组类型做的常用升级优化

ES6 对 Array 数组类型进行了许多升级和优化,下面是一些常用的:

  • 新的迭代方法:ES6 添加了一些新的数组迭代方法,如 forEach()、map()、filter()、reduce() 等,可以让我们更方便地遍历和操作数组。

  • 扩展运算符:ES6 添加了扩展运算符(…)语法,可以将数组展开为一组参数,或者将一组参数合并为一个数组,让数组的操作更加灵活。

  • 数组解构赋值:ES6 允许我们对数组进行解构赋值,这样可以方便地从数组中提取出需要的部分,如 const [a, b, c] = [1, 2, 3]。

  • Array.from() 方法:ES6 提供了一个 Array.from() 方法,可以将类数组对象或可迭代对象转换为真正的数组,方便我们处理各种数据结构。

  • 新的排序方法:ES6 为数组添加了新的排序方法,如 sort()、reverse() 等,可以让我们更方便地对数组进行排序和翻转。

这些升级和优化让 Array 数组类型更加强大和方便,让我们在开发中更加高效地操作数组。

117、什么是Vue SSR

Vue SSR(Server-Side Rendering)是指在服务端将 Vue 组件渲染成 HTML,然后将 HTML 发送给客户端,客户端接收到 HTML 后进行渲染,最终呈现出页面。SSR 可以提高首屏渲染速度、SEO 优化和用户体验,因为客户端在接收到 HTML 后不需要再进行渲染,而是直接展示页面。

在 Vue 中,可以通过使用 Vue SSR 来进行服务器端渲染。Vue SSR 使用了一些 Node.js 的特性,如 stream、http、fs 等,可以在 Node.js 环境中将 Vue 组件渲染为 HTML。在客户端,Vue SSR 只需要将服务端返回的 HTML 进行 hydration(注水)即可,即将服务端渲染的静态 HTML 转换为可以响应用户事件和交互的动态 HTML。

使用 Vue SSR 可以提高网站的性能和用户体验,特别是对于大型网站和需要高度优化的页面。SSR 还可以提高网站的 SEO(搜索引擎优化)效果,因为搜索引擎可以直接抓取到渲染后的页面,而不是等待客户端渲染完成后再抓取页面。

118、请列举出3个Vue中常用的生命周期钩子函数?

Vue.js 组件有许多生命周期钩子函数,这些钩子函数可以让我们在组件不同的生命周期阶段进行相应的操作,下面是 Vue.js 中常用的三个生命周期钩子函数:

  • created:组件实例创建后立即调用该钩子函数,此时可以进行数据初始化和操作,但组件 DOM 还未渲染,无法访问 DOM 元素。

  • mounted:组件挂载后调用该钩子函数,此时组件 DOM 已经渲染完毕,可以访问 DOM 元素,也可以进行异步操作,如请求数据。

  • updated:组件更新完成后调用该钩子函数,此时 DOM 已经重新渲染完成,可以进行一些操作,如更新一些依赖 DOM 的属性或方法。

这三个生命周期钩子函数都是在组件不同的生命周期阶段调用的,可以让我们在合适的时机进行相应的操作,使得组件更加灵活和高效。

119、Vue的路由实现:hash模式 和 history模式原理

Vue 的路由实现有两种模式:hash 模式和 history 模式。

Hash 模式:
Hash 模式是通过改变 URL 中的 hash(#)来实现路由的。比如,当访问的 URL 是 http://localhost:8000/#/home 时,就会渲染出 Home 组件。

在 Hash 模式下,Vue 会监听 window 对象的 hashchange 事件,一旦 URL 中的 hash 发生改变,就会重新渲染对应的组件。因为 hash 的改变不会触发浏览器向服务器发送请求,所以 Hash 模式的路由是纯客户端实现的,可以在不需要服务器支持的情况下进行部署。

History 模式:
History 模式是通过改变 URL 中的 path 来实现路由的。比如,当访问的 URL 是 http://localhost:8000/home 时,就会渲染出 Home 组件。

在 History 模式下,Vue 会监听 window 对象的 popstate 事件,一旦 URL 中的 path 发生改变,就会重新渲染对应的组件。因为 path 的改变会触发浏览器向服务器发送请求,所以 History 模式的路由需要服务器支持,即需要在服务器端配置一个捕获所有路径的候选资源,然后在所有资源返回 404 的情况下返回 index.html 页面,从而实现路由。

总的来说,Hash 模式和 History 模式的实现原理都是通过监听 URL 的变化来实现路由的。两者的区别在于 URL 中的标识符不同,以及是否需要服务器支持。Hash 模式的路由是纯客户端实现的,可以在不需要服务器支持的情况下进行部署;而 History 模式的路由需要服务器支持,需要在服务器端配置一个捕获所有路径的候选资源,并返回 index.html 页面,从而实现路由。

120、对MVVM的理解

MVVM 是 Model-View-ViewModel 的缩写,是一种用于构建用户界面的软件架构模式。它是 MVC 模式和 MVP 模式的演化,适用于现代化的 Web 开发。

MVVM 模式的核心思想是数据绑定,即将视图和数据通过双向绑定的方式进行关联,当数据发生变化时,视图会自动更新;当用户操作视图时,数据也会自动更新。在 MVVM 模式中,View 是用户界面,ViewModel 是视图的抽象,Model 是数据和业务逻辑。

MVVM 模式的优点:

  • 松耦合:MVVM 模式将 View 和 ViewModel 解耦,使得 View 和 ViewModel 可以分别进行单独的开发和测试。

  • 双向绑定:MVVM 模式使用双向数据绑定机制,使得数据和视图之间的关系更加紧密,可以更快速地进行开发。

  • 可维护性:MVVM 模式将业务逻辑和数据处理分离,使得代码更加清晰、可维护,也更加容易进行扩展和重构。

MVVM 模式的缺点:

  • 学习成本:由于 MVVM 模式需要掌握大量的框架和库,所以学习成本相对较高。

  • 性能问题:由于双向绑定机制的存在,MVVM 模式可能会带来一些性能问题,比如频繁的 UI 更新会影响页面的渲染效率。

总的来说,MVVM 模式是一种非常实用的软件架构模式,可以使得前端开发更加高效和可维护。

121、Vue组件间的数据传递方式有哪些

Vue 组件间的数据传递方式有以下几种:

1. Props:

Props 是组件之间传递数据最常用的方式,父组件通过 props 给子组件传递数据,子组件通过 props 来接收数据。

2. 自定义事件:

在子组件中,可以通过 $emit 方法触发自定义事件,父组件通过在子组件上使用 @ 监听该事件来接收数据。

3. Vuex:

Vuex 是 Vue 的状态管理库,用于管理应用程序的状态,可以在多个组件之间共享数据。

4. $attrs 和 $listeners:

$attrs 和 $listeners 是 Vue 提供的内置属性,用于在父子组件之间进行非 props 和非事件的数据传递,可以用于高阶组件开发。

5. Provide 和 Inject:

Provide 和 Inject 是 Vue 提供的一种高级的依赖注入机制,可以让祖先组件向子孙组件注入依赖项。

总的来说,Vue 组件间的数据传递方式非常灵活,可以根据具体的场景来选择合适的方式。在实际开发中,我们通常会根据数据的层次结构、数据的复杂度、组件之间的关系等因素来决定使用哪种数据传递方式。

122、v-if 和 v-show 区别

在 Vue 中,v-if 和 v-show 都是用于控制元素是否显示的指令,它们的作用相同,但是它们的实现方式不同,因此在某些情况下使用其中一个指令比另一个更加合适。

v-if 的工作原理是根据条件来动态地添加或移除元素,如果条件为 false,则该元素将被移除,不会在 DOM 树中保留;如果条件为 true,则该元素将被添加到 DOM 树中。因此,v-if 在切换频率较低的情况下使用更加合适,因为每次切换都会重新渲染整个元素,这可能会导致性能问题。

v-show 的工作原理是使用 CSS 的 display 属性来切换元素的可见性,如果条件为 false,则元素的 display 属性将被设置为 none;如果条件为 true,则元素的 display 属性将被设置为原来的值。因此,v-show 在切换频率较高的情况下使用更加合适,因为它不会重新渲染整个元素,只会改变元素的可见性,这可以提高性能。

总的来说,v-if 适用于切换频率较低的情况下,而 v-show 适用于切换频率较高的情况下。同时,v-if 可以在模板中使用,而 v-show 可以在组件中使用。在实际开发中,我们需要根据具体的场景来选择使用哪种指令。

123、$route和$router的区别

在 Vue 中,$route 和 $router 都是 Vue-Router 提供的对象,用于实现路由功能,它们的作用不同,具体区别如下:

  1. $route 对象包含当前路由信息,可以访问当前路由的路径、参数、查询参数等信息,是只读的,不能修改。

  2. $router 对象是路由实例,包含了整个应用程序的路由信息,可以访问路由实例的方法,如 push、replace、go 等,用于动态地跳转到不同的路由。

因此,$route 主要用于访问当前路由信息,而 $router 主要用于动态地跳转到不同的路由。在实际开发中,我们可以根据需要使用 $route 和 $router 来实现路由功能。例如,使用 $route 来获取当前路由的信息,使用 $router.push() 来动态地跳转到不同的路由。

124、如何让CSS只在当前组件中起作用?

在 Vue 中,如果想让 CSS 样式只在当前组件中起作用,可以使用以下两种方法:

  1. 使用 scoped 属性:在组件的
 <style scoped>
 /* CSS 样式 */
 style>

在上述示例中,CSS 样式只会应用到当前组件中的元素,不会影响其他组件或全局样式。

  1. 使用 CSS Modules:Vue 中提供了 CSS Modules 的支持,可以让 CSS 样式只在当前组件中起作用。要使用 CSS Modules,需要在
 <style module>
 /* CSS 样式 */
 style>

在上述示例中,CSS 样式文件名为 xxx.module.css 或者 xxx.module.scss,CSS 样式只会应用到当前组件中的元素,不会影响其他组件或全局样式。

总的来说,使用 scoped 属性或 CSS Modules 都可以让 CSS 样式只在当前组件中起作用,我们可以根据具体情况选择其中的一种方法。

125、的作用是什么

是 Vue 内置的一个组件,主要用于缓存已经渲染的组件实例,以便在需要重新渲染时能够复用这些组件实例,从而提高页面的性能和用户体验。

具体来说, 会将其包含的组件全部缓存起来,当这些组件在未激活状态下(即组件没有被渲染或已经被销毁)被访问时,会从缓存中取出对应的组件实例并复用它们,而不是重新创建新的组件实例。这样可以避免频繁的创建和销毁组件实例,提高页面的性能和响应速度。

在使用 组件时,可以使用 include 和 exclude 属性来指定需要缓存的组件和不需要缓存的组件,以便更加精细地控制缓存的行为。

总的来说, 组件是 Vue 中非常实用的一个功能,可以有效地提高页面的性能和用户体验。但需要注意,由于 组件会缓存组件实例,因此可能会占用较多的内存空间,需要根据实际情况进行合理的使用和配置。

126、在Vue中使用插槽的步骤

在 Vue 中使用插槽,通常需要以下步骤:

  1. 在父组件中定义插槽,可以使用 元素来定义插槽。例如:
   <template>
     <div>
       <h1>这是父组件h1>
       <slot>slot>
     div>
   template>

在上述示例中,定义了一个名为 default 的插槽,可以在其中放置子组件或其他内容。

  1. 在子组件中使用插槽,可以使用 元素来引用插槽。例如:
   <template>
     <div>
       <h2>这是子组件h2>
       <slot>slot>
     div>
   template>

在上述示例中,使用了 default 插槽,可以在父组件中将任意内容插入到这个插槽中。

  1. 在父组件中使用子组件并传递数据,可以使用属性绑定将数据传递给子组件。例如:
   <template>
     <div>
       <h1>这是父组件h1>
       <ChildComponent :message="message">ChildComponent>
     div>
   template>

   <script>
   import ChildComponent from './ChildComponent.vue'

   export default {
     components: {
       ChildComponent
     },
     data () {
       return {
         message: 'Hello, Vue!'
       }
     }
   }
   script>

在上述示例中,将 message 数据传递给了子组件 ChildComponent,并在子组件中使用插槽将该数据显示出来。

总的来说,在 Vue 中使用插槽可以很方便地实现组件之间的通信和数据共享,能够大大提高组件的复用性和可维护性。需要注意的是,在使用插槽时需要根据具体情况进行合理的配置和使用,以避免出现意外的行为。

127、Proxy 相比于 defineProperty 的优势

在 JavaScript 中,Proxy 和 defineProperty 都是用来实现对象的拦截和观察的机制,但是它们有几个不同之处,Proxy 相比 defineProperty 有以下优势:

  1. 更为灵活:Proxy 可以拦截对象的更多操作,如 has、deleteProperty、ownKeys 等,而 defineProperty 只能拦截 get、set 等几个操作,Proxy 的灵活性更高。

  2. 更为简洁:Proxy 的 API 更加简单易懂,只需要实例化一个 Proxy 对象并传入要代理的对象和处理程序对象即可,而 defineProperty 则需要逐个定义每个属性的 get 和 set 函数,代码更冗长。

  3. 更为直观:Proxy 提供了拦截器函数,可以直观地观察和修改对象的行为,而 defineProperty 的拦截方法较为隐晦,需要通过 get 和 set 函数来实现。

  4. 更为强大:Proxy 还提供了一些高级特性,如 revocable,可以使代理对象被撤销,防止对对象的非授权访问,而 defineProperty 没有这种功能。

综上所述,相比 defineProperty,Proxy 提供了更为灵活、简洁、直观和强大的机制来拦截和观察对象,因此在一些特定的场景下可以更加方便和易于使用。

128、vuex是什么?怎么使用?哪种功能场景使用它?

Vuex 是 Vue.js 官方的状态管理库,用于在 Vue 应用中管理共享的状态。它可以帮助我们解决组件之间的数据共享、数据状态管理等问题。

Vuex 的核心是 store,它包含着应用中的所有状态(state)。Vuex 将 store 对象注入到根 Vue 实例中,使得在所有子组件中可以通过 this.$store 访问到 store 对象。在 store 中,可以通过 mutations 和 actions 来更改状态,而 getters 则可以方便地获取状态。

使用 Vuex 的一般步骤如下:

  1. 创建 store:在一个单独的 JavaScript 模块中,创建一个 Vuex store 对象,定义应用中的所有状态、mutations、actions、getters 等。

  2. 注入 store:在根 Vue 实例中,通过将 store 对象作为一个参数传递给 Vuex 的注入函数 Vue.use(Vuex),将 store 注入到根 Vue 实例中。

  3. 使用 store:在组件中可以通过 this.$store 访问到 store 对象,并通过 store 对象来访问状态、调用 mutations 和 actions、获取 getters 等。

Vuex 可以用于以下场景:

  1. 大型单页应用:当一个应用的组件数量很多,状态管理变得非常复杂时,使用 Vuex 可以方便地管理状态,并且让代码更加清晰易于维护。

  2. 多个视图依赖于同一个状态:例如,一个应用中的多个组件需要访问同一个用户信息,可以将用户信息存储在 Vuex 中,方便多个组件进行访问和修改。

  3. 路由参数和组件之间的通信:当多个视图之间需要传递参数时,可以将参数存储在 Vuex 中,让多个组件进行访问和修改。

总之,使用 Vuex 可以让我们更加方便地管理应用中的状态,减少组件之间的耦合度,让代码更加清晰易于维护。但是,在应用状态管理不是非常复杂时,不需要过早地引入 Vuex,否则会增加代码的复杂度。

129、在vue使用插件的步骤

Vue.js是一个灵活的JavaScript框架,可使用插件扩展其功能。以下是在Vue.js中使用插件的步骤:

  1. 引入插件

首先,你需要引入你想使用的插件。你可以从npm包管理器中安装它们,也可以手动下载并添加它们到你的项目中。

import SomePlugin from 'some-plugin'
  1. 安装插件

接下来,在Vue实例中使用插件之前,你需要安装它。安装插件的方法是在Vue构造函数中使用Vue.use()方法。

Vue.use(SomePlugin)
  1. 使用插件

一旦插件被安装,你就可以在Vue实例中使用它的功能了。

new Vue({
  // ...
  created() {
    this.$somePluginMethod()
  }
})

有些插件可能还需要在Vue实例中配置选项。在这种情况下,你可以在Vue实例中传递选项对象。

Vue.use(SomePlugin, { someOption: true })

以上就是在Vue.js中使用插件的基本步骤。请注意,每个插件都有不同的用法和选项,因此,请务必查看插件的文档,以了解如何正确使用它们。

130、vue路由的钩子函数有哪些

Vue路由提供了多个钩子函数,可以用于在路由切换的不同阶段执行相应的操作。以下是Vue路由中常用的钩子函数:

  1. beforeEach: 在路由切换开始之前执行,可以用来进行权限控制或者全局拦截等操作。

  2. beforeResolve: 在路由解析完毕之前执行,也就是在所有异步组件被解析完毕之前执行。

  3. afterEach: 在路由切换完成之后执行,可以用来进行一些全局的数据清理或者动画效果等操作。

  4. beforeEnter: 在路由进入之前执行,可以用来进行路由独享的权限控制等操作。

  5. beforeRouteUpdate: 在当前路由被复用时执行,例如在同一个路由中切换参数时执行。

  6. beforeRouteLeave: 在路由离开之前执行,可以用来进行离开确认等操作。

以上是Vue路由中常用的钩子函数,可以根据实际需要选择使用。

131、vue2的生命周期

在 Vue 组件实例的生命周期中,会触发一系列的钩子函数,这些钩子函数按照执行顺序可以分为以下几类:

  1. 创建阶段

    • beforeCreate: 组件实例被创建之初,触发该钩子函数,此时组件的数据和事件还未初始化。
    • created: 组件实例被创建之后,触发该钩子函数,此时组件的数据和事件已经初始化完成。
  2. 挂载阶段

    • beforeMount: 组件实例被挂载到 DOM 之前,触发该钩子函数,此时组件的模板已经编译完成,但尚未挂载到页面上。
    • mounted: 组件实例被挂载到 DOM 之后,触发该钩子函数,此时组件已经被渲染到页面上。
  3. 更新阶段

    • beforeUpdate: 组件数据更新之前,触发该钩子函数,此时组件尚未重新渲染。
    • updated: 组件数据更新之后,触发该钩子函数,此时组件已经重新渲染。
  4. 销毁阶段

    • beforeDestroy: 组件实例被销毁之前,触发该钩子函数,此时组件实例仍然可用。
    • destroyed: 组件实例被销毁之后,触发该钩子函数,此时组件实例已经无法访问。
  5. 激活阶段

    • activated: 被 缓存的组件激活时,触发该钩子函数。
  6. 停用阶段

    • deactivated: 被 缓存的组件停用时,触发该钩子函数。

以上是 Vue 组件实例的生命周期钩子函数,可以在相应的钩子函数中执行一些操作,比如初始化数据、发送网络请求、手动修改 DOM 等。

你可能感兴趣的:(前端,javascript,vue.js)