css

快速布局上下左右居中对齐

将body设置成 display: flex; ,给需要布局的盒子在有宽高的前提下设置 marin: auto;

padding与margin的区别

padding为盒子的内边距,margin为外边距
作用对象不同,padding是作用域自身的,而margin是作用外部对象的外部

vw与百分比的区别

百分比具有继承关系,而vw只和设备的宽度有关系

如何让谷歌浏览器支持小字体

浏览器默认支持最小字体为 font-size:12px,再缩小就需要为添加 tarnsform属性使其变为原来的多少倍。

1
2
3
.small-font{
transform: scale(0.8);
-webkit-transform: scale(e.8);}

行内元素和块级元素

行内元素和块级元素是HTML和CSS中两种基本的元素显示方式。它们之间的主要区别如下:

  1. 显示方式

    • 块级元素:独占一行,即使内容没有充满整个行宽,它仍然会拓展到其容器的整个宽度。常见的块级元素包括:<div>, <p>, <h1>~<h6>, <ul>, <li>, <ol> 等。
    • 行内元素:与其他行内元素并排显示在同一行,只占据其内容所需要的宽度。常见的行内元素包括:<span>, <a>, <strong>, <em>, <abbr> 等。
  2. 尺寸设置

    • 块级元素:可以设置宽度和高度。如果不设置宽度,默认为其父元素的宽度;如果不设置高度,默认为其内容的高度。
    • 行内元素:设置宽度和高度通常无效,它的宽高由其内容决定。
  3. margin和padding

    • 块级元素:上下左右的marginpadding都会生效。
    • 行内元素:左右的marginpadding会生效,但上下的通常不会改变元素的高度,而是影响其上下元素的距离。
  4. 用途

    • 块级元素:通常用于布局和结构化页面内容。
    • 行内元素:通常用于修饰或突出显示文本内容。
  5. 转换

    • 通过CSS的display属性,我们可以转换元素的显示方式。例如,将行内元素转为块级元素可以设置display: block;,而将块级元素转为行内元素可以设置display: inline;
  6. 默认样式:

    • 块级元素:除了默认独占一行,它们往往还有一些其他默认的样式。例如,<p> 元素通常具有默认的上下 margin,而 <ul> 列表会有默认的 paddinglist-style
    • 行内元素:默认没有上下 marginpadding。例如,<span><a> 标签通常不会为你添加任何额外的空间。
  7. 布局特点

    • 块级元素:更容易用于CSS布局技术,如Flexbox和Grid。这是因为它们天然具有清晰的尺寸和结构定义。
    • 行内元素:对于Flexbox和Grid布局,你可能需要将它们转换为块级元素或 inline-block 来更好地控制它们。
  8. 嵌套规则

    • 块级元素:可以包含其他块级元素和行内元素。
    • 行内元素:通常只能包含其他行内元素,不建议包含块级元素。例如,尽管一些浏览器可能会渲染嵌套在 <span> 内部的 <div>,但这种做法违反了HTML的标准,可能导致不可预测的布局问题。
  9. 上下文关系

    • 有时候,元素的 display 值会因为它们所处的上下文而改变。例如,当 li 元素处于 ulol 中时,它是块级元素,但如果它不在这些上下文中,它可能不会表现为块级元素。
  10. 现代CSS布局

    • 随着现代CSS布局方法,如Flexbox和Grid的出现,display 属性的值变得更加复杂和多样。例如,使用 display: flex; 会使容器变成一个Flex容器,其子元素默认表现为Flex项目。这意味着即使子元素原本是行内元素,它们现在也会有块级元素的一些特性。

注意:在实际开发中,还有一种常见的display属性值是inline-block,这种元素结合了块级元素和行内元素的特点,即它既可以设置宽度和高度,又能与其他元素在同一行显示。

JavaScript

let与var的区别(Es6)

