1.为什么3⼤框架出现以后就出现很多native(RN)框架(虚拟DOM)
前端框架的发展过程中,React、Angular和Vue这三大主流框架的出现,极大地推动了前端开发的进步。这些框架提供了丰富的功能和优化手段,使得开发者能够更高效地构建复杂的前端应用。然而,随着Web应用的不断复杂化,传统的Web视图渲染方式逐渐暴露出性能瓶颈。为了解决这个问题,原生框架(如React Native)和虚拟DOM技术应运而生。
原生框架(如React Native)允许开发者使用相同的代码库来构建跨平台的移动应用,这样可以减少开发成本和时间。而虚拟DOM则是一种用于提高前端应用性能的技术。通过在内存中创建一个虚拟的DOM树,虚拟DOM可以将实际DOM操作的数量最小化。当状态改变时,虚拟DOM会计算出最小的差异,并只更新实际DOM中需要变更的部分,从而提高了应用的性能。
总之,三大框架的出现催生了许多原生框架和虚拟DOM技术,这些技术旨在解决传统Web视图渲染方式在性能和跨平台方面的局限性,为开发者提供更高效、更灵活的前端开发方案。
私信【学习】即可获取前端资料 都整理好啦!!!
2. 虚拟DOM主要做了什么
虚拟DOM主要做的是提高Web应用的性能和响应速度。
首先,虚拟DOM是一个用来描述真实DOM结构的JS对象。它不是真实的DOM元素,而是一个轻量级的副本,这样可以更快地进行操作。
其次,通过这个虚拟树,我们可以计算出最小的操作来实现状态的变更,然后将这些操作应用到真实的DOM上。这样可以减少直接操作DOM的次数,从而提高性能。
此外,虚拟DOM还提供了一种高效的方式来更新UI。当数据发生变化时,我们不需要直接更新整个DOM树,而是通过比较新旧虚拟DOM之间的差异来计算出最小的变更集,然后只更新需要变更的部分。
总结来说,虚拟DOM通过提供一个抽象层来管理DOM更新,使得我们可以更高效地处理状态变更和UI更新,从而提高了前端应用的性能和响应速度。
私信【学习】即可获取前端资料 都整理好啦!!!
3. 虚拟DOM本身是什么(JS对象)
虚拟DOM是真实DOM的抽象描述,以JavaScript对象的形式存在。它描述了应该被渲染到页面上的DOM结构,但本身并不直接对应真实的DOM元素。当应用的状态发生变化时,框架会通过比较新旧虚拟DOM树来计算出最小的变更集,然后高效地将这些变更应用到真实的DOM上。
虚拟DOM的主要用途是提高前端应用的性能。通过使用虚拟DOM,我们可以减少直接操作DOM的次数,从而降低了应用的重绘成本。此外,虚拟DOM还提供了一种高效的方式来更新UI。当数据发生变化时,我们不需要直接更新整个DOM树,而是通过比较新旧虚拟DOM之间的差异来计算出最小的变更集,然后只更新需要变更的部分。
4.304是什么
Web开发中,304是一个非常常见的HTTP状态码,它代表着"Not Modified"(未修改)。这个状态码主要用于浏览器端的缓存机制。
当客户端(通常是浏览器)向服务器请求一个资源时,会发送一个If-Modified-Since
或If-None-Match
的HTTP头,用来表明本地缓存中资源的最后修改时间或ETag值。服务器接收到请求后,会检查请求的资源自上次修改以来是否有变化:
- 如果资源在客户端的缓存失效之后没有发生变化,即资源没有被修改,那么服务器会返回304状态码,并带上
Last-Modified
和ETag
响应头,告诉客户端资源没有被修改,可以使用本地的缓存资源。 - 如果资源有变化,服务器则会返回新的资源和200状态码,同时更新
Last-Modified
和ETag
响应头。
这种机制可以有效地减少不必要的数据传输,提高页面加载速度,并且降低服务器的负载。
这是一个简单的示例来说明这个过程:
- 客户端首次请求资源:
GET /resource.css HTTP/1.1
- 服务器返回200状态码和资源内容,以及
Last-Modified
和ETag
响应头:
HTTP/1.1 200 OK
Date: Sun, 06 Nov 2022 08:49:37 GMT
Last-Modified: Sun, 05 Nov 2022 08:49:37 GMT
ETag: "123456789"
Content-Type: text/css
/* Resource content */
- 客户端再次请求同一资源:
GET /resource.css HTTP/1.1
If-Modified-Since: Sun, 05 Nov 2022 08:49:37 GMT
If-None-Match: "123456789"
- 服务器检查资源未发生变化,返回304状态码和
Last-Modified
和ETag
响应头:
HTTP/1.1 304 Not Modified
Date: Sun, 06 Nov 2022 08:49:37 GMT
Last-Modified: Sun, 05 Nov 2022 08:49:37 GMT
ETag: "123456789"
通过这种方式,客户端知道资源没有被修改,可以直接使用本地缓存的版本,而不需要重新下载整个资源。
5.打包时Hash码是怎么⽣成的
前端工程化中,打包时生成的Hash码主要用于实现文件的版本控制和缓存管理。
-
内容摘要(Content Hash):
- 使用文件内容的哈希值来生成Hash码,通常是通过将文件内容作为输入传递给一个散列函数(如SHA-256、MD5等),然后得到一个固定长度的字符串作为该文件的指纹。
- 当文件内容发生变化时,哈希值也会随之变化,从而确保了文件的唯一性和版本控制。
-
构建过程(Build Hash):
- 在Webpack等构建工具中,除了基于文件内容的哈希,还会结合构建过程中的配置信息、依赖关系等因素来生成Hash码。
- 这有助于即使在源代码没有变化的情况下,如果构建配置发生了改变,也能生成新的Hash码。
-
优势:
- 利用Hash码可以有效地区分不同版本的资源文件,使得浏览器能够正确地识别哪些文件需要更新,哪些可以使用缓存中的旧版本。
- 对于长期缓存的文件,即使应用了新版本,由于文件名中包含的Hash码没有变化,用户浏览器可以直接使用本地缓存而无需重新下载。
-
代码示例:
- 在Webpack配置文件中,可以通过设置
output.filename
或output.chunkFilename
属性来包含Hash码。
// webpack.config.js module.exports = { output: { filename: '[name].[contenthash].js', // 例如:main.a1b2c3d4e5f6.js chunkFilename: '[name].[contenthash].chunk.js' }, // ...其他配置项 };
- 上述配置会将内容摘要Hash码添加到输出的JS和CSS文件名中。
- 在Webpack配置文件中,可以通过设置
总得来说,Hash码的生成是一个结合了内容摘要和构建过程信息的自动化过程,它为前端资源提供了一种有效的版本管理和优化缓存的策略。
私信【学习】即可获取前端资料 都整理好啦!!!
6.随机值存在⼀样的情况,如何避免
前端开发中,处理随机值重复的情况是一个常见的问题。这通常出现在需要生成唯一标识符或避免特定行为(如动画)的重复执行时。以下是几种策略来避免这种情况:
-
使用时间戳和随机数结合:
- 结合当前的时间戳和一个随机数可以大大降低重复的可能性。
-
使用UUID:
- UUID(Universally Unique Identifier)是一种全局唯一标识符,通常用于数据库、分布式系统等领域,可以保证在同一空间内的唯一性。
-
使用第三方库:
- 例如,
shortid
是一个能生成短且唯一ID的JavaScript库。
- 例如,
-
维护一个自增计数器:
- 如果在一个请求的生命周期内需要生成多个唯一的标识符,可以使用一个自增的计数器来保证每次生成的值都是唯一的。
-
检查新生成的值是否已存在:
- 在生成随机值后,通过查找数据结构(如Set集合)来判断这个值是否已经被生成过。如果已存在,则重新生成。
以下是一个使用时间戳和随机数结合来生成唯一ID的示例代码:
function generateUniqueId() {
const timestamp = new Date().getTime(); // 获取当前时间戳
const randomNum = Math.random(); // 生成0到1之间的随机数
return `${timestamp}-${randomNum}`; // 结合时间戳和随机数生成唯一ID
}
// 使用
const uniqueId = generateUniqueId();
console.log(uniqueId); // 输出类似 "1634872930273-0.123456789"
通过上述方法,我们可以在前端开发中有效地避免随机值重复的问题,确保应用的正确运行和用户体验的连贯性。
7. 使⽤webpack构建时有⽆做⼀些⾃定义操作
私信【学习】即可获取前端资料 都整理好啦!!!
在使用Webpack进行构建时,我们可以通过自定义操作来增强构建过程,以满足项目的特定需求。以下是一些常见的自定义操作:
-
自定义加载器(Loader):
- 可以编写或引入第三方的加载器来处理特定类型的文件。
-
插件(Plugins):
- 使用插件可以在构建过程中执行更复杂的操作,例如代码压缩、模块合并等。
-
环境变量:
- 通过设置环境变量来控制构建过程的行为,例如区分开发和生产环境。
-
构建目标(Target):
- 根据不同的构建目标(如浏览器、Node.js、Electron等)来调整输出结果。
-
拆分代码块(Code Splitting):
- 将代码分割成多个bundles,以便按需加载。
-
动态导入(Dynamic Imports):
- 在运行时动态导入模块,减少初始加载时间。
-
Tree Shaking:
- 移除未使用的代码,减小最终的打包体积。
-
自定义输出路径:
- 根据需要设置输出目录。
以下是一个Webpack配置的示例,展示了如何进行一些自定义操作:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js', // 添加Hash码到文件名
path: path.resolve(__dirname, 'dist'), // 自定义输出路径
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader', // 使用Babel处理JS文件
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'] // 使用CSS加载器和插件
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 自定义模板路径
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css' // 添加Hash码到CSS文件名
})
],
optimization: {
splitChunks: {
chunks: 'all', // 对所有模块进行拆分
},
minimize: true // 开启代码压缩
}
};
在这个例子中,我们使用了html-webpack-plugin
来简化HTML文件的创建,mini-css-extract-plugin
来分离CSS到一个单独的文件,并利用了Webpack的优化选项来进行代码拆分和压缩。
通过这些自定义操作,我们可以更好地控制Webpack的构建过程,提高构建效率,优化最终的输出结果,从而提升应用的性能和用户体验。
8.webpack做了什么
私信【学习】即可获取前端资料 都整理好啦!!!
Webpack是一个现代的JavaScript应用程序的静态模块打包器(bundler)。它的主要目的是将应用程序中的许多模块及其依赖项打包成一个或多个最终的文件,以便在浏览器或服务器上运行。以下是Webpack的一些关键功能和作用:
-
模块化打包:
Webpack能够处理各种模块系统(如CommonJS、AMD、ES6模块等),并将它们转换为可以在浏览器中运行的格式。 -
代码转换:
通过加载器(loaders)机制,Webpack可以对不同类型的文件(如JS、CSS、图片等)进行处理和转换。例如,使用Babel-loader可以将ES6+的代码转换为ES5,以保证兼容性。 -
资源处理:
Webpack可以处理并优化图片、字体、样式表等静态资源。 -
代码拆分:
通过Code Splitting特性,Webpack可以将大型应用拆分成多个小的bundles,实现按需加载,从而提高首屏加载速度。 -
热模块替换(HMR):
在开发过程中,Webpack能够实现快速重新加载修改后的模块而无需完全刷新页面,提升开发效率。 -
Tree Shaking:
Webpack能够识别并移除未被引用的代码,减少输出文件的大小。 -
环境配置:
Webpack允许根据不同的环境(开发、生产等)进行配置,以满足不同场景的需求。 -
插件系统:
Webpack拥有一个强大的插件系统,可以通过插件扩展其功能,如UglifyJsPlugin用于压缩JS代码。
以下是一个简化的Webpack配置文件示例,展示了基本的打包配置:
const path = require('path');
module.exports = {
entry: './src/index.js', // 入口文件
output: {
filename: 'bundle.js', // 输出文件名
path: path.resolve(__dirname, 'dist'), // 输出路径
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader', // 使用Babel-loader处理JS文件
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 使用style-loader和css-loader处理CSS文件
}
]
}
};
在这个例子中,我们配置了两个加载器来处理JS和CSS文件,并通过entry
和output
定义了打包的起始点和输出结果。这只是一个基础的配置,Webpack的实际应用中可能会包含更多的加载器、插件和复杂的配置,以满足项目的特定需求。
9. a,b两个按钮,点击aba,返回顺序可能是baa,如何保证是aba(Promise.then)
私信【学习】即可获取前端资料 都整理好啦!!!
要确保点击 a、b 两个按钮后,返回顺序是 aba,可以使用 Promise.then 链式调用。具体做法是在点击 a 按钮的函数中返回一个 Promise,然后在点击 b 按钮的函数中调用这个 Promise,最后在 Promise 的 then 方法中执行 b 按钮的操作。这样就能保证 a 按钮的操作先于 b 按钮的操作执行。
解析:
- 创建一个点击 a 按钮的函数,返回一个 Promise。
- 创建一个点击 b 按钮的函数,调用 a 按钮的 Promise。
- 在 a 按钮的 Promise 的 then 方法中执行 b 按钮的操作。
代码示例:
// 点击 a 按钮的函数
function clickA() {
return new Promise((resolve) => {
console.log('a');
resolve();
});
}
// 点击 b 按钮的函数
function clickB() {
console.log('b');
}
// 点击 a、b 两个按钮
clickA().then(() => {
clickB();
});
这样,无论何时点击 a、b 两个按钮,返回顺序都会是 aba。
10. node接⼝转发有⽆做什么优化
Node.js中,接口转发通常涉及到将请求从一个端口转发到另一个端口,或者将请求从本地服务器转发到远程服务器。为了优化这个过程,我们可以采取以下几种策略:
-
使用高效的HTTP模块:使用内置的http模块或第三方库(如axios、request等)来处理HTTP请求和响应,这些库通常提供了更好的性能和错误处理机制。
-
缓存:对于频繁请求的接口,可以使用缓存来减少不必要的请求。例如,可以使用内存缓存(如node-cache)或分布式缓存(如Redis)来存储已经请求过的数据。
-
连接池:对于需要频繁连接到远程服务器的场景,可以使用连接池来复用TCP连接,减少连接建立和关闭的开销。例如,可以使用http-proxy-middleware库来实现连接池。
-
负载均衡:如果需要将请求分发到多个后端服务器,可以使用负载均衡算法(如轮询、随机、加权等)来分配请求,从而提高系统的吞吐量和可用性。
-
限流:为了防止恶意请求或突发流量导致系统崩溃,可以对接口进行限流。例如,可以使用express-rate-limit库来实现限流功能。
以下是一个简单的接口转发示例,使用了http-proxy-middleware库实现连接池和负载均衡:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 接口转发配置
const proxyConfig = {
target: 'http://localhost:3000', // 目标服务器地址
changeOrigin: true, // 允许跨域
pathRewrite: {
'^/api': '' // 重写路径,去掉/api前缀
},
onProxyReq: (proxyReq, req, res) => {
console.log('Requested url: ', req.url);
},
onProxyRes: (proxyRes, req, res) => {
console.log('Response status code: ', proxyRes.statusCode);
}
};
// 使用http-proxy-middleware中间件实现接口转发
app.use('/api', createProxyMiddleware(proxyConfig));
app.listen(8080, () => {
console.log('Server is running on port 8080');
});
通过以上优化策略,可以提高接口转发的性能和稳定性。