记录一点前端面试中会出现的问题

hash 与 history 的区别?

原理区别

hash 原理

hash 通过监听浏览器 onhashchange 事件变化,查找对应路由应用。通过改变 location.hash 改变页面路由。

history 原理

利用 html5 的history Interface 中新增的 pushState() 和 replaceState() 方法,改变页面路径。

history Interface 是浏览器历史记录栈提供的接口,可通过 back、forward、go 等,可以读取历览器历史记录栈的信息,pushState、repalceState 还可以对浏览器历史记录栈进行修改。

hash history
有#号 无#号
能够兼容IE8 只能兼容到IE10
实际的url之前使用哈希字符,这部分url不会发送到服务器,不需要在服务器层面上进行任何处理 每访问一个页面都需要服务器进行路由匹配生成 html 文件再发送响应给浏览器,消耗服务器大量资源
刷新不会存在404 浏览器直接访问嵌套路由时,会报 404 问题。
不需要服务器任何配置 需要在服务器配置一个回调路由

hash 模式优势

1、从兼容角度分析。

  hash 可以兼容到 IE8,而 history 只能兼容到 IE10。

2、从网络请求的角度分析。

  使用 hash 模式,地址改变时通过 hashchange 事件,只会读取哈希符号后的内容,并不会发起任何网络请求。

  而 history 模式,每访问一个页面都要发起网络请求,每个请求都需要服务器进行路由匹配、数据库查询、生成HTML文档后再发送响应给浏览器,这个过程会消耗服务器的大量资源,给服务器的压力较大。

3、服务器配置角度分析。

  hash 不需要服务器任何配置。

  history 进行刷新页面时,无法找到url对应的页面,会出现 404 问题。因为域名后面的路由是由前端控制的,后端只能保留域名部分,所以就会造成页面丢失的问题,需要服务器端添加一个回退路由,就能解决该问题了。

hash模式的不足

1、hash 模式中的 # 也称作锚点,这里的的 # 和 css 中的 # 是一个意思,所以在 hash 模式内,页面定位会失效。

2、hash 不利于 SEO(搜索引擎优化)。

3、白屏时间问题。浏览器需要等待 JavaScript 文件加载完成之后渲染 HTML 文档内容,用户等待时间稍长。

什么是 MVVM、mvc 模型?

MVC: MVC 即 model-view-controller(模型-视图-控制器)是项目的一种分层架构思想,它把复杂的业务逻辑, 抽离为职能单一的小模块,每个模块看似相互独立,其实又各自有相互依赖关系。它的好处是: 保证了模块的智能单一性,方便程序的开发、维护、耦合度低。

img

MVVM:MVVM 即 Model-View-ViewModel,(模型-视图-控制器)它是一种双向数据绑定的模式, 用 viewModel 来建立起 model 数据层和 view 视图层的连接,数据改变会影响视图,视图改变会影响数据

img

vue 双向数据绑定的原理?

vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()**来劫持各个属性的**setter**,getter**,在数据变动时发布消息给订阅者,触发相应的监听回调。 具体步骤:

第一步: 需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化

第二步: compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步: Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是: 1、在自身实例化时往属性订阅器(dep)里面添加自己 2、自身必须有一个 update() 方法 3、待属性变动 dep.notice() 通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退。

第四步:MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化, 通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新; 视图交互变化(input) -> 数据 model 变更的双向绑定效果。

vue_02.png

vue 的生命周期有哪些?

vue2生命周期

  • beforeCreat() 创建前 在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在此生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
  • created()被创建 data 和 methods都已经被初始化好了,可以调用了
  • beforeMount() 挂载前 在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
  • mounted()已挂载 Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
  • beforeupdate()更新前 页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
  • updated()更新 页面显示的数据和data中的数据已经保持同步了,都是最新的
  • beforeDestroy() 销毁前 Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
  • destroyed()被销毁 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。

vue 生命周期原理图 - AcWing

除了beforecatecreated(它们被setup方法本身所取代),我们可以在setup方法中访问的API生命周期钩子有9个选项:

  • onBeforeMount – 在挂载开始之前被调用:相关的 render 函数首次被调用。

  • onMounted – 组件挂载时调用

  • onBeforeUpdate – 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

  • onUpdated – 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

  • onBeforeUnmount – 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。

  • onUnmounted – 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。


  • onActivated – 被 keep-alive 缓存的组件激活时调用。

  • onDeactivated – 被 keep-alive 缓存的组件停用时调用。

  • onErrorCaptured – 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

Vue3.0

Vue3.中的setup()在哪一个阶段执行?

setup是组合api的入口函数。setup()函数在beforecreate()函数执行之前,已经执行。也就是说在setup函数中我们不能够对data和methods进行操作。

v-if 和 v-show 有什么区别?

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建, 操作的实际上是 dom 元素的创建或销毁。

v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换, 它操作的是display:none/block属性。

一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好; 如果在运行时条件很少改变,则使用 v-if 较好。

async await 是什么?它有哪些作用?

async awaites7里面的新语法、它的作用就是 async 用于申明一个 function 是异步的,而 await 用 于等待一个异步方法执行完成。它可以很好的替代promise 中的 then

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇 到 await 就会先返回, 等到异步操作完成,再接着执行函数体内后面的语句。

常用的数组方法有哪些?

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined 。

findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true, 否则返回 false。

indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。 (通常用它判断数组中有没有这个元素)

join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。 如果数组只有一个项目,那么将返回该项目而不使用分隔符。

pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。

push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。

shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内 容。此方法会改变原数组。 由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删 除元素,则返回空数组。

slice() 方法同上,但不会改变原数组

reverse() 方法将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组。

sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字 符串,然后比较它们的 UTF-16 代码单元值序列时构建的

数组有哪几种循环方式?分别有什么作用?

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔 值。

filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。 注意: filter() 不会对空数组进行检测。 注意: filter() 不会改变原始数组。

forEach() 方法对数组的每个元素执行一次提供的函数。

some() 方法测试是否至少有一个元素可以通过被提供的函数方法。该方法返回一个 Boolean 类型 的值。

常用的字符串方法有哪些?

charAt() 方法从一个字符串中返回指定的字符。

concat() 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。

includes() 方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false。

indexOf() 方法返回调用它的 String 对象中第一次出现的指定值的索引,从 fromIndex 处进行搜 索。如果未找到该值,则返回 -1。 match() 方法检索返回一个字符串匹配正则表达式的的结果。

padStart() 方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的 长度。填充从当前字符串的开始(左侧)应用的。 (常用于时间补 0)

replace() 方法返回一个由替换值( replacement )替换一些或所有匹配的模式( pattern )后的新 字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要 调用的回调函数。 原字符串不会改变。 slice() 方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。

split() 方法使用指定的分隔符字符串将一个 String 对象分割成字符串数组,以将字符串分隔为 子字符串,以确定每个拆分的位置。 substr() 方法返回一个字符串中从指定位置开始到指定字符数的字符。 trim() 方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符 (space, tab, no-break space 等) 以及所有行终止符字符(如 LF,CR)。