在ECMAScript 6 (ES6) 或者称为 ECMAScript 2015 中,letvar 是两种用于声明变量的关键字。它们在作用域、提升等方面有一些明显的区别。以下是它们之间的主要区别:

  1. 块级作用域

    • let: let声明的变量仅在其所在的块内部可见。一个块是由大括号 {} 包围的代码段,例如函数、循环或if语句。
    • var: var声明的变量在其整个封闭函数内可见,而不仅仅是块内部。(没有局部作用域,红杏出墙)
  2. 变量提升:

    • let: let声明的变量会被提升,但是你不能在声明之前访问它,否则会抛出一个错误。这种区域被称为“暂时性死区”(Temporal Dead Zone, TDZ)。
    • var: var声明的变量也会被提升到函数的顶部,但你可以在声明前访问它,此时它的值是undefined。(变量提升,可以先上车再买票)
  3. 重新声明:

    • let: 在同一作用域内,不能使用let重新声明一个已经存在的变量名。
    • var: 在同一作用域内,可以多次使用var声明相同的变量名,而不会报错。(可以重复声明相同变量,如套牌车)
  4. 全局对象属性:

    • let: 当在全局作用域使用let声明变量时,该变量不会成为全局对象(例如在浏览器中的window对象)的属性。
    • var: 当在全局作用域使用var声明变量时,该变量会成为全局对象的属性。
  5. 在循环中的行为:

    • let: 在for循环的初始化部分使用let声明的变量,对于每次迭代都会有一个新的绑定,这尤其在创建闭包时非常有用。
    • var: 使用var在for循环中声明的变量,所有迭代共享同一变量。
  6. 关于严格模式:

    • let: 在ECMAScript 6及以后的版本中,let默认就是在严格模式下执行的,不需要额外声明。
    • var: 除非在代码中明确使用"use strict";,否则使用var声明的变量不会强制执行严格模式。
  7. 创建全局变量:

    • let: 在顶级作用域(非函数内)使用let声明的变量不会创建一个新的全局变量。它只是创建一个块级绑定,你不能通过window.myVariable这样的方式访问它(如果是在浏览器环境下)。
    • var: 在顶级作用域声明的var变量会成为全局对象的一个属性。
  8. 初始化的要求:

    • let: let声明的变量必须在使用前初始化,否则会抛出一个错误。
    • var: var声明的变量可以在使用前不初始化,此时它的值默认为undefined
  9. 在浏览器环境中的差异:

    • let: 使用let在浏览器中声明的变量(在顶层作用域)不会成为window对象的属性。
    • var: 使用var在浏览器的顶层作用域声明的变量会自动成为window对象的一个属性。
  10. 性能:

    • 在某些JavaScript引擎中,由于let的语义和块级作用域特性,使用let可能会带来轻微的性能提升。然而,在日常开发中,这种差异通常可以忽略不计。

总结:

随着现代JavaScript的发展,letconst(另一个块级作用域的声明,用于创建常量)成为了首选的变量声明方式,因为它们提供了更清晰和更有预测性的代码行为。然而,仍然存在大量使用var的旧代码,因此理解var的工作方式也很重要。

对于新项目或现有项目的维护,推荐使用letconst来提高代码的可读性和可维护性。

深拷贝与浅拷贝的区别

深拷贝和浅拷贝是编程中常见的两种数据复制方式。这两种方式的主要区别在于它们如何复制对象的值以及对象引用其他对象时的行为。以下是它们之间的主要区别和细节:

  1. 浅拷贝:

    • 只复制对象的顶层属性。
    • 如果对象的属性值是基本类型(如字符串、数字、布尔值等),则直接复制该值。
    • 如果对象的属性值是引用类型(如数组、对象等),则复制该引用(地址),而不是实际的对象。这意味着原对象和复制的对象都引用相同的子对象。
  2. 深拷贝:

    • 复制对象及其所有嵌套对象。
    • 无论属性值是基本类型还是引用类型,都会为新对象创建一个新的副本。
    • 深拷贝会遍历整个对象及其所有子对象,以确保创建完整的复制副本。

