1. 说说 Vue 中的 Diff 算法
Vue 的 Diff 算法 主要用于优化虚拟 DOM 和实际 DOM 之间的比较过程。它通过以下几种策略来提高性能:
- 最小化对 DOM 的操作:Vue 通过在内存中构建一个虚拟 DOM 树,在虚拟 DOM 树与真实 DOM 树之间进行比较和更新,这样就避免了频繁的直接操作 DOM,提高了性能。
- 异步更新:Vue 会对数据变化进行异步更新,并且对多次更新进行批量处理,减少了不必要的 DOM 更新。
- Key 优化:当更新列表时,Vue 使用
key
来标识每个元素,避免在排序或重新渲染时产生不必要的 DOM 操作。key
有助于 Vue 精确地追踪每个节点,做到最小的 DOM 操换。 - 节点的就地复用:Vue 在进行虚拟 DOM 比较时,会尽可能复用现有节点,而不是删除旧节点并重新创建新的节点,减少性能开销。
在 Vue 2.x 中,Vue 使用的是 最小化差异比较算法(即基于两个树进行对比),它使用了 双指针算法 来遍历不同的节点,并找到它们之间的差异。
2. Vue 模板是如何编译的? 经历了哪些过程?
Vue 模板的编译过程分为两个阶段:
1. 解析阶段(Parsing)
- 模板解析:Vue 将模板字符串解析成一个抽象语法树(AST)。在此阶段,Vue 会对模板中的 HTML 标签、指令(如
v-bind
,v-for
,v-if
)等进行解析,形成 AST 树结构。 - 优化阶段:Vue 会分析静态节点(不会变化的节点),标记出这些静态部分。在更新时,静态部分不需要重新渲染,进一步提高性能。
2. 生成阶段(Code Generation)
- 生成渲染函数:Vue 将优化后的 AST 转换成渲染函数。该渲染函数最终会被执行,生成虚拟 DOM。
在此过程后,Vue 模板编译得到一个渲染函数,这个函数会根据数据的变化动态生成虚拟 DOM。
3. Vue 中 computed 和 watch 区别? 分别适用于什么场景?
computed
-
定义:
computed
是计算属性,基于它们的依赖进行缓存,只有当相关依赖发生变化时,才会重新计算。 -
特点:
- 缓存:只要依赖的响应式数据没有变化,计算属性的值就会被缓存,不会每次都重新计算。
- 简洁:适合处理复杂的逻辑,通常用于基于数据的计算。
-
适用场景:当你需要基于某些数据的变化来计算一个值,并且该值的计算过程有缓存需求时。
watch
-
定义:
watch
用于观察某个数据的变化,执行相应的副作用操作。它不像computed
那样是一个直接返回值的计算过程,而是执行一些操作。 -
特点:
- 异步处理:可以在数据变化时触发副作用,比如发送 API 请求、更新其他数据等。
- 没有缓存:每次数据变化都会触发
watch
中的回调。
-
适用场景:当你需要对某个数据变化进行额外的处理,或者执行一些副作用(例如,异步操作、DOM 操作等)时。
总结:
computed
主要用于 计算属性,且具有缓存特性,适合需要依赖计算并且返回值的场景。watch
适合处理需要执行副作用的情况,比如异步请求、深度监听等。
4. 什么是 Vuex? 使用 Vuex 有哪些好处?
Vuex 是一个 状态管理库,用于在 Vue 应用中管理全局状态。它帮助你将状态集中管理,确保所有组件以统一的方式来访问和更新状态。Vuex 的核心概念包括:
- State:存储应用的状态。
- Getters:从 State 中派生出的状态,类似于计算属性。
- Mutations:同步修改 State 的方法。
- Actions:可以包含异步操作的函数,通常用于调用 Mutations。
- Modules:将 Store 分割成多个模块,每个模块拥有自己的 state、mutations、actions 和 getters。
Vuex 的好处:
- 集中式管理:通过 Vuex 管理应用的状态,使得状态管理更加清晰、统一。
- 状态共享:多个组件可以共享状态,避免了复杂的 props 和 events 传递。
- 易于调试:Vuex 提供了插件来帮助调试状态的变化。
- 与 Vue 紧密集成:Vuex 与 Vue 完美集成,支持热重载、调试等特性。
5. Vue Router 的 $route
和 $router
对象有什么区别?
-
$route
:是 当前路由信息,它包含了关于当前路由的所有信息,如path
、params
、query
、hash
等。你可以通过$route
获取当前页面的路由信息。console.log(this.$route.path); // 当前路由的路径
-
$router
:是 路由实例,包含了控制路由跳转的方法,如push
、replace
、go
、back
等。通过$router
可以改变路由,进行页面跳转。this.$router.push('/new-route'); // 跳转到新的路由
总结:
$route
是当前路由的状态,包含当前路由的相关信息。$router
是 Vue Router 的路由实例,用来操作路由(跳转等)。
6. Vue Router 路由有哪些模式? 各模式有什么区别?
Vue Router 提供了两种主要的路由模式:
-
Hash模式(默认模式):
- 使用 URL 中的 hash (
#
) 来表示路由状态。例如:http://example.com/#/home
。 - 由于
#
后面的内容不会发送给服务器,所以可以实现前端路由跳转。 - 优点:兼容所有浏览器,不需要服务器配置。
- 缺点:URL 中带有
#
,不够美观。
- 使用 URL 中的 hash (
-
History模式:
- 使用 HTML5 的
history.pushState()
和history.replaceState()
来改变路由,而不会重新加载页面。路由的路径看起来像普通的 URL,如http://example.com/home
。 - 优点:没有
#
,URL 更加美观。 - 缺点:需要后端支持,服务器需要配置返回同一个 HTML 文件,以便 Vue Router 在浏览器端处理路由。
- 使用 HTML5 的
7. React 中的路由懒加载是什么? 原理是什么?
路由懒加载 是指仅在用户访问某个特定路由时,才加载该路由对应的组件,避免在页面加载时一次性加载所有组件,从而提高页面加载速度。
原理:
React 提供了 React.lazy
和 Suspense
来实现路由懒加载。React.lazy
允许动态加载组件,而 Suspense
组件用于在等待加载时展示一个加载状态。
示例代码:
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./Home')); // 懒加载 Home 组件
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/home" component={Home} />
</Switch>
</Suspense>
</Router>
);
}
优点:
- 提高了页面加载速度,减少初始加载时的资源消耗。
- 只有在用户访问该路由时,相关组件才会被加载。
8. 你常用的 React Hooks 有哪些?
常用的 React Hooks 包括:
-
useState
:用于声明组件的状态。const [count, setCount] = useState(0);
-
useEffect
:用于执行副作用操作,如数据获取、订阅等。useEffect(() => { // 代码逻辑 }, [dependencies]); // 当 dependencies 变化时会执行
-
useContext
:用于在组件
树中访问 context。
const value = useContext(MyContext);
-
useRef
:用于获取对 DOM 元素或其他数据的引用。const myRef = useRef(null);
-
useMemo
:用于缓存值,避免不必要的计算。const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
-
useCallback
:用于缓存回调函数,避免因父组件重新渲染导致子组件不必要的重新渲染。const memoizedCallback = useCallback(() => { /* code */ }, [dependencies]);
-
useReducer
:用于管理复杂的状态逻辑,通常与useState
替代。const [state, dispatch] = useReducer(reducer, initialState);
这些 Hooks 可以帮助开发者简化组件的状态管理和副作用操作,提高代码的可维护性和可读性。
1. React 组件间怎么进行通信?
React 组件间的通信方式主要有以下几种:
1. 父子组件通信
- Props:父组件通过
props
向子组件传递数据,子组件通过props
接收数据并渲染。props
是单向数据流。
// 父组件
<ChildComponent name="Alice" />
// 子组件
function ChildComponent(props) {
return <div>Hello, {props.name}</div>;
}
2. 子父组件通信
- 回调函数:子组件通过回调函数将数据传递给父组件,父组件将回调函数作为
props
传递给子组件。
// 父组件
function ParentComponent() {
const handleChildData = (data) => {
console.log(data);
};
return <ChildComponent onData={handleChildData} />;
}
// 子组件
function ChildComponent(props) {
return (
<button onClick={() => props.onData('Hello from child!')}>Send Data</button>
);
}
3. 兄弟组件通信
- 通过父组件传递数据:兄弟组件之间通过将共享的数据传递到父组件,父组件再通过
props
传递给另一子组件。 - React Context API:React Context 用于跨层级共享数据,不需要通过一层层的
props
传递。
4. 全局状态管理
- Redux / MobX / Zustand:通过这些状态管理库,可以将组件的状态集中管理,任何组件都可以通过订阅和派发 actions 来共享状态。
- React Context:React 自带的 Context API 也可以作为简单的全局状态管理工具。
2. React.memo() 和 useMemo() 的用法是什么,有哪些区别?
React.memo()
- 用途:
React.memo()
是一个高阶组件,用于优化函数组件的渲染。当组件的props
没有发生变化时,React.memo()
会阻止重新渲染,从而提高性能。 - 用法:
const MyComponent = React.memo(function MyComponent(props) {
return <div>{props.name}</div>;
});
- 适用场景:当组件的
props
没有变化时,避免重复渲染(尤其是在列表、表格等渲染频繁的场景)。
useMemo()
- 用途:
useMemo()
是一个 Hook,用于缓存计算结果,避免在每次渲染时重复执行高计算量的操作。 - 用法:
const memoizedValue = useMemo(() => expensiveFunction(a, b), [a, b]);
- 适用场景:适用于需要缓存某个计算结果的场景,比如依赖复杂计算或多次重复计算的值。
区别:
React.memo()
用于缓存组件的渲染结果,防止因props
不变而重新渲染。useMemo()
用于缓存函数的计算结果,防止在每次渲染时执行重复的计算。
3. 说说你对 TypeScript 的理解? 与 JavaScript 的区别?
TypeScript 的理解:
TypeScript 是 JavaScript 的一个超集,添加了静态类型检查,允许开发者在开发阶段捕捉到类型错误。它编译成标准的 JavaScript,并可以直接运行在任何支持 JavaScript 的环境中。
与 JavaScript 的区别:
- 类型系统:TypeScript 引入了静态类型,开发者可以显式地定义变量、函数、对象的类型,从而避免许多运行时错误。
- 类型推断:TypeScript 提供了类型推断,即使不显式指定类型,TypeScript 也能根据上下文推断出类型。
- 编译:TypeScript 代码需要通过 TypeScript 编译器(
tsc
)转译成 JavaScript 代码,而 JavaScript 是直接可以执行的。 - 面向对象支持:TypeScript 提供了对类(Class)、接口(Interface)、泛型(Generics)等的更好支持。
4. TypeScript 的内置数据类型有哪些?
TypeScript 提供了丰富的数据类型,常见的有:
-
基本数据类型:
number
:所有的数字类型(包括整数和浮点数)。string
:字符串类型。boolean
:布尔类型,true
或false
。symbol
:唯一且不可变的原始数据类型(ES6 引入)。null
:表示“没有值”的类型。undefined
:表示“未定义”的类型。
-
复杂数据类型:
Array
:数组类型,可以是特定类型的元素数组。Tuple
:元组类型,表示固定大小的数组,且元素类型可以不同。Object
:对象类型,用来表示非原始类型。any
:任意类型,绕过类型检查,使用时需要小心。void
:表示函数没有返回值。never
:表示从不返回的类型(例如,抛出异常的函数类型)。
5. TypeScript 中的 Declare 关键字有什么用?
declare
用于声明一个变量、类、函数或模块的类型,而不提供具体的实现。它通常用于声明外部库、全局变量等,并且在编译时不会生成 JavaScript 代码。
例如:
declare const someGlobalVar: string;
在上面的代码中,declare
告诉 TypeScript 编译器,someGlobalVar
是一个全局变量,并且它的类型是 string
,但它的实现会在运行时提供。
6. 什么是 TypeScript 中的命名空间和模块? 两者有什么区别?
命名空间:
命名空间(namespace
)是 TypeScript 中的一种组织代码的方式,它通过将相关功能封装到命名空间中来避免命名冲突。命名空间通常用于大型应用中的内部模块,特别是在没有模块化系统(如 CommonJS 或 ES Modules)的情况下。
namespace MyNamespace {
export function greet() {
console.log('Hello');
}
}
模块:
模块(module
)是 TypeScript 中用来分割代码的机制。它允许将代码划分为多个文件,并且每个文件都是一个模块。模块中的成员默认是私有的,只有通过 export
关键字才能暴露。
// module.ts
export function greet() {
console.log('Hello');
}
区别:
- 命名空间:用于组织和封装代码,通常适用于全局范围内的代码组织,尤其在没有模块化支持的环境下。
- 模块:用于将代码分割成多个文件,并使用
import
和export
来导入和导出功能。模块支持 ES6 和 CommonJS 等模块化系统,通常在较现代的前端和后端开发中使用。
7. 说说你对 Node.js 的理解? 优缺点? 应用场景?
Node.js 的理解:
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它允许使用 JavaScript 编写服务器端代码。Node.js 是非阻塞、事件驱动的,非常适合 I/O 密集型的应用。
优点:
- 高效的异步 I/O:Node.js 使用事件驱动和非阻塞 I/O 模型,可以高效地处理大量并发请求。
- JavaScript 全栈开发:前后端使用相同的语言(JavaScript),便于开发人员统一技术栈。
- 大量的 npm 包:Node.js 拥有世界上最大的开源包管理器
npm
,为开发者提供了丰富的库和工具。 - 实时应用:非常适合实时应用(如聊天应用、实时协作工具等)。
缺点:
- 单线程:虽然 Node.js 可以通过异步非阻塞 I/O 高效处理并发,但它依然是单线程的,计算密集型任务可能导致性能瓶颈。
- 回调地狱:由于使用回调函数来处理异步操作,可能导致代码难以维护,幸好有
Promise
和async/await
可以改善这个问题。
应用场景:
- 实时应用(如聊天应用、在线游戏)。
- API 服务:Node.js 非常适合构建 RESTful API 或 GraphQL API。
- 流媒体处理:如视频流、音频流处理等。
- **
微服务架构**:由于其轻量级和高效性,Node.js 在微服务架构中得到广泛应用。
8. 什么是 Node.js 中的 process? 它有哪些方法和应用场景?
process
是 Node.js 的全局对象,提供了关于当前 Node.js 进程的相关信息和控制。它可以访问系统环境、进程的输入输出、退出状态等。
常用方法:
process.argv
:返回命令行参数。process.env
:返回环境变量。process.exit()
:退出当前 Node.js 进程。process.on('exit', callback)
:监听进程退出事件。process.stdout
、process.stderr
:向标准输出或标准错误输出数据。
应用场景:
- 获取和设置环境变量。
- 处理命令行参数。
- 在应用退出时做一些清理工作,如关闭数据库连接。
9. 什么是 npm? 你用过哪些 npm 包? 是否开发过自己的 npm 包?
npm(Node Package Manager)是 Node.js 的包管理工具,用于管理和发布 JavaScript 的第三方库和工具。它帮助开发者快速安装、更新和管理项目中的依赖。
常用 npm 包:
- express:用于构建 Web 服务器的框架。
- lodash:提供常用的 JavaScript 工具函数库。
- axios:用于发送 HTTP 请求的库。
- react 和 react-dom:用于构建前端应用的核心库。
- webpack:用于模块打包的工具。
开发 npm 包:
开发自己的 npm 包时,可以通过 npm init
创建一个新的项目,并使用 npm publish
将包发布到 npm registry 中。发布时需要提供包的名称、版本和描述等信息。
10. 什么是 Node.js 的事件循环机制? 它是怎么实现的?
Node.js 使用 事件循环(Event Loop)机制来处理异步 I/O 操作。Node.js 是基于单线程的,但它通过事件循环模型来处理并发操作,避免了传统线程模型中的开销。
事件循环的工作流程:
- 执行栈:执行 JavaScript 代码(同步代码)时,任务会被放入执行栈中。
- 事件队列:异步任务的回调函数会被放入事件队列中,等待执行。
- 事件循环:Node.js 的事件循环不断地从事件队列中取出任务,并将其放入执行栈执行。
事件循环的阶段:
事件循环包含多个阶段,包括定时器(Timers)、I/O 回调(I/O callbacks)、定时器队列(setImmediate)、关闭回调(close callbacks)等。每个阶段会根据优先级执行队列中的任务。
事件循环确保异步操作不会阻塞主线程,使得 Node.js 能够高效处理大量并发请求。
这些是关于前端开发和 Node.js 的一些常见问题与解答,希望能够帮助你深入理解这些技术。
1. Node.js 有哪些全局对象? 它们分别有什么作用?
Node.js 提供了一些全局对象,可以在任何模块中直接访问。常见的全局对象有:
-
global
:在 Node.js 中,global
是一个全局作用域对象,类似于浏览器中的window
对象。所有模块都可以通过global
访问到这个全局对象。global.myVar = 'Hello';
-
process
:提供关于当前 Node.js 进程的信息和控制方法。可以用来获取命令行参数、环境变量、控制进程的输入输出等。process.argv
:命令行参数。process.exit()
:退出进程。process.env
:环境变量。
-
__dirname
:当前模块文件所在目录的绝对路径。console.log(__dirname);
-
__filename
:当前模块文件的完整路径(包括文件名)。console.log(__filename);
-
require
:用于引入其他模块,Node.js 的模块加载系统。你可以加载本地模块、第三方模块或内置模块。const fs = require('fs');
-
module
:表示当前模块的状态。module.exports
用于导出模块内容,module.id
用于获取当前模块的唯一标识。 -
setTimeout
和setInterval
:用于设定延迟调用和定时调用。 -
Buffer
:用于操作二进制数据,特别是在处理文件和网络请求时。
2. 怎么调试 Node.js 程序?
调试 Node.js 程序有多种方式:
-
使用
console.log()
调试- 最常见的调试方法是通过
console.log()
打印变量或信息,查看程序执行情况。
- 最常见的调试方法是通过
-
Node.js 内建调试工具
- 使用
node inspect <filename>
启动调试模式,可以在命令行中进行调试。node inspect app.js
- 也可以在 Chrome 浏览器的 DevTools 中进行调试,使用
node --inspect-brk <filename>
启动并在浏览器中进行断点调试。node --inspect-brk app.js
- 然后访问
chrome://inspect
连接调试器。
- 使用
-
VSCode 调试
- VSCode 提供强大的调试支持,可以直接在编辑器中设置断点、单步执行、查看变量等。需要配置
launch.json
文件。
- VSCode 提供强大的调试支持,可以直接在编辑器中设置断点、单步执行、查看变量等。需要配置
-
使用第三方调试工具
- nodemon:自动重启应用,适合开发环境中实时调试。
- debug:一个流行的调试库,允许你有条件地打印调试信息。
3. 介绍下从 HTTP/1.0、HTTP/1.1 到 HTTP/2 再到 HTTP/3 的演化过程,各个阶段相对前一阶段做了哪些优化?
HTTP/1.0:
- 请求/响应模型:每次 HTTP 请求都会打开一个新的 TCP 连接,完成后关闭,造成连接开销较大。
- 无持久连接:每次请求都要重新建立连接,浪费了大量的资源和时间。
- 无流水线处理:HTTP/1.0 不能同时发送多个请求,必须等前一个请求响应完再发送下一个请求。
HTTP/1.1(优化版 HTTP/1.0):
- 持久连接:默认开启持久连接(
Connection: keep-alive
),请求和响应可以在同一连接上多次传输。 - 管道化(Pipelining):可以在一个 TCP 连接上并行发送多个请求,但必须按顺序响应,仍然存在队头阻塞问题。
- 分块传输编码:解决了响应内容长度不确定的情况,可以一边传输一边接收数据。
HTTP/2(基于 HTTP/1.1 进一步优化):
- 多路复用:可以在一个连接上并行处理多个请求和响应,解决了 HTTP/1.1 中的队头阻塞问题。
- 头部压缩:使用 HPACK 算法对请求头进行压缩,减少数据传输量。
- 服务器推送:服务器可以主动推送资源到客户端,而不是等客户端请求资源。
- 二进制协议:HTTP/2 使用二进制格式传输数据,相比 HTTP/1.1 的文本格式更高效。
HTTP/3(基于 QUIC 协议):
- 基于 QUIC 协议:HTTP/3 使用 Google 的 QUIC(Quick UDP Internet Connections)协议,替代了 TCP,使用 UDP 来建立连接。
- 减少连接建立延迟:QUIC 支持 0-RTT 和 1-RTT 连接建立,减少了网络延迟。
- 更强的拥塞控制:QUIC 协议具有更高效的拥塞控制和恢复机制。
- 内置加密:所有 HTTP/3 请求都加密,无需额外的 TLS 握手过程。
总结:
- HTTP/1.0 → HTTP/1.1:主要优化了连接复用和请求响应机制。
- HTTP/1.1 → HTTP/2:引入多路复用和头部压缩,减少了延迟和带宽消耗。
- HTTP/2 → HTTP/3:从 TCP 协议转向基于 UDP 的 QUIC 协议,进一步减少连接延迟并增强性能。
4. DNS HTTP 缓存有哪些实现方式? 什么是协商缓存和强制缓存?
DNS 缓存:
- 本地 DNS 缓存:操作系统和浏览器都能缓存域名解析结果,减少 DNS 查询次数,提高性能。
- DNS 记录缓存:DNS 服务器会缓存查询过的域名解析结果(例如,TTL),减少后续查询的延迟。
HTTP 缓存:
HTTP 缓存有两种方式:强制缓存和协商缓存。
强制缓存:
- 强制缓存是指资源在缓存中存在且没有过期,浏览器直接使用缓存而不发送请求。
- 常见的强制缓存机制:
Cache-Control
和Expires
。Cache-Control: max-age=3600
:资源会在缓存中保存 3600 秒。Expires
:指定缓存的过期时间。
协商缓存:
- 协商缓存是在缓存过期后,通过向服务器发送请求,确认资源是否有更新。如果资源没有变化,服务器返回
304 Not Modified
状态码,浏览器可以继续使用缓存。 - 常见的协商缓存机制:
Last-Modified
和ETag
。Last-Modified
:服务器返回资源最后修改时间,客户端下次请求时带上If-Modified-Since
,服务器根据该时间判断是否返回缓存。ETag
:资源的唯一标识符,客户端带上If-None-Match
头部,服务器根据ETag
判断资源是否更新。
5. 简述 TCP/IP 网络模型,分为几层? 每层的职责和作用是什么?
TCP/IP 网络模型分为 4 层:
-
应用层:
- 提供应用程序与网络通信的接口。
- 包括 HTTP、FTP、DNS、SMTP 等协议。
-
传输层:
- 负责数据的可靠传输。
- 主要协议有 TCP(可靠、面向连接)和 UDP(不可靠、无连接)。
-
网络层:
- 负责数据包的路由和转发,确保数据包从源主机传输到目的主机。
- 主要协议有 IP、ICMP、ARP 等。
-
数据链路层:
- 负责在物理设备之间传输数据帧,并处理硬件地址和纠错等问题。
- 主要协议有 Ethernet、PPP、HDLC 等。
6. 什么是 webpack? 它有什么作用?
Webpack 是一个模块化打包工具,用于将 JavaScript、CSS、图片等资源打包成浏览器可以加载的静态文件。
主要作用:
- 模块化:Webpack 支持将 JavaScript、CSS、图片等资源视为模块,提供模块化打包功能。
- 代码分割:Webpack 允许将代码分割成多个文件,提高加载速度。
- 自动化构建:Webpack 可以自动化处理文件压缩、优化、合并等任务。
- 加载器(Loaders):Webpack 使用加载器来
处理不同类型的文件(如 Sass、TypeScript 等)。
- 插件(Plugins):Webpack 提供插件机制来进行额外的任务,比如压缩代码、提取公共模块等。
7. 如何提高 webpack 的打包速度?
提高 Webpack 打包速度的几种方法:
- 使用
cache
配置:启用缓存,减少重复打包的时间。 - 使用多线程:使用
thread-loader
或parallel-webpack
并行处理构建任务。 - 合理配置
exclude
:避免对不必要的文件进行编译,比如 node_modules。 - 按需加载:使用代码分割(
splitChunks
)来减小打包体积。 - 优化插件和加载器配置:避免不必要的插件和加载器,尤其是对大型文件的处理。
- 升级 Webpack 版本:Webpack 5 相比 4 版本优化了构建性能。
8. 什么是 webpack 的热更新? 它的实现原理是什么?
热模块替换(HMR):Webpack 的热更新功能,即热模块替换,是一种在应用运行时动态替换模块而不需要重新加载页面的技术。
原理:
- 在开发模式下,Webpack 启动开发服务器(如
webpack-dev-server
)。 - Webpack 监控文件的变化,当文件发生变化时,Webpack 会将更新的模块推送到浏览器。
- 浏览器通过 WebSocket 连接接收到模块的更新,并动态替换页面中的模块,而不重新加载整个页面。
- 这样可以保持应用状态,同时加快开发过程中的反馈速度。
9. 什么是前后端分离? 它有什么优点和缺点?
前后端分离是指前端和后端开发通过 API 进行交互,前端和后端可以独立开发和部署。
优点:
- 开发效率高:前后端可以并行开发,不受对方开发进度影响。
- 技术栈灵活:前后端可以使用不同的技术栈,前端可以使用 React、Vue 等,后端可以使用 Node.js、Java、Python 等。
- 可扩展性强:前后端可以独立扩展和维护,容易支持多端应用(Web、Mobile)。
缺点:
- 跨域问题:前后端分离需要解决跨域请求问题,通常通过 CORS 或代理服务器来解决。
- 接口管理复杂:前后端需要共同约定接口规范,避免出现数据不一致或接口不兼容问题。
10. 你用过哪些包管理工具? 它们各有什么特点?
常见包管理工具:
-
npm(Node Package Manager)
- 最流行的 Node.js 包管理工具。
- 官方默认的包管理工具,拥有庞大的包生态。
- 支持版本控制和依赖管理。
-
Yarn
- Facebook 开发的包管理工具,兼容 npm。
- 提供更快速的安装速度和更稳定的版本锁定。
- 支持并行安装和离线缓存。
-
pnpm
- 一个高效的包管理器,使用硬链接技术来共享依赖,提高磁盘利用率。
- 相比 npm 和 Yarn,安装速度更快,减少磁盘空间占用。
-
Bower(已不再推荐使用)
- 前端包管理工具,主要用于管理前端依赖。
- 已被废弃,推荐使用 npm 或 Yarn 代替。
希望这些答案能帮助你更好地理解和解决相关问题。如果有其他问题,欢迎继续提问!
1. 什么是 CSS 工程化? 你用过哪些相关的工具?
CSS 工程化是指通过一些工具、方法和流程规范化 CSS 的开发与管理,提升开发效率、代码可维护性以及性能优化。它关注的主要问题包括代码的组织、模块化、自动化处理等。
常见的 CSS 工程化工具和方法包括:
- CSS 预处理器:如 Sass、LESS 和 Stylus,它们提供了变量、嵌套、函数、混入等功能,使得 CSS 更加灵活和模块化。
- CSS 构建工具:如 PostCSS,用于自动化处理任务,如自动加前缀(通过 autoprefixer)、压缩 CSS、合并 CSS 等。
- CSS 模块化:使用如 CSS Modules 或 Styled-components(在 React 中)来实现样式的局部作用域,避免样式冲突。
- BEM(块元素修饰符)方法论:通过规范化 CSS 类名来提高代码可读性和可维护性。
- CSS in JS:将 CSS 写入 JavaScript 中,动态生成样式,常见的库有 styled-components(React)和 Emotion。
- 工具链:使用 Webpack、Gulp、Grunt 等构建工具来自动化处理 CSS 任务,如样式压缩、合并、图片优化等。
2. 说说常规的前端性能优化手段
常规的前端性能优化手段包括:
-
资源压缩与合并:
- 使用 Webpack、Gulp 等工具压缩 JS、CSS 和图片文件。
- 合并多个 JS 或 CSS 文件,减少 HTTP 请求。
-
代码分割和懒加载:
- 对 JavaScript 和 CSS 进行代码分割,按需加载,减少首屏加载时间。
- 使用 React Lazy、Vue Async Component 等实现懒加载。
-
缓存控制:
- 设置 HTTP 缓存头(
Cache-Control
、ETag
)来缓存资源,避免重复请求。 - 使用 Service Worker 实现离线缓存,进一步提高性能。
- 设置 HTTP 缓存头(
-
图片优化:
- 压缩图片,使用合适的图片格式(如 WebP)和尺寸。
- 使用 srcset 和 picture 标签实现响应式图片加载。
-
减少 DOM 操作和重绘回流:
- 减少 DOM 操作次数,批量修改 DOM 节点。
- 避免不必要的 CSS 动画,减少页面的回流和重绘。
-
延迟加载和异步加载:
- 使用
defer
和async
属性异步加载 JS 文件,避免阻塞渲染。 - 实现 图片懒加载,延迟加载页面中的图片和媒体资源。
- 使用
-
减少第三方库和插件的使用:
- 精简不必要的第三方库或插件,避免引入过多的代码和依赖。
3. 前端性能优化指标有哪些? 怎么进行性能检测?
常见的前端性能优化指标包括:
-
首屏渲染时间(First Paint/FCP):
- 页面从开始加载到呈现出第一个内容的时间。
-
可交互时间(Time to Interactive, TTI):
- 页面完全加载并且响应用户输入的时间。
-
页面加载时间(Page Load Time):
- 页面完全加载并且呈现完毕的时间。
-
资源加载时间:
- 单个资源(如 JS、CSS、图片等)的加载时间。
-
内存使用情况:
- 页面运行时的内存消耗,过高的内存使用会导致性能下降。
-
回流和重绘次数:
- 页面布局变化和重新渲染的次数,频繁的回流和重绘会降低页面性能。
性能检测工具:
- Chrome DevTools:提供了多种性能监控和优化工具,包括网络请求监控、页面渲染过程分析、内存分析等。
- Lighthouse:Google 提供的自动化网站性能评估工具,可以分析网页的性能、可访问性、SEO 等。
- WebPageTest:可以测试网页加载速度,提供详细的性能报告。
- GTmetrix:提供网站性能检测,包括资源加载时间、页面速度等分析。
- Bundle Analyzer:Webpack 插件,用于分析项目打包后的资源体积,找出可能优化的部分。
4. DNS 预解析是什么? 怎么实现?
DNS 预解析是通过提前解析域名来加速页面加载过程的技术。在用户访问页面时,浏览器可以通过提前查询外部资源(如图片、字体、JS 文件等)域名的 IP 地址,减少请求资源时的 DNS 查找延迟。
实现方法:
-
在 HTML 中使用
<link rel="dns-prefetch" href="https://example.com">
来提前解析域名。<link rel="dns-prefetch" href="https://fonts.gstatic.com"> <link rel="dns-prefetch" href="https://example.com">
-
这种技术适用于访问多个外部资源的场景,可以显著提高加载速度。
5. 怎么进行站点内的图片性能优化?
站点内的图片性能优化可以通过以下几种方式实现:
-
选择合适的图片格式:
- 使用 WebP 格式来替代 JPG 或 PNG,以达到更高的压缩率和更小的文件大小。
- 对于简单的图标,使用 SVG 格式,它是一种矢量图形格式,支持无损缩放,文件大小小。
-
图片压缩:
- 使用工具如 ImageOptim、TinyPNG、Squoosh 等压缩图片文件大小。
-
响应式图片:
- 使用
srcset
属性和<picture>
元素来根据设备屏幕大小、分辨率加载合适的图片。
<img src="image.jpg" srcset="image-320w.jpg 320w, image-480w.jpg 480w, image-800w.jpg 800w" alt="example">
- 使用
-
图片懒加载:
- 使用
loading="lazy"
属性来延迟加载页面中的图片,尤其是那些位于屏幕外的图片。
<img src="image.jpg" loading="lazy" alt="example">
- 使用
-
图片裁剪和缩放:
- 在上传图片时,避免上传过大的图片。通过前端或服务器端裁剪和缩放图片,确保它们的尺寸适合展示。
6. SPA(单页应用)首屏加载速度慢怎么解决?
SPA 首屏加载速度慢的问题可以通过以下几种方式优化:
-
代码分割:
- 使用 Webpack 的代码分割(
splitChunks
)功能,将应用分成多个小块,按需加载,减少首屏加载时的 JS 文件体积。 - 动态导入(
import()
)来实现懒加载,只有用户访问到某个路由时才加载相应的代码。
- 使用 Webpack 的代码分割(
-
懒加载路由组件:
- 对每个路由的组件进行懒加载,使用 Vue 或 React 中的路由懒加载特性,按需加载组件。
const Home = () => import('./views/Home.vue');
-
服务端渲染(SSR):
- 使用 Nuxt.js(Vue)或 Next.js(React)等框架实现服务端渲染,提前将 HTML 渲染到服务器,减少首屏加载时间。
-
减少阻塞渲染的资源:
- 异步加载 JS 文件,使用
async
或defer
属性避免阻塞 HTML 渲染。 - 尽量将 CSS 和 JS 文件分离,减少 CSS 阻塞的情况。
- 异步加载 JS 文件,使用
7. git stash 命令有什么作用? 什么时候适合用它?
git stash
用于临时保存当前的工作进度(包括修改的文件和暂存区的内容),以便稍后恢复。
常见用途:
- 在切换分支之前,如果当前工作未完成,可以使用
git stash
保存当前的修改状态。 - 临时保存修改,以便进行紧急的 bug 修复。
示例:
git stash # 临时保存工作区的修改
git stash apply # 恢复保存的修改
git stash pop # 恢复并删除保存的修改
8. git pull 和 git fetch 命令分别有什么作用? 二者有什么区别?
git fetch
:从远程仓库获取最新的
更改(如新的提交、分支等),但不会自动合并到当前分支。
git pull
:相当于git fetch
+git merge
,从远程仓库获取更改并自动合并到当前分支。
区别:
git fetch
:不会自动合并,安全性较高。git pull
:会自动合并,可能导致合并冲突。
9. 什么是低代码? 你用过哪些低代码工具?
低代码是通过图形化界面、拖拽组件和配置表单等方式,简化开发过程的技术,使得非开发人员也能快速创建和部署应用。
常见的低代码工具:
- OutSystems:支持企业级应用的开发,提供强大的数据集成和部署能力。
- Mendix:提供可视化设计器、预构建模板和流程自动化等功能。
- Bubble:适用于构建 Web 应用,支持拖拽式开发。
- Appgyver:支持创建复杂的应用,具备丰富的组件和插件。
10. 什么是前端跨平台? 你用过哪些跨平台框架?
前端跨平台是指通过一套代码实现多个平台(如 Web、iOS、Android)上的应用。
常见的跨平台框架:
- React Native:基于 React 开发移动端应用,可以同时发布到 iOS 和 Android。
- Flutter:Google 提供的 UI 框架,支持跨平台开发,适用于 Web、移动端、桌面端等。
- Electron:用于开发跨平台桌面应用的框架,支持 Windows、macOS 和 Linux。
- Ionic:基于 Angular 和 Web 技术的跨平台开发框架,主要用于构建移动端应用。
11. 如何实现 PC 打开是 Web 应用,手机打开是一个 H5 应用?
你可以通过以下方式实现:
-
响应式设计:使用媒体查询(
@media
)根据设备屏幕大小来调整布局。@media (max-width: 768px) { /* 手机端样式 */ }
-
User-Agent 检测:通过 JavaScript 检测用户的设备类型,判断是否为移动设备,从而决定加载不同的应用。
const isMobile = /Mobi|Android/i.test(navigator.userAgent); if (isMobile) { window.location.href = 'https://m.example.com'; // 手机端跳转到 H5 应用 }
-
跳转 URL:可以通过后台判断用户的设备类型,返回不同的页面或静态文件。