写点什么

早已忘却的面试题,需要在隆冬忆起

作者:这我可不懂
  • 2023-06-25
    福建
  • 本文字数:9164 字

    阅读完需:约 30 分钟

一、Vue

Vue 自定义指令

  • 通过自定义指令,我们可以扩展 Vue 的行为,让它在渲染 DOM 元素时添加额外的特性和事件,从而更好地完成业务需求

  • vue 自定义指令分为两种类型:全局指令和局部指令(组件内指令)

  • 全局指令会注册到 Vue.directive 上,可以全局使用,局部指令则只能在组件内使用

下面是一个全局自定义指令的示例:注册一个名为 v-focus 的全局自定义指令注册了一个名为 v-focus 的全局自定义指令,并实现了 inserted 钩子函数,当该指令所绑定的元素插入到 DOM 中时,该钩子函数会被调用,从而实现元素的聚焦功能

Vue.directive("focus",{   // 当绑定元素插入到DOM中执行   inserted:function(el){       //聚焦元素       el.focus()   }})
复制代码

下面是一个局部自定义指令的示例:在组件内定义一个名为 v-highlight 的局部自定义指令

export default {    derectives:{        highlight:{            //当绑定元素插入到DOM中时执行            inserted:function(el){                //添加样式类                el.classList.add("highlight")            },            //当绑定元素从DOM中移出时执行            unbind:function(el){                //移除样式类                el.classList.remove("highlight")            }        }    }}
复制代码

Vue 中 hash 路由与 history 路由的区别

hash模式    在hash模式下,路由路径会带有一个#符号    hash模式的路由通过监听 window.location.hash的变化来进行路由切换。    hash模式的好处是兼容性较好,可以在不支持HTML5 History API的浏览器中正常运行    缺点是URL中带有#符号,不够美观History模式:    在history模式下,路由路径不带有#符号    history模式利用HTML5 HitoryAPI中的pushState和replaceState方法来实现路由切换    history模式的好处是URL更加美观,没有#符号    缺点是兼容性较差,需要在服务器端进行配置,以防止在刷新页面时出现404错误
复制代码
自定义指令在权限控制方面的应用
  1. 可以根据用户的角色信息来控制某些按钮或者表格中的行列是否可见,可编辑,可删除,这个时候就可以通过自定义指令来实现这样的权限控制.

  2. 定义一个名为 v-permission 的全局自定义指令,并实现了 bind 钩子函数,在该函数中通过当前用户的角色信息,判断该用户是否具有该元素的权限,如果没有,则将该元素隐藏


下面是一个示例代码

//定义一个名为v-permission的全局自定义指令Vue.directive("permission",{    //bind 钩子函数只在指令第一次绑定到元素时执行一次    //如果需要对指令的绑定值进行响应式的操作,应该在update钩子函数中进行    bind:function(el,binding,vnode){        //获取当前登录用户的角色信息        const currentUser = getUserInfoFromLocalStorage().role;        //获取绑定的值        const {value} = binding        //判断当前用户是否有该按钮的权限        if(value&&value.length&&!value.includes(currentUser)){            el.style.display = "none";  //隐藏该元素        }    }})

<button v-permission="['admin','superAdmin']">Delete</button>
复制代码

Vue 的动态路由

Vue中的动态路由是指在路由中使用参数来匹配路径的一种方式,通过动态路由,我们可以轻松实现页面参数传递和多个类似页面的复用{    path:'/user/:id',    name:'user',    component:User}:id表示该路由是一个动态路由,所以其被称为参数,它的值会被传递给User组件进行处理
复制代码

Vue 的 key 的作用

key 是用来唯一标识一个节点的属性。当 Vue 渲染 Dom 时,它会根据节点的 key 来判断是否需要重新渲染当 Vue 发现节点的 key 发生变化时。它会将该节点从 DOM 树中移出,然后重新创建一个新的节点插入到合适的位置,这样可以减少 DOM 操作次数,提高渲染性能。

Router 路由守卫

Router 中的一项重要功能,它允许开发者在导航到某个路由或离开当前路由时执行一些控制和验证逻辑。Vue Router 提供了全局的、路由级别的和组件级别的三种不同类型的路由守卫,包括:

  1. 全局前置守卫 beforeEach 用于验证用户是否登录等全局控制

  2. 全局解析守卫 beforeResolve 用于在全局前置守卫之后在组件渲染之前被调用

  3. 全局后置钩子 afterEach 用于在路由完成后进行清理

  4. 路由独享的守卫 beforeEnter 用于在特定路由进入之前进行验证

  5. 组件内部的守卫 beforeRouteEnter、beforeRouteUpdate 和 beforeRouteLeave 用于处理页面内部控制逻辑

二、Javascript

Event Loop

事件循环(Event Loop)是一种用于处理异步任务的机制,它是 js 运行时的一部分,用于管理和调度任务的执行顺序。在 js 中,任务可以分为两种类型:1.同步任务:按照代码的顺序依次执行,直到执行完成 2.异步任务:不会立即执行,而是在将来的某个时间执行。异步任务通常涉及网络请求、定时器、事件监听等。事件循环的工作原理如下:

1 执行同步任务,直到遇到第一个异步任务。2 将异步任务放入相应的任务队列(如宏任务队列,微任务队列)中。3 继续执行后续的同步任务,直到执行栈为空。4 检查微任务队列,如果有任务则按顺序执行所有的微任务。5 执行宏任务队列中的一个人物6.回到第三步,重复以上步骤
复制代码

在每个事件循环中,会先执行所有的微任务,然后执行一个宏任务。这样的机制保证了异步任务的执行顺序,并且能够及时响应用户的交互。常见的宏任务包括 setTimeout, setInterval,网络请求等,而微任务包括 Promise、MutationObserver 等。理解实际那循环对于编写高效的异步代码非常重要,它能够帮助我们合理地处理任务并避免阻塞主线程。

作用域链:

  • 在 js 中每个函数都有自己的作用域,

  • 当在一个函数内部引用一个变量,js 会按照代码中出现的顺序,从当前作用域开始依次向上查找

  • 直到找到第一个包含这个变量的作用域为止,这个过程被称为作用域链的查找

  • 如果整个作用域链都没有找到,就会报错抛出 ReferenceError 异常

function outer(){    const a = 10    function inner(){        console.log(a);        console.log(b);//Uncaught: ReferenceError:b is not defined    }    inner()}outer()
复制代码

js 在转时间时时分秒不满 10 补 0 的几种方式

const h = Math.floor(seconds / 3600);const m = Math.floor((seconds % 3600) / 60);const s = seconds % 60;
1 使用ES6模版字符串padStart指定字符串的长度,如果不满,则在前面补上自己定义的字符串`${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`
2 使用三元运算符(h<10?"0"+h:h)+ ":" + (m<10?"0"+m:m)+":"+(s<10?"0"+s:s)
3 使用Array.map和Array.join方法
const timeArr = [h,m,s].map(value=>{ return value <10?"0"+value:value})timeArr.join(":")
复制代码

当我们在浏览器地址栏中输入一个网址时,发生了什么

1.DNS 解析: 浏览器会检查自己有无缓存,如果有该域名对应的 IP 地址就直接使用,否则则向 DNS 发起请求

2.发起 Http 请求,浏览器会根据 URL 的协议头向服务器发起相应的网络请求,同时浏览器也会发送一些请求头和请求参数

3.建立 TCP 连接:在 HTTP 请求建立之前,浏览器需要和服务器建立 TCP 连接,TCP 是一种面向连接的、可靠的、基于字节流的传输协议,通过三次握手来确保数据能够准确可靠的发送到服务端,而不会出现数据丢失或乱序的情况

4.发送 HTTP 请求,在建立了 TCP 连接之后,浏览器就能够向服务器发送 HTTP 请求报文。HTTP 请求报文包括请求头,请求行,和请求体

5.接受服务器相响应报文:当服务器收到请求报文后,会解析请求,然后返回相应的响应报文给客户端。HTTP 响应报文包括状态行、响应头和响应体三个部分,其中状态行包含了该请求的结果状态码。

6.解析渲染页面:当浏览器接收到服务器返回的响应报文之后,会根据相应报文中的内容(如 HTML,CSS、javascript 等资源),解析出对应的 DOM 树、CSS 规则树和 javascript 代码并且根据它们构建出一个渲染树。最后将这些内容交给浏览器的渲染引擎进行渲染,生成我们最终看到的页面。

7.断开 TCP 连接,当浏览器接收到服务器返回的响应报文后,会关闭该连接,释放资源。如果浏览器需要请求更多的资源,则需要重新建立新的 TCP 连接

如何减少重排和重绘

重排和重绘是浏览器渲染过程中的两个关键步骤重排是指浏览器计算元素的位置和大小属性,并重新布局页面的过程。重绘是指根据元素的样式属性,重新绘制元素的外观重排和重绘是耗费性能的操作,因此减少重排和重绘可以提高页面的性能和响应速度

1. 批量修改样式:避免对元素的样式属性进行频繁的单独修改,而是尽量将多个样式的修改合并为一个操作。可以使用Css类名的方式一次性地修改多个样式属性。2. 使用文档片段:如果需要通过js动态地插入一系列元素,可以先将它们添加到文档片段(Document Fragment)中,然后再一次性的讲文档片段添加到文档中,这样可以减少重排的次数。3. 避免频繁读取布局信息,当获取元素的位置、大小等布局信息时,尽量避免在每次操作中都去读取这些信息,而是将其缓存起来,以减少重排的次数4. 使用CSS3动画和过渡:CSS3动画和过渡是基于浏览器的硬件加速,可以减少重绘和重排的开销。尽量使用CSS3动画和过渡来实现动画效果,而不是使用js来操作样式属性。5. 使用requestAnimationFrame: 使用requestAnimationFrame来执行动画可以更好的与浏览器的重绘机制结合,减少不必要的重绘和重排操作6. 避免频繁的DOM操作:DOM操作会导致重排和重绘,尽量避免频繁的对DOM进行增删改操作,可以先将需要操作的DOM元素从文档中移除,进行批量操作后再重新插入。7. 使用css布局工具:使用Css的flexBox和Grid等布局工具可以更好地控制页面布局,减少重排的次数。
复制代码

import 和 require 是 js 两种不同的模块化规范

1、用法不同

import 时 ES6 中新增的模块化语法,用于在代码中引入其他 ES6 模块的导出对象。它是一个顶级声明

只能出现在 js 代码最外层或其他类似的顶级位置,不能在其他代码块中使用 import {debounce} from "lodash"

require 是 CommonJS 规范中的模块化语法,用于在代码中引入 CommonJS 模块的导出对象,它可以在任何位置使用,包括函数内部或代码内部 const debounce = require("lodash/debounce")

2、加载时机不同

import 在代码编辑时就会被处理,因此在代码执行前就已经加载了相应的模块。这使得 import 可以在代码运行之前进行静态分析,从而在构建时进行优化

require 是在运行时才会被加载,这意味着当代码中使用 require 时,它会在代码被执行的到的时候加载模块,并将其导出对象作为结果返回

3、语法不同

import 的语法相对简洁,并且可以进行模块的命名空间分离和解构。同时,它也支持异步加载模块


//引入lodash模块中的debounce方法并重命名为myDebounce //解构 只导出需要的内容,减少导出对象的大小 import {debounce as myDebounce} from "lodash" import * as myModule from "./model.js" //命名空间分离 通过一个对象承载模块的所有导出内容。从而实现命名空间分离 //使用异步函数动态引入模块 const someAsyncModule = await import ('./path/to/module')
复制代码


require 的语法相对复杂,特别是在需要进行多层路径嵌套时更为明显,它不能进行命名空间分离,也不支持 ES6 中的解构语法。同时也不能异步加载模块,需要额外的库或者手动编写异步加载逻辑

总之,如果需要使用 Es6 中的新特性或者异步加载模块,使用 import, 如果需要兼容 node 或者 CommonJS 环境,可以使用 require

WebSocket 的使用

WebSocket 是一种在 Web 浏览器和服务器之间进行全双工通信的协议。它提供了更强大的实时数据传输能力,相对于传统的 HTTP 请求-响应模式,WebSocket 允许服务器主动向客户端推送数据,实现了真正的双向通信。

//创建WebSocket连接   得到一个Websocket对象,url是websocket服务器的地址const socket = new WebSocket("ws://example.com/socket")
监听事件: WebSocket对象支持多个事件,如`open`,'message',"error"和'close'。通过给WebSocket对象添加对应的事件监听器函数,可以处理连接打开、接收到消息、出现错误和连接关闭等情况// 监听连接打开事件socket.addEventListener("open",event=>{ console.log("WebSocket 连接已打开");})// 监听接受到消息事件socket.addEventListener("message",event=>{ console.log("收到消息",event.data);})// 监听连接错误事件socket.addEventListener("error",error=>{ console.log("WebSocket 错误:",error);})
//监听连接关闭事件socket.addEventListener("close",event=>{ console.log("Websocket 连接已关闭");})
//发送消息 send(data)方法可以向服务器发送消息。服务器接收到消息后,可以通过‘message’ 事件监听器处理。//客户端可以使用event.data获取服务器发送的消息内容socket.send("Hello,Websocket")
//关闭连接:当不再需要连接时,可以使用WebSocket对象的close方法来关闭连接socket.close()

复制代码

WebSocket 连接需要服务器支持,服务器需要实现相应的 WebSocket 协议来处理连接和消息的传输。在实际使用中,可以使用诸如 Node.js 的 ws 模块或者 Websocket 框架来实现服务器端的功能

微任务和宏任务以及使用场景

微任务和宏任务是用来管理 js 异步任务执行顺序的机制。它们决定了任务在事件循环中的执行顺序。微任务是由 js 引擎提供的任务队列,它的执行优先级高于宏任务,常见的微任务有 Promise 的回调函数,MutationObserver 和 progress.nextTick 宏任务是由浏览器提供的任务队列,它的执行优先级较低,常见的宏任务有定时器 setTimeout,setinterval, DOM 事件回调和 Ajax 请求等


使用场景:


微任务的使用场景:

1 需要在当前事件循环的末尾执行的任务,可以使用微任务,例如 Promise 的回调函数。

2 需要立即执行的任务,可以使用微任务,例如 MutationObserver 监听 DOM 变化并立即做出相应


宏任务的使用场景:

1 需要延迟执行的任务,可以使用宏任务,例如定时器的回调函数。

2 需要在事件循环中的下一个循环中执行的任务,可以使用宏任务,例如 DOM 时间回调和 Ajax 请求


在一个事件循环中,当所有的宏任务执行完毕后,会先执行所有的微任务,然后再进行下一个宏任务的执行,这样可以保证微任务的优先级高于宏任务,确保及时响应和更新总结起来,微任务和宏任务是用来管理异步任务执行顺序的机制,微任务的执行优先级高于宏任务,适合处理需要立即执行或在当前循环末尾执行的任务。宏任务适合处理需要延迟执行或在下一个循环中执行的任务

ES6 中一些常用的特性和语法

  1. let 和 const 关键字

  2. 箭头函数

  3. 解构赋值

  4. 扩展运算符

  5. 类和继承

  6. 模版字符串

  7. promise 和 async/await

  8. 模块化:Symbol、Map、Set、Proxy、Reflect

三、React

Vue 和 React 的区别

分五个方面来说:

1、模版语法:

Vue 使用了 HTML 的模版语法来编写组件模版,比较易于理解和学习,使得开发者可以快速的编写出页面。这样做的优点在于,将 HTML 和 JavaScript 代码分离开来,有利于代码的维护性和阅读性。Vue 的模版语法也提供了一些强大的功能,如条件渲染,循环渲染,事件处理等.

React 则推崇:一切都是 javascript,它采用了 jsx 语法,即 Javascript 和 Xml 的混合语法,使用 jsx 可以轻松地创建复杂的 UI,并提高了应用程序的性能,

不过使用 jsx 的同事,你需要学习更多的语法和编程范式


2、数据绑定

Vue 提供了双向数据绑定的功能,可以实现视图和数据的自动同步,极大方便了数据管理和操作。双向绑定包括两个部分:数据模型和视图模型。在 Vue 中,数据模型即组件实例中的数据;视图模型则负责数据模型中的数据绑定到视图中去。

React 的数据流是则是单向的,采用了组件间 props 和 State 的传递和管理数据,通过 props 向组件传递属性值,并监听这个属性的更改事件来更新 UI

从而实现数据的单向流动;而 state 则是保存组件内部状态的地方,是可变的数据,推荐在合适的情况下尽量使用不可变数据。


3、组件分类

Vue 将组件分为有状态组件和无状态组件 functional 两种,有状态组件包含了应用程序中的逻辑和数据,可以实现更复杂的操作和交互;无状态组件只提供了一个对应的 UI 界面,没有数据和逻辑的处理,通常用于小组件或纯展示类组件。Vue 的组件具有生命周期函数,可以在组件的不同阶段执行不同的处理逻辑。

React 没有严格的组件分类,但通常会将组件分为函数式组件和 Class 组件两种。函数式组件是一种纯 javascript 函数,接受 props 对象作为参数,返回一个 React 元素,不支持状态和生命周期函数;class 组件则是使用面向对象编程的方式来构建组件,支持状态和生命周期函数。所以 React 组件也具有生命周期,可以在组件的不同阶段执行相关操作。


4、生命周期

Vue 和 React 都拥有各自的生命周期函数,并分别在不同的组件阶段调用这些函数。Vue 的生命周期函数包括了 created、mounted、updated 和 destroyed 等每个函数都有特定的用途和功能

React 的生命周期函数包含了 componentWillMount、componentDidMount、shouldComponentUpdate、componentWillUnmount 等也都有各自的特点和用途。

生命周期函数可以用于解决组件挂载、更新、销毁等过程的一系列问题和逻辑


5、渲染效率

Vue 采用了虚拟 DOM 和异步渲染等技术来提高程序的性能,虚拟 DOM 是将真实的 DOM 抽象成 js 对象,可以快速的进行对比和计算,从而减少 DOM 操作带来的性能损耗;异步渲染则是让浏览器在空闲时间渲染组件,从而提高渲染效率。React 则使用了一种名为 reconciliation 的算法在不重新渲染整个组件数的情况下更新 UI,这也使得 React 具有较高的渲染效率。在组件更新时,React 会通过对比虚拟 DOM 的变化来更新 UI,从而避免大量的 DOM 操作

Vue 和 React 都是非常优秀的前端框架,采用不同的实现方式,适用于不同的开发场景。Vue 更加注重开发体验和易用性,适合快速开发小型和中型的应用;React 更加注重应用程序的可维护性和性能,适合大型应用或需要更好的可扩展性的应用。选择哪个框架取决于项目的需求和团队的偏好

四、Webpack

webpack 的摇树

摇树(tree Shaking)是指在打包过程中,通过静态分析的方式,去掉没有使用的代码,从而减小最终打包后的文件体积
在Webpack中摇树是通过ES6模块化语法和静态分析工具(如UglifyJS)来实现的。当Webpack打包时,它会分析模块之间的依赖关系,并且检查哪些代码被实际使用了,哪些去除掉没有被使用的代码
摇树的原理是基于ES6模块化的静态特性,它可以在编译时进行静态分析,因为ES6模块化的导入和导出是静态的,而CommonJS模块化的导入和导出是动态的
要实现摇树,需要满足以下条件:1 使用ES6模块化语法进行导入和导出2 代码中的导入必须是静态的,不能使用动态导入3 代码中的导出必须是静态的,不能使用动态导出
当满足这些条件时,Webpack在打包的过程中会自动进行摇树优化,去掉没有使用的代码,从而减小打包后的文件体积需要注意的是,摇树只能去掉没有使用的代码,而不能去除没有被导入但被使用的代码。
复制代码

npm run dev 时,webpack 做了什么

当你运行npm run dev命令时,webpack会执行一系列的操作来构建和打包你的项目。1. 根据配置文件(通常是webpack.config.js),webpack会读取配置中的入口文件(entry)和出口文件(output)的路径信息。2. 根据入口文件的路径,webpack会分析项目的依赖关系,找到所有需要打包的模块。3. webpack会根据配置中的加载器(loader)对模块进行处理。加载器可以将非js文件(如css,图片等)转换为js模块,或者对js模块进行预处理(如使用Babel进行ES6转换)4. webpack会根据配置中的插件(plugin)对模块进一步的处理。插件可以用于优化打包结果、拓展webpack的功能等。5. webpack会根据配置中的出口文件路径和文件名,将打包后的模块输出到指定的目录中6. 在开发模式下,webpack会启动一个开发服务器( dev server),并监听文件的变化,当文件发生变化时,webpack会自动重新构建并刷新浏览器7 webpack 还会生成一个包含构建信息的统计文件,可以用于分析打包结果、性能优化等
复制代码

总的就是:webpack 在运行 npm run dev 时,会根据配置文件对项目进行构建和打包,并提供开发服务器以及自动编译的功能,方便开发人员进行实时调试和开发

webpack 缓存

Webpack 提供了缓存机制,可以通过缓存来提高投建性能。webpack 的缓存机制有两个方面:

  1. Loader 缓存:在 webpack 构建过程中,Loader 可以使用缓存来提高性能 Loader 缓存可以避免对于同一个文件的重复处理,从而加快构建速度。通过设置 cache:true 来开启 Loader 缓存,例如:

module:{   rules:[{     test:/\.js$/,     use:'babel-loader',     options:{         cacheDirectory:true     }   }] }
复制代码

2、Module 缓存:

在 webpack 构建过程中,Webpack 会根据模块的内容生成一个唯一的标识符(hash).

如果模块内的内容没有发生变化,Webpack 会复用之前的构建结果,避免重新构建该模块,从而提高构建速度。

Module 缓存是基于文件的,只有在文件内容发生变化时才会重新构建

Module 缓存是默认开启的,可以通过设置 cache:true 来显示启用缓存。

通过使用 Loader 缓存和 Module 缓存,Webpack 可以避免重复处理和构建不变的模块,从而提高构建的性能和速度。

需要注意的是,缓存机制只有在构建过程找那个文件内容没有发生变化时才会生效,如果文件内容发生变化时,Webpack 会重新构建整个模块和依赖树。

五、TypeScript

ts 中如何新增一个类型

在 ts 中有几种方式可以创建新类型:

1. 类型别名(Type Alias):    使用type 关键字创建一个类型别名,可以给现有类型起一个新的名字    例如: type myType = string|Number;2.接口(interface):    使用interface 关键字创建一个接口,用于定义一个对象的结构。    例如interface MyInterface{name:string;age:number;}3.类(Class):    使用class 关键字创建一个类,用于定义一个对象的结构和行为。    例如: class MyClass{name:string;age:number}4.枚举(Enum):    使用enum关键字创建一个枚举类型,用于定义一组具名的常量值。    例如:enum MyEnum{A,B,C}
复制代码

any 与 unkown 的区别

在 Typescript 中,any 和 unkown 都是用来表示不确定类型的,都是表示任意类型,但是它们之间有一些区别:

1 可赋值性:    any 类型可以赋值给任何类型,也可以从任何类型中获取值    unknown类型只能被赋值给unkown和any类型,不能直接从中获取值。2 类型检查和类型判断:  any类型的变量不会进行类型检查,编译器不会对其进行类型推断或类型检查。  unknow类型的变量在编译器中会进行类型检查,使用之前必须进行类型检查或类型断言3 方法和属性的调用:  any类型的变量可以调用任何方法和属性,而不会报错。  unknown类型的变量不能调用任何方法或属性,除非先进行类型断言或类型检查4 类型安全性:  使用any类型会丧失类型安全性,因为它可以接受任何类型的值。  使用unkonw类型可以提供更好的类型安全性,因为在使用之前必须进行类型检查。  因此,unkown类型相比于any类型提供了更好的类型检查和类型推断,以及更好的类型安全性,因此,尽量使用unkown
复制代码


发布于: 刚刚阅读数: 3
用户头像

低代码技术追随者,为全民开发而努力 2023-02-15 加入

大家好,我是老王,专注于分享低代码图文知识,感兴趣的伙伴就请关注我吧!

评论

发布
暂无评论
早已忘却的面试题,需要在隆冬忆起_Vue_这我可不懂_InfoQ写作社区