举个例子来说明两者的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let obj1 = {
a: 1,
b: { c: 2 }
};

// 浅拷贝
let shallowCopy = Object.assign({}, obj1);

// 深拷贝(一个简单的方法,但并不完美)
let deepCopy = JSON.parse(JSON.stringify(obj1));

// 修改原对象
obj1.b.c = 3;

console.log(obj1.b.c); // 输出: 3
console.log(shallowCopy.b.c); // 输出: 3,因为它与obj1共享同一个子对象
console.log(deepCopy.b.c); // 输出: 2,因为它是子对象的一个完全独立的复制

需要注意的是,使用JSON.parse(JSON.stringify(obj))进行深拷贝存在一些局限性:

  • 它不能复制函数、undefinedRegExpSymbol、特殊的对象如ErrorDate等。
  • 它不能复制对象的原型链。
  • 它不能处理循环引用的情况。

在实践中,可以使用像 lodash 的 _.cloneDeep() 这样的库函数来进行可靠的深拷贝。

理解深拷贝和浅拷贝对于避免在处理对象和数组时出现不可预期的副作用是很重要的。

例如:

  1. 第一段代码:(浅拷贝)

    1
    2
    3
    4
    let arr =[1,2,3];
    let newArr = arr;
    newArr.push(4)
    console.log(arr, newArr);

    结果将是:[1,2,3,4] [1,2,3,4]
    这是因为newArrarr都引用同一个数组。当你更改newArr时,arr也会受到影响。

  2. 第二段代码:(结构赋值为深拷贝)

    1
    2
    3
    4
    let arr = [1,2,3];
    let newArr = [...arr];
    newArr.push(4);
    console.log(arr, newArr);

    结果将是:[1,2,3] [1,2,3,4]
    使用...运算符进行数组解构赋值,它创建了原数组的一个深拷贝。因此,newArrarr是两个不同的数组。

  3. 第三段代码

    1
    2
    3
    4
    let arr2 = [[1,2,3],[4,5,6]];
    let newArr2 = [...arr2];
    newArr2[0].push(888);
    console.log(arr2, newArr2);

    结果将是:[[1,2,3,888],[4,5,6]] [[1,2,3,888],[4,5,6]]
    这是因为虽然我们使用了...运算符创建了arr2的一个浅拷贝,但数组中的子数组仍然是通过引用共享的。所以,当你修改newArr2中的子数组时,arr2中对应的子数组也会受到影响。

结论:解构赋值对于一维数组和对象来说,表现得像深拷贝;但对于多维数组或对象中包含对象的情况,它是浅拷贝。

手写深拷贝

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
39
40
41
42
/**
* 深拷贝函数
* @param {*} source - 要拷贝的源对象或数组
* @returns {*} 拷贝后的新对象或数组
*/
function deepClone(source) {
// 如果source是null或非对象/数组,则直接返回source
if (source === null || typeof source !== 'object') return source;

// 根据source的类型(数组或对象)初始化一个新的targetObj
const targetObj = source.constructor === Array ? [] : {};

// 遍历source的每一个属性/元素
for (let keys in source) {
// 确保只拷贝source自身的属性,不拷贝原型链上的属性
if (source.hasOwnProperty(keys)) {
// 如果当前属性的值是对象或数组,则递归进行深拷贝
if (source[keys] && typeof source[keys] === 'object') {
// 维护层代码
targetObj[keys] = sourece[keys].constructor === Array ? [] :{};
// 递归
targetObj[keys] = deepClone(source[keys]);
} else {
// 如果当前属性的值不是对象或数组,则直接赋值
targetObj[keys] = source[keys];
}
}
}
// 返回深拷贝后的对象或数组
return targetObj;
}

// 使用示例
const obj = {
a: 1,
b: [2, 3, { d: 4 }],
c: { e: 5, f: { g: 6 } }
};
const clonedObj = deepClone(obj);
clonedObj.c.e=6;
clonedObj.b[0]=3;
console.log(clonedObj); // { a: 1, b: [ 3, 3, { d: 4 } ], c: { e: 6, f: { g: 6 } } }

