web前端面试题-事件循环\浏览器渲染原理\vue\js\网络\css

25 阅读25分钟

一、事件循环

进程:(类比房屋)一块内存空间,用于隔离应用,相互独立,如需通信需要双方同意

线程:(类比人)每个进程至少有一个线程-主线程

**浏览器的进程:**多进程、多线程的模型。包括浏览器进程、网络进程、渲染进程等

**渲染进程:**每个浏览器标签页都会开辟新的渲染进程,渲染主线程(html、c s s、j s、布局、图层、f p s画面绘制等),只有一个!最开始渲染主线程会无限循环看任务队列是否有新消息,有就依次执行,其他线程来的新任务会添加在任务队列最后,这就是事件循环。

**同步:**事件无法立即执行。需要排队等待任务一个解决完后再解决下一个,会造成事件拥堵、浏览器卡死。

**异步:**j s是一门单线程语言,运行在渲染主线程中,主线程只取事件队列里的任务,如遇到setTimeOut、x h r、addEventLisener事件时,主线程立刻结束此任务的执行,将此任务交给其他线程去处理,自己则处理下一个任务队列中的任务,渲染主线程永不阻塞!

**任务优先级:**任务没有优先级,在消息队列中先进先出

**消息队列优先级:**消息队列有优先级,每个任务都有一个类型,同一类型的任务都必须在一个队列,不同类型的任务可以在不同队列。微队列优先级最高(必须先执行完),其次交互队列、其次延时队列(定时器);如 Promise.resolve.then(()⇒{代码直接进入微队列!!})

二、浏览器渲染原理

网络进程拿到html的文档(字符串)后—给到网络线程,生成一个渲染任务,并把这个渲染任务加入到渲染主线程的消息队列。

渲染进程在事件循环机制的作用下,渲染主线程拿到渲染任务,执行渲染。

渲染流程:

1.解析html(得到dom和cssom)——样式计算(得到样式的最终值,颜色会变成r g ba,em变成p x)——布局(得到布局树,每个元素的大小、位置,隐藏元素除外,所以do m树和布局树不会一一对应)——分层——绘制(主线程形成指令集,接下来的操作交给合成线程)|||||—分块(合成线程分块)—光栅化(根据分块,计算每块的像素点颜色,此过程会用到GPU加速)—画(合成线程计算每块在屏幕上的位置,交给GPU和显卡进行最终呈现),上一个阶段的输出成为下一个阶段的输入。

2.parse Html 成dom树和cssom树,开一个预解析线程率先下载c s s和解析c s s,提高解析效率,所以c s s不会阻塞html的解析。

3.解析是从上往下进行,遇到j s暂停一切行为,因为要启动v8引擎执行j s,j s有可能改变dom,所以j s会阻塞html的解析。

相关面试题:

1.重排:影响元素几何信息都会造成,重新计算布局以及后续所有操作,j s应该尽可能把这些操作合并计算,reflow是异步完成的,这就是j s在获取属性时,可能无法正确获取到最新的值的原因。当获取属性时应该立即reflow。

2.重绘:影响元素外观属性,比如颜色、背景、边框等。不会重新计算布局,重排一定会引起重绘!

3.为什么transform效率高:因为它经过样式计算后直接到最后一步画,不会经过中间的一系列步骤。

4.如果优化重排,使用tranform,使用will-change创建分层,避免使用table布局、合并操作样式,尽可能使用className来替换样式不使用style。

三、VUE

1.数据响应式与双向绑定

数据响应式:

当数据改变时会自动运行一些相关函数。(1.函数中读取到的数据,2.该数据是响应式对象的某个属性;3.函数是被监控的是函数,vue2:被watcher监控 vue3:被effect监控 常见的有render、watch、watch Effect、computed;)必须是数据和函数的关联

双向绑定:

依托于响应式系统,

VUE2:

1.数据劫持,在组件初始化阶段render函数会通过Object.defineProperty**给每一个属性添加get和set方法,从而能监听属性值的变化**

2.依赖收集,当属性值被读取的时候,触发get函数,收集到哪些组件依赖了此属性。

