前端面试第 70 期 - 2024.10.18 更新前端面试问题总结(20 道题)目录: 初级开发者相关问题【共计 1
2024.10.13 - 2024.10.18 更新前端面试问题总结(20 道题) 获取更多面试相关问题可以访问 github 地址: github.com/pro-collect… gitee 地址: gitee.com/yanleweb/in…
目录:
-
初级开发者相关问题【共计 1 道题】
-
- HTML 中的 input 标签有哪些 type【热度: 197】【web 应用场景】
-
-
中级开发者相关问题【共计 11 道题】
-
- [Vue] vue3 如何监听数组变化【热度: 199】【web 框架】【出题公司: 美团】
-
- [Vue] vue3 还是使用的虚拟 dom 吗?【热度: 345】【web 框架】
-
- 如果我期望,页面加载和解析完成之后出发事件, 我该怎么做【热度: 299】【web 应用场景】
-
- JS 如何翻转一个字符串【热度: 442】【代码实现/算法】
-
- JS 如何翻转一个数组【热度: 327】【代码实现/算法】
-
- css 如何实现分栏布局【热度: 502】【CSS】
-
- 前端如何快速获取页面 url query 参数【热度: 888】【web 应用场景】
-
- 介绍一下 URLSearchParams API【热度: 10】【web 应用场景】【出题公司: TOP100 互联网】
-
- 如何把一个数组随机打乱【热度: 550】【代码实现/算法】
-
- package.json 依赖申明的方式有哪些, 他们有何却别【热度: 193】【web 应用场景】
-
- npm workspaces 是什么概念, 主要是解决什么问题【热度: 100】【工程化】
-
-
高级开发者相关问题【共计 8 道题】
- 998. [Vue] nextTick 作用是什么, 原理是什么【热度: 177】【web 框架】【出题公司: 美团】
-
- 现代框架如 React、Vue 相比原生开发有什么优势【热度: 897】【web 框架】
-
- [Vue] vue3 对虚拟 dom 做了哪些优化【热度: 446】【web 框架】
-
DOMContentLoaded
事件和load
事件有什么区别【热度: 210】【web 应用场景】
-
- 如何计算页面白屏时间【热度: 400】【web 应用场景】
-
- 如何计算页面首屏时间【热度: 426】【web 应用场景】
-
- [webpack] 是如何处理 commonjs 模块的文件, 使其编译结果能被浏览器使用。【热度: 210】【工程化】
-
- 前端如何处理一个页面多主题色可供选择的场景【热度: 797】【web 应用场景】
初级开发者相关问题【共计 1 道题】
1011. HTML 中的 input 标签有哪些 type【热度: 197】【web 应用场景】
关键词:input 标签 type 属性
HTML 中的<input>
标签有多种type
属性值,以下是一些常见的类型:
一、文本输入类型
-
text
:- 用于输入单行文本。这是最常见的输入类型之一,用户可以在输入框中输入任何文本内容。
- 例如:
<input type="text">
。
-
password
:- 用于输入密码,输入的内容会以掩码形式显示,以保护密码的安全性。
- 例如:
<input type="password">
。
二、数值输入类型
-
number
:- 用于输入数值。可以设置最小值、最大值、步长等属性来限制输入的范围。
- 例如:
<input type="number" min="0" max="100" step="1">
。
-
range
:- 以滑块的形式显示,用户可以通过拖动滑块来选择一个数值范围内的值。
- 例如:
<input type="range" min="0" max="100">
。
三、日期和时间输入类型
-
date
:- 用于选择日期。通常会显示一个日期选择器,方便用户选择日期。
- 例如:
<input type="date">
。
-
time
:- 用于选择时间。可以选择小时、分钟和秒。
- 例如:
<input type="time">
。
-
datetime-local
:- 用于选择日期和时间,包括本地时区信息。
- 例如:
<input type="datetime-local">
。
四、选择类型
-
checkbox
:- 复选框,用户可以选择多个选项。
- 例如:
<input type="checkbox">
。
-
radio
:- 单选按钮,用户只能选择一个选项。通常多个单选按钮具有相同的
name
属性,以确保只能选择其中一个。 - 例如:
<input type="radio" name="option">
。
- 单选按钮,用户只能选择一个选项。通常多个单选按钮具有相同的
-
select
:- 下拉列表,用户可以从预定义的选项中选择一个值。可以使用
<option>
标签来定义选项。 - 例如:
- 下拉列表,用户可以从预定义的选项中选择一个值。可以使用
<select>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
五、按钮类型
-
submit
:- 提交按钮,用于提交表单数据。通常与
<form>
标签一起使用。 - 例如:
<input type="submit" value="Submit">
。
- 提交按钮,用于提交表单数据。通常与
-
reset
:- 重置按钮,用于重置表单中的所有输入字段为初始状态。
- 例如:
<input type="reset" value="Reset">
。
-
button
:- 普通按钮,可以通过 JavaScript 为其添加自定义的行为。
- 例如:
<input type="button" value="Click Me">
。
六、其他类型
-
email
:- 用于输入电子邮件地址。浏览器可能会对输入的内容进行有效性验证。
- 例如:
<input type="email">
。
-
url
:- 用于输入 URL 地址。浏览器可能会对输入的内容进行有效性验证。
- 例如:
<input type="url">
。
-
search
:- 用于输入搜索关键词。通常会显示一些特定的样式,如圆角等。
- 例如:
<input type="search">
。
-
hidden
:- 隐藏输入字段,用于在表单中传递数据,但不会在页面上显示给用户。
- 例如:
<input type="hidden" value="some-value">
。
-
color
:- 用于选择颜色。通常会显示一个颜色选择器。
- 例如:
<input type="color">
。
-
file
:- 用于上传文件。可以设置
multiple
属性允许选择多个文件。 - 例如:
<input type="file">
或<input type="file" multiple>
。
- 用于上传文件。可以设置
中级开发者相关问题【共计 11 道题】
997. [Vue] vue3 如何监听数组变化【热度: 199】【web 框架】【出题公司: 美团】
关键词:vue3 监听数组变化
在 Vue 3 中,可以通过以下几种方式监听数组的变化:
一、使用watch
函数监听数组引用变化
-
直接监听数组变量:
- 可以使用
watch
函数来监听一个数组变量的变化。当数组被重新赋值时,watch
会触发。
import { reactive, watch } from "vue"; const state = reactive({ arr: [1, 2, 3], }); watch( () => state.arr, (newValue, oldValue) => { console.log("数组变化了", newValue, oldValue); } );
- 在这个例子中,当
state.arr
数组被重新赋值时,watch
函数中的回调函数会被执行,打印出新旧值。
- 可以使用
-
监听特定属性的数组:
- 如果你只想监听数组中的特定属性,可以使用
watch
函数结合函数返回值来实现。
import { reactive, watch } from "vue"; const state = reactive({ arr: [1, 2, 3], otherProperty: "some value", }); watch( () => state.arr.length, (newValue, oldValue) => { console.log("数组长度变化了", newValue, oldValue); } );
- 这里监听了数组的长度属性,当数组的长度发生变化时,回调函数会被执行。
- 如果你只想监听数组中的特定属性,可以使用
二、使用watchEffect
函数自动追踪数组变化
watchEffect
函数会立即执行传入的函数,并在其依赖的响应式数据发生变化时重新执行。
import { reactive, watchEffect } from "vue";
const state = reactive({
arr: [1, 2, 3],
});
watchEffect(() => {
console.log("数组变化了", state.arr);
});
在这个例子中,每当state.arr
数组发生变化时,watchEffect
中的函数会自动重新执行,打印出数组的当前值。
三、使用computed
计算属性间接监听数组变化
可以通过创建一个计算属性来间接监听数组的变化。
import { reactive, computed } from "vue";
const state = reactive({
arr: [1, 2, 3],
});
const arrLength = computed(() => state.arr.length);
arrLength.value; // 触发计算属性的求值
// 当数组变化时,计算属性会自动更新,你可以在其他地方监听这个计算属性的变化
可以在需要的地方监听计算属性arrLength
的变化,从而间接得知数组的变化。
Vue 3 对于数组的某些操作(如push
、pop
、shift
、unshift
、splice
等)是可以自动响应的,但对于直接通过索引赋值等操作可能需要特殊处理才能正确响应。
1000. [Vue] vue3 还是使用的虚拟 dom 吗?【热度: 345】【web 框架】
关键词:vue3 虚拟 dom
Vue 3 仍然使用虚拟 DOM(Virtual DOM)。
一、虚拟 DOM 在 Vue 3 中的重要性
-
高效的 DOM 操作:虚拟 DOM 是一种在内存中表示真实 DOM 结构的树形数据结构。在 Vue 3 中,当数据发生变化时,首先会在虚拟 DOM 上进行比较和计算,确定最小化的 DOM 操作集合,然后再将这些操作应用到真实 DOM 上。这样可以避免直接频繁地操作真实 DOM,从而提高性能。
- 例如,当一个组件中的数据发生变化时,Vue 3 会先更新虚拟 DOM 树,然后通过对比新旧虚拟 DOM 树的差异,找出需要更新的真实 DOM 节点,最后只对这些节点进行实际的 DOM 操作。
-
跨平台开发支持:虚拟 DOM 使得 Vue 3 不仅可以在浏览器中运行,还可以通过一些工具和技术进行跨平台开发。例如,使用 Vue Native 可以将 Vue 3 应用编译为原生移动应用,在移动平台上运行。这是因为虚拟 DOM 可以在不同的平台上进行渲染,而不需要依赖特定平台的 DOM 操作。
- 比如,在开发一个同时支持 Web 和移动平台的应用时,可以使用 Vue 3 的虚拟 DOM 来实现一套代码在多个平台上的运行,提高开发效率和代码复用性。
二、Vue 3 对虚拟 DOM 的优化
-
静态提升(Static Hoisting):Vue 3 在编译阶段会分析组件的模板,将静态的节点提升到渲染函数之外。这样在每次渲染时,不需要为静态节点创建新的虚拟 DOM 节点,从而减少了虚拟 DOM 的创建和比较开销。
- 例如,如果一个组件的模板中有一些静态的文本节点或元素,Vue 3 会在编译时将这些静态节点提取出来,在渲染时直接复用,而不是每次都重新创建虚拟 DOM 节点。
-
补丁算法优化:Vue 3 对虚拟 DOM 的补丁算法进行了优化,使得在更新 DOM 时更加高效。新的补丁算法可以更快地找到需要更新的节点,减少不必要的比较和操作。
- 比如,在对比新旧虚拟 DOM 树时,Vue 3 可以更准确地判断节点的类型和属性变化,只对真正发生变化的节点进行更新,提高了渲染性能。
-
事件处理优化:在 Vue 3 中,事件处理也进行了优化。对于静态的事件监听器,同样会在编译阶段进行提升,减少了每次渲染时的创建和绑定开销。
- 例如,如果一个组件中有一个静态的点击事件监听器,Vue 3 会在编译时将这个事件监听器提取出来,在渲染时直接复用,而不是每次都重新绑定。
1002. 如果我期望,页面加载和解析完成之后出发事件, 我该怎么做【热度: 299】【web 应用场景】
关键词:DOMContentLoaded 和 onload 事件
如果期望在页面加载和解析完成之后触发事件,可以通过以下几种方式实现:
一、使用DOMContentLoaded
事件
-
传统方式:
- 使用
addEventListener
方法来监听DOMContentLoaded
事件。当页面的 DOM 结构加载和解析完成后,该事件会被触发。
document.addEventListener("DOMContentLoaded", function () { // 这里的代码会在页面 DOM 加载和解析完成后执行 console.log("DOM 加载和解析完成"); });
- 在这个回调函数中,可以执行任何需要在 DOM 准备好后执行的操作,如获取 DOM 元素、设置事件监听器等。
- 使用
-
现代方式(异步函数):
- 在支持 ES6 及以上版本的环境中,可以使用异步函数和
await
来等待DOMContentLoaded
事件。这种方式使代码更加简洁和易读。
async function onDOMLoaded() { await new Promise((resolve) => { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", resolve); } else { resolve(); } }); // 这里的代码会在页面 DOM 加载和解析完成后执行 console.log("DOM 加载和解析完成"); } onDOMLoaded();
- 这种方式使用了异步函数和 Promise,确保在
DOMContentLoaded
事件触发后才执行后续的代码。
- 在支持 ES6 及以上版本的环境中,可以使用异步函数和
二、使用jQuery
(如果项目中使用了 jQuery)
$(document).ready()
方法:- jQuery 提供了
$(document).ready()
方法,它在 DOM 加载和解析完成后执行。这个方法与原生的DOMContentLoaded
事件类似,但在不同的浏览器中有更好的兼容性。
$(document).ready(function () { // 这里的代码会在页面 DOM 加载和解析完成后执行 console.log("DOM 加载和解析完成(使用 jQuery)"); });
- 在这个回调函数中,可以使用 jQuery 的选择器和方法来操作 DOM 元素。
- jQuery 提供了
三、使用load
事件(但不推荐作为首选)
window.onload
方法:window.onload
事件在整个页面(包括所有的资源如图片、样式表等)完全加载后触发。虽然它也能确保页面加载完成后执行代码,但通常比DOMContentLoaded
事件触发得更晚。
window.onload = function () { // 这里的代码会在页面完全加载后执行 console.log("页面完全加载完成"); };
- 如果只需要在 DOM 加载和解析完成后执行代码,使用
DOMContentLoaded
事件会更快,因为它不需要等待所有资源的加载。
1006. JS 如何翻转一个字符串【热度: 442】【代码实现/算法】
关键词:翻转字符串
在 JavaScript 中,可以通过以下几种方式翻转一个字符串:
一、使用循环和字符串拼接
-
原理:
- 通过从字符串的末尾开始遍历每个字符,并将其依次拼接起来,从而实现字符串的翻转。
- 首先获取字符串的长度,然后从最后一个字符开始,逐个将字符添加到一个新的字符串中。
-
代码示例:
function reverseString(str) {
let reversedStr = "";
for (let i = str.length - 1; i >= 0; i--) {
reversedStr += str[i];
}
return reversedStr;
}
const originalString = "hello world";
const reversedString = reverseString(originalString);
console.log(reversedString); // 'dlrow olleh'
二、使用数组方法和join
-
原理:
- 将字符串转换为字符数组,然后使用数组的
reverse
方法翻转数组的顺序,最后再使用join
方法将数组转换回字符串。 - 这种方法利用了 JavaScript 中数组操作的便利性来实现字符串的翻转。
- 将字符串转换为字符数组,然后使用数组的
-
代码示例:
function reverseString(str) {
return str.split("").reverse().join("");
}
const originalString = "hello world";
const reversedString = reverseString(originalString);
console.log(reversedString); // 'dlrow olleh'
三、使用递归
-
原理:
- 递归地将字符串的最后一个字符与剩余部分的翻转结果拼接起来,直到字符串为空。
- 每次递归调用都将字符串缩短一个字符,并将最后一个字符放在结果的前面。
-
代码示例:
function reverseString(str) {
if (str === "") {
return "";
}
return str[str.length - 1] + reverseString(str.slice(0, -1));
}
const originalString = "hello world";
const reversedString = reverseString(originalString);
console.log(reversedString); // 'dlrow olleh'
1007. JS 如何翻转一个数组【热度: 327】【代码实现/算法】
关键词:翻转数组
在 JavaScript 中,可以通过以下几种方法来翻转一个数组:
一、使用循环和数组拼接
-
原理:
- 通过从数组的末尾开始遍历每个元素,并将其依次添加到一个新的数组中,实现数组的翻转。
- 首先确定数组的长度,然后从最后一个元素开始,逐个将元素添加到新数组中。
-
代码示例:
function reverseArray(arr) {
const reversedArr = [];
for (let i = arr.length - 1; i >= 0; i--) {
reversedArr.push(arr[i]);
}
return reversedArr;
}
const originalArray = [1, 2, 3, 4, 5];
const reversedArray = reverseArray(originalArray);
console.log(reversedArray); // [5, 4, 3, 2, 1]
二、使用数组的reverse
方法
-
原理:
- JavaScript 的数组对象提供了一个
reverse
方法,可以直接原地翻转数组,即修改原始数组。 - 这个方法会改变原始数组的顺序,将第一个元素变为最后一个,第二个元素变为倒数第二个,以此类推。
- JavaScript 的数组对象提供了一个
-
代码示例:
const originalArray = [1, 2, 3, 4, 5];
const reversedArray = originalArray.reverse();
console.log(reversedArray); // [5, 4, 3, 2, 1]
三、使用扩展运算符和reverse
方法(不修改原始数组)
-
原理:
- 首先使用扩展运算符(
...
)将原始数组展开为一个新的数组,然后对这个新数组调用reverse
方法,这样可以在不修改原始数组的情况下得到一个翻转后的数组。
- 首先使用扩展运算符(
-
代码示例:
const originalArray = [1, 2, 3, 4, 5];
const reversedArray = [...originalArray].reverse();
console.log(reversedArray); // [5, 4, 3, 2, 1]
console.log(originalArray); // [1, 2, 3, 4, 5](原始数组未被修改)
这些方法可以根据具体的需求选择使用,如果你希望修改原始数组,可以直接使用reverse
方法;如果你希望保留原始数组并得到一个翻转后的新数组,可以使用循环或者扩展运算符结合reverse
方法。
1008. css 如何实现分栏布局【热度: 502】【CSS】
关键词:分栏布局
在前端布局中,可以通过 CSS 实现分栏布局。以下是几种常见的实现方式:
一、使用float
属性
-
基本原理:
float
属性可以让元素向左或向右浮动,从而实现多栏布局。通过将多个元素设置为浮动,可以让它们并排显示。- 例如,可以将一个容器中的子元素设置为左浮动或右浮动,以实现两栏或多栏布局。
-
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.column {
width: 50%;
float: left;
}
</style>
</head>
<body>
<div class="container">
<div class="column">
<p>左栏内容</p>
</div>
<div class="column">
<p>右栏内容</p>
</div>
</div>
</body>
</html>
在这个例子中,使用float: left
将两个.column
元素设置为左浮动,从而实现两栏布局。每个栏的宽度为50%
。
二、使用flexbox
布局
-
基本原理:
flexbox
(Flexible Box Layout)是一种弹性布局模型,可以轻松地实现分栏布局。通过设置容器的display
属性为flex
,可以将容器内的子元素进行弹性布局。- 可以使用
flex-direction
属性来控制子元素的排列方向,例如设置为row
可以实现水平排列,设置为column
可以实现垂直排列。
-
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
display: flex;
}
.column {
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="column">
<p>左栏内容</p>
</div>
<div class="column">
<p>右栏内容</p>
</div>
</div>
</body>
</html>
在这个例子中,将容器的display
属性设置为flex
,然后将子元素的.column
类设置为flex: 1
,使每个子元素占据相等的空间,实现两栏布局。
三、使用grid
布局
-
基本原理:
grid
(Grid Layout)是一种网格布局模型,可以更精细地控制布局。通过设置容器的display
属性为grid
,可以将容器划分为网格,然后将子元素放置在网格中的特定位置。- 可以使用
grid-template-columns
和grid-template-rows
属性来定义网格的列和行,以及使用grid-column
和grid-row
属性来指定子元素在网格中的位置。
-
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
display: grid;
grid-template-columns: 1fr 1fr;
}
.column {
padding: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="column">
<p>左栏内容</p>
</div>
<div class="column">
<p>右栏内容</p>
</div>
</div>
</body>
</html>
1012. 前端如何快速获取页面 url query 参数【热度: 888】【web 应用场景】
关键词:获取 url 参数
在前端,可以通过以下几种方式快速获取页面 URL 的查询参数:
一、使用 URLSearchParams API
- 基本用法:
URLSearchParams
是一个内置的 JavaScript API,用于处理 URL 的查询参数。它提供了一种方便的方式来获取、设置和删除查询参数。- 首先,可以使用
window.location.search
获取 URL 的查询字符串,然后将其传递给URLSearchParams
构造函数来创建一个URLSearchParams
对象。 - 例如:
const urlParams = new URLSearchParams(window.location.search);
- 获取单个参数值:
- 可以使用
get
方法来获取指定参数的值。例如,要获取名为paramName
的参数值,可以使用以下代码:
- 可以使用
const paramValue = urlParams.get("paramName");
- 遍历所有参数:
- 可以使用
forEach
方法来遍历所有的参数。例如:
- 可以使用
urlParams.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
二、手动解析查询字符串
-
基本思路:
- 如果不使用
URLSearchParams
,也可以手动解析 URL 的查询字符串。首先,获取window.location.search
,它包含了查询字符串(例如?param1=value1¶m2=value2
)。 - 然后,可以使用字符串的分割和遍历操作来提取参数名和参数值。
- 如果不使用
-
示例代码:
const queryString = window.location.search.substring(1);
const params = {};
const paramPairs = queryString.split("&");
paramPairs.forEach((pair) => {
const [key, value] = pair.split("=");
if (key) {
params[key] = decodeURIComponent(value);
}
});
在这个例子中,首先提取查询字符串,然后将其分割成参数对数组。对于每个参数对,再次分割得到参数名和参数值,并将其存储在一个对象中。最后,可以通过params
对象来访问各个参数的值。
三、使用第三方库
-
库的选择:
- 有一些第三方库也提供了方便的方法来处理 URL 的查询参数。例如,
qs
库是一个流行的用于处理查询字符串的库。 - 可以使用
npm
或yarn
安装qs
库:npm install qs
或yarn add qs
。
- 有一些第三方库也提供了方便的方法来处理 URL 的查询参数。例如,
-
使用示例:
import qs from "qs";
const queryString = window.location.search.substring(1);
const params = qs.parse(queryString);
在这个例子中,使用qs.parse
方法将查询字符串解析为一个对象,其中键是参数名,值是参数值。
1013. 介绍一下 URLSearchParams API【热度: 10】【web 应用场景】【出题公司: TOP100 互联网】
关键词:URLSearchParams API 介绍
URLSearchParams
是 JavaScript 中的一个内置 API,用于处理 URL 的查询参数部分。它提供了一系列方法来方便地操作和获取 URL 中的查询参数。
一、创建URLSearchParams
对象
- 从现有 URL:
- 可以从当前页面的 URL 中提取查询参数来创建
URLSearchParams
对象。例如:
- 可以从当前页面的 URL 中提取查询参数来创建
const urlParams = new URLSearchParams(window.location.search);
- 这里使用
window.location.search
获取当前页面 URL 的查询字符串,然后将其传递给URLSearchParams
构造函数来创建一个新的对象。
- 从字符串:
- 也可以直接从一个查询字符串创建
URLSearchParams
对象。例如:
- 也可以直接从一个查询字符串创建
const queryString = "param1=value1¶m2=value2";
const urlParams = new URLSearchParams(queryString);
二、主要方法
get()
方法:- 用于获取指定参数的第一个值。例如:
const value = urlParams.get("paramName");
- 如果参数不存在,
get()
方法将返回null
。
set()
方法:- 设置指定参数的值。如果参数不存在,将添加一个新的参数。例如:
urlParams.set("paramName", "newValue");
append()
方法:- 向现有参数添加一个新的值。如果参数不存在,将添加一个新的参数。例如:
urlParams.append("paramName", "anotherValue");
delete()
方法:- 删除指定参数。例如:
urlParams.delete("paramName");
has()
方法:- 检查是否存在指定参数。返回一个布尔值。例如:
const hasParam = urlParams.has("paramName");
- 遍历参数:
- 可以使用
forEach()
方法遍历所有参数。例如:
- 可以使用
urlParams.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
三、优点和用途
-
方便性:
URLSearchParams
提供了一种简洁、直观的方式来处理 URL 查询参数,避免了手动解析和拼接查询字符串的繁琐过程。
-
兼容性:
- 它在现代浏览器中广泛支持,可以在各种前端开发场景中使用。
-
动态操作:
- 可以方便地在运行时修改查询参数,例如在单页应用程序中根据用户操作动态更新 URL 的查询参数。
-
与 URL 对象结合:
- 可以与
URL
对象结合使用,方便地构建和操作完整的 URL。例如:
- 可以与
const url = new URL("https://example.com");
url.searchParams.set("paramName", "value");
console.log(url.toString());
总之,URLSearchParams
是一个强大而方便的 API,用于处理 URL 的查询参数,在前端开发中具有广泛的应用。
1014. 如何把一个数组随机打乱【热度: 550】【代码实现/算法】
关键词:数组随机打乱
在 JavaScript 中,可以使用以下几种方法来随机打乱一个数组:
一、使用sort
方法结合随机函数
-
基本原理:
- JavaScript 的数组
sort
方法可以接受一个比较函数作为参数。通过提供一个随机的比较函数,可以实现对数组的随机排序,从而打乱数组的顺序。
- JavaScript 的数组
-
示例代码:
const array = [1, 2, 3, 4, 5];
array.sort(() => Math.random() - 0.5);
console.log(array);
在这个例子中,sort
方法的比较函数每次都会返回一个随机的正负值,使得数组元素的排序顺序完全随机,从而实现数组的随机打乱。
二、Fisher-Yates 洗牌算法
-
基本原理:
- Fisher-Yates 洗牌算法是一种经典的随机打乱数组的算法。它的基本思想是从数组的最后一个元素开始,随机选择一个位置与当前元素交换,然后逐步向前移动,重复这个过程,直到处理完第一个元素。
-
示例代码:
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
const array = [1, 2, 3, 4, 5];
const shuffledArray = shuffleArray(array);
console.log(shuffledArray);
在这个例子中,定义了一个shuffleArray
函数,该函数使用 Fisher-Yates 洗牌算法随机打乱输入的数组,并返回打乱后的数组。
1015. package.json 依赖申明的方式有哪些, 他们有何却别【热度: 193】【web 应用场景】
关键词:前端依赖申明方式
一、dependencies
(生产依赖)
-
定义和用途:
dependencies
用于声明项目在生产环境中运行所必需的依赖项。- 这些依赖项是项目正常运行所不可或缺的,无论是在开发阶段还是在部署到生产环境后。
-
示例:
- 比如,如果你的项目使用了 Express.js 框架来构建服务器,那么 Express.js 就应该被声明在
dependencies
中。 "express": "^4.17.1"
表示安装 Express 版本 4.17.1 或更高的兼容版本。
- 比如,如果你的项目使用了 Express.js 框架来构建服务器,那么 Express.js 就应该被声明在
-
安装和使用:
- 当你运行
npm install
或yarn install
时,这些依赖项会被自动安装到项目的node_modules
目录中。 - 在生产环境中部署项目时,这些依赖项也会被一同部署。
- 当你运行
二、devDependencies
(开发依赖)
-
定义和用途:
devDependencies
用于声明仅在开发过程中需要的依赖项。- 这些依赖项通常包括开发工具、测试框架、代码格式化工具等,它们不是项目在生产环境中运行所必需的。
-
示例:
- 例如,Jest 是一个流行的 JavaScript 测试框架,如果你的项目使用 Jest 进行测试,那么 Jest 应该被声明在
devDependencies
中。 "jest": "^26.6.3"
表示安装 Jest 版本 26.6.3 或更高的兼容版本。
- 例如,Jest 是一个流行的 JavaScript 测试框架,如果你的项目使用 Jest 进行测试,那么 Jest 应该被声明在
-
安装和使用:
- 同样,当你运行
npm install
或yarn install
时,这些依赖项会被安装到项目的node_modules
目录中。 - 但是,在生产环境中部署项目时,通常不会部署这些开发依赖项,以减小项目的体积和复杂性。
- 同样,当你运行
区别总结:
- 使用场景不同:
dependencies
中的依赖项是项目在生产环境中运行所必需的,而devDependencies
中的依赖项仅在开发过程中使用。
- 部署方式不同:
- 生产环境部署时,通常只部署
dependencies
中的依赖项,而不部署devDependencies
中的依赖项。
- 生产环境部署时,通常只部署
- 影响项目体积和复杂性:
- 将不必要的依赖项放在
devDependencies
中可以减小项目在生产环境中的体积和复杂性,提高性能和安全性。
- 将不必要的依赖项放在
三、peerDependencies
(对等依赖)
-
定义和用途:
peerDependencies
用于声明当前包所依赖的其他包,但这些依赖项不会被自动安装。- 它通常用于插件或扩展的场景,表明当前包与特定版本的其他包兼容,并且期望宿主环境已经安装了这些对等依赖。
-
示例:
- 假设你正在开发一个 React 插件,你的插件可能需要特定版本的 React 才能正常工作。在这种情况下,你可以在
package.json
的peerDependencies
中声明对 React 的依赖。 "peerDependencies": { "react": "^17.0.2" }
表示这个插件期望宿主环境安装了 React 17.0.2 或更高的兼容版本。
- 假设你正在开发一个 React 插件,你的插件可能需要特定版本的 React 才能正常工作。在这种情况下,你可以在
-
安装和使用:
- 当用户安装你的包时,他们需要确保在自己的项目中手动安装了满足
peerDependencies
要求的包。如果没有安装或版本不匹配,可能会导致运行时错误。
- 当用户安装你的包时,他们需要确保在自己的项目中手动安装了满足
区别总结:
- 与
dependencies
的区别:dependencies
中的依赖项会在安装当前包时自动安装,而peerDependencies
中的依赖项不会自动安装,需要用户在宿主项目中自行安装。
- 与
devDependencies
的区别:devDependencies
是仅在开发过程中使用的依赖项,而peerDependencies
是与当前包在运行时的兼容性相关的依赖项,不一定只在开发过程中使用。
1016. npm workspaces 是什么概念, 主要是解决什么问题【热度: 100】【工程化】
关键词:npm workspaces 概念
npm workspaces
是一个在 npm 中用于管理多包项目的功能。它主要解决以下几个问题:
一、项目结构管理
-
多包项目组织:
- 在大型项目中,可能包含多个独立的软件包或模块。
npm workspaces
允许将这些包组织在一个统一的项目结构中,方便管理和开发。 - 例如,一个企业级应用可能由一个前端库、一个后端服务和一个共享的工具包组成。使用
npm workspaces
,可以将这些包放在同一个项目目录下,而不是作为独立的项目进行管理。
- 在大型项目中,可能包含多个独立的软件包或模块。
-
依赖共享:
- 多个包之间可能存在共享的依赖项。
npm workspaces
可以自动管理这些共享依赖,避免重复安装和版本冲突。 - 当一个包安装了某个依赖项时,其他包可以直接使用这个依赖,而不需要再次安装。这可以减少项目的体积和安装时间,提高开发效率。
- 多个包之间可能存在共享的依赖项。
二、开发效率提升
-
单一命令行界面:
- 使用
npm workspaces
,可以在项目的根目录下使用单一的npm
命令来管理所有的包。这避免了在每个包的目录下分别运行npm
命令的繁琐过程。 - 例如,可以在根目录下运行
npm install
来安装所有包的依赖项,或者运行npm run test
来执行所有包的测试。
- 使用
-
同时开发多个包:
- 开发人员可以在同一个项目中同时开发多个包,而不需要在不同的项目目录之间切换。这可以提高开发效率,特别是在需要频繁修改多个包的情况下。
- 例如,可以在一个编辑器中打开多个包的代码,进行同时编辑和调试。
三、版本管理和发布
-
统一版本控制:
- 在多包项目中,需要确保各个包的版本保持一致。
npm workspaces
可以帮助管理包的版本,确保在发布时所有的包都使用相同的版本号。 - 可以在项目的根目录下使用
npm version
命令来统一更新所有包的版本号,并生成相应的版本标签。
- 在多包项目中,需要确保各个包的版本保持一致。
-
简化发布流程:
- 发布多个包时,通常需要分别在每个包的目录下执行发布命令。使用
npm workspaces
,可以在项目的根目录下使用单一的发布命令来发布所有的包。 - 例如,可以使用
npm publish --workspaces
来发布项目中的所有包,而不需要分别进入每个包的目录进行发布。
- 发布多个包时,通常需要分别在每个包的目录下执行发布命令。使用
高级开发者相关问题【共计 8 道题】
998. [Vue] nextTick 作用是什么, 原理是什么【热度: 177】【web 框架】【出题公司: 美团】
关键词:vue nextTick 作用、原理
在 Vue 中,nextTick
主要有以下作用和工作原理:
一、作用
-
确保 DOM 更新后执行操作:
- Vue 是异步更新 DOM 的。当你在代码中修改了数据,Vue 不会立即更新 DOM,而是将这些更新操作放入一个队列中,等待下一个“tick”(事件循环的一个周期)再统一进行 DOM 更新。
- 如果在数据变化后,你需要立即操作更新后的 DOM,就可以使用
nextTick
。它会在 DOM 更新完成后执行回调函数,确保你能获取到最新的 DOM 状态。 - 例如,你在修改了一个数据后,想要获取某个元素的新尺寸或位置,就可以在
nextTick
的回调函数中进行操作。
-
处理异步操作后的 DOM 操作:
- 在一些异步操作(如定时器、Ajax 请求等)之后,如果需要操作 DOM,也可以使用
nextTick
来确保 DOM 已经更新。 - 比如,在一个 Ajax 请求成功后,你想要根据返回的数据更新 DOM,这时可以在请求成功的回调函数中使用
nextTick
来确保 DOM 更新已经完成。
- 在一些异步操作(如定时器、Ajax 请求等)之后,如果需要操作 DOM,也可以使用
二、原理
-
利用事件循环:
- Vue 的
nextTick
实现利用了 JavaScript 的事件循环机制。在浏览器环境中,JavaScript 是单线程执行的,事件循环负责管理异步任务的执行顺序。 - Vue 将
nextTick
的回调函数放入微任务队列(在 Promise.then、MutationObserver 和 process.nextTick 中执行)或宏任务队列(在 setTimeout、setInterval 和 setImmediate 中执行),具体取决于浏览器的支持情况。 - 当当前执行栈为空时,事件循环会从任务队列中取出任务执行。如果微任务队列中有任务,会先执行微任务队列中的任务,然后再执行宏任务队列中的任务。这样可以确保
nextTick
的回调函数在 DOM 更新之后执行。
- Vue 的
-
内部实现:
- Vue 内部维护了一个异步任务队列,用于存储
nextTick
的回调函数。当调用nextTick
时,回调函数会被添加到这个队列中。 - Vue 在更新 DOM 后,会检查这个异步任务队列是否为空。如果不为空,会取出队列中的第一个任务并执行它。
- 这样就保证了在 DOM 更新完成后,
nextTick
的回调函数能够按照调用的顺序依次执行。
- Vue 内部维护了一个异步任务队列,用于存储
例如:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<button @click="updateMessage">Update Message</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
message: "Hello Vue!",
};
},
methods: {
updateMessage() {
this.message = "Updated Message";
console.log("Before nextTick");
Vue.nextTick(() => {
console.log("After DOM update");
const pElement = document.querySelector("p");
console.log(pElement.textContent);
});
},
},
});
app.mount("#app");
</script>
</body>
</html>
在这个例子中,点击按钮后,数据被更新,但立即获取<p>
元素的文本内容时,还是旧的值。而在nextTick
的回调函数中获取<p>
元素的文本内容时,就已经是更新后的新值了。
综上所述,nextTick
在 Vue 中是一个非常有用的工具,用于确保在 DOM 更新后执行特定的操作,其原理是利用 JavaScript 的事件循环机制来实现异步任务的调度。
999. 现代框架如 React、Vue 相比原生开发有什么优势【热度: 897】【web 框架】
关键词:现代框架优势
现代框架如 React 和 Vue 相比原生开发具有以下优势:
一、提高开发效率
-
组件化开发:
- React 和 Vue 都采用了组件化的开发模式。这使得开发者可以将用户界面拆分成独立的、可复用的组件。每个组件都有自己的逻辑和样式,易于维护和扩展。
- 例如,在一个电商网站中,可以将商品列表、商品详情、购物车等部分分别构建为不同的组件。当需要修改某个部分的功能或样式时,只需要修改对应的组件,而不会影响其他部分。
- 组件化开发提高了代码的可维护性和可复用性,减少了重复开发的工作量,从而提高了开发效率。
-
虚拟 DOM:
- 这两个框架都使用了虚拟 DOM(Virtual DOM)技术。虚拟 DOM 是一种在内存中构建的树形结构,它与真实的 DOM 结构相对应。当数据发生变化时,框架会先比较虚拟 DOM 的新旧状态,计算出最小的 DOM 操作集合,然后再将这些操作应用到真实的 DOM 上。
- 这种方式避免了直接操作真实 DOM 带来的性能开销,提高了页面的渲染性能。同时,由于只需要更新变化的部分,而不是整个页面,也减少了不必要的重绘和回流,进一步提高了性能。
- 例如,在一个列表展示页面中,如果只有一个列表项的数据发生了变化,虚拟 DOM 会只更新这个列表项对应的 DOM 节点,而不是整个列表。
二、更好的用户体验
-
响应式数据绑定:
- React 和 Vue 都提供了响应式的数据绑定机制。当数据发生变化时,框架会自动更新与之相关的 UI 部分,无需手动操作 DOM。
- 这使得开发者可以专注于业务逻辑的实现,而不必担心数据和 UI 的同步问题。同时,也提高了用户体验,因为用户可以立即看到数据变化后的效果。
- 例如,在一个表单输入页面中,当用户输入数据时,页面上的实时预览会自动更新,而不需要用户手动触发更新操作。
-
高效的状态管理:
- 对于复杂的应用,状态管理是一个重要的问题。React 有 Redux、MobX 等状态管理库,Vue 有 Vuex。这些状态管理工具可以帮助开发者更好地管理应用的状态,实现数据的共享和同步。
- 状态管理使得不同组件之间可以方便地共享数据,避免了数据传递的复杂性。同时,也可以更好地处理异步操作和数据的持久化,提高了应用的可靠性和稳定性。
- 例如,在一个多页面应用中,用户的登录状态需要在多个页面中共享。使用状态管理工具,可以将登录状态存储在一个全局的状态容器中,各个页面可以方便地获取和更新这个状态。
三、跨平台开发
-
基于 Web 技术:
- React 和 Vue 都是基于 Web 技术开发的框架,这意味着它们可以在任何支持 Web 浏览器的平台上运行。无论是桌面浏览器、移动浏览器还是其他设备上的浏览器,都可以运行使用这些框架开发的应用。
- 这使得开发者可以使用一套代码库开发多个平台的应用,减少了开发成本和维护成本。同时,也提高了应用的可移植性和兼容性。
- 例如,使用 React Native 或 Vue Native,可以将基于 React 或 Vue 的 Web 应用转换为原生移动应用,实现跨平台开发。
-
服务器端渲染(SSR):
- React 和 Vue 都支持服务器端渲染。服务器端渲染可以提高应用的首屏加载速度,改善 SEO(搜索引擎优化),并提供更好的用户体验。
- 在服务器端渲染中,框架会在服务器上生成 HTML 页面,并将其发送给客户端。客户端接收到 HTML 页面后,可以立即显示内容,而不需要等待 JavaScript 代码的下载和执行。
- 例如,对于一个新闻网站,服务器端渲染可以确保搜索引擎能够抓取到完整的页面内容,提高网站的搜索排名。同时,用户也可以更快地看到页面内容,提高用户体验。
1001. [Vue] vue3 对虚拟 dom 做了哪些优化【热度: 446】【web 框架】
关键词:vue3 虚拟 dom
Vue 3 对虚拟 DOM 进行了多方面的优化,主要包括以下几点:
一、静态提升(Static Hoisting)
-
原理与作用:
- 在 Vue 3 的编译阶段,会分析模板中的静态内容,将静态的节点提升到渲染函数之外。这意味着对于静态的元素和文本,不会在每次渲染时都重新创建虚拟 DOM 节点,而是在组件首次渲染时创建一次,然后在后续的渲染中直接复用。
- 例如,如果一个组件的模板中有一个静态的标题文本,在 Vue 2 中,每次渲染都会为这个文本创建一个新的虚拟 DOM 节点。而在 Vue 3 中,这个静态文本会被提升,只在组件首次渲染时创建虚拟 DOM 节点,后续渲染直接使用已创建的节点。
-
性能提升:
- 减少了虚拟 DOM 的创建和比较开销,特别是在组件频繁渲染时,效果尤为明显。因为静态内容通常不会改变,避免了不必要的重复操作,提高了渲染性能。
二、补丁算法优化
-
更高效的比较策略:
- Vue 3 改进了虚拟 DOM 的补丁算法,能够更快速地找到新旧虚拟 DOM 树之间的差异。新的算法在比较节点时更加智能,能够准确判断节点的类型和属性变化,只对真正发生变化的节点进行更新操作。
- 例如,当一个列表中的某个元素的文本内容发生变化时,Vue 3 能够快速定位到这个变化的节点,而不会像 Vue 2 那样对整个列表进行逐一比较。
-
减少不必要的操作:
- 通过更精确的比较,Vue 3 避免了一些不必要的 DOM 操作。例如,如果一个元素的样式属性没有发生变化,Vue 3 不会触发样式的重新设置,从而减少了对浏览器渲染引擎的压力。
三、事件处理优化
-
静态事件提升:
- 对于静态的事件监听器,Vue 3 也会在编译阶段进行提升。如果一个组件的模板中有一个静态的点击事件监听器,在 Vue 3 中,这个事件监听器会被提取出来,在组件首次渲染时进行绑定,后续渲染中不再重复绑定。
- 例如,一个按钮的点击事件在组件的整个生命周期中都不会改变,那么在 Vue 3 中,这个事件监听器只会在首次渲染时绑定一次,提高了性能。
-
事件缓存:
- Vue 3 还对事件进行了缓存处理。当一个组件被销毁时,它的事件监听器不会立即被移除,而是被缓存起来。如果这个组件在后续的渲染中再次出现,Vue 3 可以直接复用缓存的事件监听器,而不需要重新绑定,进一步提高了性能。
四、编译器优化
-
更好的代码生成:
- Vue 3 的编译器能够生成更高效的渲染函数代码。通过对模板的分析和优化,编译器可以生成更加简洁、高效的虚拟 DOM 创建和更新代码。
- 例如,编译器可以根据模板中的条件判断和循环结构,生成更加优化的虚拟 DOM 操作代码,减少不必要的计算和操作。
-
类型推断:
- Vue 3 的编译器支持类型推断,这使得在开发过程中可以获得更好的类型提示和错误检查。同时,类型推断也可以帮助编译器生成更加高效的代码,因为编译器可以根据类型信息进行一些优化操作。
- 例如,如果一个组件的 props 定义了明确的类型,编译器可以在生成虚拟 DOM 代码时,针对这些类型进行优化,提高代码的执行效率。
1003. DOMContentLoaded
事件和load
事件有什么区别【热度: 210】【web 应用场景】
关键词:DOMContentLoaded 和 onload 事件
DOMContentLoaded
事件和load
事件主要有以下区别:
一、触发时机
-
DOMContentLoaded
事件:- 触发时机是在文档的 DOM(文档对象模型)完全加载和解析完成后,不等待样式表、图片和子框架等外部资源的加载完成。这意味着当 HTML 结构被完全解析,并且可以通过 JavaScript 访问和操作 DOM 元素时,该事件就会触发。
- 例如,当打开一个网页时,浏览器首先下载 HTML 文档并进行解析。一旦 HTML 文档的解析完成,
DOMContentLoaded
事件就会触发,此时可以立即执行一些与 DOM 操作相关的 JavaScript 代码,而不必等待页面上的所有图像和其他资源加载完毕。
-
load
事件:- 触发时机是在整个网页(包括所有的资源如样式表、图片、脚本等)完全加载完成后。这意味着不仅 HTML 文档的 DOM 结构要被加载和解析,而且所有的外部资源(如图片、CSS 文件、JavaScript 文件等)也必须完全下载并加载到浏览器中,该事件才会触发。
- 例如,在一个包含大量图片和复杂样式表的网页中,
load
事件可能会在所有这些资源都加载完成后才触发,这可能需要相对较长的时间,具体取决于网络速度和资源的大小。
二、用途
-
DOMContentLoaded
事件:- 通常用于在 DOM 准备好后尽快执行一些关键的 JavaScript 代码,这些代码可能需要操作 DOM 元素或设置事件监听器,但不需要等待所有外部资源的加载。这样可以提高页面的交互性和响应速度,因为用户可以在页面的主要内容加载完成后就开始与页面进行交互,而不必等待所有资源加载完毕。
- 例如,可以在
DOMContentLoaded
事件的回调函数中初始化页面的布局、设置表单验证规则或显示一些初始的动画效果。
-
load
事件:- 主要用于在确保整个页面的所有资源都加载完成后执行一些特定的操作,这些操作可能依赖于所有的资源都可用。例如,在一个需要确保所有图片都加载完成后才能正确显示的画廊页面中,可以使用
load
事件来触发图片的展示动画或调整页面布局以适应所有图片的尺寸。 - 另外,
load
事件也可以用于在页面完全加载后进行一些性能监测或统计工作,例如记录页面的加载时间或检查所有资源是否正确加载。
- 主要用于在确保整个页面的所有资源都加载完成后执行一些特定的操作,这些操作可能依赖于所有的资源都可用。例如,在一个需要确保所有图片都加载完成后才能正确显示的画廊页面中,可以使用
三、性能影响
-
DOMContentLoaded
事件:- 由于它在 DOM 加载和解析完成后就触发,不等待外部资源的加载,所以可以更快地执行相关的 JavaScript 代码,减少用户等待的时间。这对于提高页面的性能和用户体验非常重要,特别是在网络速度较慢或页面包含大量资源的情况下。
- 例如,如果一个页面有很多大尺寸的图片,使用
DOMContentLoaded
事件可以让用户在图片还在加载的过程中就开始与页面进行交互,而不必等待所有图片都加载完成。
-
load
事件:- 因为它需要等待所有资源的加载完成,所以可能会导致页面的加载时间较长,特别是在网络状况不佳或资源较多的情况下。然而,在某些情况下,确保所有资源都加载完成是必要的,所以
load
事件仍然有其特定的用途。 - 例如,如果一个页面的布局或功能依赖于所有的样式表和图片都加载完成,那么使用
load
事件可以确保在执行相关代码时所有资源都可用,避免出现布局错误或功能不正常的情况。
- 因为它需要等待所有资源的加载完成,所以可能会导致页面的加载时间较长,特别是在网络状况不佳或资源较多的情况下。然而,在某些情况下,确保所有资源都加载完成是必要的,所以
1004. 如何计算页面白屏时间【热度: 400】【web 应用场景】
关键词:白屏时间
白屏时间是指从浏览器开始请求页面到页面开始显示内容的时间。可以通过以下方法计算白屏时间:
一、使用浏览器的性能 API
现代浏览器提供了performance
对象,可以用来获取页面加载过程中的各种时间戳。通过这些时间戳的差值可以计算出白屏时间。
- 具体步骤:
- 在页面的
<head>
标签中插入一段 JavaScript 代码,在页面加载时记录关键时间点。 - 使用
performance.timing.navigationStart
表示浏览器开始导航的时间戳。 - 寻找一个表示页面开始有内容显示的时间点,通常可以使用
performance.timing.domInteractive
(文档被解析完成,开始加载外部资源时的时间戳)或者performance.timing.domContentLoadedEventStart
(DOMContentLoaded
事件开始的时间戳)作为近似的白屏结束时间点。 - 白屏时间 = 白屏结束时间点 -
performance.timing.navigationStart
。
- 在页面的
例如:
<!DOCTYPE html>
<html>
<head>
<script>
const timing = performance.timing;
const whiteScreenTime = timing.domInteractive - timing.navigationStart;
console.log(`白屏时间为:${whiteScreenTime}ms`);
</script>
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
- 注意事项:
- 不同的浏览器对于性能时间戳的定义可能略有不同,需要在多种浏览器上进行测试以确保准确性。
- 这种方法计算的白屏时间是一个近似值,因为很难精确地确定页面开始显示内容的瞬间。
二、使用自定义标记和日志
- 步骤:
- 在页面的 HTML 结构中,在
<head>
标签之后插入一个不可见的元素,例如一个空的<div>
元素,并给它一个特定的 ID。 - 在页面的 CSS 中,将这个元素的背景颜色设置为与页面背景相同的颜色,使其在页面加载初期不可见。
- 在页面的 JavaScript 代码中,当页面开始有内容显示时,通过修改这个元素的样式使其变为可见,并记录当前时间。
- 白屏时间 = 元素变为可见的时间 - 浏览器开始导航的时间。
- 在页面的 HTML 结构中,在
例如:
<!DOCTYPE html>
<html>
<head>
<style>
#whiteScreenMarker {
display: none;
}
</style>
<script>
const timing = performance.timing;
document.addEventListener("DOMContentLoaded", function () {
document.getElementById("whiteScreenMarker").style.display = "block";
const whiteScreenTime = performance.now() - timing.navigationStart;
console.log(`白屏时间为:${whiteScreenTime}ms`);
});
</script>
</head>
<body>
<div id="whiteScreenMarker"></div>
<!-- 页面内容 -->
</body>
</html>
- 注意事项:
- 这种方法需要手动在页面中插入标记元素和相应的 JavaScript 代码,可能会增加一些开发工作量。
- 同样需要考虑在不同浏览器上的兼容性和准确性。
1005. 如何计算页面首屏时间【热度: 426】【web 应用场景】
关键词:首屏时间
页面首屏时间是指从浏览器开始请求页面到页面的首屏内容完全显示的时间。以下是几种计算首屏时间的方法:
一、使用浏览器性能 API 和自定义事件
- 具体步骤:
- 在页面的
<head>
标签中插入一段 JavaScript 代码,在页面加载时记录浏览器开始导航的时间戳,即performance.timing.navigationStart
。 - 当页面的首屏内容加载完成时,触发一个自定义事件。可以通过监听特定元素的加载完成或者使用特定的标志来判断首屏内容是否加载完成。
- 在自定义事件的处理函数中,获取当前时间戳,并减去开始导航的时间戳,得到首屏时间。
- 在页面的
例如:
<!DOCTYPE html>
<html>
<head>
<script>
const timing = performance.timing;
let firstScreenLoaded = false;
function onFirstScreenLoaded() {
if (!firstScreenLoaded) {
const firstScreenTime = performance.now() - timing.navigationStart;
console.log(`首屏时间为:${firstScreenTime}ms`);
firstScreenLoaded = true;
}
}
window.addEventListener("load", function () {
// 假设首屏内容加载完成的标志是某个特定元素的出现
const firstScreenElement = document.getElementById("first-screen-element");
if (firstScreenElement) {
onFirstScreenLoaded();
}
});
</script>
</head>
<body>
<!-- 页面内容 -->
<div id="first-screen-element">首屏关键内容元素</div>
</body>
</html>
- 注意事项:
- 需要准确确定首屏内容加载完成的标志,这可能因页面结构和内容而异。
- 不同浏览器对于性能时间戳的定义和准确性可能略有不同,需要在多种浏览器上进行测试。
二、使用可视化工具和浏览器插件
-
一些可视化性能分析工具,如 Lighthouse、WebPageTest 等,可以测量页面的首屏时间。
- Lighthouse 是一个由 Google 开发的开源工具,可以在 Chrome 浏览器的开发者工具中运行。它会生成一个详细的性能报告,其中包括首屏时间等指标。
- WebPageTest 是一个在线性能测试工具,可以从不同的地理位置和设备类型进行测试,并提供详细的性能数据,包括首屏时间。
-
一些浏览器插件,如 PageSpeed Insights、YSlow 等,也可以提供页面性能指标,包括首屏时间的估算。
-
注意事项:
- 这些工具和插件的测量结果可能会受到网络环境、测试设备等因素的影响。
- 需要结合实际情况进行分析和优化,不能完全依赖工具的测量结果。
三、手动计时和标记
- 步骤:
- 在页面的 JavaScript 代码中,在浏览器开始请求页面时手动记录一个时间戳。
- 在页面的首屏内容加载完成时,再次记录一个时间戳。
- 首屏时间 = 第二个时间戳 - 第一个时间戳。
例如:
<!DOCTYPE html>
<html>
<head>
<script>
let startTime;
window.addEventListener("load", function () {
if (!startTime) {
startTime = performance.now();
}
// 假设首屏内容加载完成的标志是某个特定元素的出现
const firstScreenElement = document.getElementById("first-screen-element");
if (firstScreenElement) {
const endTime = performance.now();
const firstScreenTime = endTime - startTime;
console.log(`首屏时间为:${firstScreenTime}ms`);
}
});
</script>
</head>
<body>
<!-- 页面内容 -->
<div id="first-screen-element">首屏关键内容元素</div>
</body>
</html>
- 注意事项:
- 这种方法需要手动在代码中插入计时逻辑,可能会比较繁琐。
- 同样需要准确确定首屏内容加载完成的标志。
1009. [webpack] 是如何处理 commonjs 模块的文件, 使其编译结果能被浏览器使用。【热度: 210】【工程化】
关键词:webpack 打包处理 commonjs 模块
以下是详细讲解 Webpack 如何将 CommonJS 模块转换为浏览器可以执行的 JavaScript 文件:
一、模块解析阶段
-
识别 CommonJS 模块:
- Webpack 从项目的入口文件(通常在配置中指定,如
entry: './src/index.js'
)开始扫描代码。当遇到require('some-module')
这样的语句时,Webpack 识别出这是一个 CommonJS 模块的引入。 - 它会记录下这个模块的依赖关系,以便后续处理。
- Webpack 从项目的入口文件(通常在配置中指定,如
-
构建依赖图:
- 对于每个引入的模块,Webpack 会继续深入解析该模块内部的依赖关系,递归地构建出一个完整的模块依赖图。
- 例如,如果模块 A 引入了模块 B 和模块 C,而模块 B 又引入了模块 D,那么 Webpack 会构建出一个反映这些依赖关系的有向无环图。
二、模块转换阶段
- 处理
require
函数:- 在浏览器环境中,没有原生的
require
函数。Webpack 会将 CommonJS 中的require
函数转换为一种在浏览器中可行的模块加载方式。 - 通常,Webpack 会使用一种称为“模块加载器”的机制。在打包后的文件中,会生成一个模块加载函数,这个函数负责在运行时加载和执行模块。
- 例如,对于
const moduleB = require('moduleB')
这样的语句,Webpack 可能会将其转换为类似于以下的代码:
- 在浏览器环境中,没有原生的
// 假设模块加载函数名为 __webpack_require__
const moduleB = __webpack_require__("./moduleB.js");
- 处理
module.exports
和exports
:- CommonJS 使用
module.exports
或exports
来导出模块的内容。Webpack 会将这些导出的内容转换为在浏览器中可访问的形式。 - 如果一个模块使用
module.exports = { someFunction: () => {} }
这样的方式导出,Webpack 可能会将其转换为:
- CommonJS 使用
// 假设模块的 ID 为 1
__webpack_module.exports__ = { someFunction: () => {} };
- 然后,在加载这个模块时,可以通过模块加载函数获取到这个导出的对象:
const module = __webpack_require__(1);
console.log(module.someFunction());
- 代码优化和转换:
- Webpack 还会进行一系列的代码优化和转换操作。例如:
- Tree Shaking:去除未使用的代码,减小文件大小。如果一个模块中的某个函数从未被其他模块引用,Webpack 会在打包过程中去除这个函数的代码。
- 代码压缩:减小输出文件的大小,提高加载速度。Webpack 可以使用工具如 UglifyJS 或 Terser 对代码进行压缩。
- 模块合并:如果多个模块具有相同的依赖,Webpack 可能会将这些模块合并在一起,减少重复的代码加载。
- Webpack 还会进行一系列的代码优化和转换操作。例如:
三、输出打包文件阶段
- 生成浏览器可执行的文件:
- 经过模块转换和优化后,Webpack 会将所有的模块及其依赖关系打包成一个或多个文件。这些文件通常包含了模块加载函数和所有模块的代码。
- 打包后的文件可以直接在浏览器中通过
<script>
标签引入。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
- 当浏览器加载这个文件时,模块加载函数会开始执行,根据需要动态地加载和执行各个模块。
1010. 前端如何处理一个页面多主题色可供选择的场景【热度: 797】【web 应用场景】
关键词:主题色切换
在前端处理一个页面有多个主题色可供选择的场景,可以通过以下几种方式实现:
一、使用 CSS 变量
- 定义 CSS 变量:
- 在 CSS 中,可以使用
--
来定义变量。例如,可以定义一些代表主题色的变量:
- 在 CSS 中,可以使用
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
}
- 这里定义了两个变量
--primary-color
和--secondary-color
,分别代表主色和辅助色。
- 在 CSS 中使用变量:
- 然后在 CSS 规则中使用这些变量:
.button {
background-color: var(--primary-color);
color: white;
}
- 在这个例子中,
.button
类的按钮背景颜色使用了--primary-color
变量定义的颜色。
- 在 JavaScript 中切换主题:
- 在 JavaScript 中,可以通过修改
document.documentElement.style
来改变 CSS 变量的值,从而切换主题色:
- 在 JavaScript 中,可以通过修改
const setTheme = (theme) => {
document.documentElement.style.setProperty("--primary-color", theme.primaryColor);
document.documentElement.style.setProperty("--secondary-color", theme.secondaryColor);
};
const theme1 = {
primaryColor: "#007bff",
secondaryColor: "#6c757d",
};
const theme2 = {
primaryColor: "#ff5733",
secondaryColor: "#999999",
};
// 切换到主题 1
setTheme(theme1);
// 切换到主题 2
setTheme(theme2);
- 在这个例子中,
setTheme
函数接受一个主题对象,然后通过document.documentElement.style.setProperty
方法修改 CSS 变量的值。可以定义多个主题对象,然后根据用户的选择切换主题。
二、使用预处理器(如 Sass、Less)
- 定义变量和混合:
- 在 Sass 或 Less 中,可以定义变量来代表主题色。例如,在 Sass 中:
$primary-color: #007bff;
$secondary-color: #6c757d;
.button {
background-color: $primary-color;
color: white;
}
- 这里定义了变量
$primary-color
和$secondary-color
,并在.button
类中使用了这些变量。
-
创建多个主题文件:
- 可以创建多个主题文件,每个文件定义不同的变量值。例如,创建
theme1.scss
和theme2.scss
两个文件,分别定义不同的主题色。
- 可以创建多个主题文件,每个文件定义不同的变量值。例如,创建
-
在 JavaScript 中切换主题文件:
- 在 HTML 中,可以通过
<link>
标签引入不同的 CSS 文件来切换主题。在 JavaScript 中,可以动态地修改<link>
标签的href
属性来切换主题文件:
- 在 HTML 中,可以通过
const setTheme = (theme) => {
const link = document.getElementById("theme-link");
link.href = theme.href;
};
const theme1 = {
href: "theme1.css",
};
const theme2 = {
href: "theme2.css",
};
// 切换到主题 1
setTheme(theme1);
// 切换到主题 2
setTheme(theme2);
- 在这个例子中,
setTheme
函数接受一个主题对象,然后通过修改<link>
标签的href
属性来切换主题文件。可以定义多个主题对象,每个对象包含不同的主题文件路径。
三、使用 JavaScript 动态修改样式
- 定义样式类:
- 在 CSS 中定义多个样式类,每个类代表一种主题。例如:
.theme1 {
background-color: #007bff;
color: white;
}
.theme2 {
background-color: #ff5733;
color: white;
}
- 这里定义了两个样式类
.theme1
和.theme2
,分别代表不同的主题。
- 在 JavaScript 中切换样式类:
- 在 JavaScript 中,可以通过修改元素的
classList
属性来切换样式类,从而切换主题:
- 在 JavaScript 中,可以通过修改元素的
const setTheme = (theme) => {
const element = document.getElementById("my-element");
element.classList.remove("theme1", "theme2");
element.classList.add(theme);
};
// 切换到主题 1
setTheme("theme1");
// 切换到主题 2
setTheme("theme2");
- 在这个例子中,
setTheme
函数接受一个主题类名作为参数,然后通过修改元素的classList
属性来切换主题。首先移除当前元素的所有主题类名,然后添加指定的主题类名。
转载自:https://juejin.cn/post/7426886728059535395