闭包(沟通内外方法的桥梁)

  1. 闭包是什么?方法里返回一个方法
    例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 1. 闭包 : 内层函数 + 外层函数变量
    function outer() {
    const a = 1
    function f() {
    console.log(a)
    }
    f()
    }
    outer()
  2. 闭包存在的意义?
  • 延长变量的生命周期
  • 创建私有环境
  1. 闭包会常驻内存=>慎用闭包

Vue data()为什么是一个函数?

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
let makeCounter = function () {
let num = e;
function changeBy(val){
num += val
}
//给你什么,你才能拿
return {
add: function() {
changeBy(1)
},
redece: function() {
changeBy(-1)
},
value: function() {
return num
}
}
}

let counter1 = makeCounter()
let counter2 = makeCounter()
counter1.add()
counter1.add()// 2
// counter2 // 0
counter2.add()//
console.log(counter2.value())// 1
console.log(counter1.value())// 2
// 两者互补干扰

作用域链

1
2
3
4
5
6
7
8
9
10
11
let name = "小明"
function fn2(){
let name = "小白";
function fn3(){
let name = "小红";
console.log(name)
}
fn3()
}
fn2()
// 打印时会逐层查找,找到就输出。

防抖(英雄回城)

防抖是指在一定时间间隔内,如果有连续的动作触发,只会执行最后一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//防抖=>将多次操作变成一次
let telInput = document.querySelector( ' input ' )
telInput.addEventListener( 'input ', antiShake(demo,2000))
//防抖封装
function antiShake(fn,wait){
let timeout = null;
return args =>{
if(timeOut) clearTimeout(timeOut)
timeOut = setTimeout(fn,wait);
}
}
function demo(){
console.log('发起请求')
}

节流(鼠标移动mousemove)

节流是一种在一定时间段内只允许函数执行一次的技术。
应用场景:

  • 提交表单
  • 高频监听事件
1
2
3
4
5
6
7
8
9
10
11
12
13
//节流=>一定时间内只调用一次函数
let box = document.querySelector( ".box")
function throttle(event,time){
let timer = null
return function() {
if(!timer) {
itimer = setTimeout(() =>{
event();
timer = null;
}, time);
}
}
}

null与undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
console.log(typeof null)//表示为“无”对象0
console.log(typeof undefined)//表示“无”的原始值 NaN
// undefined的几种情况
// 1. 以声明,没赋值
// 2. 访问对象上不存在的属性
// 3. 在传参时少传参数
// 4. 常规函数的默认返回值(构造函数除外,它返回的当前的构造对象)
function fn(){
console.log('123')
}
console.log(f()) //undefined

// null的几种情况
// 1. 手动释放内存
let obj = {};
obj = null;
// 2. 作为函数的参数(此参数不是对象)
// 3. 作为原型链的顶端

foreach()与map()的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// foreach
// 1. 没有返回值 undefined
// 2. 不能用break,否则会报错
// 3. 遍历的是value
let arr = [ 'a', 'b', 'c']
let res = arr.forEach(element =>{
console.log(element)
// break;
return element + '1'
}) //a b c
// console.log(res)

//map
// 1. 有返回值(数组),默认return是undefined
// 2. 接受的参数是一个函数(value,key)
// 不能用break打断
let arr = [ 'a', 'b', 'c']
let res = arr.map((value,key) =>{
// break;
return value + '1'
})
console.log(res) //[a1,b1,c1]

js递归求和

1
2
3
4
5
6
7
8
9
10
function add(num1,num2){
let num = num1+num2;
if( num2+1 > 100){
return num;
}else{
return add(num,num2+1)
}
}
let sum = add(1,2)
console.log(sum)//5050

Es6相关面试题