3.发布订阅,当属性值被设置的时候,触发set函数,通知依赖此属性的所有组件去更新

VUE3:

1.在数据劫持的步骤做出了优化,通过**reactive用Object.proxy来代理对象本身,而不是某个属性。其他的都差不多。

2. VUE2 defineProperty和VUE3 proxy的差异:

vue2中创建一个observer观察器用来观察对象的属性,采用递归遍历每一个对象的每一个属性进行Object.defineProperty进行get(依赖收集)和set(派发更新);缺点1是效率低因为要遍历,2是只能观察到已有的属性,新增属性遗漏,3.需要对数组的方法进行重写;

vue3中创建一个reactive用proxy来代理对象本身,不需要深度遍历读取每个属性,只有当读到某个属性为对象时才会递归,且可以监听到对象的任何操作,新增删除属性;对象深层监听、对数组的自然观察。

3.插槽:

本质上是一个个的对象,每个对象的名字就是插槽的名字,也就是具名插槽。

默认插槽的名字叫default;对象的值是一个函数,里面可以传递参数,也就是作用域插槽;函数返回一个虚拟节点。

4.diff算法与虚拟DOM

虚拟DOM: 本质上是js对象_vnode描述页面的结构,把dom数据化。每一个组件render函数都会创建虚拟dom树。因为直接操作真实dom的创建、更新、插入,会很影响性能,重绘重排。

diff算法:智能高效性能优化的关键技术,首先会根据真实DOM生成虚拟VDOM,使用patch方法比较(循环两边向中间收拢)。当虚拟VDOM的数据发生改变时,会生成新Vnode,使用新的Vnode和oldVnode相比较,发现有不同就直接修改在真实DOM上(打补丁),然后使oldVnode=Vnode,实现一边比较一边打补丁,并且新旧节点为同级比较,如果第一层不一样就直接替换,否则逐层比较。

流程为:当数据改变时、set方法会调用dep.notify通知所有订阅者watcher,订阅者调用patch函数给真实dom打补丁。

5.路由模式

hash:获取不到链接不会发请求,不会给后端带来困扰。可以配一个404页面。且地址栏中带有#。打包后可以看到内容。window.onhashChange监听

history:找不到链接也会发请求。地址栏有/。打包后看不到内容,利用HTML的history API 监听,比如:history.pushState replaceState.

6.vue2和vue3的升级-能说出几个

(1)引入了组合式api

(2)传送门:允许将模版代码渲染到任意节点上,跨层级。

(3)Fragments:片段,允许多个根元素使用的时候进行包裹

(4)suspense异步组件:异步组件加载状态显示占位内容

(5)proxy取代defineProperty

(6)天然t s支持和tree shaking(未引入不打包进去)

(7)静态节点提升:将静态节点、纯文本等提升到渲染函数外,避免重复渲染

(8)v-model增强:支持多个v-model,且可以自定义修饰符,definModel的增加

(9)生命周期调整:由setup代替beforCreate和created.

(10)父子组件的生命周期:

开始——父beforCreate/onBeforeMount——子mounted/onMounted

更新——父beforeUpdate——子beforeUpdate updated——父updated

销毁——父beforeDestory/onBeforeUnmount——子beforeDestory destoryed——父destoryed/onUnmounted

7.vue3 setup函数

语法糖:不需要return,最终语法糖中的代码都会写成setup函数的形式,defineExpose宏可以暴露成员,

script 函数中:需要return,expose可以暴露return中的成员,接受两个参数props和context上下文(attires\slots\emit\expose)

8.vue传值与通信

vue2:事件总线、props、$emit、provide\inject、vuex

vue3:mi t t、 props、emit、 provide\inject、pinia

9.pinia和vuex的区别和使用

**vuex:**有modules可以管理多个模块,commit提交mutations,actions提交mutations,diapatch分发actions,适用于vue2。

pinia: 简化状态管理器的模块只保留state\getter\actions,同步异步都可在actions中操作;可以定义多个store,每个store就是一个模块,用ID划分;可以批量修改数据。适用于vue3。

仓库持久化:在页面离开前保存仓库数据到localStorage,在页面渲染时取出来替换store的值,都有插件可用。

