问题集合
inline和block的区别?
block:该元素生成一个块级盒子,在正常的流中,该元素之前和之后产生换行。
inline:该元素生成一个或多个行级盒子,它们之前或者之后并不会产生换行。在正常的流中,如果有空间,下一个元素将会在同一行上。
inline-block:结和了块级元素:可以设置属性:width、height、margin-top和margin 和内联元素:多个元素在一行显示的特点。
行内元素:<span>
、<a>
、<strong>
、<em>
、<img>
、<input>
、<label>
块级元素:<div>
、<p>
、<h1>-<h6>
、<ul>
、<ol>
、<li>
、<table>
行内元素和块级元素的区别?
1、行内元素不产生独立的框,宽度和高度由内容决定,而块级元素会生成一个独立的矩形框,可以设置宽度、高度、边距和填充等属性;
2、行内元素在同一行上水平排列,而块级元素会自上而下按顺序排列;
3、行内元素不能包含块级元素,而块级元素可以包含其他块级元素和行内元素等。
块级元素独占一行,可以设置属性:width、height、margin-top和margin-bottom。
行级元素多个元素在一行显示,设置属性:width、height、margin-top、margin-bottom无效。
页面头部的meta标签的作用?
meta是文档级元数据元素,用来表示那些不能由其它 HTML 元相关元素之一表示的任何元数据。
- name属性:
<meta name="author" content="aaa@mail.abc.com">
author:用来表示网页的作者的名字,例如某个组织或者机构。
description:简短精确的、对页面内容的描述。SEO
keywords:与页面内容相关的关键词,使用逗号分隔。某些搜索引擎在遇到这些关键字时,会用这些关键字对文档进行分类。
viewpoint:为viewport的初始化大小提供指示。
robots:爬虫相关:content:all
、none
、index
[索引此页]、follow
[链接其他网页] - http-equiv属性:
X-UA-Compatible:做IE适配。<meta http-equiv="content-type" content="text/html;charset=utf-8">
content-type:用来声明文档类型和字符集。
html代码第一行的作用?
HTML 代码的第一行用于声明文档的类型,并且告诉浏览器使用哪种 HTML 的标准来解析页面。
实现div的水平垂直布局?
- 已知宽高,设置
margin:auto
,可以实现元素的水平居中。 - 设置position.
- 1
1
2
3
4
5
6
7
8
9
10
11
12
13.father{
width:100px;
height:100px;
position:relative; <!-- 父元素设置相对定位 -->
}
.son{
width:50px;
height:50px;
position:absolute; <!-- 子元素设置绝对定位 -->
top:50%; <!-- 这里的50%是父元素高度的50% -->
left:50%;
transform:translate(-50%,-50%); // 这里的50%是子元素的宽高的50%
} - 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.father{
width:100px;
height:100px;
position:relative; <!-- 父元素设置相对定位 -->
}
.son{
width:50px;
height:50px;
position:absolute; <!-- 子元素设置绝对定位 -->
top:0; <!--可以简写成 inset:0-->
left:0;
top:0;
bottom:0;
margin:auto;
}
- 1
- 视口居中,以上代码中设置
position:fixed
。 - 未知尺寸元素居中。以上代码中设置
height:fit-content
。 - 文本居中。以上代码中设置
text-align:center
。 - flex/grid。
- flex
1
2
3
4
5
6
7.father{
width:100px;
height:100px;
position:flex;
justify-content:center;
align-items:center;
} - gridhttps://www.joshwcomeau.com/css/center-a-div/
1
2
3
4
5
6
7.father{
width:100px;
height:100px;
position:grid;
place-content: center;
place-items: center; <!--单个单元格内的元素也居中-->
}Cookies、sessionStorge、localStorge?
- flex
cookies:4kb~8kb,特点:可以设置token过期时间,客户端和服务端都可以修改。
安全性:容易受到CSRF攻击。用途:主要用于跟踪用户会话,存储用户偏好设置以及实现永久登陆等功能。
sessionStorge:特点:只要关闭网站页面标签或关闭浏览器就需要重新登陆,因为数据会被清除,刷新不会导致重新登陆。安全性:安全性较高,不容易受CSRF攻击。用途:适合临时存储会话数据,比如临时保存表单数据等。
localStorge:特点:LocalStorage 是持久化存储机制,数据在浏览器关闭后仍然保留,除非显式删除。数据在同源的所有窗口、标签页中均可共享。安全性:不容易受到CSRF攻击,但可能受到XXS(跨站脚本攻击)影响,用途:主要用于跟踪用户会话,存储用户偏好设置以及记住登陆状态等功能。
href和src的区别?
src用于替换当前元素,而href用于在当前文档和引用资源之间建立联系。
当我们在link元素中使用href属性来链接CSS文件时,浏览器会识别该文档为CSS文档,并行下载该文档,同时不会停止对当前文档的处理。
我们在script元素中使用src属性来链接JavaScript文件时,浏览器在解析到该元素时,会暂停浏览器的渲染,直到该资源加载完毕。
vue中computed和watch的区别?
computed:
computed 是一个计算属性,它依赖于一个或多个响应式数据,并根据这些依赖自动计算一个新的派生值。
computed 的结果会被缓存,只有当依赖的响应式数据发生变化时,才会重新计算结果。
computed 的值是同步获取的,可以像访问普通属性一样使用,不需要显式地调用函数。
watch:
watch 用于观察一个或多个数据的变化,并在数据变化时执行指定的回调函数。
watch 的回调函数是异步执行的,默认情况下在数据变化后的下一个事件循环周期中执行。
watch 可以用于监听多个数据的变化,也可以执行一些异步操作,比如发起网络请求或执行动画等。
watch和watchEffect的区别?
watch:
watch 需要显式地指定要观察的响应式数据,并在回调函数中处理数据变化。
watch 的回调函数接收两个参数,新值和旧值,以便你可以比较它们的差异。
watch 可以监听多个数据,通过配置选项来进行更复杂的操作。
watch 需要指定immediate: true
强制侦听器的回调立即执行。
watchEffect:
watchEffect 是一个更简化的 API,它会自动追踪在其内部使用的响应式数据,并在这些数据变化时自动运行回调函数。
watchEffect 的回调函数不需要显式地指定要观察的数据,它会自动检测依赖并运行。
watchEffect 的回调函数不接收新值和旧值,因为它只关心执行代码块时的数据状态。
watchEffect 会立即执行一次。
在watchEffect传入异步可能会影响依赖收集。
float?
float CSS 属性指定一个元素应沿其容器的左侧或右侧放置,允许文本和内联元素环绕它。该元素从网页的正常流动(文档流)中移除,但是仍然保持部分的流动性。当一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
清除浮动?
1 | clear: left/right/both |
包含块
包含块分为两种,一种是根元素(HTML 元素)所在的包含块,被称之为初始包含块(initial containing block)。对于浏览器而言,初始包含块的的大小等于视口 viewport 的大小,基点在画布的原点(视口左上角)。它是作为元素绝对定位和固定定位的参照物。
另外一种是对于非根元素,对于非根元素的包含块判定就有几种不同的情况了。大致可以分为如下几种:
- 如果元素的 positiion 是 relative 或 static ,那么包含块由离它最近的块容器(block container)的内容区域(content area)的边缘建立。
- 如果 position 属性是 fixed,那么包含块由视口建立。
- 如果元素使用了 absolute 定位,则包含块由它的最近的 position 的值不是 static (也就是值为fixed、absolute、relative 或 sticky)的祖先元素的内边距区的边缘组成。
HTMLCollection和NodeList
HTMLCollection:
- Document.getElementsByClassName()
- Document.getElementsByTagName()
返回的是伪数组,和DOM元素强绑定,这意味着DOM发生变化,数组也会自动变化。
NodeList:
Document.querySelectorAll()
返回的是伪数组,不和DOM强绑定。
箭头函数和普通函数
- 箭头函数不能通过
new
调用,普通函数可以。 - 箭头函数没有自己的this,内部的this指向定义时的上层作用域。
- 箭头函数不存在变量提升问题,所以调用要在定义之后。
- 箭头函数不能使用
yield
,不能作为generator函数。 - 箭头函数不能使用
arguments
对象。
socketIO解决了什么问题?
把消息放在不同的事件当中,通过监听和触发事件来实现对不同消息的处理。
深拷贝?
1 | function DeepClone(target, hash = new WeakMap()) { |
js类型==转换?
- 布尔 vs 数值。 布尔转成数值再比较【true=>1】
- 字符串 vs 数值。字符串转成数值再比较。
- object vs not object。调用对象的valueOf()取得原始值再比较。
- null == undefined
- NaN != NaN
- false、””、0、NaN、null、undefined五种情况会被转换为false。
虚拟DOM?
虚拟DOM的价值不在性能。
- 研发体验/研发效率的问题,虚拟DOM是数据驱动视图的载体。
- 跨平台的问题,虚拟DOM是DOM结构的抽象,用以表述真实DOM。
- 批量更新,实现集中化的DOM批量更新。
线程池调度?
1 | class Scheduler { |
proxy和object.defineProperty的区别?
- proxy用于拦截对象的基本方法,defineProperty是基本方法之一。
- proxy可以监听数组的变化(push、shift、splice),defineProperty无法拦截。
flex是哪些属性的缩写?
flex-grow、flex-shrink、flex-basis。
- flex-grow:这个属性规定了flex-grow项在flex容器中分配剩余空间的相对比例,默认为0;
- flex-shrink:指定了flex元素的收缩规则。flex元素仅在默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flex-shrink 的值。初始值1,不能是负数。
- flex-basis:指定了 flex 元素在主轴方向上的初始大小。
BFC?
在一个BFC内:
- 内部的盒子会在垂直方向上一个接一个的放置
- 对于同一个BFC的俩个相邻的盒子的margin会发生重叠,与方向无关。
- 每个元素的左外边距与包含块的左边界相接触(从左到右),即使浮动元素也是如此
- BFC的区域不会与float的元素区域重叠
- 计算BFC的高度时,浮动子元素也参与计算
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然
触发条件:
- body根元素
- float除了none以外的值
- position:absolute/fixed
- display:inline-block、table-cell、flex
- overflow:除了visible以外的值(hidden/auto/scroll)
用处:
避免外边距重叠。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<div class="container">
<div class="inner">
<div class="box"></div>
</div>
<div class="box"></div>
</div>
<style>
.inner{
/* 触发BFC */
display: inline-block;
}
.box {
margin: 100px;
width: 100px;
height: 100px;
background-color: blue;
}
</style>清除浮动。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<div class="container">
<div class="box"></div>
<div class="box"></div>
</div>
<style>
.container{
/* 触发BFC 否则container 只会显示一条边 */
display: inline-block;
background-color: antiquewhite;
border: 2px solid black;
}
.box {
margin: 100px;
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
</style>阻止元素被浮动元素覆盖。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<div class="box1"></div>
<div class="box2"></div>
<style>
.box1 {
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
.box2 {
width: 200px;
height: 200px;
background-color: rebeccapurple;
overflow: hidden;
}
</style>
手写new?
1 | function myNew(constructor, ...args) { |
手写instanceof?
1 | function myInstanceof(obj, constructor) { |
手写Object.create?
1 | function myCreate(obj){ |
原型链?
每一个 JavaScript 对象( null 除外)在创建时会与之关联另一个对象,这个被关联的对象称之为 原型(原型对象)。每一个对象都会从原型中‘继承’(委托)原型对象的属性。原型对象就是用来存放实例中共有的那部分属性。每个函数都有一个特殊的属性叫作原型(prototype),这个属性指向调用该构造函数而创建的实例的原型。原型对象中有一个属性 constructor, 它指向函数对象。
当读取实例对象的属性时,如果找不到,就会查找与该对象关联的原型对象中的属性,如果还查不到,就去找原型的原型,一直找到为止,如果还找不到就是 null。在此过程中,由互相关联的原型组成的链状结构就是 原型链。
webpack的构建流程?
webpack启动后,从entry开始,递归解析entry依赖的所有module,找到module.rules里配置的loader进行相应的转换处理,对module转换后解析模块,解析的结果是一个一个的chunk,最后wenpack会将所有chunk转换。在整个构建过程中,webpack会执行plugin当中的插件,完成plugin的任务。
entry:模块入口,使得源文件加入到构建流程中
output:配置如何输出最
module:配置各种类型的模块的处理规则
plugin:配置扩展插件的devServer:实现本地服务:包括 http模块热替换source map等
隐藏一个元素?
display:none
:DOM结构中存在但不占据空间。visibility: hidden
:元素不可见但是占据空间,事件不会触发。opacity: 0
:元素不可见但可以触发元素上的事件。
vue diff
patch做的事情就是三种:创建节点、删除节点、更新节点。目的就是在旧的VNode上进行改进,尽可能地复用节点将其改造成新的VNode的结构。
diff的是时机:组件创建或者数据改变的时候,首先通过render生成VNode,然后通过update传入虚拟dom数的根节点,对新旧两棵树进行比对。
diff算法的逻辑实际是针对标注key的节点进行diff的过程。如果没有key,类型相同则认为节点相同。
diff的比较策略:深度优先;同层进行,循环从两边向中间进行收拢。
首先,如果旧的树不存在【首次渲染】,直接生成DOM就可以了。
旧树存在的情况下:
根节点不同,重新生成DOM树。
根节点相同,如果属性有变化,则更新到DOM中,进入后续对比子节点:
比较在同层中进行。
首先比较旧树和新树的头节点,节点相同则更新。然后指针后移;
否则,比较尾节点,节点相同则更新。然后指针前移。
如果头节点不同,位节点也不同,则比较旧树头节点和新树尾节点,相同则更新,然后将DOM的位置移动到旧树尾指针的后面,然后指针分为后移前移;
不同的话则比较旧树尾节点和新树头节点,相同则更新,然后将DOM移动到旧树头指针的前面,然后移动指针。
如果不满足以上四种情况,则在旧树中找新树的头结点,然后进行更新,并将DOM移动到旧头结点的前面。还找不到就重新创建。
响应式系统
初始化阶段通过Object.defineProperty()对数据进行拦截。
依赖收集阶段,组件挂载的时候,实例化watcher对象,把watcher压入Dep的栈中,然后调用getter()方法触发数据的getter。在数据的getter中,每个数据都有对应的dep实例,触发getter的时候会调用dep的depend方法进行依赖收集。之前压入Dep栈中的watcher就记录了dep,后面递归调用子项的getter,完成依赖收集的过程。
依赖收集后还有一个依赖清除的过程,这一块做的工作就是将本次收集的依赖和之前收集的依赖进行比对,移除旧deps中在新deps里面不存在的dep,避免资源浪费。
派发更新阶段,当组件中对响应式数据进行更改的时候会触发setter。在setter里面通过调用dep.notify()进行数据变化的通知,触发对应watcher的update()方法,这里会将watcher放到一个队列里面,同时保证相同的watcher仅添加一次。然后通过nextTick异步的执行更新操作。
watch/watchEffect
https://juejin.cn/post/6946168100584685598#heading-0
路由预加载
webpack预加载
1
2
3
4import(/* webpackPrefetch: true */ './path/to/LoginModal.js')
import(/* webpackPreload: true */ './path/to/LoginMdal.js')webpack 预加载和路由无关联,也就是说不管加载什么页面的时候,这段 JS 都会预加载,这可能会出现加载资源浪费情况。
原生标签
1
2<link rel="preload" href="index.js" as="script" />
<link rel="prefetch" href="index.css" as="style" />preload优先级高于prefetch。
preload:告诉浏览器立即加载资源。
prefetch:浏览器空闲的时候加载。
两种方式仅加载资源但不会执行。
- 通过一个中间组件去加载。
1
2
3
4
5
6
7
8
9
10<script>
import LazyComp1 from "./LazyComp1.vue";
import LazyComp2 from "./LazyComp2.vue";
export default {
components:{
LazyComp1,
LazyComp2,
}
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<template>
<Prefech v-if="loadPrefetch"></Prefech>
</template>
<script>
export default {
components: {
Prefech: () => import("./Prefetch");
},
data() {
return {
loadPrefetch: false
}
},
mounted() {
setTimeout(() => {
this.loadPrefetch = true;
}, 1000);
}
}
</script>
HTML的生命周期
DOMContentLoaded:浏览器已经完全加载 HTML,DOM 树已经构建完毕,js 可以访问所有DOM节点,初始化界面。但是像是<img>
和样式表等外部资源可能并没有下载完毕。
load:浏览器已经加载了所有的资源( 图像,样式表等 )。可以在此事件触发时获得图像的大小( 如果没有被在 HTML/CSS 中指定 )
beforeunload / unload:当用户离开页面的时候触发。(可以询问用户是否保存了更改以及是否确定要离开页面。)
vue模板编译
Vue 中的模板编译原理可以简单概括为将模板字符串转换成渲染函数(render function),这个过程涉及到以下几个步骤:
解析模板:Vue 编译器会先对模板进行解析,将模板字符串转换成抽象语法树(AST)的形式。
静态分析:在静态分析阶段,Vue 会识别模板中的指令、插值表达式、事件处理等,并建立相应的 AST 节点。
优化:Vue 会对生成的 AST 进行优化,包括静态节点提升、标记静态根节点等优化操作,以提高渲染性能。
生成渲染函数:最终,Vue 将优化后的 AST 转换成渲染函数,这个渲染函数接收一个 createElement 函数作为参数,用于创建 VNode(虚拟 DOM 节点)。
执行渲染函数:当组件需要渲染时,会执行生成的渲染函数,通过 createElement 创建 VNode,并最终渲染成真实的 DOM 元素。
总的来说,Vue 的模板编译原理将模板字符串转换成渲染函数,通过渲染函数的执行来生成虚拟 DOM,并最终更新到真实的 DOM 上,实现数据驱动视图的更新。这个过程大大简化了开发者编写组件的工作,同时也提高了 Vue 应用的性能和效率。
vue3 diff
Vue 3 中的 diff 流程与 Vue 2 中有所不同,主要是因为 Vue 3 引入了新的响应式系统和编译器优化。
编译器优化:在编译期间,Vue 3 的编译器会对模板进行静态分析,并将静态节点提取出来,生成标记静态根节点的渲染函数。这样可以减少渲染函数的生成和执行次数,提高渲染性能。
响应式更新:当数据发生变化时,Vue 3 的响应式系统会立即触发组件的重新渲染。
生成新的虚拟 DOM 树:重新渲染组件时,Vue 3 会生成新的虚拟 DOM 树。
比较新旧虚拟 DOM 树:Vue 3 会将新的虚拟 DOM 树和旧的虚拟 DOM 树进行比较,找出两者之间的差异。
Diff 算法:Vue 3 中使用了一种名为“递归分裂算法”的 diff 算法,它通过将新旧节点的子节点按照一定规则分成多个小区间,然后进行比较和更新,从而减少了无意义的比较,提高了 diff 的效率。
生成变更记录:在比较过程中,Vue 3 会生成一份变更记录,记录需要对实际 DOM 进行的操作,包括插入、移动、更新和删除等操作。
应用变更:最后,Vue 3 会根据变更记录逐条对实际 DOM 进行操作,将变更应用到实际 DOM 上,完成视图的更新。
Vue 3 中的 Virtual DOM diff 流程借助编译器优化和新的递归分裂算法,提高了渲染性能和效率,同时保证了视图的正确性。这个过程减少了无意义的比较,提高了 diff 的效率,从而实现了性能优化和视图的高效更新。
Vue3相对于Vue2进行了哪些优化?
Vue 3 引入了 Composition API,这是一种基于函数的 API 风格,使得代码组织更加灵活和可复用。相比于 Vue 2 的 Options API,Composition API 更适合大型项目和复杂组件的开发。
性能优化:Vue 3 在编译器、响应式系统和虚拟 DOM 等方面进行了优化,提高了整体性能。具体来说,Vue 3 引入了编译器优化、树懒执行、静态提升、模块化的运行时,以及更快的虚拟 DOM 渲染等功能,从而使应用程序更加高效。
更灵活的响应式系统:Vue 2.x 中响应式系统的核心是 Object.defineProperty,劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。Vue 3.x 中使用 Proxy对象重写响应式系统。
更快的渲染速度:Vue3 的编译器生成的渲染函数比 Vue2 生成的更高效。
编译阶段:Vue 2.x 通过标记静态节点,优化 diff 的过程。Vue 3.x中标记和提升所有的静态节点,diff的时候只需要对比动态节点内容。
更小的体积:Vue3 将源码拆分为多个独立的模块,这样就可以按需导入所需的模块,从而减小了整个库的体积。
更好的 TypeScript 支持:Vue3 对 TypeScript 的支持更加友好,内部使用了更先进的 TypeScript 特性,并为其提供了更好的声明文件。
更好的组件系统:比如,Vue3中引入了一个新的 Fragment 组件,它可以替代原来的 template 标签作为根节点
https://www.nowcoder.com/discuss/667057128502325248?sourceSSR=search
Vue2、vue3区别?
- 新增组合式API。
- 生命周期的变化。
- vue3的template不需要根标签。
- vue2 v-for优先级大于v-if,vue3相反。
- 响应式原理使用proxy来实现,并没有完全抛弃defineProperty,使用ref定义的响应式数据需要.value拿到值。
- tree-shaking按需引入。
- diff:
vue2 中,diff算法,它是遍历每一个虚拟节点,进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,用patch记录的信息去更新dom,有一个缺点就是,它会比较每一个节点,对于一些不参与更新的元素,会产生一定的性能消耗
vue3 中,diff算法,会给每一个虚拟节点添加一个patchflag标识,只会去比较这个标识发生变化的节点,进行视图更新,对于标识没有发生变化的元素,就标为静态标记,在渲染的时候直接复用 - 静态节点标记,diff的时候可以跳过。
- 不推荐mixin,推荐hook写法。便于搭配组合式API。
- v-model:vue2- value/监听change/input事件;vue3- modelValue 监听update:modelValue。
- ts支持。
break change:https://v3-migration.vuejs.org/zh/breaking-changes/
css选择器优先级
!important >
行内样式 >
ID选择器 >
类选择器、伪类、属性选择器 >
标签、伪元素选择器 >
通配符、子类选择器、兄弟选择器 >
继承 >
浏览器默认属性
在计算权重的时候,可以用一组向量标志来表示:(0, 0, 0, 0),从左到右:
第一位是行内元素,在有行内元素的时候加 1
第二位是 ID 选择器的数量
第三位是 class、属性和伪类选择器的数量
第四位是类型和伪元素选择器的数量
如果计算结果权重相同,那么以定义顺序靠后的选择器优先,或者有!important标记优先。
https://zxuqian.cn/css-selector-specificity/