前段时间面试了一个大厂,被问了一些犄角旮旯的面试题,整场下来,一脸懵逼,决定痛定思痛,把这些题总结下来,不断更新。每天一遍,每天进步。。。
目录
1、npm cnpm yarn pnpm的区别,各自的优势是什么? 你项目中用的什么?
2、devDependencies、dependencies,peerDependencies的区别
14、react的onClick和vue的@click分别是怎么实现的
19、http和https的区别?websocket和http的区别?
40、请求方式都有哪些,假如post请求最大支持2M,那么上传一个3M的文件应该怎么做最合适?
41、了解HTTP/2的特点、优势以及与HTTP/1.x的区别,以及如何在前端项目中优化使用HTTP/2协议。
44、nodejs的事件循环面试官:说说对Nodejs中的事件循环机制理解? | web前端面试 - 面试官系列
55、什么是JavaScript中的柯里化(Currying)和偏函数应用(Partial Application)?它们在JavaScript中有哪些应用场景?
65、Cookie和LocalStorage与SessionStorage的区别
1、npm cnpm yarn pnpm的区别,各自的优势是什么? 你项目中用的什么?
首先说一下Node.js,简单的说 Node.js 就是运行在服务端的 JavaScript。node通过更改连接到服务器的方式,可以处理高并发任务。
npm:
nodejs的包管理器,用于node插件管理(包括安装、卸载、管理依赖等)
npm安装速度慢的一个重要原因:npm远程服务器在国外,必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平化的node_modules结构。不过,npm有本地缓存,它保存了已经下载的每个版本的压缩包,缓存可以减少安装时间。
yarn
像npm一样,yarn使用本地缓存。与npm不同的是,yarn无需互联网连接就能安装本地缓存的依赖项,它提供了离线模式。
Yarn Workspaces(工作区)是Yarn提供的monorepo的依赖管理机制,从Yarn 1.0开始默认支持,用于在代码仓库的根目录下管理多个package的依赖。
Workspace 能更好的统一管理有多个项目的仓库,既可在每个项目下使用独立的 package.json 管理依赖,又可便利的享受一条 yarn命令安装或者升级所有依赖等。更重要的是可以使多个项目共享同一个 node_modules目录,提升开发效率和降低磁盘空间占用。
pnpm
利用硬链接和符号链接来避免复制本地缓存源文件,继承了yarn的所有优点,包括离线模式和确定性安装.内部基于内容寻址的文件系统存储磁盘上所有的文件:1.不会重复安装同一个包。2.即使包的不同版本,pnpm也会极大程度的复用之前版本的代码。
对比其优劣势
npm
node_modules扁平化结构
支持workspace,大部分开发人员用npm也能很好的打包
逐行安装(速度较慢)
问题:幻影依赖,依赖分身
yarn
node_modules扁平化解构
支持workspace,monorepo
yarn主要为了解决语义版本控制而导致npm安装不确定的问题,由yarn.lock文件,不过npm也出了package-lock文件
并行安装(速度较快)
问题:幻影依赖,依赖分身
pnpm
pnpm采用内存寻址存储的方法,通过软硬链接引用依赖,实现node_modules非扁平化结构,
支持workspace,monorepo,速度快,磁盘占用空间小(速度最快)
通过内容寻址存储解决了幻影依赖,依赖分身的问题,安全性高。
pnpm 的安装速度在大多数场景都比 npm 和 yarn 快 2 倍,节省的磁盘空间也更多。
问题:
因为依赖源文件是安装在 store 中,调试依赖或 patch-package 给依赖打补丁也不太方便,可能会影响其他项目。
由于 pnpm 创建的 node_modules 依赖软链接,因此在不支持软链接的环境中,无法使用 pnpm,比如 Electron 应用。
2、devDependencies、dependencies,peerDependencies的区别
npm install mypack & npm install mypack -save 将 mypack安装到node_modules目录下,并且将该包名放入package.json下的dependencies中
npm install mypack -save-dev 将 mypack安装到node_modules目录下,并且将该包名放入package.json下的devdependencies中
npm install mypack -g 将 mypack安装你 node 的安装目录 及 全局目录。该包名不放入package.json中,可以直接在命令行里使用。如npm
dependencies 字段中指定的是项目运行时需要的依赖包,也就是生产环境下需要的依赖。这些依赖将会被安装在生产环境中,并被打包进最终的发布版本中。一般来说,这些依赖是指与项目密切相关的库和框架,比如 express、lodash、axios 等。当我们使用 npm install 安装项目时,dependencies 中指定的依赖包也会被自动安装。使用场景:适用于项目的主要功能模块、框架以及必要的第三方库。这些依赖项会随着应用程序一起部署,并在生产环境中运行。
devDependencies 字段中指定的是开发环境下所需的依赖包。这些依赖通常是开发人员在编写和测试代码时使用的,而不会被打包到最终的发布版本中。一般来说,这些依赖包是用于构建、测试、调试等用途,比如 Babel、Webpack、Mocha 等。当我们使用 npm install 安装项目时,devDependencies 中指定的依赖包不会被安装。若需要安装 devDependencies 中的依赖,需要使用 npm install --dev。使用场景:适用于开发过程中的辅助工具、测试框架、构建工具、代码质量检查工具等。这些依赖项不会影响应用程序的实际运行,只在开发环境中使用。
peerDependencies 字段中指定的是项目所依赖的其他包的版本号范围。这些依赖会要求安装方在安装项目时手动安装所需要的版本。peerDependencies 通常用于告知用户项目运行时所依赖的某些库或框架的版本,而且这些库或框架已经被全局安装或者被安装在项目外面。peerDependencies 可以确保安装的库版本与项目所依赖的版本一致,从而减少版本兼容性问题。
使用场景:
开发库或模块依赖特定版本的外部库:如果你正在开发一个库或模块,它依赖于外部库的特定版本来实现某些功能,但你不希望将这些外部库包含在你的库中,那么你可以在你的 package.json 中指定这些外部库为 peerDependencies。
版本兼容性:有时,不同的库可能依赖于同一个外部库的不同版本,这可能导致版本冲突。通过在你的库中使用 peerDependencies,你可以确保使用者在安装你的库时会同时满足这些依赖项的版本要求,从而避免版本冲突。
库的可插拔性:如果你的库需要与其他库或模块协同工作,而这些库或模块可能在项目中以插件或扩展的形式存在,你可以使用 peerDependencies 来确保插件与你的库兼容。
提供建议性的依赖项:有时候,你可能希望为使用者提供一些建议性的依赖项,虽然这些依赖项不是强制性的。在这种情况下,你可以将这些依赖项列为 peerDependencies,使用者可以根据自己的需求来决定是否安装。
使用 peerDependencies 时需要注意以下几点:
使用 peerDependencies 不会自动安装依赖项,它只是告诉使用者需要安装这些外部依赖项,并确保版本兼容性。
使用 peerDependencies 时,使用者需要手动安装符合要求的外部依赖项,以便与你的库或模块正常工作。
为了避免冲突和混淆,建议在文档中清楚地说明使用者需要安装的外部依赖项以及版本要求。
3、生产环境,开发环境,测试环境等不同的环境的面试题
开发环境:
开发环境时程序猿们专门用于开发的服务器,配置可以比较随意,为了开发调试方便,一般打开全部错误报告和测试工具,是最基础的环境。开发环境的分支,一般是feature分支。
测试环境
一般是克隆一份生产环境的配置,一个程序在测试环境工作不正常,那么肯定不能把它发布到生产服务器上,是开发环境到生产环境的过度环境。测试环境的分支一般是develop分支,部署到公司私有的服务器或者局域网服务器上,主要用于测试是否存在bug,一般会不让用户和其他人看到,并且测试环境会尽量与生产环境相似。
生产环境
生产环境是指正式提供对外服务的,一般会关掉错误报告,打开错误日志,是最重要的环境。部署分支一般为master分支。
上述环境也可以说是系统开发的三个阶段:开发->测试->上线,其中生产环境也就是通产说的真实的环境,最后交给用户的环境。
如何在Vue中丝滑的切换环境呢?
- 在根目录建立.env系列文件
- .env.development(开发环境,用于serve启动的项目)
- .env.production(生产环境,用于build打包的项目)
- .env.test(测试环境)
//在文件中可以配置下面的变量 后面写请求服务器的地址
VUE_APP_BASE_API = '需要请求API'
1 //对应的 "scripts": 2 "dev": "vue-cli-service serve", 对应开发环境 3 "test": "vue-cli-service serve --mode test", 对应测试环境 4 "build": "vue-cli-service build", 对应生产环境 5 "build:test": "vue-cli-service build --mode test",对应测试环境
在vue中使用的话可以直接使用 process.env.VUE_APP_BASE_API 进行取值,也就是我们预先配置好的请求地址
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000,
})
生产环境中如何调试,如何查找报错对应的位置?
(1)服务器通过搜索生产环境的日志
(2)SoueceMap
虽然map文件提供了便利,但是在生产环境,为了安全,是建议关闭SourceMap的,因为通过.map文件和编译后代码可以很容易反编译出项目的源码,这样就相当于泄露了项目的代码。
生产环境的代码,经过压缩、编译,很不利于debug。由于生产环境没有配置SourceMap,所以代码报错时,或是通过Fundebug,Sentry等工具搜集到的报错信息,得到报错代码的行和列都是编译后的代码,这样很不易于定位问题。针对这个问题,需要准备一份生产环境代码的map文件,为了方便,可以在项目的package.json增加debug命令用来生成map文件。这条命令除了开启sourcemap,其他的具体webpack配置和生产环境配置相同。
"scripts": {
"start": "vue-cli-service serve --mode dev",
"stage": "vue-cli-service build --mode staging",
"online": "vue-cli-service build",
"debug": "vue-cli-service build --mode debug"
},
有了map文件,通过SourceMap提供的API就可以定位到源码的位置。下面是实现的核心代码。
// Get file content
const sourceMap = require('source-map');
const readFile = function (filePath) {
return new Promise(function (resolve, reject) {
fs.readFile(filePath, {encoding:'utf-8'}, function(error, data) {
if (error) {
console.log(error)
return reject(error);
}
resolve(JSON.parse(data));
});
});
};
// Find the source location
async function searchSource(filePath, line, column) {
const rawSourceMap = await readFile(filePath)
const consumer = await new sourceMap.SourceMapConsumer(rawSourceMap);
const res = consumer.originalPositionFor({
'line' : line,
'column' : column
});
consumer.destroy()
return res
}
最重要的就是使用SourceMap提供的 originalPositionFor API。 SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
originalPositionFor API的参数为一个包含line和column属性的对象
line 编译生成代码的行号,从1开始
column 编译生成代码的列号,从0开始
这个方法会返回一个具有以下属性的对象
{ source: 'webpack:///src/pages/common/403.vue?c891', // 源代码文件的位置,如果无法获取,返回null。
line: 4, // 源代码的行号,从1开始,如果无法获取,返回null。
column: 24, // 源代码的列号,从0开始,如果无法获取,返回null。
name: 'get' // 源代码的标识,如果无法获取,返回null。
}
4、库的版本问题
(1)版本号前面的“^”和“~”代表什么:
“^”它表示尽量使用最新版本,但保证不产生兼容问题。
~符号,表示版本号只能改变最末尾那段(如果是 ~x.y 末尾就是 y,如果是 ~x.y.z 末尾就是 z),比如这种情况:~1.2 等于 >=1.2.0; <2.0.0
因为0的特殊性所以如果你要指定 0 开头的库那一定要注意:~0.1 这种写法是很危险的,因为 ~0.1 等于 >=0.1.0; <1.0.0,可能出现无法向下兼容的情况,比较保险的写法还是:^0.1(等于 >=0.1.0; <0.2.0)
(2)改变版本号怎么做
npm version
命令用于更改版本号的信息,并执行commit
操作;该命令执行后, package.json
里的 version
会自动更新。
一般来说,当版本有较大改动时,变更第一位, 执行命令:npm version major -m "description"
, 例如1.0.0
-> 2.0.0
;
当前包变动较小时,可变更第二位,执行命令:npm version minor -m "description"
, 例如: 1.0.0
-> 1.1.0
;
当前包只是修复了些问题时,可变更第三位,执行命令:npm version patch -m "description"
, 例如: 1.0.0
-> 1.0.1
;
(3)pack.json的配置有哪些?
- 描述配置 主要是项目的基本信息,包括名称,版本,描述,仓库,作者等,部分会展示在 npm 官网上。
- 文件配置 包括项目所包含的文件,以及入口等信息。
- 脚本配置
命令行方式启动预设置的脚本
- 依赖配置 项目依赖其他包引用的相关信息。
- 发布配置 主要是和项目发布相关的配置。
- 系统配置 和项目关联的系统配置,比如 node 版本或操作系统兼容性之类。这些要求只会起到提示警告的作用,即使用户的环境不符合要求,也不影响安装依赖包。
- 第三方配置 一些第三方库或应用在进行某些内部处理时会依赖这些字段,使用它们时需要安装对应的第三方库。
5、有关提升项目性能的方案
1、减少 HTTP 请求次数:可以使用雪碧图、图片压缩等方法,减少静态资源的 HTTP 请求次数。
2、浏览器缓存:在合适的情况下,使用浏览器缓存可以显著减少请求时间,提高页面加载速度。
通过设置 http 头信息,可用的参数如下:其中 Expires 和 Cache-control 属于强缓存;Last-Modified/If-Modified-Since 和 Etag/If-None-Match 属于协商缓存。
* Expires
* Cache-control
* Last-Modified/If-Modified-Since
* Etag/If-None-Match
3、使用 CDN 加速:使用 CDN 加速可以将静态资源分发到多个节点,减少请求延迟,提高页面加载速度。
(1)、进行webpack的配置,将一些基本的npm包放在了CDN服务器上
(2)注入cdn变量 由于在开发环境时,文件资源还是可以从本地 node_modules 中取出,而只有项目上线了,才需要去使用外部资源。此时我们可以使用环境变量来进行区分。
(3)注入CDN配置到html模板 之后通过 html-webpack-plugin注入到 index.html之中:
4、延迟加载组件:对于一些比较耗时的组件,可以使用懒加载的方式,等到用户需要使用的时候再加载,避免在页面加载时一次性请求过多资源导致页面卡顿。
5、使用 Webpack 进行打包和压缩:Webpack 可以将多个 JS、CSS 文件打包成一个文件,减少 HTTP 请求次数;同时还可以进行代码压缩,减少文件大小,提高页面加载速度。
(1)下载压缩相关的库
1 # js 压缩
2 npm install uglifyjs-webpack-plugin --save-dev
3 # css提取
4 npm install mini-css-extract-plugin --save-dev
5 # css压缩
6 npm install css-minimizer-webpack-plugin --save-dev
7 # 读取环境变量
8 npm install cross-env --save-dev
(2)配置 webpack.config.js
1 const path = require("path")
2 const webpack = require("webpack")
3 const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html入口文件
4 const {CleanWebpackPlugin} = require('clean-webpack-plugin') // 编译文件时,清理 build/dist 目录,再生成新的
5
6 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
7 const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
8 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
11 const isProduction = process.env.NODE_ENV === "production"
12 const isDevelopment = process.env.NODE_ENV === "development"
13 console.log("print env: ", isProduction)
14
15 module.exports = {
16 entry: {
17 app: path.resolve(__dirname, "src/index.tsx"),
18 },
19 mode: "development",
20 module: {
21 rules: [
22 {
23 test: /\.(js|jsx|ts|tsx)$/,
24 exclude: /node_modules/,
25 use: [
26 {
27 loader: "babel-loader",
28 options: {
29 presets: [
30 [
31 "@babel/preset-env",
32 // 配置信息
33 {
34 targets: {
35 "chrome": "58",
36 "ie": "11",
37 },
38 // 指定 corejs 的版本
39 "corejs": "3",
40 // 使用 corejs 的方式 "usage" 表示按需加载
41 "useBuiltIns": "usage"
42 }
43 ],
44 [
45 "@babel/preset-typescript",
46 ],
47 [
48 "@babel/preset-react",
49 {}
50 ]
51 ]
52 }
53 },
54 {
55 loader: "ts-loader" // 1. 先加载ts
56 }
57 ],
58 },
59 {
60 test: /\.(css|scss)$/,
61 use: [
62
63 {
64 loader: process.env.NODE_ENV === "production"
65 ? MiniCssExtractPlugin.loader // 提取css到文件中,放到head中
66 : "style-loader", // 4. 加载样式文件到head
67
68 },
69 {
70 loader: "css-loader", // 3. 加载css
71 options: {
72 importLoaders: 1,
73 sourceMap: true
74 }
75 },
76 {
77 loader: "postcss-loader", // 2. 加载postcss(项目里配置的是 autoprefixer 和 px2rem) 转换 css里的rem和厂商前缀
78 options: {
79 postcssOptions: {
80 config: path.resolve(__dirname, 'postcss.config.js')
81 },
82 sourceMap: true
83 }
84 },
85 {
86 loader: "sass-loader", // 1. 加载 scss 转换成css
87 options: {
88 sourceMap: true
89 }
90 }
91 ]
92 },
93 // {
94 // test: /\.(png|jpg|gif)$/,
95 // use: [
96 // {
97 // loader: 'file-loader',
98 // options: {
99 // name: '[sha512:hash:base64:7].[ext]'
100 // }
101 // }
102 // ]
103 // } // by junfenghe 2021-12-06
104 {
105 test: /\.(png|jpg|gif|ico)$/,
106 type: 'asset/resource',
107 generator: {
108 filename: 'static/media/[hash][ext][query]'
109 }
110 },
111 {
112 test: /\.svg/,
113 type: 'asset/inline'
114 },
115 // 20230216 打包html中的图片
116 {
117 test: /\.(htm|html)$/i,
118 loader: "html-withimg-loader"
119 }
120 // {
121 // test: /\.(txt|pdf|excel)$/,
122 // type: 'asset/source'
123 // }
124 ]
125 },
126 resolve: {
127 extensions: ["*", ".js", ".jsx", ".ts", ".tsx"], // 尝试按顺序解析这些后缀名。能够用户再引入模块时不带扩展,例如:import File from '../path/to/file'
128 },
129 output: {
130 path: path.resolve(__dirname, "build"), // 文件输出的路径
131 // filename: "bundle.js", //对于单一入口(entry point)起点,filename 会是一个静态名称;
132 filename: "static/js/[name].[fullhash].bundle.js", // 决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。// 当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin) 创建多个 bundle,应该赋予每个 bundle 一个唯一的名称
133 assetModuleFilename: "images/[hash][ext][query]" // by junfenghe 2021-12-06 // by junfenghe 20221228
134 },
135 //20230216
136 optimization: {
137 minimizer: [
138 // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
139 `...`,
140 new CssMinimizerPlugin({
141 parallel: true,//使用多进程并发执行,
142 minimizerOptions:{
143 preset:[
144 "default",
145 {
146 discardComments: {removeAll:true},//移除所有注释
147 },
148 ]
149 }
150 }),
151 ]
152 },
153 //20230216
154 plugins: [
155 new HtmlWebpackPlugin({
156 template: path.join(__dirname, 'public/index.html')
157 }),
158 new CleanWebpackPlugin({
159 path: path.join(__dirname, 'build')
160 }),
161 // // 20230216 代码压缩
162 // new UglifyJsPlugin({
163 // parallel: true,// 使用多进程并行以提高构建速度
164 // sourceMap: true,// 使用源映射将错误信息位置映射到模块(这将会减慢编译速度)。
165 // // extractComments:true,//启用禁用提取注释
166 // cache: true,//启用缓存
167 // uglifyOptions: {
168 // comments: false,//如果你构建时不想出现注释,可以按照以下配置将 uglifyOptions.output.comments 设置为 false:
169 // },
170 // }),
171 // // 提取css
172 new MiniCssExtractPlugin({
173 // Options similar to the same options in webpackOptions.output
174 // both options are optional
175 filename: 'static/css/[name].[contenthash:8].css',
176 chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
177 }),
178 new CssMinimizerPlugin()
179
180 ],
181 devServer: {
182 static: {
183 directory: path.join(__dirname, "public")
184 },
185 historyApiFallback: true, //When using the HTML5 History API, the index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true:
186 port: 3003,
187 compress: true
188 },
189 devtool: isProduction?false:'source-map',//生产环境关闭sourcemap
190 }
191
192 if (isProduction) {
193 pluginsProduction = [
194 // 20230216 代码压缩
195 new UglifyJsPlugin({
196 parallel: true,// 使用多进程并行以提高构建速度
197 sourceMap: true,// 使用源映射将错误信息位置映射到模块(这将会减慢编译速度)。
198 // extractComments:true,//启用禁用提取注释
199 cache: true,//启用缓存
200 uglifyOptions: {
201 comments: false,//如果你构建时不想出现注释,可以按照以下配置将 uglifyOptions.output.comments 设置为 false:
202 },
203 }),
204
205 ]
206 module.exports.plugins.push(
207 ...pluginsProduction
208 )
209
210 }
(3)基于 React.lazy() 的优化
1 import DimensionReading from "../../pages/dimension/dimension-reading/dimension-reading";
2 import DimensionWriting from "../../pages/dimension/dimension-writing/dimension-writing";
3 import DimensionPhoto from "../../pages/dimension/dimension-photo/dimension-photo";
4 import DimensionPlan from "../../pages/dimension/dimension-plan/dimension-plan";
5 import DimensionAdd from "../../pages/dimension/dimension-add/dimension-add";
6 import DimensionHome from "../../pages/dimension/dimension-home/dimension-home";
7 import SelfIntro from "../../pages/self-intro/self-intro";
1 const DimensionReading = lazy(() => import("../../pages/dimension/dimension-reading/dimension-reading"))
2 const DimensionWriting = lazy(() => import("../../pages/dimension/dimension-writing/dimension-writing"))
3 const DimensionPhoto = lazy(() => import("../../pages/dimension/dimension-photo/dimension-photo"));
4 const DimensionPlan = lazy(() => import("../../pages/dimension/dimension-plan/dimension-plan"));
5 const DimensionAdd = lazy(() => import("../../pages/dimension/dimension-add/dimension-add"));
6 const DimensionHome = lazy(() => import("../../pages/dimension/dimension-home/dimension-home"));
7 const SelfIntro = lazy(() => import("../../pages/self-intro/self-intro"));
6、使用 Web Workers:对于一些计算密集型任务,可以使用 Web Workers 将任务分发到多个线程中,提高运算效率,避免页面卡顿。
7、优化 JavaScript 代码:优化 JavaScript 代码可以减少页面的运行时间,提高页面性能。一些常见的优化方案包括:避免使用全局变量,减少 DOM 操作,避免不必要的循环等。
8、使用响应式布局:使用响应式布局可以使页面适应不同大小的设备屏幕,提高用户体验,避免出现滚动条等不必要的元素。
9、使用 CSS3 动画:使用 CSS3 动画可以减少对 JavaScript 的依赖,提高动画效果的性能。
10、使用服务端渲染:服务端渲染可以将页面的渲染工作在服务端完成,减少客户端的渲染时间,提高页面性能。
11、如何对项目中的图片进行优化:
-
压缩图片: 使用专业的图片压缩工具(如TinyPNG、JPEGmini等)来减小图片的文件大小,以减少加载时间和节省带宽。
-
选择适当的格式: 根据图片的内容和用途,选择合适的图片格式。通常JPEG适合照片和复杂的图像,PNG适合具有透明部分或需要保留细节的图像,而SVG适合矢量图形。
-
调整图片尺寸: 通过调整图片的尺寸来匹配实际显示大小,避免加载过大的图片。不要通过CSS缩放大图片,而是使用适当尺寸的图片。
-
使用懒加载: 对于页面上加载大量图片的情况,使用懒加载技术可以延迟图片加载,只有当用户滚动到可见区域时才加载图片,减少页面初始加载时间。
-
响应式图片: 对于响应式网站,使用srcset和sizes属性或者Picture标签来提供不同尺寸的图片,以确保在不同设备上呈现最佳的图片质量。
-
使用CSS Sprites: 将多个小图片合并成一张大图,在网页中使用CSS Sprites技术,减少HTTP请求次数和提高加载速度。
-
使用CDN存储图片: 将图片文件存储在内容分发网络(CDN)上,可以加快图片的加载速度,减少服务器负载,提高用户体验。
6、前端性能优化指标RAIL是什么?
1. **R - Response