var let和const

  1. var:

    • 作用域: 使用var声明的变量具有函数级作用域,这意味着在函数内声明的var变量可以在整个函数内部访问,但不受代码块(如if语句或for循环)的限制。
    • 提升: var声明的变量会被提升至函数的顶部。这意味着在变量声明之前访问该变量会得到undefined,而不会产生错误。
    • 重复声明: 在同一个作用域内可以多次使用var声明相同的变量名,而不会报错。
  2. let:

    • 作用域: let声明的变量具有块级作用域,因此在for循环或if语句等代码块中声明的let变量在代码块之外是不可见的。
    • 提升: let声明的变量存在暂时性死区,意味着在声明之前访问它会产生错误。
    • 重复声明: 在同一作用域内,不能使用let重复声明一个已经存在的变量名,坚持使用会抛出语法错误。
  3. const:

    • 作用域: 与let相同,const声明的变量具有块级作用域。
    • 不可变性: const声明的变量必须在声明时进行初始化,且后续不可重新赋值。但需要注意的是,如果const声明的是对象或数组,你仍然可以修改它们的内部属性或元素,但不能重新分配整个对象或数组。
    • 重复声明: 与let相同,const不允许在同一作用域内重复声明变量,坚持使用会抛出语法错误。

总结:在现代JavaScript编程中,推荐使用letconst而避免使用var,因为它们提供更严格和更易于预测的行为,特别是在复杂的应用和大型代码库中。

数组去重(技巧)

1
2
3
let arr = [12,13,16,12,59]
let newarr = [...new Set(arr)]
console.log(newarr)

Promise

1
2
3
4
5
6
7
8
9
10
11
const promise = new Promise((resolve,reject) =>{
console.log(1)
resolve();
console.log(2)
}
)
promise.then(() =>{
console.log(3)
}
)
console.log(4)

打印结果为:1,2,4,3
原因:构造函数同步执行,.then() 为异步执行,所以说最后打印 3。

filter (不影响原数组)

1
2
3
4
5
6
7
8
9
let a = [1231415]
// 1. current =>当前值
// 2. index =>当前值的下标
// 3.array这个数组对象
let b = a.filter((current,index,array) =>{
return current > 10
}
console.log(a)
console.log(b)

Vue

数据驱动的原理,如何使数据”可观测”

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
39
40
41
42
export class Observer {
constructor(value){
this.value = value
if(Array.isArray(value)){
//数组的逻辑
}else{
//对象的逻辑
this.walk(value)
}
}
walk(obj){
// {name: '小明', age:18}
const keys = Object.keys(obj)
for(let i = 0; i < keys.length; i++){
defineReactive(obj,keys[i])
}
}
}

// 让对象的每一个属性都可侦测
function defineReactive(obj,key,val){
if(arguments.length ===2){
val = obj[key] //对象的某个值
}

if(typeof val === 'object'){
// 递归
new Observer(val);
}
Object.defineProperty(obj, key,{
enumberable: true, //可枚举
configurable: true, //可改变
get(){
console.log(`${key}属性被读取了`);
return val;
},
set(newVal){
console.log(`${key}属性被修改了,更改后值为${newVal}`);
val = newVal;
}
})
}

导入

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import {Observer} from './1.js'
let obj= new Observer({
name : "小明",
age: 18,
demo:{
a: 1,
b: 2,
}
})
// console.log(obj.value.name);
// obj.value.age = 20;
// console.log(obj.value.age);
console.log(obj.value.demo.a);
obj.value.demo.a = 100
console.log(obj.value.demo.a);
</script>
</body>
</html>

虚拟 DOM 是什么?

虚拟 DOM 是真实 DOM 的抽象表示,它的本质是一个 JavaScript 对象。Vue 2.x 采用了虚拟 DOM 机制。它可以跨平台。

虚拟 DOM 在 Vue 中做了什么?

  1. vue的渲染过程(html,css,js)
  2. 将真实dom转化为虚拟dom(js对象),在更新时作对比。

    在 Vue 中,当组件的状态改变时,Vue 会使用虚拟 DOM 生成一个代表新状态的新树。然后,Vue 会比较新旧两棵虚拟 DOM 树,计算出最小的差异或更改,并将这些更改应用于真实 DOM。这一过程被称为“差异化”或“diffing”。

虚拟 DOM 是如何提升 Vue 的渲染效率的?

  • 局部更新(节点数据)
  • 将直接操作dom的地方拿到两个js对象之中去做比较
  1. 减少真实 DOM 的操作:操作真实 DOM 是开销很大的,而虚拟 DOM 可以通过计算最小差异,只更新真实 DOM 中的必要部分。
  2. 轻量级:虚拟 DOM 是一个简单的 JavaScript 对象,操作它比直接操作真实 DOM 要快得多。
  3. 有效的 Diff 算法:Vue 的 Diff 算法高效地对比新旧虚拟 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 1. 初始化 patch
function patch(container, vnode) {
// 创建元素
let el = createElement(vnode);
// 将元素添加到容器中
container.appendChild(el);
}

// 2. 更新
function update(oldVnode, newVnode) {
if (oldVnode.tag !== newVnode.tag) {
// 创建一个新的DOM元素并替换旧的DOM元素
let newEl = createElement(newVnode);
oldVnode.el.parentNode.replaceChild(newEl, oldVnode.el);
} else {
// 如果标签相同,则直接修改对应的DOM元素
patchElement(oldVnode.el, oldVnode, newVnode);
}
}
// 3. 创建虚拟dom
// 虚拟dom生成的三个要素
function createElement(vnode) {
let tag = vnode.tag; // 目标元素
let attrs = vnode.attrs || {}; // 属性
let children = vnode.children || []; // 子节点

if (!tag) {
return null;
}

// 1. 创建对应的dom
let elem = document.createElement(tag);

// 2. 给dom添加属性
let attrName;
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
elem.setAttribute(attrName, attrs[attrName]);
}
}

