虚拟 dom:
虚拟 dom 就是 vue 通过 js 对象渲染虚拟 dom 的,虚拟 dom 的 js 对象包含节点的类型、属性、子节点等信息,这些虚拟 dom 节点会构成一棵树形结构,用来表示整个页面的结构。
当 vue 组件更新时,会通过 diff 算法进行计算,diff 算法是先比较记录再批量更新操作。
diff 算法流程分别有(新 dom 树会和缓存的旧 dom 树进行比较)
根节点比较、逐层比较
:从树的根节点开始,确定根节点是否相同、找出节点直接的差异(如果节点类型不同则会停止对子级节点比较,但只是节点属性、内容不同则会记录,并继续对子级的比较记录)。这只是 diff 算法的第一步,用于记录确定哪些部分需要更新。
同级比较
:在同一级的节点直接进行比较、找出增删改的节点。
叶子节点比较
:对叶子节点进行比较
key比较
:如果节点有唯一标识符 key,则 diff 算法会使用这些表示来更精确的比较节点
示例:
<div id="app">
<h1>Hello, {{ name }}!</h1>
<p>This is a paragraph.</p>
</div>
虚拟 dom 的 js 树形对象结构如下
{
tag: 'div',
attrs: {
id: 'app'
},
children: [
{
tag: 'h1',
children: [
'Hello, ',
{
data: 'name' // 数据绑定的部分
},
'!'
]
},
{
tag: 'p',
children: [
'This is a paragraph.'
]
}
]
}
双向数据绑定的原理
采用数据劫持结合发布者-订阅者模式的方式,data 数据在初始化的时候,会实例化一个 Observe 类,在它会将 data 数据进行递归遍历,并通过 Object.defineProperty 方法,给每个值添加上一个 getter 和一个 setter。在数据读取的时候会触发 getter 进行依赖(Watcher)收集,当数据改变时,会触发 setter,对刚刚收集的依赖进行触发,并且更新 watcher 通知视图进行渲染。
slot 插槽
默认插槽:又名匿名插槽,当 slot 没有指定 name 属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
具名插槽:带有具体名字的插槽,也就是带有 name 属性的 slot,一个组件可以出现多个具名插槽。
作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。
keep-alive 的理解
keep-alive 是 Vue.js 的一个内置组件。它能够将不活动的组件实例保存在内存中,而不是直接将其销毁,它是一个抽象组件,不会被渲染到真实 DOM 中,也不会出现在父组件链中。
keep-alive 内部其实是一个函数式组件,没有 template 标签。在 render 中通过获取组件的 name 和 include、exclude 进行匹配。匹配不成功,则不需要进行缓存,直接返回该组件的 vnode。
匹配成功就进行缓存,获取组件的 key 在 cache 中进行查找,如果存在,则将他原来位置上的 key 给移除,同时将这个组件的 key 放到数组最后面(LRU)也就实现了 max 功能。
不存在的话,就需要对组件进行缓存。将当前组件 push(key)添加到尾部,然后再判断当前缓存的 max 是否超出指定个数,如果超出直接将第一个组件销毁(缓存淘汰策略 LRU)。
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
$nextTick 原理及作用
Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。 nextTick 是将回调函数放到一个异步队列中,保证在异步更新 DOM 的 watcher 后面,从而获取到更新后的 DOM。
因为在 created()钩子函数中,页面的 DOM 还未渲染,这时候也没办法操作 DOM,所以,此时如果想要操作 DOM,必须将操作的代码放在 nextTick()的回调函数中。
Vue 模版编译原理
模版编译主要过程:template —> ast —> render,分别对象三个方法
parse 函数解析 template
optimize 函数优化静态内容
generate 函数创建 render 函数字符串
调用 parse 方法,将 template 转化为 AST(抽象语法树),AST 定义了三种类型,一种 html 标签,一种文本,一种插值表达式,并且通过 children 这个字段层层嵌套形成了树状的结构。
optimize 方法对 AST 树进行静态内容优化,分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化。
generate 将 AST 抽象语法树编译成 render 字符串,最后通过 new Function(render)生成可执行的 render 函数
路由守卫
全局前置钩子:
beforeEach:在每次路由跳转之前触发。可以用于进行全局的导航守卫,例如检查路由权限验证、路由跳转控制
beforeResolve:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后触发。常用于等待异步逻辑完成后再跳转。
afterEach:在导航成功完成之后触发。适合用于执行一些页面切换后的全局操作,比如页面埋点或者页面加载完成的动画。
路由独享守卫:
beforeEnter:在路由配置中独立定义的守卫,针对某个具体路由生效。可以用于对特定路由进行额外的验证或处理。
组件内钩子:
beforeRouteEnter:在进入路由对应的组件之前被调用,此时组件实例还未创建,无法访问 this。可以通过传入回调函数来访问组件实例。
beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用。可以用于在路由参数发生变化时更新组件数据。
beforeRouteLeave:在离开当前路由对应的组件时触发。可以用于在离开页面前进行一些确认操作,比如弹出确认框或者保存数据。
设置动态路由
params 传参
query 传参
mixin 和 mixins 区别
mixin 用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的。
mixins 应该是最常使用的扩展组件的方式了。如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。
v-model 是如何实现的,语法糖实际是什么?
Vue 中数据双向绑定是一个指令 v-model,可以绑定一个响应式数据到视图,同时视图的变化能改变该值。
当作用在表单上:通过 v-bind:value 绑定数据,v-on:input 来监听数据变化并修改 value
当作用在组件上:本质上是一个父子通信语法糖,通过 props 和$emit 实现。
Computed 和 Watch 的区别
computed 计算属性,通过对已有的属性值进行计算得到一个新值。它需要依赖于其他的数据,当数据发生变化时,computed 会自动计算更新。computed 属性值会被缓存,只有当依赖数据发生变化时才会重新计算,这样可以避免重复计算提高性能。
watch 用于监听数据的变化,并在变化时执行一些操作。它可以监听单个数据或者数组,当数据发生变化时会执行对应的回调函数,和 computed 不同的是 watch 不会有缓存。
MVVM 的理解
MVVM 是一种软件架构模式,MVVM 分为 Model、View、ViewModel:
Model 代表数据模型,数据和业务逻辑都在 Model 层中定义;
View 代表 UI 视图,负责数据的展示;
ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;
Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。
async/await 的理解
通过 async 关键字声明一个异步函数, await 用于等待一个异步方法执行完成,并且会阻塞执行。
async 函数返回的是一个 Promise 对象,如果在函数中 return 一个变量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。如果没有返回值,返回 Promise.resolve(undefined)
说说你对 Promise 的理解
Promise 是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了地狱回调。
Promise 的实例有三个状态:
Pending(初始状态)
Fulfilled(成功状态)
Rejected(失败状态)
Promise 的缺点:
无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
promise.all()可以完成并行任务,将多个 Promise 实例数组,包装成一个新的 Promise 实例,返回的实例就是普通的 Promise。有一个失败,代表该 Primise 失败。当所有的子 Promise 完成,返回值时全部值的数组
promise.race()类似 promise.all(),区别在于有任意一个完成就算完成
promise.allSettled() 等所有 Promise 执行完毕后,不管成功或失败, 都会把每个 Promise 状态信息放到一个数组里面返回
原型
prototype : js 通过构造函数来创建对象,每个构造函数内部都会一个原型 prototype 属性,它指向另外一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。
proto: 当使用构造函数创建一个实例对象后,可以通过proto访问到 prototype 属性。
constructor:实例对象通过这个属性可以访问到构造函数
原型链
每个实例对象都有一个proto属性指向它的构造函数的原型对象,而这个原型对象也会有自己的原型对象,一层一层向上,直到顶级原型对象 null,这样就形成了一个原型链。
当访问对象的一个属性或方法时,当对象身上不存在该属性方法时,就会沿着原型链向上查找,直到查找到该属性方法位置。
原型链的顶层原型是 Object.prototype,如果这里没有就只指向 null
对闭包的理解已经它的使用场景
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量。
闭包优点:
创建全局私有变量,避免变量全局污染
可以实现封装、缓存等
闭包缺点:
创建的变量不能被回收,容易消耗内存,使用不当会导致内存溢出
解决: 在不需要使用的时候把变量设为 null
使用场景:
用于创建全局私有变量
封装类和模块
实现函数柯里化
闭包一定会造成内存泄漏吗?
闭包并不一定会造成内存泄漏,如果在使用闭包后变量没有及时销毁,可能会造成内存泄漏的风险。只要合理的使用闭包,就不会造成内存泄漏。
对作用域、作用域链的理解
作用域是一个变量或函数的可访问范围,作用域控制着变量或函数的可见性和生命周期。
全局作用域:可以全局访问
最外层函数和最外层定义的变量拥有全局作用域
window 上的对象属性方法拥有全局作用域
为定义直接复制的变量自动申明拥有全局作用域
过多的全局作用域变量会导致变量全局污染,命名冲突
函数作用域:只能在函数中访问使用哦
在函数中定义的变量,都只能在内部使用,外部无法访问
内层作用域可以访问外层,外层不能访问内存作用域
ES6 中的块级作用域:只在代码块中访问使用
使用 ES6 中新增的 let、const 什么的变量,具备块级作用域,块级作用域可以在函数中创建(由{}包裹的代码都是块级作用域)
let、const 申明的变量不会变量提升,const 也不能重复申明
块级作用域主要用来解决由变量提升导致的变量覆盖问题
作用域链:
变量在指定的作用域中没有找到,会依次向一层作用域进行查找,直到全局作用域。这个查找的过程被称为作用域链。
JavaScript 共有八种数据类型
分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。
这些数据可以分为原始数据类型和引用数据类型(复杂数据类型),他们在内存中的存储方式不同。
堆: 存放引用数据类型,引用数据类型占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,如 Object、Array、Function。
栈: 存放原始数据类型,栈中的简单数据段,占据空间小,属于被频繁使用的数据,如 String、Number、Null、Boolean。
Sass、Less 的区别
他们都是 CSS 预处理器,是 CSS 上的一种抽象层。他们是一种特殊的语法/语言编译成 CSS。都支持变量、混合(mixin)和嵌套规则
语法:
Sass 使用严格的缩进来表示代码块和层级关系。支持条件语句,可以使用 if{}else{},for{}循环等等,而 Less 不支持,可以用来生成重复的样式规则。
Less 使用类似于更接近传统的 CSS 的语法,但添加了一些额外的功能,如变量、混合(mixin)和嵌套规则。
扩展性:
Sass 提供了更多的功能和内置函数,如条件语句、循环等,使得它在某种程度上更加强大和灵活。
Less 相对来说功能较为简单,但也足够满足大多数的样式表需求。
变量符不一样,Less 是@,而 Scss 是$。
进程和线程
进程(Process)
进程是计算机中正在运行的程序的实例,一个进程就是一个程序运行实例。它拥有独立的内存空间、代码和数据,并且由操作系统负责调度和管理。每个进程在执行时都会分配独立的内存空间,不同进程之间的内存是隔离的,一个进程的错误不会直接影响其他进程。 进程之间通过进程间通信(IPC)机制来交换数据和进行通信,常见的 IPC 方式包括管道、消息队列、共享内存等。进程的切换开销较大,因为需要保存和恢复进程的完整状态,涉及到内存保护和虚拟内存的切换。
线程(Thread)
线程是进程的子任务,一个进程可以包含多个线程。它们共享相同的代码和数据,但拥有独立的执行栈和寄存器集合。多个线程可以在同一进程内并发执行,共享进程的资源,如内存空间、打开的文件等。线程间的通信和数据交换比进程间的通信更加方便,因为它们共享相同的地址空间。线程的切换开销较小,因为线程共享进程的地址空间,切换时不需要切换内存页表,速度较快。
区别:
进程和线程都可以实现并发执行,但进程是独立的执行实体,而线程是依赖于进程的。
进程之间资源相互隔离,线程共享所属进程的资源。
创建和销毁线程的开销较小,而创建和销毁进程的开销较大。
多线程程序的编程复杂度通常比单线程程序高,但多线程可以更好地利用多核处理器来提高程序的执行效率。
浏览器有哪些进程
主进程:负责处理用户输入、渲染页面等主要任务。
渲染进程:渲染进程负责解析 HTML、CSS 和 JavaScript,并将网页渲染成可视化内容。
GPU 进程:负责处理浏览器中的 GPU 加速任务。
网络线程:网络进程负责处理浏览器中的网络请求和响应,包括下载网页和资源等。
插件进程:负责浏览器插件运行。
Cookie、LocalStorage、SessionStorage 区别
Cookie
大小只有 4kb
跨域不能共享
不安全,容易被劫持
只存在请求头中
SessionStorage
存储在内存中,体积相对较大
页面关闭,数据会消失
相对 Cookie 安全
LocalStorage
体积大,可以存储更多内容。
生命周期长,除非手动删除,不然会一直存在
存储在硬盘中,不会像 Cookie 一样被请求携带
什么是同源策略
跨域问题其实就是浏览器的同源策略造成的。 同源指的是:协议、端口号、域名必须一致。
如何解决跨越问题
CORS:服务器开启跨域资源共享
JSONP:利用<script>标签不存在跨域限制,只支持 GET 请求,且不安全。
nginx 代理跨域
nodejs 中间件代理跨域,通过 node 开启一个代理服务器。
webpack 的构建流程
初始化参数:从配置文件或者 shell 语句中读取合并参数
开始编译:用参数初始化 Compiler 对象,加载所有配置的插件,执行 run 方法。
确定入口:根据 entry 参数找到入口文件
编译模块:从⼊⼝⽂件出发,调⽤所有配置的 Loader 对模块进⾏翻译,再找出该模块依赖的模块,再递归本步骤直到所有⼊⼝依赖的⽂件都经过了本步骤的处理;
完成模块编译:在经过第 4 步使⽤ Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
输出资源:根据⼊⼝和模块之间的依赖关系,组装成⼀个个包含多个模块的 Chunk,再把每个 Chunk 转换成⼀个单独的⽂件加⼊到输出列表,这步是可以修改输出内容的最后机会;
输出完成:在确定好输出内容后,根据配置确定输出的路径和⽂件名,把⽂件内容写⼊到⽂件系统
总结就是三个阶段:
初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
Webpack 的热更新(Hot Module Replacement)?原理是什么?
Webpack 的热更新(Hot Module Replacement,简称 HMR),在不刷新页面的前提下,将新代码替换掉旧代码。
HRM 的原理实际上是 webpack-dev-server(WDS)和浏览器之间维护了一个 websocket 服务。当本地资源发生变化后,webpack 会先将打包生成新的模块代码放入内存中,然后 WDS 向浏览器推送更新,并附带上构建时的 hash,让客户端和上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求获取到更改后的内容(文件列表、hash),通过这些信息再向 WDS 发起 jsonp 请求获取到最新的模块代码。
如何提高 webpack 的打包速度
利用缓存:利用 Webpack 的持久缓存功能,避免重复构建没有变化的代码。可以使用 cache: true 选项启用缓存。
使用多进程/多线程构建 :使用 thread-loader、happypack 等插件可以将构建过程分解为多个进程或线程,从而利用多核处理器加速构建。
使用 DllPlugin 和 HardSourceWebpackPlugin: DllPlugin 可以将第三方库预先打包成单独的文件,减少构建时间。HardSourceWebpackPlugin 可以缓存中间文件,加速后续构建过程。
使用 Tree Shaking: 配置 Webpack 的 Tree Shaking 机制,去除未使用的代码,减小生成的文件体积
移除不必要的插件: 移除不必要的插件和配置,避免不必要的复杂性和性能开销。
如何减少打包后的代码体积
代码分割(Code Splitting):将应用程序的代码划分为多个代码块,按需加载。这可以减小初始加载的体积,使页面更快加载。
Tree Shaking:配置 Webpack 的 Tree Shaking 机制,去除未使用的代码。这可以从模块中移除那些在项目中没有被引用到的部分。
压缩代码:使用工具如 UglifyJS 或 Terser 来压缩 JavaScript 代码。这会删除空格、注释和不必要的代码,减小文件体积。
使用生产模式:在 Webpack 中使用生产模式,通过设置 mode: 'production’来启用优化。这会自动应用一系列性能优化策略,包括代码压缩和 Tree Shaking。
使用压缩工具:使用现代的压缩工具,如 Brotli 和 Gzip,来对静态资源进行压缩,从而减小传输体积。
利用 CDN 加速:将项目中引用的静态资源路径修改为 CDN 上的路径,减少图片、字体等静态资源等打包。
宏任务和微任务
Javascript 是单线程语言,所以一切 Javascript “多线程” 都是单线程模拟出来的既然是单线程,就会出现拥挤,也就有了 “同步任务和异步任务。”
队列分为两种:宏任务队列和微任务队列,宏任务(macrotask)和微任务(microtask)是与事件循环(event loop)相关的概念,用于管理异步操作的执行顺序。
同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入 Event Table 并注册函数。
当指定的事情完成时,Event Table 会将这个函数移入 Event Queue。
主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的 Event Loop(事件循环)。
- 同步任务(普通任务):例如全局代码、函数调用等,按照代码的顺序同步执行,不会被放入宏任务队列或微任务队列中。
- 宏任务(macrotask):
-
宏任务包括:
-
Script(脚本任务):整体代码块作为一个宏任务执行。
-
setTimeout 和 setInterval:通过
setTimeout
和setInterval
添加的任务会被放入宏任务队列中,在指定的时间后执行。 -
I/O 操作:包括读写文件、网络请求等异步操作,它们会被放入宏任务队列中,等待执行。
-
UI 渲染:浏览器需要执行的与页面渲染相关的任务,比如重绘和回流,也属于宏任务。
-
事件处理程序:当用户触发事件(比如点击、滚动等)时,事件处理程序会被添加到宏任务队列中,等待执行。
-
requestAnimationFrame:用于在浏览器下一次重绘之前执行指定的任务,也属于宏任务。
-
宏任务会被放入宏任务队列中,在事件循环的每一轮中执行一个宏任务。
-
当执行完一个宏任务后,事件循环会检查微任务队列,然后执行所有微任务,再进入下一个宏任务。
-
发起者:宿主(Node、浏览器)
-
会触发新一轮 Tick
-
微任务的执行时长会影响到当前宏任务的时长。比如一个宏任务在执行过程中,产生了 100 个微任务,执行每个微任务的时间是 10 毫秒,那么执行这 100 个微任务的时间就是 1000 毫秒,也可以说这 100 个微任务让宏任务的执行时间延长了 1000 毫秒。所以你在写代码的时候一定要注意控制微任务的执行时长
- 微任务(microtask):
- 微任务包括:
- Promise:当 Promise 对象状态变为 resolved 或 rejected 时,与之相关联的回调函数会被放入微任务队列中,等待执行。
- MutationObserver:用于监视 DOM 变化的 API,当指定的 DOM 变化发生时,注册的回调函数会被放入微任务队列中。
- process.nextTick(Node.js 环境下):用于将回调函数放入微任务队列,确保在当前操作完成后立即执行。
- queueMicrotask:用于将一个微任务函数放入微任务队列中。
微任务会在每一轮事件循环中的特定阶段执行,通常在当前宏任务执行完毕后、下一个宏任务开始之前执行。事件循环会首先处理所有的微任务,然后再执行下一个宏任务。这种机制确保微任务能够及时响应异步操作的完成,并且在当前任务执行结束后立即执行。
- 微任务会被放入微任务队列中,在每个宏任务执行完毕之前会立即执行微任务。
- 微任务队列优先级高于宏任务队列,即微任务队列中的任务会在下一个宏任务之前执行完。
- JS 引擎
- 不会触发新一轮 Tick
下面是一个简单的示例,演示了宏任务和微任务的执行顺序:
console.log("Start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");
在这个例子中,代码的执行顺序是:
- 打印 ‘Start’
- 打印 ‘End’
- 执行 Promise 微任务,打印 ‘Promise’
- 执行 setTimeout 宏任务,打印 ‘setTimeout’
下面进阶版示例:
console.log("1");
setTimeout(function () {
console.log("2");
process.nextTick(function () {
console.log("3");
});
new Promise(function (resolve) {
console.log("4");
resolve();
}).then(function () {
console.log("5");
});
});
process.nextTick(function () {
console.log("6");
});
new Promise(function (resolve) {
console.log("7");
resolve();
}).then(function () {
console.log("8");
});
setTimeout(function () {
console.log("9");
process.nextTick(function () {
console.log("10");
});
new Promise(function (resolve) {
console.log("11");
resolve();
}).then(function () {
console.log("12");
});
});
- 先执行同步任务输出 1
- 执行 promise 微任务里的 pending 初始化状态 输出 7
- 根据先进先出原则,对 nextTick 的代码输出 6
- 执行 promise 微任务 输出 8
- 现在执行宏任务,第一个 setTimeout 里有同步任务,输出 2
- 继续执行第一个 setTimeout 里微任务 promise 的 pending 初始化状态 输出 4
- 先进先出原则,执行微任务 输出 3
- 先进先出原则,执行微任务 输出 5
- 执行第二个宏任务里的 setTimeout,该宏任务先执行同步任务 输出 9
- 继续执行第二个 setTimeout 里微任务 promise 的 pending 初始化状态 输出 11
- 先进先出原则,执行微任务 输出 10
- 先进先出原则,执行微任务 输出 12
在一个事件循环中,执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
这里容易产生一个错误的认识:就是微任务先于宏任务执行。实际上是先执行同步任务,异步任务有宏任务和微任务两种,先将宏任务添加到宏任务队列中,将宏任务里面的微任务添加到微任务队列中。所有同步执行完之后执行异步,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行。之后就一直循环…
h5 自适应方案
在网页开发中,实现 H5 页面的自适应可以让页面在不同设备上展现出更好的效果。以下是一些常用的 H5 自适应方案:
- Viewport Meta 标签:Viewport Meta 标签是用来控制页面在移动设备上的视口大小和缩放比例的。通过设置 Viewport Meta 标签,可以让页面根据设备的宽度进行自适应调整。例如:
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
-
流式布局:使用百分比或者 em/rem 单位来设置元素的宽度,使得页面元素随着视口大小的改变而自动调整大小。rem 是一种相对长度单位,通常用于前端开发中的样式表(CSS)中。REM 单位是相对于根元素(html 元素)的字体大小(font-size)来计算的。
-
媒体查询(Media Queries):通过 CSS3 的媒体查询功能,根据设备的特性(如屏幕宽度、设备类型等)来应用不同的样式。可以针对不同的屏幕尺寸定义不同的样式,从而实现响应式设计。
@media screen and (max-width: 600px) {
/* 在屏幕宽度小于600px时应用的样式 */
}
- Flexbox 布局:使用 Flexbox 布局可以更方便地实现弹性布局,使得页面元素在不同设备上的排列和对齐更加灵活。
前端知识大全地址:https://juejin.cn/post/7270471613547249699