10.$nextTick作用

在下次DOM更新循环结束后,执行延迟回掉,在修改数据之后立即使用这个方法,获取更新后的DOM

11.v-model的使用

(1)vue2:v-model

一个组件上的 v-model 默认会利用名为 value的 属性 和名为 input 的事件

(1)

父组件//////////

子组件////////

<select :value="value" @change="change">

北京

...

// 当在组件上使用v-model时,这里的变量名必须是 value

props:['value'],

methods: {

change(e){

// 当在组件上使用v-model时,这里自定义事件的名字必须是 input

this.$emit('input', e.target.value)

}

}

(2)

在表单元素上,v-model 在内部为不同的输入元素使用不同的property 并抛出不同的事件

可以用.sync 修饰符实现自定义属性的传值

父组件////////////

子组件///////////

<select :value="homeId" @change="change">

山东

...

props:['homeId'],

methods: {

change(e){

this.$emit('update:homeId', e.target.value)

}

}

(2)vue3:dinfineModel

v-model默认使用 modelValue属性 和 update:modelValue事件

12.SSR和SSG的理解

SSR:更快的首屏加载、更好的SEO、统一的语言开发;服务负载高、部署繁杂、开发限制

SSG:适用于静态数据页面的首屏加载、

四、项目构建

1.绝对路径和相对路径:一种书写方式.

指的是url地址,和文件夹毫无关系。发请求一定是完整的url地址,可以用绝对路径和相对路径的书写方式!