// 3. 如果有子节点,递归创建子节点的DOM元素并添加到当前DOM元素中
children.forEach(childVnode => {
elem.appendChild(createElement(childVnode));
});

// 为vnode添加一个引用到真实DOM元素的属性
vnode.el = elem;

return elem;
}

function patchElement(el, oldVnode, newVnode) {
// 这里可以添加代码来比较和更新元素的属性和子节点...
// 例如,可以比较oldVnode和newVnode的属性和子节点,并相应地修改el。
}

更新的进一步理解

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
/**
* 更新子节点。
* 这个函数的目的是递归地比较两个虚拟节点的子节点,然后确定是否更新它们或替换它们。
*
* @param {Object} vnode - 旧的虚拟节点。
* @param {Object} newVnode - 新的虚拟节点。
*/
function updateChildren(vnode, newVnode) {
// 获取旧虚拟节点的子节点,如果没有子节点则默认为空数组。
let children = vnode.children || [];

// 获取新虚拟节点的子节点,如果没有子节点则默认为空数组。
let newChildren = newVnode.children || [];

// 遍历旧虚拟节点的子节点。
children.forEach((childVnode, index) => {
// 获取相应的新虚拟节点子节点。
let newChildVnode = newChildren[index];

// 检查新虚拟节点的子节点是否存在,并且它们的标签是否相同。
if (newChildVnode && childVnode.tag === newChildVnode.tag) {
// 如果标签相同,递归更新子节点。
updateChildren(childVnode, newChildVnode);
} else if (newChildVnode) {
// 如果标签不相同,替换旧节点。
replaceNode(childVnode, newChildVnode);
}
// 注意:上面的代码没有处理新虚拟节点子节点比旧虚拟节点子节点多的情况。在实际实现中,还需要处理这种情况。
});
}

Vue应用层面试题

$nextTick()

$nextTick() 是 Vue 中的一个非常有用的方法,它用于延迟回调函数的执行,直到下次 DOM 更新周期结束。当你更改某些数据,Vue将这些更改添加到一个队列中并异步地更新DOM。如果你想在DOM更新后立即执行某些作,可以使用 $nextTick()。

