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:allnoneindex[索引此页]、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;
      }
  • 视口居中,以上代码中设置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;
      }
    • grid
      1
      2
      3
      4
      5
      6
      7
          .father{
      width:100px;
      height:100px;
      position:grid;
      ‌place-content: center;
      place-items: center; <!--单个单元格内的元素也居中-->
      }
      https://www.joshwcomeau.com/css/center-a-div/

      Cookies、sessionStorge、localStorge?

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
2
3
4
5
6
7
8
9
10
11
12
13
14
function DeepClone(target, hash = new WeakMap()) {
if (!target) return target
if (target instanceof Date) return new Date(target)
if (target instanceof RegExp) return new RegExp(target)
if (typeof target != 'object') return target
if (hash.has(target)) return hash.get(target)
const clone = new target.constructor()
hash.set(target, clone)

Reflect.ownKeys(target).forEach((key) => {
clone[key] = DeepClone(target[key], hash)
})
return clone
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Scheduler {
constructor(maxRunningTask) {
this.max = maxRunningTask
this.currentTask = 0
this.tasks = []
}

wrapTask(task) {
if (typeof task.then === 'function') return task
return () => new Promise((resolve) => {
task()
resolve()
})
}

run(task) {
if (this.currentTask > this.max) {
this.tasks.push(task)
return
}
const deepRun = (handle) => {
this.wrapTask(handle)().then(() => {
this.currentTask--
if (this.tasks.length > 0) {
const newtask = this.tasks.shift()
deepRun(newtask)
}
})
}
this.currentTask++
deepRun(task)
}
}

const schedule = new Scheduler(2)

schedule.run(task1)
schedule.run(task2)

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function myNew(constructor, ...args) {
// 1. 创建一个空对象,并将其原型链接到构造函数的 prototype 上
const obj = Object.create(constructor.prototype);

// 2. 调用构造函数,将 this 绑定到新创建的对象上
const result = constructor.apply(obj, args);

// 3. 如果构造函数返回了一个对象类型的值,则返回该值
// 否则,返回新创建的对象
return (result && typeof result === 'object') ? result : obj;
}

// 示例
function Person(name, age) {
this.name = name;
this.age = age;
}

const person = myNew(Person, 'Alice', 25);
console.log(person.name); // 输出: Alice
console.log(person.age); // 输出: 25

手写instanceof?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function myInstanceof(obj, constructor) {
// 1. 检查 constructor 是否是一个函数
if (typeof constructor !== 'function') {
throw new TypeError('Right-hand side of instanceof is not callable');
}

// 2. 获取对象的原型
let prototype = Object.getPrototypeOf(obj);

// 3. 遍历原型链,查找 constructor.prototype
while (prototype !== null) {
if (prototype === constructor.prototype) {
return true; // 找到匹配
}
prototype = Object.getPrototypeOf(prototype); // 向上查找原型链
}

return false; // 没有找到匹配
}

// 示例
function Person() {}
const alice = new Person();

console.log(myInstanceof(alice, Person)); // 输出: true
console.log(myInstanceof(alice, Array)); // 输出: false

手写Object.create?

1
2
3
4
5
function myCreate(obj){
function F(){}
F.prototype = obj
return new F()
}

原型链?

每一个 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移动到旧头结点的前面。还找不到就重新创建。

响应式系统

  1. 初始化阶段通过Object.defineProperty()对数据进行拦截。

  2. 依赖收集阶段,组件挂载的时候,实例化watcher对象,把watcher压入Dep的栈中,然后调用getter()方法触发数据的getter。在数据的getter中,每个数据都有对应的dep实例,触发getter的时候会调用dep的depend方法进行依赖收集。之前压入Dep栈中的watcher就记录了dep,后面递归调用子项的getter,完成依赖收集的过程。

  3. 依赖收集后还有一个依赖清除的过程,这一块做的工作就是将本次收集的依赖和之前收集的依赖进行比对,移除旧deps中在新deps里面不存在的dep,避免资源浪费。

  4. 派发更新阶段,当组件中对响应式数据进行更改的时候会触发setter。在setter里面通过调用dep.notify()进行数据变化的通知,触发对应watcher的update()方法,这里会将watcher放到一个队列里面,同时保证相同的watcher仅添加一次。然后通过nextTick异步的执行更新操作。

watch/watchEffect

https://juejin.cn/post/6946168100584685598#heading-0

路由预加载

  1. webpack预加载

    1
    2
    3
    4
    import(/* webpackPrefetch: true */ './path/to/LoginModal.js')

    import(/* webpackPreload: true */ './path/to/LoginMdal.js')

    webpack 预加载和路由无关联,也就是说不管加载什么页面的时候,这段 JS 都会预加载,这可能会出现加载资源浪费情况。

  2. 原生标签

    1
    2
    <link rel="preload" href="index.js"  as="script" />
    <link rel="prefetch" href="index.css" as="style" />

    preload优先级高于prefetch。

preload:告诉浏览器立即加载资源。
prefetch:浏览器空闲的时候加载。
两种方式仅加载资源但不会执行。

  1. 通过一个中间组件去加载。
    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/