居然之家(1.5个小时40道题)
全程面了一个半小时吧,大概有40道题,先整理出来了20道
- HTML5新增的属性
- 移动端适配
- flex布局,4个盒子如何平均分配排列到一行上
- 水平垂直居中的几种方法,尽可能多说
- 你了解grid布局吗?
- 组件封装的原则
- 后端一次返回一万条数据,前端怎么处理?
- 虚拟列表怎么实现的
- 怎么判断一个元素是否在可视区域内
- 首屏优化有哪些方式?
- 交互上有不合理的要求怎么办
- vue双向数据绑定原理
- vue3双向数据绑定原理,解决了vue2的什么问题
- vue2 怎么监听数组和对象的变化,该怎么处理响应式
- 组件间的通讯方式有哪些
- 什么时候使用vuex,看过vuex的源码吗?
- 路由守卫,它的方法和使用场景
- keep-alive了解过吗,它的使用场景
- 小程序的页面数据存储有哪些方法
- 深浅拷贝有哪些方法
- JSON.parse(JSON.stringify(object)) 有什么缺点
- 前端浏览器的存储方式有哪些
- 防抖节流,应用场景,手写一个防抖节流
- Promise是干什么的?Promise.all()接受三个promise,如果第二个失败了还会执行第三个吗?
- async/await 怎么捕获异常
- React,哪一个版本有hooks,怎么进行数据通信
- React生命周期
- webpack,它的插件loader,plugin,拆包
- 事件循环
- 代码执行顺序, 那些是宏任务和微任务,区别?
- new()方法会执行什么?
- 单点登录?怎么判断是否登录?
- 微前端,低代码有了解吗?
- 平时怎么学习前端的
- 最近在看那些技术?
- attribute 和 property 的区别
- webpack构建流程
移动端适配
移动端适配是指在不同设备的移动端上,调整网页的布局和字体大小,以便在不同大小和分辨率的屏幕上呈现合适的效果。常见的移动端适配方法有:
-
使用rem布局:根据屏幕宽度动态计算html的字体大小,然后使用rem作为长度单位。当屏幕尺寸发生变化时,html的字体大小也会相应地改变,从而保证网页的布局和元素大小能够自适应变化。
-
使用Viewport缩放:可通过设置Viewport元标签的各种属性(如width、initial-scale等),来调整页面在不同设备的显示效果。例如,可以通过设置meta viewport的initial-scale值,来控制页面的缩放比例。
-
使用JS动态计算:通过JavaScript动态计算设备像素比(devicePixelRatio)和屏幕宽度,然后设置网页的css样式来实现自适应布局。
flex布局,4个盒子如何平均分配排列到一行上
使用CSS的flex布局,将每个子元素的flex-grow
属性设置为1,就可以让每个子元素平均分配父容器的宽度。
水平垂直居中的几种方法
- 使用flex布局设置居中。
- 使用flex 时也能通过给子项设置margin: auto实现居中。
- 使用绝对定位的方式实现水平垂直居中。
- 使用grid设置居中。
- 使用grid时还能通过给子项设置margin: auto实现居中。
- 使用tabel-cell实现垂直居中。
- 还有一种不常用的方法实现垂直居中。
- 最后还有一种奇葩的方法。容器设置position: relative。孩子设置 top、left、bottom、right都设置为0
grid布局
grid是二维网格布局。相较于传统的CSS布局方式,Grid布局的优势在于可以自由地指定行列大小和位置,同时可以实现对齐、分布等复杂的布局效果。通常来说,Grid布局需要在容器的父元素上添加display: grid样式声明,并控制容器内的子元素的位置和大小。
- display: grid: 指定一个元素使用 Grid 布局。
- grid-template-columns 和 grid-template-rows: 指定网格每一列和每一行的大小
- grid-column 和 grid-row: 指定一个元素所占的格子
- grid-gap: 指定网格之间的间隔大小
- justify-content 和 align-items: 控制元素在网格中的水平和垂直位置 -grid-auto-columns 和 grid-auto-rows: 指定网格自动填充的列宽和行高
组件封装的原则
-
单一职责原则:一个组件封装只做一件事,不要将多种功能和逻辑混杂在同一个组件中。
-
可扩展性原则:组件应该能够容易地进行扩展和修改,而不会影响到其他代码的功能。
-
可复用性原则:组件应该能够在多个项目中使用,而不需要进行大量的修改。
-
开放封闭原则:一个组件封装要对扩展开放,对修改关闭。尽可能地使用可配置的属性来实现组件的灵活性。
-
高内聚低耦合原则:组件代码应该关注自己的职责和功能,组件之间的依赖应该尽可能的少,这样才能提高组件的可重用性。
-
继承与复用原则:一个好的组件封装应该具备良好的继承性和复用性,可以被多个项目使用,减少代码重复。
-
功能独立原则:组件的功能应该是独立的,不会对其他组件的功能产生影响。
-
易于维护原则:好的组件应该易于修改和维护,充分利用面向对象编程的基本原则,包括高内聚低耦合、抽象、封装和继承等。
后端一次返回一万条数据,前端怎么处理?
让后端分页
前端角度:虚拟列表,分批渲染
虚拟列表怎么实现的
虚拟列表其实是按需显示的一种实现,只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。
- 首屏加载时,只加载可视区域内需要的列表项
- 当滚动时,动态通过计算获取可视区域内的列表项
实际上就是在首屏加载的时候,只加载可视区域内需要的列表项,当滚动发生时,动态通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除。
怎么判断一个元素是否在可视区域内?
getBoundingClientRect
方法。,返回一个DOMRect对象。该对象拥有left, top, right, bottom, x, y, width, 和 height属性。
当页面发生滚动的时候,top, left, right, bottom属性值都会随之改变。
top:就是元素上外边框到视口顶端距离。
left:就是元素左外边框到视口左端距离。
bottom:就是元素下外边框到视口顶端距离。
right:就是元素右外边框到视口左端距离。
当 top 大于等于 0,left 大于等于 0
bottom 小于等于视窗高度,right 小于等于视窗宽度时就说明元素在可视区域内
function isVisible(dom) {
const totalHeight = window.innerHeight || document.documentElement.clientHeight;
const totalWidth = window.innerWidth || document.documentElement.clientWidth;
// 当滚动条滚动时,top, left, bottom, right时刻会发生改变。
const { top, right, bottom, left } = dom.getBoundingClientRect();
return (top >= 0 && left >= 0 && right <= totalWidth && bottom <= totalHeight);
}
首屏优化有哪些方式?
-
图片优化:对于图片,可以采用压缩、懒加载等方式进行优化;
-
代码压缩:使用压缩工具,精简代码体积,提高页面加载速度;
-
前端资源合并和压缩:将多个js、css文件合并成一个文件,并通过页面压缩工具对代码进行压缩和处理;
-
模块异步加载:可以采用动态加载和懒加载技术,将页面中一些不是首次展现即可执行的模块延迟到后面加载,优化首次渲染;
-
CDN 加速:使用 CDN 加速服务,可以将静态资源分布到全球各地的服务器上,通过就近访问提高加载速度;
-
SSR服务端渲染:将客户端请求的动态页面通过服务器模板引擎渲染成 HTML 等静态文件并返回给客户端展示的过程。与传统的前端渲染方式不同,服务端渲染将部分页面的处理操作放到了服务端,在浏览器端只需接收到已经生成好的静态资源文件,并渲染页面,以此来提高页面性能和加载速度。
交互上有不合理的要求怎么办
可以和项目经理或产品经理进行沟通,说明自己的困惑和担忧;根据自己的经验和前端技能,尝试给出可行的解决方案
vue2双向数据绑定
当数据传入vue的实例中作为data数据时,vue将遍历此对象的所有property,并且使用Object.defineProperty把他们转化为getter和setter方法,而get和set正是对属性的读写进行监控
vue3双向数据绑定原理,解决了vue2的什么问题
Vue 3.x 采用了 Proxy 换掉了 Vue 2.x 中的 Object.defineProperty 实现双向绑定,Vue 2.x能够监听数据对象的变化,但是监听不到对象属性的增删、数组元素和长度的变化;
vue2 怎么监听数组和对象的变化,该怎么处理响应式
可以使用Vue.set()或this.$set()方法来添加属性
Vue 2 提供了一系列方法用来修改数组数据,包括:push()、pop()、shift()、unshift()、splice()、sort() 和 reverse() 等方法。Vue 2 通过重写这些数组的方法,来实现对数组的监听
组件间的通讯方式有哪些
- Props / Events:通过父组件向子组件传递数据(Props),并通过子组件向父组件派发事件(Events)进行通信
- emit/on:emit 方法在子组件中触发事件,父组件可通过 $on 方法监听这个事件,并处理相应逻辑。这种方式适用于组件之间存在互动的场景。
- Provide / Inject:从父组件向所有子孙组件传递数据,可以让所有子孙组件注入这个数据,不需要一层一层地传递数据。
- Vuex 是 Vue 官方提供的状态管理工具,会把需要共享的数据放到全局 store 中,每个组件都可以直接访问这个 state。通过 Vuex,可以方便地实现组件间数据的共享和管理。
什么时候使用vuex,看过vuex的源码吗?
Vuex通常用于应用程序中的大型状态管理。如果应用程序具有多个组件并且需要共享状态,则使用Vuex可以使状态管理变得更加容易和可维护。
Vuex 是一个专为Vue.js 应用程序开发的状态管理模式
vuex中有几个核心的方法:
- state用于存放全局数据
- getter用于对Store中的数据进行加工处理形成新的数据。不会修改原数据
- mutations里面就是写对数据进行操作的方法的
- action用于处理异步任务。
Vuex源码的核心内容包括以下几个方面:
-
Store类:这是Vuex的核心类,它实例化时可以传递一个对象参数,这个参数也就是全局状态树的状态值,并对外暴露一些方法,包括 commit、dispatch、subscribe 等。Store类实现了Vuex的主要功能,如集中式状态管理、状态更新、异步操作等。
-
Module类:Vuex允许将状态树进行模块化,不同模块拥有自己的 state、mutations、getters 和 actions。Module类实现了部分 Store功能,并且可以递归地注册子模块。
-
Mutation类:用于修改state中的状态,是Vuex中重要的概念之一。Mutation类实现了可以异步修改状态的commit方法。
-
Action类:用于异步操作,Action类实现了dispatch方法,表示派发异步操作请求,Action 可以包含任意异步操作,并且可以返回 Promise 以实现链式异步操作。
路由守卫,它的方法和使用场景
路由守卫是指路由实例上直接操作的钩子函数,他的特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数。
使用场景:
- 清除当前组件中的定时器
- 当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转
全局路由守卫有个两个:一个是全局前置守卫:在路由跳转之前触发,可以用于进行用户登录验证等操作
router.beforeEach((to, from, next) => {
// to: 即将要跳转的路由对象
// from: 当前导航正要离开的路由
// next: 用于进行路由的下一步操作,必须调用,且只调用一次
const isLogin = localStorage.getItem('token')
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
if (isLogin) { // 通过验证
next()
} else { // 没有登录,跳转到登录页面
next({
path: '/login',
query: { redirect: to.fullPath } // 把要跳转的地址作为参数传到登录页面
})
}
} else {
next()
}
})
一个是全局后置守卫:在路由跳转之后触发,一般用于页面的统计或清理操作
router.afterEach((to, from) => {
// to: 跳转之后的路由对象
// from: 跳转之前的路由对象
console.log(`从 ${from} 到 ${to} 的页面访问次数统计`)
})
组件内的路由守卫:在组件的生命周期钩子函数内调用
export default {
name: 'MyComponent',
beforeRouteEnter(to, from, next) {
// 进入路由之前调用,可以使用 next() 进行跳转
},
beforeRouteLeave(to, from, next) {
// 离开路由之前调用,可以使用 next() 进行跳转
},
beforeRouteUpdate(to, from, next) {
// 路由参数变化时调用,可以使用 next() 进行跳转
}
}
keep-alive了解过吗,它的使用场景
keep-alive
用于缓存动态组件或路由,从而在组件切换时,保留原组件的状态和避免重新渲染。
使用场景如下:
- 缓存动态组件或路由,避免多次重复渲染,提升渲染性能。
- 保留动态组件或路由的状态,避免每次切换时重新加载数据或重置状态。
- 在表单提交后,返回上一层级界面,避免需要重新渲染上一层级组件和重新加载数据。
keep-alive 组件同时还提供了一些钩子函数,如 activated 和 deactivated 等,用于处理被缓存的组件的激活和停用操作。
小程序的页面数据存储有哪些方法
-
setData方法:setData方法是小程序开发中最常用的页面数据存储方式,用于将数据存储到当前页面的data对象中。当数据改变时,调用setData方法即可更新页面数据。
-
wx.setStorageSync 和 wx.getStorageSync 方法:wx.setStorageSync 方法用于将数据同步存储到本地缓存中,而 wx.getStorageSync 方法用于从缓存中同步获取存储的数据。该方法适用于小数据量的存储。
-
wx.setStorage 和 wx.getStorage 方法:wx.setStorage 方法用于将数据异步存储到本地缓存中,而 wx.getStorage 方法用于从缓存中异步获取存储的数据。该方法适用于大数据量的存储,但并不保证实时性,存储过程是异步的。
-
Page 之间传值:小程序中的多个页面之间可以通过跳转页面传值的方式来存储数据。例如可以通过 wx.navigateTo 和 wx.navigateBack 方法传递数据。
-
App 全局变量:可以通过将数据存储到 App 对象中,从而实现在多个页面之间共享数据的目的。需要注意的是,在 App 对象上存储的数据会在小程序整个生命周期内一直存在,需要对数据进行手动清除。
HTML5新增的属性
语义化标签,例如header,footer,section,article等 语义化标签的作用:提升页面的阅读性(结构性增强),更有利于SEO,对于使用屏幕阅读器的人来说会更友好
新增媒体元素,audio、video; audio和video标签能够很容易的输出音频或视频流,提供便利的获取文件信息的API
用于绘画的canvas属性 Canvas API 提供了一个通过JavaScript 和 HTML的canvas元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
新增本地存储方式:sessionStorage、localStorage sessionStorage 用于存储会话级别的数据,会话关闭,数据消失,不可设置过期时间。 localStorage 用于存储需要进行持久化存储的数据,只要不主动删除,数据不会消失。
Localstorage 与 sessionstorage的区别
localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。同源可以读取并修改localStorage数据。
sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问,并且当会话结束后数据也随之销毁。只允许同一窗口访问。
session和cookie的区别
cookie 是保存在客户端或者说浏览器中的一小块数据,大小限制大致在 4KB 左右,Cookie是服务器发送给浏览器的一段字符串信息,浏览器在访问对应域名的时候都要把这段字符串带上,Session是会话,表示浏览器与服务器一段时间的会话
Cookie 存在浏览器的文件里,Session 存在服务器的文件里
Session 是基于 Cookie 实现的,具体做法就是把 SessionID 存在 Cookie 里
attribute 和 property 的区别
attribute 是 HTML 标签上的特性,也就是 html 代码中经常看到的键值对
property 是 DOM 中的属性,是 JavaScript 里的对象
DOM 节点在初始化的时候会将 html 规范中定义的 attribute 赋值到 property 上。而自定义的 attribute 并不属于这个范围内,自然生成的 DOM 节点就没有这个 property。
Attribute
-
值只能为 string 类型
-
如果编辑 HTML 时设置了元素的 attribute 值,之后就算改了这个值,attribute 依旧是默认的值。
-
允许创建自定义的值
property
-
值可以是多种类型:boolean,string,number等都可以
-
可以通过 DOM 对象访问
-
获取不到规定属性以外的自定义属性
webpack 构建流程
webpack 的运行流程是一个串行的过程,它的工作流程就是将各个插件串联起来
从启动到结束会依次执行以下三大步骤:
- 初始化流程:从配置文件和 Shell 语句中读取与合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数
- 编译构建流程:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理
- 输出流程:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统
根据真实面试经历盘点面试题目,总结面试经验,分类总结面试题目答案