例如:

1
2
3
4
5
Copy code
this.message = 'Hello Vue!';
this.$nextTick(() => {
// 这个回调将在DOM更新后被调用
});

单页与多页优缺点

单页应用(SPA)的优点:

  • 更快的页面切换,因为不需要重新加载整个页面。
  • 更好的用户体验,页面间的流畅性增强。
  • 更容易与移动应用进行集成。
  • 能够更容易地实现复杂的交互和动画。

单页应用的缺点:

  • 初始加载时间可能较长,因为必须加载整个应用。
  • SEO 可能更加挑战。
  • 可能更难以实现浏览器的前进/后退功能。

多页应用(MPA)的优点:

  • 对于SEO更友好。
  • 可以更好地利用浏览器的原生功能,如前进/后退。
  • 初始页面加载速度可能更快。

多页应用的缺点:

  • 页面切换时,用户体验可能不如SPA流畅。
  • 开发和部署可能更复杂,特别是当涉及到多个互相依赖的页面。

v-if与v-for

v-if 和 v-for 都是Vue中用于渲染元素的指令,但它们的使用场景和性能影响有所不同。

  • v-if: 是一个条件渲染指令,只有当其表达式的值为真时,它会渲染元素。这意味着它可以动态地添加或删除DOM元素。
  • v-for: 是用于循环渲染元素的。你可以使用它来迭代数组或对象,并为每个项目渲染一个元素。

注意:
当 v-if 和 v-for 在同一个元素上使用时,v-for 优先于 v-if。但是,通常不建议同时在同一个元素上使用这两个指令,因为它可能导致性能问题。
跳转相关

在Vue中,页面跳转通常与Vue Router库相关。以下是一些关键点:

router-link: 一个Vue组件,用于导航。例如,<router-link to="/about">About</router-link>

router.push(): 一个程序化导航的方法。例如,this.$router.push('/about')

router-view: 一个组件,用于渲染当前路由匹配到的组件。

mode:Vue Router中,您可以选择hash模式(URL中带有 #)或history模式(更干净的URL,但需要服务器配置)。

导航守卫:Vue Router中的功能,允许您在导航发生之前或之后执行某些操作,如身份验证检查。

注意: 为了实现跳转功能,你需要确保已经正确安装和配置了Vue Router。

Vue-routerlocation.href有什么区别?

  • location.href: 简单方便,刷新页面
  • Vue-router: 实现了按需加载,减少了dom消耗

v-model双向数据绑定原理

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
<!-- HTML -->
<input placeholder="请输入名字" id="username">
<p id="uName"></p>

<script>
let obj = {};

// 使用Object.defineProperty实现双向数据绑定
Object.defineProperty(obj, "username", {
get: function() {
console.log("取值");
return this._username; // 返回内部属性
},
set: function(val) {
console.log("设置值");
this._username = val; // 更新内部属性值
document.getElementById("uName").innerText = val; // 更新显示的值
}
});

// 监听输入框的值变化
document.getElementById("username").addEventListener("keyup", function(event) {
obj.username = event.target.value;
});
</script>

v-if与v-show的区别

  • v-if:不满足条件,不会渲染dom =>单次判断
  • v-show:隐藏dom =>多次切换
  • 初始渲染开销:
  • v-if:具有更高的初始渲染开销,因为它涉及到真实的 DOM 元素的创建和销毁。
  • v-show:具有更低的初始渲染开销,因为它只是修改 CSS 属性。
  • 切换开销:
  • v-if:当条件反复切换时,v-if 可能会导致更高的性能开销,因为它需要创建或销毁真实的 DOM 元素。
  • v-show:切换开销较小,因为它只是改变元素的 CSS 属性。

使用场景:

  • v-if:更适用于条件很少改变或元素需要被完全销毁/重建的场景。
  • v-show:更适用于经常需要显示和隐藏的场景。