HTML
IE盒子和普通盒子有什么区别
IE盒模型(又称"怪异盒模型")与标准盒模型(W3C盒模型)在计算元素总宽度和高度时有显著差异。
a标签的合法target属性?
_self
_blank
_parent
_top
target 属性还可以使用自定义名称
CSS
Css引入的方式有哪些?
<!-- 内联样式 -->
<p style="color: red; font-size: 16px;">这是一个使用内联样式的段落。</p>
<!-- 内部样式表 -->
<style>
p {
color: blue;
font-size: 18px;
}
</style>
<!-- 外部样式表 -->
<link rel="stylesheet" href="styles.css">
style.css文件
<style>
/* 导入样式表 */
@import url('imported.css');
</style>
inset代表什么属性的缩写?
用于定位,可以用px或百分比等设置定位的4个偏移量。可应用于任何writing-mode属性。
用法:inset:length|percentage|auto|inherit继承|initial默认|unset;
顺序同padding:也是上右下左
.child{
position:absolute;
inset:10px 40px 30px 0px;
}
flex:1;代表哪几个属性的缩写?
flex: 1; 主要是用来告诉 Flex 容器中的项目,如果有剩余空间,它们应该等比例地放大,如果空间不足,它们也应该等比例地缩小,而它们的初始大小(在没有额外空间或需要缩小的情况下)是 0%(尽管这通常不是预期的行为,特别是当你想让项目根据其内容大小开始时)。为了更精确地控制项目的大小和伸缩行为,你可能会想使用更明确的 flex 属性值,比如 flex: 1 1 auto;
等同于:
flex-grow:1; 放大比例
flex-shrink:1; 缩小比例
flex-basis:0%;平分剩余空间%,
Flex: auto 与下列哪项效果相同?
Flex: 1 1 auto
css优先级排序?伪类排哪里 ?
- !important最高(ie6不支持)
- 内联样式,标签加style
- ID选择器
- 类.class、伪类:hover、属性[title]-[title^=a]
- 元素,伪元素::before
- 通配符*
css设置100%,是相对什么?
是相对父元素的,浮动后也是相对自身的;如果有soblute定位就是相对第一个定位父级;fixed定位是相对窗口;margin是相对自身。
css实现一个容器,高度超出视窗就固定高度满屏,不超出视窗则500px固定高度
//方法1
.container {
min-height: 500px; /* 最小高度 500px */
max-height: 100vh; /* 最大高度为视窗高度 */
height: auto; /* 自动调整 */
overflow-y: auto; /* 超出时滚动 */
}
svg和canvas的区别
都是H5支持的2种可视化技术,都可以在画布上绘制和放入图片。
- canvas是h5标签,可用number设置标签的宽高,用js的getContext('2d')作为画笔进行绘制。
- svg可以缩放矢量图,做标签用时stroke可修改颜色。
| canvas | SVG | |
|---|---|---|
| 绘制格式 | getContext绘制依赖分辨率,能以.png和.jpg保存为图片,可以说是位图 | h5中直接绘制矢量图 |
| 事件处理器 | 不支持,图像是canvas的一部分,不能用js获取图形 | 支持 |
| 工作机制围 | 逐像素渲染,绘制完就结束 | svg通过DOM操作显示 |
| 适用范围 | 有许多对象要被频繁重绘的图形密集型游戏 | 有大型渲染区域的,如地图 |
如何写一个0.5px的线
- transform scaleY
.half-px-line {
transform: scaleY(0.5);
}
2. 使用svg矢量图
<svg height="1" width="100%">
<line x1="0" y1="0.5" x2="100%" y2="0.5" stroke="#000" stroke-width="0.5" />
</svg>
display有哪些属性
block块级元素 将元素显示为块级元素,元素会独占一行,并且可以设置宽度和高度。常见的块级元素有<div>、<p>、<h1>-<h6>等。inline-block
元素会像内联元素一样排列在一行,但又可以设置宽度和高度,同时保留块级元素的部分特性。list-item
将元素显示为列表项,通常会带有列表项标记(如圆点、数字等)。inline
元素会以内联元素的形式显示,不会独占一行,宽度和高度由内容决定,并且无法设置宽度和高度。常见的内联元素有<span>、<a>、<img>等。table
将元素显示为表格,类似于<table>标签。table-row
将元素显示为表格的行,类似于<tr>标签。table-cell
将元素显示为表格的单元格,类似于<td>标签。flex
将元素显示为弹性容器,其子元素会成为弹性项目,可以使用弹性布局的相关属性来控制子元素的排列和对齐方式。inline-flex
类似于flex,但元素会以内联元素的形式显示。grid
将元素显示为网格容器,其子元素会成为网格项目,可以使用网格布局的相关属性来控制子元素的排列和对齐方式。
div {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
inline-grid
类似于grid,但元素会以内联元素的形式显示。
div {
display: inline-grid;
grid-template-rows: repeat(2, 1fr);
}
none
元素不会在页面中显示,并且不会占用页面空间。contents
元素本身不会产生任何框,但其子元素会正常显示,就好像该元素不存在一样。run-in
元素会根据上下文和周围元素的情况,以块级元素或内联元素的形式显示。不过该属性的兼容性较差,使用时需谨慎。
如何写个居中的样式
在父元素上设置 text-align: center 来实现行内元素或行内块级元素的水平居中。
块元素设置左右外边距为 auto 来实现块级元素的水平居中。
单行文本的垂直居中,可以通过设置元素的 line-height 等于元素的 height 。
display: flex;
align-items: center;
justify-content: center;
.child {
width: 200px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -100px;
background-color: lightblue;
}
css函数有哪些
1. 值与单位函数
-
calc() - 动态计算值 width: calc(100% - 50px);
-
min() - 取最小值 width: min(100vw, 1200px);
-
max() - 取最大值 font-size: max(1rem, 12px);
-
clamp() - 限制在范围内 font-size: clamp(1rem, 2.5vw, 2rem);
2. 颜色函数
-
rgb()/rgba() - RGB颜色 color: rgb(255, 0, 0); background: rgba(0, 0, 255, 0.5);
-
hsl()/hsla() - HSL颜色 color: hsl(120, 100%, 50%);
-
color-mix() - 颜色混合 color: color-mix(in srgb, red 30%, blue 70%);
3. 渐变函数
-
linear-gradient() - 线性渐变 background: linear-gradient(45deg, red, blue);
-
radial-gradient() - 径向渐变 background: radial-gradient(circle, red, yellow, green);
-
conic-gradient() - 锥形渐变
background: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
4. 图形变换函数
-
rotate() - 旋转 transform: rotate(45deg);
-
scale() - 缩放 transform: scale(1.2);
-
translate() - 位移 transform: translate(50px, 100px);
-
matrix() - 矩阵变换 transform: matrix(1, 2, 3, 4, 5, 6);
5. 滤镜函数
-
blur() - 模糊 filter: blur(5px);
-
brightness() - 亮度 filter: brightness(0.8);
-
contrast() - 对比度 filter: contrast(200%);
10. 其他实用函数
-
var() - 使用CSS变量 color: var(--main-color);
-
attr() - 获取HTML属性 content: attr(data-tooltip);
-
url() - 引用资源 background-image: url("image.jpg");
rem和px有什么区别
- px(像素) :它属于固定的物理单位,一个
px就代表屏幕上的一个物理像素点。在不同设备上,px所代表的实际尺寸是固定的。比如,设置一个元素的宽度为100px,那在任何设备上它都会占据 100 个物理像素的宽度。 - rem(根元素字体大小的倍数) :
rem是相对单位,它基于 HTML 根元素(<html>)的字体大小来计算。要是根元素的字体大小为16px,那么1rem就等同于16px;若根元素字体大小变为20px,1rem就等于20px。
浏览器
页面渲染经历了哪些过程
- 解析阶段(Parsing)
HTML解析:
浏览器从上到下解析HTML文档
构建DOM(Document Object Model)树
遇到
CSS解析:
解析所有CSS文件(外部、内部、行内样式)
构建CSSOM(CSS Object Model)树
解析过程会阻塞渲染(CSS是渲染阻塞资源)
- 样式计算(Style Calculation) 将CSS规则应用到DOM节点上
计算每个DOM节点的最终样式(继承、层叠、优先级)
生成带有样式的DOM树(Render Tree的基础)
- 布局/回流(Layout/Reflow) 计算每个可见元素在视口中的确切位置和大小
从根节点开始递归计算几何信息
创建布局树(Layout Tree),包含位置、尺寸等信息
首次布局称为"布局",后续改变称为"回流"
- 分层(Layer) 浏览器将页面分为多个图层(Layer)
特定元素会创建新图层(如transform、z-index元素)
分层可以提高渲染效率(单独绘制和合成)
- 绘制(Paint) 将每个图层的节点转换为屏幕上的实际像素
执行栅格化(Rasterization)过程
生成绘制指令列表(Paint Records)
- 合成与显示(Composite & Display) 将各图层按正确顺序合成(Compositing)
通过GPU加速合成过程
最终显示在屏幕上
JavaScript
js数据类型
- 基本数据类型:string、number、boolean、undefined、null、symbol、
bigint - 引用数据类型:Object[array、object、function、dateRegExp]
- “undefined”X“not defined” undefined表示变量未赋值、对象的属性不存在,是js数据类型之一,合理存在。 not defined表示变量未声明就被使用了,属于报错信息。
判断数据类型的方法有哪些?
- typeof,直接返回基础类型 可区分 null之外的原始类型,例:typeof null //object
- instanceof,返回布尔值 不能判断基础类型,(结果不可靠,因为symbol可以直接修改Symbol.hasInstance覆盖默认性能为)。例:
var num = new Number()
num innstanceof Number // true
- Object.prototype.toString.call()
- Array.isArray()判断是否数组
typeOf
// 1. 数值类型 (number)
// 整数
const integerNumber = 42;
console.log(`typeof ${integerNumber} 结果: ${typeof integerNumber}`); // 输出: typeof 42 结果: number
// 浮点数
const floatNumber = 3.14;
console.log(`typeof ${floatNumber} 结果: ${typeof floatNumber}`); // 输出: typeof 3.14 结果: number
// NaN
const notANumber = NaN;
console.log(`typeof ${notANumber} 结果: ${typeof notANumber}`); // 输出: typeof NaN 结果: number
// Infinity
const infinityNumber = Infinity;
console.log(`typeof ${infinityNumber} 结果: ${typeof infinityNumber}`); // 输出: typeof Infinity 结果: number
// 2. 字符串类型 (string)
const stringValue = 'Hello, World!';
console.log(`typeof ${stringValue} 结果: ${typeof stringValue}`); // 输出: typeof Hello, World! 结果: string
// 3. 布尔类型 (boolean)
const trueValue = true;
const falseValue = false;
console.log(`typeof ${trueValue} 结果: ${typeof trueValue}`); // 输出: typeof true 结果: boolean
console.log(`typeof ${falseValue} 结果: ${typeof falseValue}`); // 输出: typeof false 结果: boolean
// 4. 未定义类型 (undefined)
let undefinedVariable;
console.log(`typeof ${undefinedVariable} 结果: ${typeof undefinedVariable}`); // 输出: typeof undefined 结果: undefined
// 5. 空值类型 (null)
const nullValue = null;
// 注意:typeof null 返回 'object',这是 JavaScript 语言的一个历史遗留问题
console.log(`typeof ${nullValue} 结果: ${typeof nullValue}`); // 输出: typeof null 结果: object
// 6. 符号类型 (symbol)
const symbolValue = Symbol('example');
console.log(`typeof ${symbolValue.toString()} 结果: ${typeof symbolValue}`); // 输出: typeof Symbol(example) 结果: symbol
// 7. 引用数据类型
// 对象
const objectValue = { key: 'value' };
console.log(`typeof ${JSON.stringify(objectValue)} 结果: ${typeof objectValue}`); // 输出: typeof {"key":"value"} 结果: object
// 数组
const arrayValue = [1, 2, 3];
console.log(`typeof ${arrayValue} 结果: ${typeof arrayValue}`); // 输出: typeof 1,2,3 结果: object
// 函数
function exampleFunction() {
return 'Function result';
}
console.log(`typeof ${exampleFunction.name} 结果: ${typeof exampleFunction}`); // 输出: typeof exampleFunction 结果: function
如何判断一个字符串中是否包含某个字符?
// 返回 `true` 或 `false`。
str.includes(searchString, position?);
// 返回查找到的index下标,或者-1
str.indexOf(searchString, position?);
// search正则,类似indexOf,返回index下标或-1
str.search(/world/i)
regex.test(str); //正则匹配,返回布尔值
str.match(regexp); //正则匹配,返回匹配正则表达式的子串数组,未找到则返回 `null`
写个方法取0~10随机整数
Math.random()生成[0, 1)的随机浮点数(包含 0,不包含 1)- 【0,10】要向下取整才有0,那么*11才能取到10
Math.floor()向下取整,得到0, 1, 2, ..., 10
Math.floor(Math.random() * 11); // 0~10
数组的方法有哪些?增删改查、循环等
菜鸟官网有个列表,很全www.runoob.com/jsref/jsref…
- 伪数组:有数组的结构,没有push、pop等属性
- 伪数组->数组:Array.from(arr)
- 改变原数组有9个:
pop, push, shift, unshift, reverse, splice, sort, copywithin, fill - 不改变原数组有8个:
join, toLowcaleString, toString, slice, cancat, indexOf, lastIndexOf, includs - 遍历12个:
forEach遍历, map操作数组每一个元素并返回新数组, every是否都符合, some判断有无符合的, filter过滤符合的,find查询符合条件的元素, reduce累加, reduceRight, 查询符合条件的元素的下标:findIndex, keys, values, entries
一. 改变原数组的9个
// 增&删&改
1. push增尾 --- 2. pop删尾
3. unshift增头 --- 4. shift删头
5. reverse --- 颠倒数组中元素的顺序(arr.reverse();)
6. splice --- splice(index, length, 替换的item1, item2...)删除并返回删除元素是数组,未删除则返回空数组[]
7. sort --- 排序,按字母或数字
sort(function(a, b) {
return a - b
}) //数组排序
sort(function(a, b) {
return b - a
}) //数组排序
8. copyWithin --- 从数组的指定位置拷贝元素到数组的另一个指定位置中。
myArr.copyWithin(targetIndex, start, end): (修改原数组内容,length不变)
9. fill --- 使用一个固定值来填充数组。Array.fill(value, [start, end]) 修改数组,用value替换原数组内容
二. 不改变原数组有8个
- join
定义:将数组的所有元素连接成一个字符串,可以指定分隔符。
语法:arr.join([separator])
const fruits = ['Apple', 'Banana', 'Orange'];
console.log(fruits.join()); // "Apple,Banana,Orange"
console.log(fruits.join('-')); // "Apple-Banana-Orange"
console.log(fruits.join(' ')); // "Apple Banana Orange"
console.log(fruits.join('')); // "AppleBananaOrange"
- toLowcaleString
定义:返回一个表示数组元素的字符串,元素使用各自的toLocaleString方法转换为字符串。
语法:arr.toLocaleString([locales[, options]])
const numbers = [123456.789, new Date()];
const prices = [1234.56, 7890.12];
console.log(numbers.toLocaleString('en-US'));
// "123,456.789,5/10/2023, 3:25:45 PM"
console.log(prices.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }));
// "1.234,56 €,7.890,12 €"
- toString
定义:返回一个表示数组的字符串,元素用逗号分隔。
语法:arr.toString()
const arr = [1, 2, 'a', '1a'];
console.log(arr.toString()); // "1,2,a,1a"
- slice
定义:返回一个新的数组对象,包含从开始到结束(不包括结束)的浅拷贝。
语法:arr.slice([begin[, end]])
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2)); // ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4)); // ["camel", "duck"]
console.log(animals.slice(1, 5)); // ["bison", "camel", "duck", "elephant"]
console.log(animals.slice(-2)); // ["duck", "elephant"]
console.log(animals.slice(2, -1));// ["camel", "duck"]
- cancat
定义:用于合并两个或多个数组,返回一个新数组。
语法:arr.concat(value1[, value2[, ...[, valueN]]])
const arr1 = ['a', 'b', 'c'];
const arr2 = ['d', 'e', 'f'];
const arr3 = arr1.concat(arr2);
console.log(arr3); // ["a", "b", "c", "d", "e", "f"]
// 也可以合并值
console.log(arr1.concat(1, [2, 3])); // ["a", "b", "c", 1, 2, 3]
- indexOf
定义:返回在数组中可以找到给定元素的第一个索引,如果不存在则返回-1。
语法:arr.indexOf(searchElement[, fromIndex])
const beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];
console.log(beasts.indexOf('bison')); // 1
console.log(beasts.indexOf('bison', 2)); // 4
console.log(beasts.indexOf('giraffe')); // -1
- lastIndexOf
定义:返回指定元素在数组中的最后一个索引,如果不存在则返回-1。
语法:arr.lastIndexOf(searchElement[, fromIndex])
const animals = ['Dodo', 'Tiger', 'Penguin', 'Dodo'];
console.log(animals.lastIndexOf('Dodo')); // 3
console.log(animals.lastIndexOf('Tiger')); // 1
console.log(animals.lastIndexOf('Giraffe')); // -1
- includs
定义:判断数组是否包含某个元素,返回布尔值。
语法:arr.includes(searchElement[, fromIndex])
const array1 = [1, 2, 3];
console.log(array1.includes(2)); // true
console.log(array1.includes(4)); // false
console.log([1, 2, NaN].includes(NaN)); // true
// 从索引2开始查找
console.log([1, 2, 3].includes(1, 1)); // false
三、 遍历数组12个
- forEach
定义:对数组的每个元素执行一次提供的函数。
语法:arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
const fruits = ['apple', 'banana', 'orange'];
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// 输出:
// 0: apple
// 1: banana
// 2: orange
- map
定义:创建一个新数组,其结果是该数组中的每个元素调用一次提供的函数后的返回值。
语法:arr.map(callback(currentValue [, index [, array]])[, thisArg])
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6]
3.filter
定义:创建一个新数组,包含通过函数要求的所有元素。
语法:arr.filter(callback(element [, index [, array]])[, thisArg])
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
- reduce累加
定义:返回单个值,对数组中的每个元素执行一个reducer函数,将其结果汇总为单个返回值。
语法:arr.reduce(callback(accumulator, currentValue [, index [, array]])[, initialValue])
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 10
- reduceRight
定义:与reduce()类似,但从右向左执行。
语法:arr.reduceRight(callback(accumulator, currentValue [, index [, array]])[, initialValue])
const numbers = [1, 2, 3, 4];
const result = numbers.reduceRight((acc, curr) => acc - curr);
console.log(result); // -2 (计算方式: 4 - 3 - 2 - 1)
- find
定义:返回数组中满足提供函数要求的第一个元素的值,否则返回undefined。
语法:arr.find(callback(element [, index [, array]])[, thisArg])
const numbers = [5, 12, 8, 130, 44];
const found = numbers.find(num => num > 10);
console.log(found); // 12
7. findIndex
定义:返回数组中满足提供的测试函数的第一个元素的索引,否则返回-1。
语法:arr.findIndex(callback(element [, index [, array]])[, thisArg])
const numbers = [5, 12, 8, 130, 44];
const index = numbers.findIndex(num => num > 10);
console.log(index); // 1
8. some
定义:返回布尔值,数组中是否至少有一个元素符合函数的要求。
语法:arr.some(callback(element [, index [, array]])[, thisArg])
const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
- every
定义:返回布尔值,数组中是否所有元素都通过了函数的测试。
语法:arr.every(callback(element [, index [, array]])[, thisArg])
const numbers = [2, 4, 6, 8, 10];
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // true
10. for...of
定义:创建一个循环来迭代可迭代对象(包括数组)。
语法:for (variable of iterable) { statement }
const fruits = ['apple', 'banana', 'orange'];
for (const fruit of fruits) {
console.log(fruit);
}
// 输出:
// apple
// banana
// orange
11. entries
定义:返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。
语法:arr.entries()
const fruits = ['apple', 'banana', 'orange'];
const iterator = fruits.entries();
for (const [index, fruit] of iterator) {
console.log(`${index}: ${fruit}`);
}
// 输出:
// 0: apple
// 1: banana
// 2: orange
12. keys
定义:返回一个新的Array Iterator对象,它包含数组中每个索引的键。
语法:arr.keys()
const fruits = ['apple', 'banana', 'orange'];
const keys = fruits.keys();
for (const key of keys) {
console.log(key);
}
// 输出:
// 0
// 1
// 2
13. values
定义:返回一个新的Array Iterator对象,该对象包含数组每个索引的值。
语法:arr.values()
const fruits = ['apple', 'banana', 'orange'];
const iterator = fruits.values();
for (const value of iterator) {
console.log(value);
}
// 输出:
// apple
// banana
// orange
类数组或伪数组转数组
- 创建数组
// 创建:
[];
new Array()
Array.of(1,2) == [1,2]
Array.from() // 伪数组转数组
类数组或伪数组转数组:
- Array.from
- Array.prototype.slice.call(obj)
这么使用是改变数组方法slice的作用域,使其能作用域非数组对象。slice方法是截取数组的一部分并返回一个新数组;而call方法用于在函数调用时把它内部的this指针绑定到另一个对象上。
Array.prototype.slice.call就是把Array原型链上的方法slice,其内部的this指针绑定到()里的对象上,使对象可以使用slice方法,也就是把对象转换为数组。 - 扩展运算符
- Array.prototype.map().call(obj)
//类数组转数组:
// 1. Array.from
const pseudoArray = {0: "a", 1: "b", 2: "c", length: 3};
const array = Array.from(pseudoArray);
console.log(array); // ["a", "b", "c"]
//2. 使用Array.prototype.slice.call()方法:
const array = Array.prototype.slice.call(pseudoArray);
console.log(array); // ["a", "b", "c"]
//3. 扩展运算符
const array = [...pseudoArray];
console.log(array); // ["a", "b", "c"]
//4. 使用Array.prototype.map().call方法:
const array = Array.prototype.map.call(pseudoArray, function(value) {
return value;
});
console.log(array); // ["a", "b", "c"]
什么是this指针?严格模式下呢?
this跟执行环境、声明环境、class有关。
- 普通函数内指向调用者,如obj.fuc,fuc的this指向obj。
- 对象内,this指向方法所在对象的name属性。
- function函数无调用者,指向window
- new构造函数--指向new的对象,class同。
- call,apply指向调用对象
- 箭头函数,外层函数的this就是内层函数的this==自身没有this,从外层找到后继承,如果就一层指向window,
严格模式,指向undefined
箭头函数没有自己的this,它的this是继承来的。默认指向定义时的宿主。
高阶函数有什么?
高阶函数是指:如果函数的参数,或者返回值,也是函数,那就是高阶函数。
举例:promise, 计时器, map, filter, reduce等
call,apply,bind
- 相同:
这3种是function的prototype上的方法,所以谁都可以用。作用都一样:改变this的指向,说白了就是用一个obj调用一个函数,补足函数没有值的参数。 - 不同:
- apply第二个参数是数组形式;
- bind需要再小括号调用()一次。
- bind参数类似apply也有数组,会拼接在callback参数之前。
- ES6+ 使用展开运算符替代
apply
Math.max(...[1, 2, 3]) // 替代 Math.max.apply(null, [1, 2, 3]) - ES6+ 使用箭头函数替代
bind
button.addEventListener('click', () => this.handleClick())
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
// call
const person = { name: 'Alice' };
greet.call(person, 'Hello', '!'); // 输出: "Hello, Alice!"
// apply
const person = { name: 'Bob' };
const args = ['Hi', '!!!'];
greet.apply(person, args); // 输出: "Hi, Bob!!!"
// bind
const person = { name: 'Charlie' };
const greetCharlie = greet.bind(person, 'Hello');
greetCharlie('...'); // 输出: "Hello, Charlie..."
适用场景:
- Object.prototype.toString.call(obj)实现类型检测。
首先,我们知道js全员皆对象,都是从Object继承而来,而Object原型上的toString是可以输出对象类型的,这个toString方法也被继承给了每一个类,但是他们在继承时都做了改写。所以,使用Object最原始的也就是prototype上的toString方法是可以判断类型的,只要把this指向需要判断的元素,就可以直接输出类型了。 - Math.max.call(Math, arr)计算数组最大值
效果等同于Math.max(...arr)解构数组 - 实现继承,如下:
function peaple(name) {
this.name = name;
this.showName = function () {
console.log(this.name);
}
}
function student(name) {
peaple.call(this, name);
}
var xiaoming = new student('xiaoming');
xiaoming.showName();
- 数组追加:
var array1 = [1 , 2 , 3, 5];
var array2 = ["xie" , "li" , "qun" , "tsrot"];
Array.prototype.push.apply(array1, array2);
console.log(array1);
类似于 let c = array1.concat(array2)
call把原本people的this指向了student,所以student继承了原来的所有。
用现有的函数去处理call的值。
- 保存this变量:
var foo = {
bar : 1,
eventBind: function(){
var _this = this ;
$('.someClass').on('click',function(event) {
console.log(_this.bar);
});
}
}
var foo = {
bar : 1,
eventBind: function(){
$('.someClass').on('click',function(event) {
console.log(this.bar);
}.bind(this));
}
}
- 如何实现一个.call
// 函数作用:调用函数,传参,改变this指针
// 步骤: 1.this,context;
// 2.把调用的函数设置为对象的方法;
// 3.调用函数,得到返回值并返回。
Function.prototype.myCall = function(context, ...args) {
// 1. 处理context为null或undefined的情况(非严格模式下会指向window/global)
context = context || window;
// 2. 确保context是对象(原始值会被包装)
context = Object(context);
// 3. 创建一个唯一的属性键,避免覆盖原有属性
const fnKey = Symbol('fn');
// 4. 将当前函数(this)赋值给context的fnKey属性
context[fnKey] = this;
// 5. 执行函数,并传入参数
const result = context[fnKey](...args);
// 6. 删除临时添加的属性
delete context[fnKey];
// 7. 返回函数执行结果
return result;
};
// 使用示例
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Alice' };
// 使用原生call
greet.call(person, 'Hello', '!'); // 输出: "Hello, Alice!"
// 使用自定义myCall
greet.myCall(person, 'Hi', '!!!'); // 输出: "Hi, Alice!!!"
说说对原型的理解?什么是原型链
ES6 的 class 本质上是原型继承的语法糖
- 什么是原型
在 JavaScript 中,每个对象都有一个隐藏的[[Prototype]]属性(可以通过__proto__访问),这个属性指向该对象的原型对象。原型对象也是一个普通对象,它包含可以被其他对象共享的属性和方法。 - 构造函数与原型
- 每个函数都有一个
prototype属性(注意:这是函数特有的属性) - 当使用
new操作符调用构造函数创建实例时,实例的__proto__会指向构造函数的prototype
- 原型链的定义 当访问一个对象的属性时,JavaScript 会:
-
- 先在对象自身属性中查找
-
- 如果找不到,就去它的原型对象上查找
-
- 如果还找不到,就去原型对象的原型上查找
-
- 依此类推,直到找到属性或到达原型链顶端(
null)
这种层层向上的查找机制就形成了原型链。
- 依此类推,直到找到属性或到达原型链顶端(
// 原型链:
alice -> Person.prototype -> Object.prototype -> null
代码中创建一个function,浏览器会对应生成一个object(js全员皆对象),每个对象都初始化一个prototype内部属性,就是该对象的原型。除了Object.prototype对象以外的对象都是通过其他对象的原型创建的。
访问一个对象的属性或方法时,如果没找到就会向上继续找,去对象的Prototype里找,这个prototype又有自己的prototype,这样一直找下去就形成了原型链。这种查找机制就是原型链。
js的继承常见的原型链继承,构造函数继承,原型+构造,寄生,寄生组合。
总结:
- 原型是 JavaScript 实现继承和属性共享的机制
- 原型链是通过
__proto__连接起来的对象链,用于属性查找 - 每个对象都有原型(除了
Object.create(null)创建的对象) - 函数的
prototype属性决定了用它创建的实例的原型 - 原型链的终点是
Object.prototype,其__proto__为null
说一下防抖和节流
resize、onscroll、input内容校验onkeydown,类似这样的操作如果无限制触发调用函数,js-css-layout-paint-composite,会加重浏览器负担,debounce防抖和throttle节流就应运而生了。 不同:
- 防抖--抖动是没有必要的,只要最后一次就行。在delay之前触发会重新计时,最终只执行一次。(多次执行变最后一次执行)
- 节流--类似王者技能的触发,有CD时间,不能无限制发动技能。实时更新用这个,在1分钟内只触发一次,多次触发无效。1分钟后重新计时再执行下一次。(多次执行变每隔一段时间触发一次)
工作中,可以原生js实现,lodash插件有提供现成的方法。
介绍async和await
- async是声明function是异步的,awaite是等待一个异步执行完。
- 规则:
- awaite只能在async里使用,不可单独使用。
- async返回的是一个promise对象,awaite就是等待这个promise的结果,再继续执行。
- awaite等待的是一个promise,后面必须跟一个promise对象,但是不必写then(),直接得到返回值。
- async/awaite的特点
- async放在函数前,函数变为异步函数。
- 在函数前加async,打印函数调用,是一个Promise对象,而不是函数体内容。
- async配合awai使用。
- 调用依次发生
- Promis.then是链式调用,一直...,符合书写代码习惯。
只有async没有await时,函数返回什么:一个Promise
浏览器关闭前,怎么确保发出请求
方法1:
使用 Fetch 的keepalive标志
方法2:
使用Navigator.sendBeacon()
axios和ajax的区别,axios怎么发同步请求
ajax基于XMLHttpRequest。
axios
- axios是基于Promise的HTTP客户端,用于浏览器和node.js,内部判断环境是前端还是后台,选择用XMLHttpRequest还是Http对象。它提供了一个简洁的 API 来发送 HTTP 请求,并支持拦截请求和响应、转换请求和响应数据、取消请求、自动转换 JSON 数据等功能。
- axios有个拦截器叫intercepters,它有2对象分别是request和response,俩对象都有.use方法,每次调用都向callback里push{res1,rej1,res2,rej2}。
- 所有的回调都要储存起来吧,那么就有一个chain,用来存储所有回调的数组,它是给Promise用的,有2个初始值为dispatchRequest和undefined,再继续存储所有回调,存储顺序是什么样呢?Request的{res1,rej1,res2,rej2...}按顺序逐个unshift插入到chain最前面(注意:所以intercapters的request顺序看起来是前后颠倒了),Response的{r1,j1,r2,j2...}按顺序逐个push到chain最后面,所以chain完整列表就是{rej2, res2, rej1, res1, dispatchRequest, undefined,r1,j1,r2,j2 }
- dispatchRequest,是用来执行axios的request,dispatchRequest里有个adapter,可以判断当前是前端还是后台,如果是后台就用HTTP对象,前端就用XMLHttpRequest,调用后adapter会对返回值进行处理,就是我们看到的response响应对象。
- 那么,Promise开始用chain了,Promise.resolve(config).then(fn1, fn2)。当config里结果是fullfilled(成功),就把config里的值传给fn1执行;如果config里结果是reject(失败),就把错误结果传给fn2执行。也就是说,前面的等同于Promise.resolve(config).then(chain[0],chain[1])。chain[0]是成功回调,chain[1]是失败回调。config里配置项很多,可能是一个string或者方法等。

axios怎么发送同步请求?
axios默认发送异步请求,这是因为它基于promise。如果需要发送同步请求,可以使用async/await和者Promise.resolve来模拟同步。
async function fetchData() {
try {
const response = await axios.get('/api/data');
// 处理响应数据
console.log(response.data);
} catch (error) {
// 处理错误
console.error(error);
}
}
前端开发的内存溢出怎样处理
一、内存溢出常见表现
- 页面卡顿、响应迟缓
- 浏览器标签页崩溃或自动刷新
- Chrome任务管理器显示内存持续增长不释放
- 控制台出现"Out of Memory"错误
诊断步骤
1. 打开无痕窗口(排除插件干扰)
2. 记录初始Heap Snapshot
3. 执行可疑操作多次
4. 记录操作后Heap Snapshot
5. 对比两次快照,查找Retained Size大的对象
前端的浅拷贝,深拷贝,举例说明
浅拷贝:Object.assign(), ...扩展运算符
// 浅拷贝对象
const originalObj = {
name: 'John',
age: 30,
hobbies: ['reading', 'swimming']
};
// 使用 Object.assign() 进行浅拷贝
const shallowCopyObj = Object.assign({}, originalObj);
// 修改浅拷贝对象的引用数据类型属性
shallowCopyObj.hobbies.push('running');
console.log(originalObj.hobbies); // 输出: ['reading', 'swimming', 'running']
console.log(shallowCopyObj.hobbies); // 输出: ['reading', 'swimming', 'running']
// 浅拷贝数组
const originalArr = [1, [2, 3], 4];
// 使用扩展运算符进行浅拷贝
const shallowCopyArr = [...originalArr];
// 修改浅拷贝数组的引用数据类型元素
shallowCopyArr[1].push(6);
console.log(originalArr[1]); // 输出: [2, 3, 6]
console.log(shallowCopyArr[1]); // 输出: [2, 3, 6]
深拷贝:lodash.cloneDeep, JSON.parse(JSON.stringify())无法处理特殊数据类型,structuredClone兼容性不好,手写deepCopy函数如下:
// 深拷贝函数
function deepCopy(obj) {
if (typeof obj!== 'object' || obj === null) {
return obj;
}
let newObj = Array.isArray(obj)? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key]);
}
}
return newObj;
}
const deepCopyObj = deepCopy(originalObj);
JS阻塞和非阻塞执行调整的意图
阻塞行为:如网络请求、文件读取等。当耗时操作完成后,会通过回调函数、Promise 或 async/await 等方式通知主线程处理结
获取日期年月日怎么写
- new Date(),可封装函数
// 获取当前日期
const now = new Date();
// 获取年月日
const year = now.getFullYear(); // 2023(四位数年份)
const month = now.getMonth() + 1; // 0-11 → 需要+1
const day = now.getDate(); // 1-31
console.log(`${year}-${month}-${day}`); // 输出如:2023-5-15
// 使用padStart补零
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 05
const day = String(now.getDate()).padStart(2, '0'); // 15
console.log(`${year}-${month}-${day}`); // 2023-05-15
2. 第三方插件:moment.js或date-fns
上传下载?多个文件同时上传怎样处理?上传失败了怎么找出是哪个文件
- formData
const formData = new FormData();
formData.append('file', file);
2. 大文件断点续传
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', i);
formData.append('totalChunks', chunks);
formData.append('uploadId', uploadId);
formData.append('originalName', file.name);
- 上传失败处理与问题定位
方法一:使用Promise.allSettled
async function handleUploads(files) {
const results = await Promise.allSettled(
files.map(file => uploadFile(file))
);
const failedFiles = results
.filter(result => result.status === 'rejected')
.map((result, index) => ({
file: files[index].name,
reason: result.reason.message
}));
if (failedFiles.length > 0) {
console.error('以下文件上传失败:', failedFiles);
// 显示失败文件列表给用户
}
}
最佳实践:文件校验+并发控制
性能优化自己常用的方法
JavaScript输出题
闭包
function foo() {
var a = 0;
return function () {
console.log(a++)
}
}
var f1 = foo() //0
var f1 = foo() //1
var f2 = foo() //0
es6
Promise
1.是什么
js中处理异步操作的对象。
2.为什么用它
传统解决方案是callback回调,多层嵌套,容易形成回调地狱。
getData(function(data1) {
getMoreData(data1, function(data2) {
getMoreData(data2, function(data3) {
// 层层嵌套,难以阅读和维护
});
});
});
Promise 的优势:
✅ 链式调用(.then().catch()),避免回调地狱。
✅ 更好的错误处理(.catch() 统一捕获错误)。
✅ 支持并发控制(Promise.all、Promise.race)。
3. Promise 的三种状态
状态一旦改变,就不可逆
- Pending(等待中) :初始状态,既未完成也未失败。
- Fulfilled(已完成) :操作成功完成,返回结果(
resolve被调用)。 - Rejected(已失败) :操作失败,返回错误(
reject被调用)。
4. new Promise
创建
const promise = new Promise((resolve, reject) => {
// 异步操作(如 AJAX、定时器等)
setTimeout(() => {
const success = true;
if (success) {
resolve("操作成功!"); // 状态变为 Fulfilled
} else {
reject("操作失败!"); // 状态变为 Rejected
}
}, 1000);
});
使用 .then() 和 .catch() 处理结果,.finally()总是执行。可以链式调用.then.then
promise
.then((result) => {
console.log(result); // "1操作成功!"
})
.then((result) => {
console.log(result); // "2操作成功!"
})
.catch((error) => {
console.error(error); // "操作失败!"
});
.finally(() =>
console.log("操作结束!")
);
捕捉错误可以用try/catch + async+await
async function loadData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
loadData();
5. 多任务执行时
- Promise.all()(全部成功才返回)
当其中一个任务执行错误时,会立即返回,被.catch捕捉。其他任务会继续执行,所以.then和.catch都会捕捉到。
const p1 = Promise.resolve("任务1");
const p2 = Promise.resolve("任务2");
Promise.all([p1, p2])
.then((results) => {
console.log(results); // ["任务1", "任务2"]
})
.catch((error) => {
console.error(error); // 如果有一个失败,立即返回错误
});
2. Promise.race()(谁先完成就返回谁)
const p1 = new Promise((resolve) => setTimeout(resolve, 500, "快的任务"));
const p2 = new Promise((resolve) => setTimeout(resolve, 1000, "慢的任务"));
Promise.race([p1, p2])
.then((result) => {
console.log(result); // "快的任务"
});
3. Promise.allSettled()(等所有 Promise 完成,无论成功/失败
const p1 = Promise.resolve("成功");
const p2 = Promise.reject("失败");
Promise.allSettled([p1, p2])
.then((results) => {
console.log(results);
// [
// { status: "fulfilled", value: "成功" },
// { status: "rejected", reason: "失败" }
// ]
});
4. Promise.any()(只要有一个成功就返回,全部失败才报错)
const p1 = Promise.reject("错误1");
const p2 = Promise.resolve("成功");
Promise.any([p1, p2])
.then((result) => {
console.log(result); // "成功"
})
.catch((errors) => {
console.error(errors); // 如果全部失败,返回 AggregateError
});
箭头函数的this问题
1. 静态绑定(Lexical this)
箭头函数的 this 在函数定义时就确定了,而不是在调用时确定。它会捕获其所在上下文的 this 值。
const obj = {
name: 'Alice',
regularFunc: function() {
console.log(this.name); // this指向obj
},
arrowFunc: () => {
console.log(this.name); // this指向外层作用域(通常是window)
}
};
obj.regularFunc(); // 输出 "Alice"
obj.arrowFunc(); // 输出 undefined(严格模式下可能是undefined)
2. 解决回调函数中的 this 问题
// 普通函数需要bind或保存this
function Timer() {
this.seconds = 0;
setInterval(function() {
this.seconds++; // 错误!这里的this指向window
}, 1000);
}
// 箭头函数自动绑定外层this
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // 正确!this指向Timer实例
}, 1000);
}
3. 不适合使用箭头函数的场景
- 对象方法:
const person = {
name: 'Bob',
// 错误写法
greet: () => console.log(`Hello, ${this.name}`), // this指向window
// 正确写法
greet() {
console.log(`Hello, ${this.name}`) // this指向person
}
}
- 原型方法:
function Person(name) {
this.name = name
}
// 错误写法
Person.prototype.sayHi = () => console.log(`Hi, ${this.name}`) // this指向window
// 正确写法
Person.prototype.sayHi = function() {
console.log(`Hi, ${this.name}`) // this指向实例
}
4. 如何确定箭头函数的 this
可以通过以下步骤判断:
- 找到箭头函数定义的位置(不是调用的位置)
- 查看箭头函数外层的第一个普通函数的this指向
- 如果外层没有普通函数,this将指向全局对象(严格模式下是undefined)
forEach和map区别
es6更新内容?
- 块级作用域
- 箭头函数
- 模板字符串
- 解构赋值
- 默认参数
- 拓展运算符
- 类和继承
- Promise对象
- 模块化:import、export
- 迭代器和生成器:Iterator迭代对象,Generator生成函数
ts
TS的interface和type的使用场景
框架
vue
vue获取子组件的数据
$refs: this.$refs.childRef.childData
子组件触发:this.$emit('childDataEvent', this.childData);
使用 Vuex
v-for用于哪些数据
数组、对象,数字范围,字符串
v-for="n in 3" //1,2,3
说说vue3的proxy
Vue 3 使用 JavaScript 的 Proxy 替代 Vue2的 Object.defineProperty 来实现响应式系统
一、Proxy 基本概念
Proxy 是 ES6 引入的新特性,可以创建一个对象的代理,用于拦截和自定义对象的基本操作。
const target = {}
const handler = {
get(target, key) {
console.log(`读取 ${key}`)
return target[key]
},
set(target, key, value) {
console.log(`设置 ${key} 为 ${value}`)
target[key] = value
return true
}
}
const proxy = new Proxy(target, handler)
proxy.name = 'Vue' // 输出: 设置 name 为 Vue
console.log(proxy.name) // 输出: 读取 name → Vue
二、Vue 3 如何使用 Proxy
Vue 3 的 reactive() 函数就是基于 Proxy 实现的:
三、相比 Vue 2 的优势
四、Proxy 实现响应式的核心原理
-
Getter 拦截:
- 追踪依赖(track)
- 返回属性值
-
Setter 拦截:
- 设置新值
- 触发更新(trigger)
unction reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key) // 追踪依赖
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
trigger(target, key) // 触发更新
return true
}
// 还有has、deleteProperty等其他拦截器
})
}
五、Proxy 的独特能力
-
检测所有属性变化 const obj = reactive({}) obj.newProp = 'value' // 自动响应,无需Vue.set delete obj.someProp // 自动响应,无需Vue.delete
-
完美支持数组 const arr = reactive([]) arr.push('item') // 正常工作 arr.length = 0 // 正常工作 arr[3] = 'new' // 正常工作
-
支持Map/Set等集合类型 const map = reactive(new Map()) map.set('key', 'value') // 响应式更新
六、局限性
-
浏览器兼容性:
- 不支持IE11及以下浏览器
- 现代浏览器全面支持
-
性能考量:
- 对于简单小对象,Proxy 可能比 defineProperty 稍慢
- 对于大型复杂对象,Proxy 性能更好
七、与Reflect的配合
Vue 3 使用 Reflect API 与 Proxy 配合:
const handler = {
get(target, key, receiver) {
// 使用Reflect保证正确的this指向
return Reflect.get(target, key, receiver)
}
}
Vue2和Vue3的区别
1.核心区别
2.生命周期
3. Vue3的优势
(1. 更高效的响应式系统
- Proxy 替代
Object.defineProperty- Vue 2 需递归遍历对象属性进行劫持,且无法检测新增/删除属性(需
Vue.set)。 - Vue 3 的
Proxy直接代理整个对象,支持动态属性变化监听。
- Vue 2 需递归遍历对象属性进行劫持,且无法检测新增/删除属性(需
(2. Composition API(组合式 API),结合setup函数
(3. 新特性与兼容性改进
<Teleport> :将组件渲染到 DOM 任意位置(如全局弹窗)。
<Teleport to="body">
<Modal /> <!-- 渲染到 body 而非当前组件 -->
</Teleport>
<Suspense> :优雅处理异步组件加载状态。
在 Vue 3 里,Suspense 是一个内置组件,用于处理异步依赖,在等待异步操作(像异步组件加载、数据获取这类操作)完成时,显示加载状态。它具备两个插槽:
#default:这里放置的是包含异步依赖的组件。#fallback:当异步依赖还未完成时,会显示这个插槽里的内容。
<Suspense>
<template #default><AsyncComponent /></template>
<template #fallback><LoadingSpinner /></template>
</Suspense>
多 `v-model:支持多个双向绑定。
<ChildComponent v-model:title="title" v-model:content="content" />
4、迁移注意事项
- 破坏性变化
v-model语法变更(.sync被移除)。- 事件总线(
$on/$off)废弃,推荐使用mitt或 Pinia/Vuex。
- IE11 不支持:Vue 3 依赖 Proxy,无法兼容 IE11(需用 Vue 2)。
- 生态适配:Vue Router 4.x、Vuex 4.x 为 Vue 3 专用版本。
Vue 2X3的生命周期
在上一条区别里
Vue3 生命周期钩子
Vue3 的生命周期钩子相比 Vue2 有一些变化,主要区别在于 Composition API 中的命名和 beforeDestroy/destroyed 被重命名为 beforeUnmount/unmounted:
- setup() - Composition API 的入口,在 beforeCreate 之前调用
- onBeforeMount() - 组件挂载之前
- onMounted() - 组件挂载完成后
- onBeforeUpdate() - 数据变化,DOM 更新之前
- onUpdated() - 数据变化,DOM 更新完成后
- onBeforeUnmount() - 组件卸载之前 (Vue2 的 beforeDestroy)
- onUnmounted() - 组件卸载完成后 (Vue2 的 destroyed)
- onActivated() - 被 keep-alive 缓存的组件激活时
- onDeactivated() - 被 keep-alive 缓存的组件停用时
- onErrorCaptured() - 捕获子孙组件错误时
vue组件如何传值
- 父:obj + 子props
- 父@handle + 子emit
- emit + off
- vueX和Pinia,状态管理
- 插槽
vue常用指令有哪些
v-text:更新元素的文本内容v-html:渲染原始 HTML(注意XSS风险)v-bind(缩写:):动态绑定属性v-if/v-else-if/v-else:条件渲染v-show:通过CSS控制显示/隐藏(频繁切换时性能更好)v-for:列表渲染v-on(缩写@):绑定事件监听器v-model:双向数据绑定(表单输入)v-slot(缩写#):定义插槽内容v-once:只渲染一次,后续数据变化不再更新v-bind:[attr]/v-on:[event]:动态参数
vue的修饰符
- 事件修饰符
<!-- 阻止默认行为 -->
<form @submit.prevent="onSubmit"></form>
<!-- 阻止事件冒泡 -->
<div @click.stop="doThis"></div>
<!-- 事件只触发一次 -->
<button @click.once="doThis"></button>
2. 键盘修饰符
<!-- 回车键触发 -->
<input @keyup.enter="submit">
<!-- 特定键触发 -->
<input @keyup.page-down="onPageDown">
3. 表单修饰符
<!-- 输入时去除首尾空格 -->
<input v-model.trim="msg">
<!-- 自动将输入转为数字 -->
<input v-model.number="age" type="number">
<!-- 惰性更新(change事件后更新而非input事件) -->
<input v-model.lazy="msg">
vue自定义指令
全局自定义指令,可在整个应用中使用
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
// 定义全局自定义指令
app.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
mounted(el) {
// 聚焦元素
el.focus();
}
});
app.mount('#app');
局部自定义指令,在本组件内使用
<template>
<input v-focus />
</template>
<script setup>
// 定义局部自定义指令
const vFocus = {
mounted(el) {
el.focus();
}
};
</script>
指令钩子
app.directive('example', {
// 指令绑定到元素时调用
beforeMount(el, binding, vnode, prevVnode) {
// 可以进行初始化操作
console.log('beforeMount');
},
// 元素插入到 DOM 中时调用
mounted(el, binding, vnode, prevVnode) {
// 可以进行 DOM 操作
console.log('mounted');
},
// 在包含组件的 VNode 更新之前调用
beforeUpdate(el, binding, vnode, prevVnode) {
console.log('beforeUpdate');
},
// 在包含组件的 VNode 及其子 VNode 更新之后调用
updated(el, binding, vnode, prevVnode) {
console.log('updated');
},
// 在绑定元素的父组件卸载之前调用
beforeUnmount(el, binding, vnode, prevVnode) {
console.log('beforeUnmount');
},
// 绑定元素的父组件卸载之后调用
unmounted(el, binding, vnode, prevVnode) {
console.log('unmounted');
}
});
指令参数
自定义指令可以接收参数,通过 binding 对象传递。
<template>
<div v-color="'red'">This text will be red</div>
</template>
<script setup>
const vColor = {
mounted(el, binding) {
el.style.color = binding.value;
}
};
</script>
vue中,父子组件加载时的生命顺序,传值时的呢?
- 父创建,子创建,子挂载,父挂载==》
父 beforeCreate →
父 created →
父 beforeMount →
子 beforeCreate →
子 created →
子 beforeMount →
子 mounted →
父 mounted
父组件销毁过程:
父 beforeDestroy →
子 beforeDestroy →
子 destroyed →
父 destroyed
当父组件数据变化触发更新时:
父 beforeUpdate →
子 beforeUpdate →
子 updated →
父 updated
2. 因为子mounted在前,所以,组件数据回显时,在父组件mounted里获取值,子组件的mounted是拿不到的,所以无法直接回显。按道理可以在父created里取值,但是因为请求是异步的,所以这样也不保险。项目中怎么解决呢?
- 子组件加v-if="data",这样请求到数据后再加载子组件。
- 子组件watch,监听props值。
nextick是什么原理
利用js的时间循环机制来实现异步执行。一个组件中多个nextTick会合并处理,协调宏任务和微任务放到合适的地方。Vue数据修改后并非马上立刻修改DOM,而是经过策略计算后修改,所以要在nextTick里执行。
keep-alive是做什么的,原理呢?
一、keep-alive 的作用
keep-alive 是 Vue 内置的一个抽象组件,用于缓存不活动的组件实例,避免重复渲染和销毁带来的性能损耗。主要功能包括:
- 组件缓存:当组件被切换时(如路由切换或动态组件切换),默认会被销毁,而使用
keep-alive可以保留组件状态 - 状态保持:保留组件的所有状态(包括数据、DOM 状态等)
- 避免重复渲染:再次激活时直接使用缓存实例,无需重新创建
二、基本使用方式
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
<!-- 或用于路由视图 -->
<keep-alive>
<router-view></router-view>
</keep-alive>
三、 实现流程
- 渲染阶段:
keep-alive组件本身不会渲染任何额外DOM元素- 通过插槽(slot)获取第一个子组件
- 缓存判断:
- 根据组件名(name)和key生成唯一标识
- 检查缓存中是否已有该组件实例
- 缓存处理:
- 如果存在缓存:直接取出实例,不执行组件的创建过程
- 如果不存在缓存:正常创建组件实例,并存入缓存
- 生命周期处理:
- 缓存组件时会触发
deactivated钩子 - 恢复组件时会触发
activated钩子
- 缓存组件时会触发
- 钩子:activated和deactivated
- include、exclude、max
四、 适合场景
- 表单内容缓存(如多步骤表单)
- 列表页到详情页的返回保持
- 频繁切换但需要保持状态的组件(如标签页)
五、 面试题:怎么保存孙子组件
keep-alive 默认只能直接缓存其直接子组件,而不会自动缓存孙子组件(子组件的子组件)
-
- 直接嵌套:
// 1. 直接嵌套
<keep-alive>
<parent-component>
<keep-alive>
<grandchild-component />
</keep-alive>
</parent-component>
</keep-alive>
-
- 【推荐】使用
include属性指定需要缓存的孙子组件名:
- 【推荐】使用
<!-- 父组件中 -->
<template>
<keep-alive :include="cachedComponents">
<child-component />
</keep-alive>
</template>
<script>
export default {
data() {
return {
cachedComponents: ['grandchild-a', 'grandchild-b']
}
}
}
</script>
<!-- 孙子组件中 -->
<script>
export default {
name: 'grandchild-a', // 必须设置明确的name
activated() {
// 激活时的数据刷新逻辑
},
deactivated() {
// 停用时的清理逻辑
}
}
</script>
-
- 将孙子组件作为动态组件使用:
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
-
- 如果使用 Vue Router,可以在路由配置中设置,然后在路由视图中判断meta信息
{
path: '/parent',
component: ParentComponent,
children: [
{
path: 'grandchild',
component: GrandchildComponent,
meta: { keepAlive: true }
}
]
}
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
-
- 高级实现原理:深度控制孙子组件的缓存,可以基于以下原理实现 (不明白,求解)
1. 递归查找:修改 keep-alive 源码,使其能递归查找所有子孙组件
2. 自定义缓存键:为每个需要缓存的组件生成唯一标识
3. 手动缓存管理:使用 Vue 的缓存机制手动管理组件实例
有什么封装的组件?
对element组件的二次封装
- 统一分页组件:封装el-pagination,统一分页样式和逻辑
- 增强型表格:在el-table基础上添加通用功能如导出、列配置等
- 表单生成器:基于el-form实现动态表单生成
- 对话框封装:统一对话框的打开/关闭逻辑和样式
- 上传组件增强:在el-upload基础上添加更多业务逻辑
封装注意事项
- 正确处理attrs和listeners:使用
v-bind="$attrs"和v-on="$listeners"(Vue2)或直接使用$attrs(Vue3) - 保持插槽透传:使用
<slot></slot>或具名插槽<slot name="xxx"></slot> - 类型定义:为TypeScript项目提供良好的类型支持
项目中的技术难点?
gridstack
借助AI实现国际化
vue的data为什么必须是function
核心原理:保证组件实例数据的独立性
-
避免数据共享问题
- 如果
data是一个对象,所有组件实例将共享同一个数据对象 - 修改一个组件实例的数据会影响到其他所有实例
- 通过函数返回对象,每个组件实例都会获得独立的数据副本
- 如果
底层原理:
-
组件复用机制
- Vue 组件是可复用的
- 当多次使用同一个组件时,会创建多个组件实例
- 如果使用对象形式,所有实例将引用同一个数据对象
-
工厂函数模式
- 通过函数每次返回新对象,实现了类似工厂模式的效果
- 每个组件实例调用 data 函数得到全新的数据对象
vuex有哪些属性,怎么同步和异步修改数据
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,它包含以下主要属性:state, getters, mutations, actions, modules。
完整使用示例:
// store.js
import { createStore } from 'vuex';
export default createStore({
state: () => ({ count: 0 }),
mutations: {
increment(state) {
state.count++
}
},
actions: {
async incrementAsync({ commit }) {
setTimeout(() => commit('increment'), 1000)
}
},
getters: {
doubleCount: state => state.count * 2
}
})
// 组件中使用
this.$store.commit('increment')
this.$store.dispatch('incrementAsync')
下面逐个解释:
-
Modules - 将 store 分割成模块 const moduleA = { state: () => ({ ... }), getters: {...} mutations: { ... }, actions: { ... } }
-
State - 存储应用状态数据 state: { count: 0, user: null }
-
Getters - 从 state 派生的计算属性 getters: { doubleCount: state => state.count * 2 }
-
Mutations - 同步修改 state 的方法 mutations: { increment(state, payload) { state.count += payload.amount } }
-
Actions - 包含异步操作,提交 mutations actions: { incrementAsync({ commit }, payload) { setTimeout(() => { commit('increment', payload) }, 1000) } }
vuex 和pinia的区别
Pinia使用示例:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
},
async incrementAsync() {
setTimeout(this.increment, 1000)
}
},
getters: {
doubleCount: (state) => state.count * 2
}
})
// 组件中使用
const counter = useCounterStore()
counter.increment()
counter.incrementAsync()
vuex在刷新页面数据会丢失怎么解决
- 使用 localStorage/sessionStorage
- 使用 vuex-persistedstate 插件
- 使用 IndexedDB (适合大量数据)
- 存储到后台数据库
vue路由模式有哪些
1. Hash 模式 (默认模式)
特点:
- 使用 URL 的 hash (
#) 来模拟完整 URL - 改变 hash 不会触发页面刷新
- 兼容性最好,支持所有浏览器
- 示例URL:
http://example.com/#/home
优点:
- 兼容性好,支持 IE9+
- 部署简单,不需要服务器额外配置
- 不会导致404错误,因为hash部分的变化不会发送到服务器
缺点:
- URL 中有
#,不够美观 - 不利于SEO优化(虽然现代搜索引擎已能抓取hash内容)
2. History 模式
特点:
- 使用 HTML5 History API (pushState, replaceState)
- URL 更美观,没有
# - 示例URL:
http://example.com/home
优点:
- URL 更简洁美观
- 对SEO更友好
- 可以利用服务器端渲染(SSR)
缺点:
- 需要服务器端支持,避免刷新出现404
- 兼容性稍差(IE10+)
- 需要正确的服务器配置
服务器配置示例(History模式)- Nginx 配置
location / {
try_files $uri $uri/ /index.html;
}
vue路由如何传值
- 通过路由参数传值,params
const routes = [
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
];
//跳转
const goToDetail = (id) => {
router.push(`/detail/${id}`);
};
//接收
<script setup>
import { useRoute } from 'vue-router';
const id = useRoute().params.id;
</script>
2. 通过查询参数传值,query
<script setup>
import { useRouter } from 'vue-router';
useRouter().push({ name: 'Detail', query: { id, name } });
</script>
//接收
$route.query.name
3. 通过路由元信息传值
const routes = [
{
path: '/detail',
name: 'Detail',
component: Detail,
meta: {
title: 'Detail Page',
description: 'This is a detail page.'
}
}
];
// 接收
route.meta.title
介绍vue的路由守卫
graph TD
开始路由 --> 触发组件离开beforeRouteLeave --> 全局路由守卫beforeEach --> 路由独享守卫beforeEnter --> 组件进入beforeRouteEnter -->全局解析守卫beforeResolve -->完成导航 -->全局后置afterEach
beforeEach 通常用于执行一些在进入路由之前必须完成的逻辑。这些逻辑可能包括但不限于:
- 登录验证:检查用户是否已经登录,如果没有登录,则重定向到登录页面。这是最常见的全局路由守卫使用场景之一。
- 权限验证:验证用户是否有权限访问某个路由。这通常涉及到检查用户的角色或权限列表,并决定是否允许其访问该路由。
- 获取数据:在进入路由之前,可能需要从服务器获取一些数据。这可以是用户数据、配置数据或其他任何需要的数据。
- 设置全局变量或状态:根据路由的变化,可能需要设置或更新一些全局变量或状态。
- 错误处理:在路由切换过程中,如果发生错误,可以在全局路由守卫中进行捕获和处理。
- 标题或面包屑更新:根据当前路由的信息,更新页面的标题或面包屑导航。
- 滚动位置管理:控制页面在路由切换时的滚动位置,例如,每次进入新页面时滚动到顶部。
怎么动态添加和修改路由
- 动态添加:router.addRoutes([arr])
- 修改:$router.options.routes是数组,直接改数组,但是不推荐,可能导致不匹配等问题。
React
react生命周期
react 常用的hooks有哪些
基础hooks
useState
const [count, setCount] = useState(0);
useEffect - 处理副作用(替代生命周期)
useEffect(() => {
// 相当于 componentDidMount 和 componentDidUpdate
document.title = `You clicked ${count} times`;
return () => {
// 相当于 componentWillUnmount
console.log('Clean up');
};
}, [count]); // 仅在 count 变化时更新
useContext - 访问上下文
const value = useContext(MyContext);
额外 Hooks
useReducer - 更复杂的状态逻辑
const [state, dispatch] = useReducer(reducer, initialState);
useCallback - 缓存函数
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
useMemo - 缓存计算结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useRef - 访问DOM或保存可变值
const inputRef = useRef(null);
// 访问DOM
<input ref={inputRef} />
useImperativeHandle - 自定义暴露给父组件的实例值
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
uniapp
前端工程化
部署方式
mobaxterm,用Linux脚本部署。
webpack
介绍webpack
一. 是什么 ,一句话说明
是一个打包、模块化js的工具,通过构建依赖图将前端项目中的各种资源(JS、CSS、图片等)转换为优化的静态资源。
在webpack里一切文件都是模块,像是模块打包机,通过loader转换文件,plugin注入钩子,最后输出由多个模块合成的文件。
它做的事:分析我的项目结构,找到js模块,以及其他浏览器无法直接解析运行的拓展语言(ts,Scss),把这些文件转换成合适的格式,打包起来供浏览器使用。
二. 核心概念(关键术语)
- 入口(Entry) :依赖分析的起点
- 输出(Output) :打包后的文件输出位置
- Loader:处理非JS文件的转换器(如CSS/图片)
- 插件(Plugins) :执行更广泛任务的扩展功能
- 模式(Mode) :开发/生产环境预设优化
- 模块热替换(HMR) :开发时局部更新不刷新页面
三、核心工作原理
1. "Webpack 的工作原理可以概括为:
- 从配置的入口文件开始构建依赖图
- 递归分析所有依赖的模块
- 根据文件类型使用对应的Loader处理
- 通过插件系统执行额外优化任务
- 最终输出优化后的静态资源包"
2.构建流程,从读取配置到输出文件?
- 串行,依次执行以下过程。
-
- 初始化参数:从配置文件到shell语句,读取合并参数,得出最终参数。
-
- 开始编译:用最终参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法,开始执行编译。
-
- 确定入口:根据配置中的entry找到所有的入口文件。
-
- 编译模块:从入口文件触发,调用所有配置的loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤,直到所有入口依赖的文件都经过了这步的处理。
-
- 完成模块编译:在上一步loader翻译完所有模块后,得到了每个模块被翻译后的最终内容,以及她们之间的依赖关系。
-
- 输出资源:根据入口和模块之间的依赖关系,组成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独文件加入到输出列表中,这步是可以修改输出内容的最后机会。
-
- 输出完成:确定输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统,在以上过程中,webpack会在特定时间点广播特定事件,插件在监听到感兴趣的事件后会执行特定逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果。
3. webpack中有很多常用loader加载器,用来处理不同类型的文件,将其处理成webpack可理解和打包的模块。
1.样式loader
- css-loader: 解析css文件,使其插入到js模块中
- Sass-loader:转换成css文件
- less-loader:同上
- style-loader:使样式插入到DOM中,适用于内联样式
2.图片loader
file和url loader同样可以处理字体文件
- svg-loader:把svg文件转换成js模块
- file-loader:把图片打包到指定目录,并返回图片url路径
- url-loader:类似file-loader,且可以对图片压缩和转换。
3.其他loader
- babel-loader:把ES6+的js语法转换成向后兼容的ES5的语法,以便在旧版本浏览器上执行。
- vue-loader:把vue组价转换成js
- ts-loader:把ts转成js
四、进阶特性(展示深度)
- Tree Shaking:删除未使用代码
- Code Splitting:分包加载优化
- 缓存策略:contenthash持久化缓存
- 性能优化:dllPlugin/thread-loader等
- 自定义Loader/Plugin开发
webpack的工具有哪些
1. Webpack Bundle Analyzer
一、基本介绍
npm install --save-dev webpack-bundle-analyzer
Webpack Bundle Analyzer 是一个可视化分析工具,用于帮助开发者:
- 直观查看 Webpack 打包后的文件组成
- 分析各个模块的体积大小
- 识别潜在的优化机会
- 检查 Tree Shaking 效果
二、替代方案比较
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Webpack Bundle Analyzer | 交互式可视化 | 本地开发深度分析 |
| source-map-explorer | 基于source map | 精确到源代码行级分析 |
| webpack-stats-plugin | 生成JSON数据 | CI/CD集成分析 |
| webpack-visualizer | 饼图展示 | 快速查看组成比例 |
针对业务场景提出
实现图片懒加载的方法和原理
- 原理:src属性是获取图片的,给一个统一的替代图片地址或者设置src_代替src属性。在图片进入视图时,赋值正确的src去请求图片。
- 实现关键在于判断元素位置,确定其是否出现在视口,有3种方式: - 滚动监听+scrollTop+offsetTop+innerHeight - 滚动监听+getBoundingClientRect() - intersetionObserve()
//方法1:
//object.getBoundingClientRect方法返回元素相对于视口的位置
getBoundingClientRect(ele).top >= 0 &&
getBoundingClientRect(ele).top <= offsetHeight
//方法2:
//intersetionObserve()非常好用 var io = new IntersectionObserver(callback, option); // 开始观察 ,可观察多个 io.observe(document.getElementById('example')); io.observe(el2) // 停止观察 io.unobserve(element); // 关闭观察器 io.disconnect();
前端开发中,搭建前端框架的步骤
- 明确需求与框架选型
- 环境搭建
- 创建项目:使用脚手架创建项目
- 路由配置
- 状态管理配置:Vuex || vite
- 样式配置:Sass、Less、Tailwind CSS
- 组件开发
- 单元测试
- 部署上线:构建项目,将构建好的静态文件部署到服务器上。
账号登录后进入主界面后前端都经历了哪些过程
-
登录表单提交
-
登录后,存储登录信息,设置HTTP拦截器 3. 路由导航与权限控制
-
主界面初始化