绝对路径:完整url地址;可以省略协议(://www.baidu.com/c.js),避免协议不… 上线后可以用当前网站的补全)

相对路径:./(找的是path部分最后的一个/) ; ../(找的是path部分倒数第二个/)

2.技术选型

项目因素:如果是门户网站,选择好用的的方便的框架;如果是需要很好的SEO,可能会考虑nuxt.js;如果需要跨端,可能会选择uni-app\flutter

技术因素:根据自身的技术栈、技术栈的生态系统、可扩展和可维护性。

框架-vue

构建工具-webpack\vite\rspack等

css-less\sass,考虑全局基础样式管理

状态管理-vuex\pinia

路由-vue-router

API-axios

功能模块目录设计---工具类、组件类、api类、样式类、页面类、插件类等等

测试-自测、单元测试、整体测试

打包和性能优化--减少体积、懒加载;

部署与上线--测试

3.npm和pnpm — 幻影依赖问题

npm:安装依赖后有很多其他的依赖出现在node_modules中,并没有出现在dependences中,但在项目中可以正常使用,可能导致部署后找不到报错,且升级某个依赖后,依赖他的依赖可能导致版本不兼容。

pnpm:使用一个仓库存放所有依赖,node_modules里面的依赖都是快捷方式,只有package.json中有的才会放到node_modules中。总仓库在.pnpm中

五、j s知识点

1.箭头函数的作用:

消除函数的二义性(js设计不规范fuc和class不分),和面向对象无关、没有this指向、不能用new、没有原型。

2.前端开发中常见的设计模式

MVC:模型-处理视图逻辑、视图-展示、控制器-处理用户输入

MVVM:模型-、视图-、视图模型-实现视图和模型的双向数据绑定

单例模式:只有一个实例,并提供一个全局访问点,如vue项目中的vue实例

观察者模式:一对多的依赖关系,当一个对象状态改变时,所有依赖的对象都将得到通知,如btn.addEventLisener('click',()),btn和关联,addEventLisener直接关联(没有媒介)

发布订阅模式:一对多的依赖关系,当一个对象状态改变时,所有依赖的对象都将得到通知(有媒介),如eventBus.emit(name)eventBus.emit('name')eventBus.on('name',()),eventBus就是媒介

工厂模式:用一个函数创建实例,返回一个new 创建的实例,如jq的$函数

3.命令式编程、声明式编程、函数式编程

命令式编程:需要明确地告诉计算机如何一步步地完成任务,强调任务的步骤,是具体明确的操作,比如jq,原生j s

声明式编程:需要告诉计算机想要达到的目标,而不关注具体如何实现,比如vue的双向绑定

函数式编程:声明式编程的子集,强调通过编写函数来实现目标。

4.promise和async await

都是处理异步操作的形式

promise:

promise一旦开始执行就不可逆,它有三种状态pending、fulfilled、rejected,他只会从pending--fulfilled或者从pending--rejected.

new promise((resolve,reject)=>{

执行异步事件,

最后通过resolve(输出成功),

reject(输出失败)}).then((res)=>{

输出异步事件的成功结果

}).catch((err)=>{

输出异步事件的错误结果,捕获异常

}).finally(()=>{

最后都会执行这一步

})

promise.race:接受一个包含多个promise的可迭代对象(数组、map、set),并返回第一个解决或拒绝的promise结果

promise.all:接受一个包含多个promise的可迭代对象(数组、map、set),等所有的promise都执行完毕后,返回一个新的promise,在then中处理成功结果,在catch中处理异常结果,只要有一个异常就立即返回失败的原因。

**async await:**是promise的语法糖,通过try/catch捕获异常

async function fetchData() {
	    try {
	        const response = await fetch('https://api.example.com/data');
	        const data = await response.json();
	        console.log(data);
	    } catch (error) {
	        console.error('Fetching data failed:', error);
	    }
	}

5.深拷贝和浅拷贝

深拷贝:

新开辟一个内存空间存放拷贝的数据,不会影响原数据

最好的办法是写一个deepClone的公共方法,传入需要克隆的树,判断数据类型,如果是对象,新建一个对象构造函数new obj.constructor,循环对象再给新对象复制,再次递归。

1.JSON.stringify();(深拷贝普通对象时推荐使用)

2.递归

 // 递归拷贝 (类型判断)

        function deepClone(value,hash = new WeakMap){ // 弱引用,不用map,weakMap更合适一点

          // null 和 undefiend 是不需要拷贝的

          if(value == null){ return value;}

          if(value instanceof RegExp) { return new RegExp(value) }

          if(value instanceof Date) { return new Date(value) }

          // 函数是不需要拷贝

          if(typeof value != 'object') return value;

          let obj = new value.constructor(); // [] {}

          // 说明是一个对象类型

          if(hash.get(value)){

            return hash.get(value)

          }

          hash.set(value,obj);

          for(let key in value){ // in 会遍历当前对象上的属性 和 __proto__指代的属性

            // 补拷贝 对象的__proto__上的属性

            if(value.hasOwnProperty(key)){

              // 如果值还有可能是对象 就继续拷贝

              obj[key] = deepClone(value[key],hash);

            }

          }

          return obj

          // 区分对象和数组 Object.prototype.toString.call

        }

3.第三方库lodash的cloneDeep()方法

浅拷贝:

直接复制,会指向同一个地址,修改一个会影响另一个

6.数组的方法

Array.form():可以把类数组对象转化为数组

Array.of():将一组值转化为数组Array.of(1,2,3) =[1,2,3]

Array.copyWithin():将指定位置的成员复制到其他位置(会覆盖原有成员)

Array.includes():数组是否包含某一个元素,返回true/false

Array.flat(层级数):数组拍扁

Array.reduce(正序)/reduceRight(倒序)

将数组累计:Array.reduce((sum,currentItem,index,arr)=>{ return sum+=currentItem},0初始值)

7.原型链和原型

1.对象是new函数 出来的

2.函数是一个双胞胎,每个函数除了本身还会有一个原型对象prototype

3.这个对象的隐式原型指向prototype,最终形成一个三角关系

最终函数也是一个对象:是通过new function来的

最终prototype是Object出来的,Object的prototype 指向null

一层一层往上找prototype 就形成原型链

8.set、map、object的区别

set:

无序性、唯一性、可以存任何类型的值(无健),可用来去重new Set([...[1,2,3],...[2,3]]) == [1,2,3]

let arr = new Set()

arr.add(1)== {1}. arr.add(2)==={1,2} arr.add(2) == {1,2}

arr.has(1)==true

arr.delete(2)=={2}

arr.clear()=={}

let brr = new Set([1,2,3)]

遍历方法:

brr.values()== 值的集合

brr.keys()== 值的集合

brr.entries() == 值集合对应11 22 33

brr.forEach((value)=>{}) == 遍历set中的每个值

map:

Map 存储的是键值对、有序性、Map 的键可以是任意类型、在频繁增删情况下性能更好、避免原型链污染;

实现了iterator接口,可以使用for...of或.forEach循环。使用 .keys()、.values()、.entries() 获取迭代器。

const map = new Map();

map.set('num',111). map.set(1,'hello'). map.set({id:1},111)

map.has(key) map.get(key). map.delete(key) map.clear()

遍历方法:

map.keys() == 健的迭代器

map.values() == 值的迭代器

map.entries() == 健值对迭代器-默认迭代器

map.forEach((value,key)=>{}) == 遍历map中的每个健值对

object:

存储的是键值对、无序性、键只能为字符串或symbol、Map和Set只能通过new 来创建;

可以继承原型上的方法,比如toString()\constructor

对象本身不可直接迭代,但可以先遍历其键或值的集合,在迭代。通过for...in循环,再使用Object.keys()、Object.values()、Object.entries等方法获取键、值或键值对。

10.call、apply、bind

作用:都适用于改变this的指向,

call:立即执行,多个参数

func.call(作用域,参数1,参数2,...)

apply:立即执行,参数是数组

func.apply(作用域,[参数1,参数2,...])

bind: 不会立即执行。多个参数

func.bind(作用域,参数1,参数2,...)

11.let、const和var

let\const:

都是块级作用域(暂时性死区),只能在代码块中使用,不存在变量提升

在同一代码块中,不允许重复声明

const生命的是一个只读常量

var:

生命的变量会挂在window上

存在变量提升,在声明之前访问不报错

12.typeof和instanceof的区别

**typeof:**用于检测一个变量的基本数据类型。它返回一个表示数据类型的字符串,可能的返回值包括:"number"、"string"、"boolean"、"function"、"object"、"undefined"、"symbol"、"bigint"。对于null、数组和对象,typeof会返回"object"‌

**instanceof:**用于判断一个对象是否是某个构造函数的实例。返回true/false

13.内存泄露和闭包

垃圾:不用了的数据

内存泄漏:垃圾未被回收

垃圾回收:浏览器把访问不到的数据给标记清除,腾出内存空间

闭包:

闭包是指有权访问另一个函数作用域的变量的函数,闭包可以通过函数嵌套和变量引用实现。

1.如果一个本应该被销毁的函数没有回收,那么他里面的词法环境也无法被回收,可能导致内存泄漏

2.共享词法环境中的无用函数,也无法被销毁

防治措施:

变量不用了及时销毁

减少打印和闭包的使用

14.for in和for of

for in适合遍历对象,for in遍历的是数组的索引,对象的属性,以及原型链上的属性。

for of适合遍历数组。

六、网络问题

1.跨域解决方案

原因:浏览器同源策略-由于请求发出--服务器响应-但浏览器校验未通过,目标都是保持生产环境和开发环境一致!

代理:(前端)

webpack或者vite的devServer中配置proxy,如果生产环境是不跨域的,那么前端必须使用代理

CORS方案:(后端)

保证服务器是自己的,让服务器明确表示这个请求是通过的。浏览器收到服务器同意通过,那么允许跨域。

简单请求是在请求时的origin源头上带上访问地址,服务端同意,会在响应头加上Access-Control-Allow-Origin:*(全部通过)或者 访问地址;

预检请求第一步浏览器在Options中带着访问地址、请求方法、参数等去询问服务器,服务器需要明确告知同意允许访问地址、请求方法、参数。第二步再发送真实请求和简单请求一致。

2.简单请求和预检请求

**简单请求:**不动header、请求方式为get\head\post、content-type为text/plain或者multipart/form-data或者application/x-www-form-urlencoded

**预检请求:**凡是不满足简单请求的都是预检请求,比如axios请求传参,他的content-type为application/json

3.http和https的区别

**http:**端口号默认80;明文传输不需要证书;

https: 端口号默认443;使用SSL/TLS对数据进行加密和安全认证,需要CA证书;浏览器发起https请求,服务器会把配置好的公钥证书给到浏览器,浏览器去验证证书的有效性生成对称密钥,然后加密发给服务端,服务端使用私钥解密得到对称密钥,双方得到相同的对称密钥。请求和响应双方都要使用此对称密钥加密和解密。防止冒充、篡改、安全性更高,但更消耗服务器资源。

TCP 三次握手和四次挥手:

三次握手:客户端发起syn询问服务端(你ok吗)---服务端收到回复ack并且询问syn同时进行(我OK,你OK吗)---ke客户端收到回复ack(我也OK),最终建立链接

四次挥手:客户端发起fin告知服务端(我要结束了)---服务端收到回复ack(我知道了)---服务器处理完最后的事情告知客户端fin(我也要结束了)-客户端收到回复ack(我知道了),随后断开连接

4.JWT- json web token 身份验证

原理就是服务端利用一个 密钥+用户端传过来的信息 进行签名计算,再把结果返回到前端,前端获取到签名后存起来,每次请求带过去,后端使用密钥+前端传过来的信息计算签名与带过去的签名比较。确保信息不被篡改。

JWT把格式做了一个标准化,header+payload计算签名signature

5.单点登录SSO

用于用户在多个相关但独立的系统中,只需登陆一次就可以访问所有系统的场景。

提高生产力和用户体验、降低密码管理复杂度、简化用户信息维护等

(1)同域名下的单点登录:

sessionID+cookie模式:强控制力、认证中心服务器压力大

服务器抽离一个SSO登录系统专门管理用户登录权限

前端1登录后拿到服务器返回的凭证sessionID存到cookie,同域名下cookie共享,前端2拿到cookie直接登录

(2)不同域名下的单点登录:

双token模式-jwt:详见刷新token无感章节,无强控制力,服务器压力小

6.web3.0理解

特点:高薪、远程、低门槛、英语、风险高(岗位少、公司存活时间短)

区块链-分布式存储系统

用户数据的主动权,由平台向用户自身转变

法律风险(加密货币)

去中心化,每个模块自己管理自己,无需传统机构代理

技术:技术就是普通的一些前端技术

7.websocket与传统http的作用和区别

1.当客户端想要用websocket来进行即时通讯里,**握手阶段**是采用http/https完成的一次特殊请求。

请求地址为:ws://XXXX. wss://XXX

请求头:connection:upgrade,upgrade:webscoket,版本号、websocket-key

响应:消息码**101** 切换协议、connection:upgrade,upgrade:webscoket,版本号、websocket-key

2.区别:

都是基于TCP的协议(全双工)

websocket:(全双工)

持久高效通信帧传递、无需处理跨域问题、支持文本和二进制、与http兼容;增加服务器资源消耗、兼容性、发送的数据包不能超过2GB

http/https:(半双工)

3.socket和websocket:

socket:在传输层,将两个设备之间搭建一个通道,可以允许通信,提供一个接口功能,技术比较麻烦,但能处理更多难度的通信需求。

websocket:在网络层,主要是让网页里的双向实时通信变得简单的协议,容易上手。

8.如何取消a xi o s请求

请求实际上是不可以取消,取消的是发送端的响应接收。必须删除操作是不可以的,删除接口发送后,后端已经删除了,前端却不知道。

实际上是调用XHR的abort方法

对于axios来说,内部的cancelToken方法可以取消请求,请求重复取消接下来的请求,可以让每一个请求都创建一个唯一key保存,每次请求判断是否存在重复。

9.刷新token无感

1.登录时会返回一个长token和一个短token,长token用来请求新的短token,短token是用来请求资源的时候Authorization加上的;axios拦截请求保存两个长短对应token到localstorage。

2.请求接口当拦截到不是刷新token的请求响应401了,重新用长token去请求刷新短token,保存新的短token重新请求接口axiosServer.request(res.config)

3.当请求刷新短token的响应401了,代表长token也失效了,退出登录

10.浏览器SEO

1.网页title设置、meta标签 网站描述和关键字

2.语义化代码如head、footer、article

3.图片加上alt属性

4.重要的内容放在最前面,不用js输出

5.使用text-indent将H1标签内缩隐藏增加关键字,一般加载logo里

6.少用iframe,搜索引擎不会抓取里面的内容

7.友情链接i标签,链接和自己网站差不多内容差不多并且质量高的网站

11.大屏适配解决方案(vue3-scale-box)

1.保持16:9的比例

以1920*1080的设计图来计算,获取根节点大小,当window进行resize的时候,计算浏览器宽高innerWidth\innerHeight与设计图宽高的对应比例,找到最小的比例用来缩放

2.计算(浏览器的宽高-设计图的宽高*缩放比例)/2的值进行top和left的定位

transform:translate(left top) scale(最小值)

3,可以把变形做一个过渡动画

4.可以把窗口的改变做一个防抖

12.如何解决页面大规模请求接口

背景:大屏接口过多、数据采集平台接口多

方法:

1.滑动窗口逻辑---队列的形式

封装函数参数为(任务列表,并发数),返回一个promise,里面包含并发数任务依次执行,当前任务执行完后运行下一个已有任务,当任务执行完并后resolve。

2.防抖、节流

防抖:取短时间内的最后一次

节流:取规定时间间隔上的那些次,限制频率

3.分页加载和无限滚动

13.如何处理页面白屏

具体情况具体分析,

performance---summery看文件执行的时间长短(js\render\还是网络造成)

1.使用v-show代替v-if、静态资源压缩、异步加载方式、减少请求数量、提前渲染关键内容。

2.使用延迟装载(defer),按帧requestAnimateFrame渲染,可以使用户先看到视口内容。

3.link资源网络请求可以进行强缓存,通过和后端上了开启强缓存

4.dns域名解析提前,提前找到所有域名,通过link rel=dns-prefetch进行提前解析

5.骨架屏、使用预加载link rel=prefetch 或者 link rel=preload

6.tree shaking去除未使用代码

7.compression 压缩 资源处理首屏优化,vite主要通过Gzip压缩、代码分割和资源预加载等策略实现

七.css问题

1.清除浮动

浮动元素会脱离文档流,不占据空间,可以左右移动直到碰到父元素或者其他浮动元素。浮动元素必须有宽度,具有行内块元素的特性。紧挨浮动元素间不会有缝隙。

解决浮动元素引起父元素高度塌陷的方法:

为父元素设置高度和overflow:hidden,为最后一个子元素设置clear:borh.

2.link和@import的区别

1.link:

是XHTML的标签,不存在兼容问题,

link引入css,是同步加载,在页面引入时同时加载,还可以加载其他文件

link标签是DOM元素,支持使用js控制DOM和修改样式

2.@import:

不支持低版本浏览器,存在兼容问题

只能加载css文件

引入css,是异步加载,需要等页面完全载入后再加载css

@import是一种方式,不支持使用js控制DOM和修改样式

3.水平垂直居中

1.父元素相对定位+子元素绝对定位;子元素上下左右:0,margin:0

2.父元素相对定位+子元素绝对定位;子元素top,left设置50%,transform:translate(-50%,-50%)

3.flex布局:display:flex;justify-content:center;align-items:center;

4.grid布局:display:grid;justify-items:center;align-content:center;

4.绝对定位和浮动的区别

1.都可以让元素脱离文档流,获得块级属性,可以设置宽高

2.float仍然会占位置,绝对定位不会占位置,但会遮盖其他元素

5.文字超宽显示省略号…

a:{

    width:200px;

    overflow:hidden;

    text-overflow:ellipsis;

  white-space:nowrap

}

6.css的属性继承

当一个元素的每一个css属性没有计算出确定的值时,这时才需要继承属性。顺序是先看设置的值、再看层叠(有冲突的样式比较重要性-确定最终值)、再看继承、最后才是浏览器默认值。

可继承:字体相关、文本相关、visibility

7.盒模型

盒子的组成=content+padding+border+margin(可以除外)

浏览器属性box-sizing:content-box(默认,width和height为内容尺寸)和border-box(width和height为边框以内的尺寸)

八、其他--待补充(微前端、低代码、electron可能会放到下一篇)