CSS 常见问题解答
1. CSS盒模型
CSS盒模型由内容区域(content)、内边距(padding)、边框(border)和外边距(margin)组成,分为两种类型:
- 标准盒模型 :元素宽度 =
content
宽度 - 替代盒模型 (通过
box-sizing: border-box
设置):元素宽度 =content + padding + border
2. CSS选择器的优先级
优先级从高到低:
!important
(覆盖所有规则)- 内联样式(如
<div style="color:red">
) - ID 选择器(如
#header
) - 类/伪类/属性选择器(如
.btn
,:hover
,[type="text"]
) - 元素/伪元素选择器(如
div
,::before
) - 通配符/继承样式(如
*
)
计算规则 :(a, b, c)
形式,其中:
a
= ID 选择器数量b
= 类/伪类选择器数量c
= 元素选择器数量
例如:#nav .item
优先级为(1,1,0)
3. 隐藏元素的方法
方法 | 特点 |
---|---|
display: none |
元素不占空间,无法交互,DOM 中移除渲染 |
visibility: hidden |
元素占空间,不可见但保留布局,无法交互 |
opacity: 0 |
元素占空间,完全透明,可交互(如点击事件) |
position: absolute; left: -9999px |
移出视口,可访问性差,但保留交互性 |
4. px 和 rem 的区别
- px:绝对单位,1px 对应屏幕物理像素点,固定大小不受父元素影响。
- rem :相对单位,基于根元素(
<html>
)的字体大小(默认1rem = 16px
)。
优势:响应式设计中,通过修改根字体大小可全局调整布局(如html { font-size: 62.5%; }
使1rem ≈ 10px
)。
5. 重绘和重排的区别
- 重排(Reflow) :
布局变化(如修改宽度、位置、字体大小),浏览器重新计算元素几何属性,触发整个渲染树更新。
常见触发操作 :width
,height
,margin
,display
,offsetTop
等。 - 重绘(Repaint) :
外观变化但不影响布局(如修改颜色、背景),浏览器仅重绘受影响区域。
性能影响 :重排一定触发重绘,重绘开销较小。优化建议:减少 DOM 操作,使用transform
代替位置修改。
6. 元素水平垂直居中
Flexbox 方案(推荐):
.parent {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
Grid 方案:
.parent {
display: grid;
place-items: center; /* 同时居中 */
}
绝对定位方案:
.parent { position: relative; }
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 兼容不同尺寸元素 */
}
7. CSS 属性继承性
- 可继承属性 :
文本相关(如font-family
,color
,line-height
,text-align
)和部分列表属性(如list-style
)。 - 不可继承属性 :
布局相关(如width
,height
,margin
,padding
,border
)和显示属性(如display
,background
)。
强制继承 :对不可继承属性使用inherit
值(如border: inherit
)。
8. CSS 预处理器使用
预处理器(如 Sass, Less)通过变量、嵌套、混合宏(mixin)等功能提升开发效率:
// Sass 示例
$primary-color: #3498db;
@mixin center-flex {
display: flex;
justify-content: center;
align-items: center;
}
.container {
@include center-flex;
background: lighten($primary-color, 20%);
}
优势:代码复用、模块化、函数运算(如颜色处理),最终编译为标准 CSS。
js常见问题
1. JavaScript 有哪三部分组成?
JavaScript 主要由以下三部分组成:
- ECMAScript :定义了语言的核心语法、数据类型、语句和基本对象(如
Array
,Object
)。 - DOM (Document Object Model):用于操作HTML和XML文档的接口,允许JavaScript访问和修改页面内容。
- BOM (Browser Object Model) :提供与浏览器交互的接口,包括
window
、location
、navigator
等对象,用于控制浏览器窗口、历史记录等。
2. JavaScript 有哪些内置对象?
JavaScript 的内置对象包括:
- 基本对象:
Object
、Function
、Array
、String
、Number
、Boolean
、Symbol
(ES6 新增)。 - 其他对象:
Date
(日期处理)、Math
(数学运算)、RegExp
(正则表达式)、JSON
(JSON 解析)、Error
(错误处理),以及全局对象如window
(浏览器环境)或global
(Node.js 环境)。
3. 操作数据的方法有哪些?
操作数据的方法主要包括:
-
数组方法:如
push()
、pop()
、shift()
、unshift()
、slice()
、splice()
、map()
、filter()
、reduce()
。 -
字符串方法:如
charAt()
、substring()
、replace()
、split()
、toUpperCase()
。 -
对象方法:如
Object.keys()
、Object.values()
、Object.assign()
(ES6)。 -
其他:使用循环(如
for
、forEach
)或高阶函数处理数据。 示例代码:const arr = [1, 2, 3];
arr.push(4); // 添加元素
console.log(arr); // 输出 [1, 2, 3, 4]
4. JavaScript 对数据类型的检测方式有哪些?
检测数据类型的方式包括:
typeof
操作符:返回基本类型的字符串表示,如typeof "hello"
返回"string"
,但对null
返回"object"
(历史遗留问题)。instanceof
操作符:检测对象是否属于某个构造函数,如[] instanceof Array
返回true
。Object.prototype.toString.call()
:最准确的方式,返回[object Type]
格式,如Object.prototype.toString.call([])
返回"[object Array]"
。Array.isArray()
:专门检测数组类型,ES6 新增。
5. 闭包是什么,有什么特点?
闭包(Closure)是指一个函数能访问并记住其外部作用域变量的能力。特点包括:
-
封装性:可以创建私有变量,避免全局污染。
-
内存驻留:外部变量不会被垃圾回收,可能导致内存泄漏。
-
延迟执行:常用于回调函数、模块化设计。 示例:
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
console.log(counter()); // 输出 1
6. 前端内存泄漏怎么理解?
前端内存泄漏指浏览器中不再使用的内存没有被释放,导致应用变慢或崩溃。常见原因:
- 未清理的全局变量或事件监听器。
- 闭包引用外部变量。
- DOM 元素引用未被删除(如移除元素后仍有变量引用它)。 解决方法:使用工具(如 Chrome DevTools Memory 面板)检测,手动解除引用、移除事件监听器。
7. 事件委托是什么?
事件委托是一种优化事件处理的技术,利用事件冒泡将事件监听器绑定到父元素,而非每个子元素。优点:
-
减少内存占用(只需一个监听器)。
-
动态元素无需重新绑定。 示例:点击列表项时输出内容。
document.getElementById('parent').addEventListener('click', (e) => {
if (e.target.tagName === 'LI') {
console.log(e.target.textContent);
}
});
8. 基础数据类型和引用数据的区别?
-
基础数据类型 :包括
String
、Number
、Boolean
、null
、undefined
、Symbol
(ES6)。值直接存储在栈内存中,赋值时复制值。 -
引用数据类型 :包括
Object
、Array
、Function
。值存储在堆内存中,变量存储引用地址;赋值时复制地址,多个变量可能共享同一数据。 示例:let a = 1; // 基础类型,a 存储值
let b = a; // b 复制值,a 和 b 独立
a = 2;
console.log(b); // 输出 1let obj1 = { key: 'value' }; // 引用类型
let obj2 = obj1; // obj2 复制引用地址
obj1.key = 'new';
console.log(obj2.key); // 输出 'new',共享数据
9. 原型链是什么?
原型链是 JavaScript 实现继承的机制。每个对象都有一个原型对象(通过 __proto__
访问),原型对象也有原型,形成链式结构。当访问对象属性时,如果自身不存在,会沿原型链向上查找,直到 Object.prototype
(原型链顶端)。构造函数通过 prototype
属性设置原型。 示例:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
const person = new Person('Alice');
person.sayHello(); // 输出 "Hello, Alice",方法来自原型
10. new 操作符具体做了什么?
new
操作符创建对象实例时执行以下步骤:
-
创建一个新空对象。
-
将新对象的原型链接到构造函数的
prototype
属性。 -
执行构造函数,将
this
绑定到新对象。 -
如果构造函数返回非对象值,则返回新对象;否则返回该值。 示例:
function Car(model) {
this.model = model;
}
const myCar = new Car('Toyota');
console.log(myCar.model); // 输出 "Toyota"
11. JavaScript 是如何实现继承的?
JavaScript 主要通过原型链实现继承。常见方式:
-
原型链继承:子类原型指向父类实例。
-
构造函数继承:在子类构造函数中调用父类构造函数。
-
组合继承:结合原型链和构造函数。
-
ES6 类继承 :使用
class
和extends
关键字(语法糖,底层基于原型)。 示例(ES6):class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a sound.');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + ' barks.');
}
}
const dog = new Dog('Rex');
dog.speak(); // 输出 "Rex barks."
12. JavaScript 的设计原理是什么?
JavaScript 的设计原理基于:
- 单线程事件循环:处理异步操作,避免阻塞。
- 原型继承:替代传统类继承,更灵活。
- 动态类型:变量类型在运行时确定。
- 函数式编程支持:函数是一等公民,支持高阶函数。
- ECMAScript 规范:由 ECMA International 标准化,确保跨平台兼容。
13. JavaScript 中关于 this 指向的问题?
this
指向当前执行上下文的对象,规则包括:
-
全局环境:
this
指向window
(浏览器)或global
(Node.js)。 -
函数调用:默认指向全局对象,但在严格模式(
'use strict'
)下为undefined
。 -
方法调用:指向调用该方法的对象。
-
构造函数:指向新创建的对象。
-
显式绑定:使用
call()
、apply()
或bind()
改变this
。 示例:const obj = {
value: 'obj',
logValue() {
console.log(this.value);
}
};
obj.logValue(); // 输出 "obj",this 指向 obj
const log = obj.logValue;
log(); // 输出 undefined(严格模式下报错),this 指向全局
14. script 标签的 async 和 defer 有什么区别?
- async:脚本异步加载,加载完成后立即执行,不保证顺序。适用于独立脚本。
- defer :脚本异步加载,但延迟到文档解析完成后执行,保持顺序。适用于依赖 DOM 的脚本。 区别:
async
可能阻塞渲染,执行顺序不确定;defer
不阻塞,执行顺序确定。
15. setTimeout 最小执行时间是多少?
setTimeout
的最小执行时间取决于浏览器和系统,通常为 4ms(根据 HTML5 规范)。但实际可能因浏览器优化或标签页后台运行而延长,不能保证精确。
16. ES5 和 ES6 有什么区别?
ES5(ECMAScript 5)和 ES6(ECMAScript 2015)的主要区别:
- 语法增强 :ES6 引入
let
、const
(块级作用域)、箭头函数、模板字符串、解构赋值。 - 新特性 :ES6 添加类(
class
)、模块(import
/export
)、Promise、Symbol 等。 - 功能扩展:ES6 提供原生模块化、迭代器、生成器,而 ES5 依赖库实现类似功能。 ES6 是重大更新,提升了代码可读性和开发效率。
17. ES6 的新特性有哪些?
ES6 关键新特性包括:
- 块级作用域变量:
let
和const
。 - 箭头函数:
() => {}
,简化语法,自动绑定this
。 - 模板字符串:
${variable}
支持多行字符串。 - 解构赋值:如
const {a, b} = obj
。 - 类与继承:
class
、extends
、super
。 - 模块化:
import
和export
。 - Promise:处理异步操作。
- 新数据类型:
Symbol
(唯一值)、Map
、Set
。 - 默认参数和剩余参数:
function(a = 1, ...args)
。
18. call、apply、bind 三者的区别?
三者都用于改变函数 this
指向:
-
call :立即调用函数,参数逐个传递。如
func.call(thisArg, arg1, arg2)
。 -
apply :立即调用函数,参数以数组传递。如
func.apply(thisArg, [arg1, arg2])
。 -
bind :返回一个新函数,绑定
this
和部分参数,但不立即执行。如const newFunc = func.bind(thisArg, arg1)
。 示例:function greet(greeting) {
console.log(greeting + ', ' + this.name);
}
const person = { name: 'Alice' };
greet.call(person, 'Hello'); // 输出 "Hello, Alice"
greet.apply(person, ['Hi']); // 输出 "Hi, Alice"
const boundGreet = greet.bind(person, 'Hey');
boundGreet(); // 输出 "Hey, Alice"
19. 用递归的时候有没有遇到什么问题?
使用递归时常见问题:
- 栈溢出:递归深度过大导致调用栈溢出(如未设置基线条件)。
- 性能问题:递归可能比迭代慢,尤其在多次调用时。
- 内存消耗:每个递归调用占用栈空间。 解决方法:设置递归终止条件、改用循环(迭代)、使用尾递归优化(ES6 支持,但浏览器实现有限)。
20. 如何实现一个深拷贝?
深拷贝创建完全独立的副本,包括嵌套对象。实现方式:
-
JSON 方法 :简单但无法处理函数、Symbol 或循环引用。
const obj = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(obj));
-
递归函数 :手动处理所有类型。
function deepClone(source) { if (source === null || typeof source !== 'object') return source; const target = Array.isArray(source) ? [] : {}; for (let key in source) { if (source.hasOwnProperty(key)) { target[key] = deepClone(source[key]); } } return target; }
-
库函数 :如 Lodash 的
_.cloneDeep()
。
21. 事件循环是什么?
事件循环(Event Loop)是 JavaScript 处理异步操作的机制。基于单线程,它管理调用栈、任务队列(宏任务和微任务)。工作流程:
- 同步代码执行。
- 异步任务(如
setTimeout
)完成后,回调进入任务队列。 - 事件循环检查调用栈为空时,从队列中取出任务执行。 宏任务包括
setTimeout
、setInterval
;微任务包括Promise.then
、MutationObserver
。微任务优先于宏任务执行。
22. Ajax 是什么,怎么实现?
Ajax(Asynchronous JavaScript and XML)是一种异步 Web 开发技术,用于在后台与服务器交换数据,无需刷新页面。实现方式:
-
使用
XMLHttpRequest
对象。 -
现代方式:
fetch
API(ES6)。 示例(使用fetch
):fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
23. GET 和 POST 有什么区别?
- GET:请求数据,参数在 URL 中可见(长度受限),可缓存,幂等(多次请求结果相同)。
- POST:提交数据,参数在请求体中(不可见,长度大),不可缓存,非幂等(可能改变服务器状态)。 用途:GET 用于获取数据(如搜索),POST 用于发送数据(如表单提交)。
24. Promise 的内部原理是什么,它的优缺点是什么?
- 内部原理 :Promise 是异步编程解决方案,基于状态机(状态:pending、fulfilled、rejected)。通过
then
方法添加回调,支持链式调用。 - 优点 :避免回调地狱、支持错误传播(
catch
)、易于组合(Promise.all
)。 - 缺点:无法取消、错误处理需显式捕获、可能导致微任务队列膨胀。
25. Promise 和 async/await 的区别是什么?
-
Promise :基于回调链,使用
then()
和catch()
处理异步。 -
async/await :ES8 语法糖,基于 Promise,用同步方式写异步代码。
async
函数返回 Promise,await
暂停执行直到 Promise 解决。 区别:async/await
更简洁、可读性强,但需在函数内使用;Promise 更底层,适合简单链式调用。 示例:// Promise
fetchData().then(data => console.log(data)).catch(err => console.error(err));// async/await
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (err) {
console.error(err);
}
}
26. 浏览器的存储方式有哪些?
- Cookie:小数据(约4KB),随请求发送到服务器,可设置过期时间。
- Web Storage :
localStorage
:永久存储,同源可用。sessionStorage
:会话级存储,标签页关闭后清除。
- IndexedDB:非关系型数据库,支持大容量和复杂查询。
- Cache Storage:用于 Service Workers 缓存资源。
27. token 存在 sessionStorage 还是 localStorage?
token 通常存储在 localStorage
中,因为它持久化,适合长期保存登录状态。但 sessionStorage
更安全(会话结束清除),适用于敏感数据。选择取决于需求:localStorage
用于"记住我"功能,sessionStorage
用于临时会话。
28. token 的登录流程是什么?
典型 token 登录流程:
- 用户输入凭据(用户名/密码)。
- 客户端发送请求到服务器验证。
- 服务器验证通过后,生成 token(如 JWT)并返回。
- 客户端存储 token(如
localStorage
)。 - 后续请求在 HTTP 头(如
Authorization: Bearer <token>
)中包含 token。 - 服务器验证 token 并响应数据。
29. 页面渲染的过程是怎么样的?
页面渲染过程:
- 解析 HTML:构建 DOM 树。
- 解析 CSS:构建 CSSOM 树。
- 合并成渲染树:结合 DOM 和 CSSOM,生成渲染树(只包含可见元素)。
- 布局(Layout):计算元素位置和大小。
- 绘制(Paint):将元素渲染到屏幕。
- 合成(Composite):处理图层合并(如 CSS 动画)。 优化:减少重排(布局改变)和重绘(外观改变)。
30. DOM 树和渲染树有什么区别?
- DOM 树:由 HTML 解析生成,表示文档结构,包括所有节点(如隐藏元素)。
- 渲染树 :由 DOM 树和 CSSOM 树合并生成,只包含可见元素(如排除
display: none
),用于布局和绘制。 区别:渲染树是 DOM 树的子集,仅用于视觉渲染。
31. 精灵图和 base64 的区别是什么?
- 精灵图(Sprite) :多个小图标合并成一张大图,通过 CSS
background-position
显示部分。减少 HTTP 请求,但需手动管理位置。 - Base64:将图像编码为文本字符串,直接嵌入 CSS 或 HTML。减少 HTTP 请求,但增加文件大小(约30%),不适合大图。 用途:精灵图用于小图标;Base64 用于小图标或内联图像。
32. SVG 格式了解多少?
SVG(Scalable Vector Graphics)是一种矢量图像格式,基于 XML。特点:
- 缩放不失真,适合图标和图形。
- 可直接嵌入 HTML,支持 CSS 和 JavaScript 操作。
- 文件小,支持动画(如 SMIL)和交互。 示例:
<svg width="100" height="100"><circle cx="50" cy="50" r="40" fill="red"/></svg>
。
33. 了解 JWT 吗?
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于安全传输信息作为 JSON 对象。结构:
- Header:算法和类型。
- Payload:数据(如用户 ID)。
- Signature:签名验证完整性。 优点:无状态、可跨域;缺点:token 大小较大、无法直接废止。用于身份验证和授权。
34. npm 的底层环境是什么?
npm(Node Package Manager)的底层环境是 Node.js 运行时。它基于 JavaScript,提供包管理和脚本执行功能。核心组件包括:
- 注册表(Registry):存储包。
- CLI 工具:安装、发布包。
- 依赖解析:处理
package.json
文件。
35. HTTP 协议规定的请求头和响应头有什么?
- 请求头(Request Headers) :客户端发送,如:
Host
:服务器域名。User-Agent
:客户端信息。Accept
:可接受的响应类型。Authorization
:认证信息。Content-Type
:请求体类型(如application/json
)。
- 响应头(Response Headers) :服务器发送,如:
Content-Type
:响应体类型。Cache-Control
:缓存策略。Set-Cookie
:设置 Cookie。Status
:HTTP 状态码。
36. 浏览器的缓存策略是什么?
缓存策略控制资源存储和重用:
- 强缓存 :通过
Cache-Control
或Expires
头,直接从本地缓存读取,不请求服务器。 - 协商缓存 :通过
Last-Modified
/If-Modified-Since
或ETag
/If-None-Match
头,向服务器验证资源是否过期。 设置:服务器配置 HTTP 头,如Cache-Control: max-age=3600
。
37. 防抖和节流是什么?
-
防抖(Debounce) :事件触发后延迟执行,若在延迟内再次触发,则重新计时。用于输入框搜索(避免频繁请求)。
function debounce(func, delay) { let timer; return function() { clearTimeout(timer); timer = setTimeout(() => func.apply(this, arguments), delay); }; }
-
节流(Throttle) :事件触发后,在指定时间内只执行一次。用于滚动事件(控制频率)。
function throttle(func, limit) { let inThrottle; return function() { if (!inThrottle) { func.apply(this, arguments); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; }
38. 什么是同源策略?
同源策略(Same-Origin Policy)是浏览器安全机制,限制不同源(协议、域名、端口相同)的脚本交互。防止恶意脚本窃取数据。跨源访问需 CORS(跨域资源共享)或 JSONP 解决。
39. 什么是 JSON?
JSON(JavaScript Object Notation)是一种轻量级数据交换格式,基于文本,易于读写。语法类似 JavaScript 对象,但独立于语言。用于前后端数据传输。 示例:{"name": "Alice", "age": 30}
。
40. 有没有做过无感登录?
无感登录(自动登录)实现方式:
- 用户首次登录后,服务器返回 token 或 refresh token。
- 客户端存储 token(如
localStorage
)。 - 后续访问时,自动发送 token 验证。
- 加入 token 过期机制和刷新逻辑(如用 refresh token 获取新 token)。 关键点:安全处理 token 存储,避免 XSS 攻击。
41. 大文件上传是怎么做的?
大文件上传常用方法:
-
分片上传:将文件切分成小块,分别上传,服务器合并。
-
断点续传:记录上传进度,失败后从中断处继续。
-
使用 Web Workers:后台处理分片,避免阻塞。
-
库支持 :如
axios
或专门库(如resumable.js
)。 示例代码(分片上传):async function uploadFile(file) {
const chunkSize = 5 * 1024 * 1024; // 5MB
for (let start = 0; start < file.size; start += chunkSize) {
const chunk = file.slice(start, start + chunkSize);
await fetch('/upload', { method: 'POST', body: chunk });
}
}
42. 当数据没有请求过来的时候,该怎么做?
处理数据未请求到的情况:
- 前端处理 :
- 显示加载状态(如 spinner)或占位内容(Skeleton Screen)。
- 设置超时机制(如
setTimeout
),超时后提示用户。 - 错误处理:使用
try/catch
或 Promise 的catch
块,显示友好错误消息。 - 重试逻辑:自动或手动重试请求。
- 优化策略 :
-
使用缓存(如 localStorage)显示旧数据。
-
实现离线模式(Service Workers)。
-
监控网络状态(如
navigator.onLine
)。 示例:fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) throw new Error('Network error');
return response.json();
})
.then(data => renderData(data))
.catch(error => {
console.error(error);
showError('数据加载失败,请重试');
});
-
HTML5与CSS3相关问题
1. 语义化的理解
语义化指通过选择合适的HTML标签表达内容结构含义(而非仅样式),例如:
- 使用
<header>
表示页眉,<nav>
表示导航,<article>
表示独立内容 - 优点:
- SEO优化:爬虫更易理解页面结构
- 可访问性:屏幕阅读器能准确定位内容
- 代码可维护性:开发者通过标签名即可理解区块功能
- 未来兼容性:浏览器能更智能解析新特性
2. HTML5与CSS3新特性
HTML5 核心新特性:
- 语义化标签 :
<section>
,<figure>
,<time>
等 - 多媒体支持 :
<audio>
,<video>
原生嵌入 - 图形处理 :
<canvas>
绘图和SVG
矢量图支持 - 本地存储 :
localStorage
/sessionStorage
- 表单增强 :
- 输入类型:
email
,date
,range
- 属性:
placeholder
,required
,pattern
- 输入类型:
CSS3 核心新特性:
- 选择器增强 :属性选择器
[type="text"]
、伪类:nth-child(n)
- 视觉效果 :
- 圆角:
border-radius: 10px
- 阴影:
box-shadow: 2px 2px 5px #ccc
- 渐变:
background: linear-gradient(red, yellow)
- 圆角:
- 动画与过渡 :
- 过渡:
transition: width 0.5s ease
- 动画:
@keyframes
+animation
- 过渡:
- 响应式布局 :
@media
媒体查询 - 弹性盒子 :
display: flex
3. rem适配原理
rem(root em)是相对于根元素<html>
字体尺寸的单位,适配流程:
/* 步骤1:设置基准值(设计稿750px宽时) */
html {
font-size: 100px; /* 1rem = 100px */
}
/* 步骤2:元素使用rem单位 */
.box {
width: 1.5rem; /* 实际宽度150px */
}
/* 步骤3:通过媒体查询动态调整根字体 */
@media (max-width: 480px) {
html {
font-size: 64px; /* 小屏缩小基准值 */
}
}
数学关系 :
若设计稿元素宽度w,基准字体b,则CSS中尺寸为\\frac{w}{b} rem。
屏幕宽度变化时,通过JS或媒体查询更新b值,实现等比缩放。
4. 解决的移动端兼容问题
-
1像素边框问题
现象 :高清屏物理像素比>1时,1px CSS边框变粗
方案:.thin-border { transform: scaleY(0.5); /* Y轴压缩50% */ }
-
点击延迟300ms
原因 :早期浏览器等待双击缩放判断
方案:<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
或使用
touch-action: manipulation;
-
默认样式差异
现象 :不同设备按钮/链接样式不一致
方案:/* 统一重置 */ * { margin: 0; padding: 0; box-sizing: border-box; /* 标准盒模型 */ }
-
REM适配兼容
方案 :使用flexible.js
动态计算根字体大小,覆盖Android 4.4以下版本兼容问题。
Vue.js 全面解析
一、Vue 基础概念
-
v-if 和 v-show 的区别
v-if
:条件渲染,元素在条件为真时渲染到DOM,为假时从DOM中移除。适用于切换频率低的场景,因为涉及DOM操作开销。v-show
:通过CSS的display
属性控制显示/隐藏(display: none
),元素始终在DOM中。适用于频繁切换的场景,性能更好。
-
如何理解 MVVM
MVVM(Model-View-ViewModel)是一种架构模式:
- Model:数据层(如API返回的数据)。
- View:UI层(如Vue模板)。
- ViewModel:Vue实例,负责数据绑定和事件处理,连接Model和View。自动同步数据变化,实现双向数据绑定。
-
v-for 中的 key 值作用是什么
key
属性用于标识列表项的唯一性,帮助Vue高效更新DOM:- 提供唯一标识(如ID),避免复用错误。
- 优化diff算法性能,减少不必要的DOM操作。
-
vue 生命周期
Vue组件从创建到销毁的全过程:
- 创建阶段 :
beforeCreate
(实例初始化)、created
(数据观测完成)。 - 挂载阶段 :
beforeMount
(模板编译)、mounted
(DOM渲染完成)。 - 更新阶段 :
beforeUpdate
(数据变化前)、updated
(DOM更新后)。 - 销毁阶段 :
beforeUnmount
(卸载前)、unmounted
(卸载完成)。
- 创建阶段 :
-
created 和 mounted 去请求数据,有什么区别
created
:在实例创建后调用,此时DOM未渲染。适合初始化数据(如API请求),但无法访问DOM元素。mounted
:在DOM渲染完成后调用。适合需要操作DOM的场景(如基于元素尺寸的请求),但请求可能延迟页面显示。
-
vue 中的修饰符有哪些
修饰符用于简化事件或表单处理:
- 事件修饰符 :如
.stop
(阻止冒泡)、.prevent
(阻止默认行为)、.once
(只触发一次)。 - 表单修饰符 :如
.lazy
(输入完成后更新)、.number
(输入转为数字)、.trim
(去除首尾空格)。
- 事件修饰符 :如
二、组件与表单
-
element ui 是怎么做表单验证
基于Vue的
v-model
和Element UI的el-form
组件:- 使用
rules
属性定义验证规则(如必填、长度)。 - 通过
validate
方法触发验证,错误时显示提示信息。
- 使用
-
vue 如何进行组件通信
常见方式:
- Props/Events :父组件通过
props
传数据给子组件,子组件通过$emit
触发事件。 - Provide/Inject :祖先组件
provide
数据,后代组件inject
获取。 - Vuex:全局状态管理。
- Event Bus :使用Vue实例作为中央事件总线(
$on
,$emit
)。 - Refs :父组件通过
ref
访问子组件实例。
- Props/Events :父组件通过
-
keep-alive 是什么,怎么使用
-
作用:缓存组件实例,避免重复渲染(如保留表单状态)。
-
使用 :包裹动态组件或路由组件,例如:
<keep-alive> <component :is="currentComponent"></component> </keep-alive>
-
-
axios 是怎么做封装的
封装以复用请求逻辑:
- 创建实例:
axios.create()
设置基础URL、超时时间。 - 拦截器:
interceptors
处理请求/响应(如添加token、错误处理)。 - 封装API模块:统一导出方法(如
getUser()
)。
- 创建实例:
三、路由管理
-
vue 路由怎么传参
方式:
- Params :
this.$router.push({ name: 'user', params: { id: 1 } })
,在路由中定义path: '/user/:id'
。 - Query :
this.$router.push({ path: '/user', query: { id: 1 } })
,参数在URL中(/user?id=1
)。
- Params :
-
vue 路由的 hash 模式和 history 模式的区别
- Hash 模式 :URL带
#
(如http://example.com/#/home
),基于浏览器hashchange事件,无需服务器配置。 - History 模式 :URL无
#
(如http://example.com/home
),基于HTML5 History API,需服务器支持(避免404错误)。
- Hash 模式 :URL带
-
路由拦截是怎么实现的
使用
router.beforeEach
全局前置守卫:router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !isAuthenticated) next('/login'); // 验证登录 else next(); // 放行 });
-
vue 的动态路由
基于参数匹配:
- 定义:
{ path: '/user/:id', component: User }
。 - 访问:
this.$route.params.id
获取参数。
- 定义:
-
如何解决刷新后二次加载路由
在Vue Router中,确保路由持久化:
- 使用
addRoute
动态添加路由。 - 结合Vuex存储路由状态,避免刷新丢失。
- 使用
四、状态管理(Vuex)
-
vuex 刷新数据会丢失吗,怎么解决
- 会丢失:Vuex状态存储在内存中,刷新页面时重置。
- 解决 :
- 使用
localStorage
或sessionStorage
持久化数据。 - 插件如
vuex-persistedstate
自动同步。
- 使用
-
computed 和 watch 的区别
- computed :计算属性,基于依赖数据缓存结果(如
fullName = firstName + lastName
),适合派生数据。 - watch:侦听器,观察数据变化执行回调(如API请求),适合异步操作。
- computed :计算属性,基于依赖数据缓存结果(如
-
vuex 在什么场景会去使用,属性有哪些
- 场景:跨组件共享状态(如用户登录信息、全局配置)。
- 属性 :
state
:存储数据。getters
:计算状态(类似computed)。mutations
:同步修改状态(通过commit
触发)。actions
:异步操作(通过dispatch
触发)。modules
:模块化管理。
-
vue 双向数据绑定原理
基于
Object.defineProperty
(Vue2)或Proxy
(Vue3):- Vue2:劫持数据属性的getter/setter,通知依赖更新视图。
- Vue3 :使用
Proxy
代理对象,直接监听属性变化,性能更好。
-
diff 算法和虚拟 DOM
- 虚拟DOM:轻量级JS对象表示真实DOM,减少直接操作。
- Diff算法 :比较新旧虚拟DOM树(如通过
key
优化),仅更新变化部分,提高渲染效率。
五、性能优化与高级特性
-
vue 和 jquery 的区别
- Vue:响应式框架,组件化开发,数据驱动视图。
- jQuery:库,操作DOM为主,无数据绑定机制。
-
vuex 的响应式处理
Vuex 的响应式处理依赖于 Vue.js 的响应式系统,通过将 store 状态包装为响应式对象,并结合 mutations 和 actions 来管理状态变化。这确保了状态与视图的自动同步,提升了开发效率。在实际项目中,遵循 Vuex 的规则(如使用 mutations 修改状态)和 Vue 的响应式最佳实践,可以避免常见问题。
-
如何搭建脚手架
使用Vue CLI:
npm install -g @vue/cli vue create my-project cd my-project npm run serve
-
如何封装一个组件
步骤:
- 定义props接收数据。
- 使用slots提供内容分发。
- 暴露事件(
$emit
)。 - 示例:封装按钮组件。
-
封装一个可复用的组件,需要满足什么条件
- 单一职责:只做一件事。
- 可配置:通过props控制行为。
- 可扩展:支持slots和事件。
- 文档化:提供使用说明。
-
vue 的过滤器怎么使用
用于文本格式化:
- 定义:
Vue.filter('capitalize', value => value.toUpperCase())
。 - 使用:
{``{ message | capitalize }}
。
- 定义:
-
vue 中如何做强制刷新
- 组件级:
this.$forceUpdate()
强制重新渲染。 - 全局:修改key属性触发更新(如
:key="reloadKey"
)。
- 组件级:
-
vue3 和 vue2 有哪些区别
- 性能:Vue3使用Proxy,优化响应式。
- API :Vue3引入Composition API(
setup()
)。 - 体积:Vue3更小(Tree-shaking支持)。
- 其他:Fragment(多根节点)、Teleport(传送组件)。
-
vue 的性能优化怎么做
- 代码层面:懒加载组件、v-if/v-show合理使用。
- 构建优化:代码分割、压缩资源。
- 运行时:避免深层响应式对象、使用v-once。
-
首屏优化该怎么做
- 懒加载:路由和组件按需加载。
- SSR:使用Nuxt.js服务端渲染。
- CDN:静态资源分发。
- 预渲染:生成静态HTML。
-
vue3 的性能为什么比 vue2 好
- 响应式优化:Proxy 比 Object.defineProperty 更高效。
- 编译优化:静态树提升、补丁标志减少diff开销。
- Tree-shaking:按需引入代码,减小体积。
-
vue3 为什么使用 proxy
- 直接代理整个对象,无需递归劫持属性。
- 支持数组和动态属性监听,解决Vue2的限制。
六、项目实践与扩展
-
说一下对组件的理解
组件是可复用的UI单元,封装HTML、CSS和JS:
- 提高代码复用性。
- 便于团队协作。
- 通过组合构建复杂应用。
-
如何规划项目文件
标准结构:
src/ ├── assets/ # 静态资源 ├── components/ # 通用组件 ├── views/ # 页面组件 ├── router/ # 路由配置 ├── store/ # Vuex 状态 ├── services/ # API 封装 └── App.vue # 根组件
-
是否使用过 nuxt.js
Nuxt.js 是基于Vue的服务端渲染框架:
- 支持SSR(首屏优化)。
- 简化路由和状态管理。
- 适合SEO密集型应用。
-
SEO 如何优化
- SSR/SSG:使用Nuxt.js服务端渲染或静态生成。
- Meta 标签 :动态设置
title
和description
(如vue-meta插件)。 - 语义化HTML:合理使用header、section等标签。
- Sitemap:生成XML站点地图。
ECharts常用组件介绍
ECharts是一个基于JavaScript的开源数据可视化库,广泛应用于Web开发中,用于创建交互式图表。其核心功能通过一系列组件实现,这些组件协同工作来构建完整的图表。以下是一些常用组件的详细说明,帮助您逐步理解和使用:
-
标题(title)
用于设置图表的标题,包括主标题和副标题。可以自定义位置、样式和链接。
示例配置:
title: { text: '销售数据统计', subtext: '2023年度报告' }
-
图例(legend)
显示不同数据系列的标识(如颜色标记),便于用户区分多个系列。支持交互操作,如点击隐藏/显示系列。
示例配置:
legend: { data: ['产品A', '产品B', '产品C'], orient: 'horizontal' }
-
坐标轴(axis)
包括x轴(xAxis)和y轴(yAxis),用于定义数据的坐标系。支持类型(如类目型、数值型)、刻度、标签格式等设置。
示例配置:
xAxis: { type: 'category', data: ['一月', '二月', '三月'] }, yAxis: { type: 'value' }
-
系列(series)
核心组件,定义图表类型(如折线图、柱状图、饼图)和数据源。每个系列对应一组数据点,并可配置样式和动画。
示例配置(柱状图):
series: [{ name: '销量', type: 'bar', data: [120, 200, 150] }]
-
提示框(tooltip)
当用户悬停在数据点上时,显示详细信息。支持格式化内容、触发方式(如鼠标悬停或点击)和自定义样式。
示例配置:
tooltip: { trigger: 'axis', formatter: '{b}: {c}' }
-
工具栏(toolbox)
提供实用工具,如保存图片、数据视图、数据缩放和重置。方便用户交互和导出图表。
示例配置:
toolbox: { feature: { saveAsImage: {}, dataView: {} } }
-
数据缩放(dataZoom)
允许用户缩放和漫游图表区域,适用于大数据集的可视化。支持滑动条或内置缩放控件。
示例配置:
dataZoom: [{ type: 'slider', start: 0, end: 100 }]
-
视觉映射(visualMap)
将数据值映射到视觉元素(如颜色、大小),常用于热力图或散点图。支持连续或分段映射。
示例配置:
visualMap: { min: 0, max: 100, calculable: true }
-
网格(grid)
定义图表的布局区域,包括位置、大小和边距。确保多个组件(如坐标轴)在画布上正确对齐。
示例配置:
grid: { left: '10%', right: '10%', containLabel: true }
-
其他辅助组件
- 标记点(markPoint):在系列中添加特殊点(如最大值、最小值)。
- 标记线(markLine):添加参考线(如平均值线)。
- 数据标签(label):在数据点上直接显示数值标签。
UniApp分包
在UniApp中,分包(Subpackage)是一种优化策略,用于将代码拆分成多个独立的包,以减少启动加载时间并管理代码体积(尤其在小程序平台如微信小程序)。以下是详细步骤和实现方法,确保结构清晰、易于理解。
1. 理解分包概念
- 目的:主包包含启动页面和核心资源,分包包含非关键页面(如二级页面),实现按需加载。
- 适用平台:主要针对小程序平台(微信、支付宝等),在H5或App端可能无效或不需配置。
- 限制:微信小程序要求主包不超过2MB,总包不超过8MB(具体以平台文档为准)。
2. 配置分包步骤
在UniApp项目中,通过修改配置文件实现分包。以下是核心步骤:
步骤1: 创建分包目录
-
在项目根目录下创建分包文件夹,例如
subpackageA
(名称自定义)。 -
将分包页面文件(如
.vue
文件)放入此目录,例如:project-root/ ├── pages/ # 主包页面 ├── subpackageA/ # 分包目录 │ ├── pages/ # 分包页面文件夹 │ │ ├── page1.vue │ │ └── page2.vue ├── pages.json # 配置文件 └── ...
步骤2: 修改pages.json
文件
-
在
pages.json
中添加subPackages
字段,定义分包信息。 -
关键字段 :
root
: 分包根目录路径(字符串)。pages
: 分包页面列表,每个页面包括path
和style
(类似主包页面配置)。
-
示例配置 :
{ "pages": [ { "path": "pages/index/index", "style": { ... } // 主包页面配置 } ], "subPackages": [ { "root": "subpackageA", // 分包根目录 "pages": [ { "path": "pages/page1", // 页面路径,相对于root "style": { ... } // 页面样式配置 }, { "path": "pages/page2", "style": { ... } } ] } ], // 其他全局配置... }
步骤3: 编译和测试
-
运行编译命令(如
npm run dev:mp-weixin
),检查控制台输出,确保分包成功。 -
在开发者工具中查看包大小:主包应较小,分包单独加载。
-
访问分包页面:在代码中使用路由跳转,例如:
uni.navigateTo({ url: '/subpackageA/pages/page1' // 路径格式:/分包根目录/页面路径 });
3. 完整代码示例
以下是一个简单的UniApp项目结构及pages.json
配置示例:
// pages.json
{
"pages": [
{
"path": "pages/home",
"style": {
"navigationBarTitleText": "首页"
}
}
],
"subPackages": [
{
"root": "userCenter",
"pages": [
{
"path": "profile",
"style": {
"navigationBarTitleText": "个人中心"
}
},
{
"path": "settings",
"style": { ... }
}
]
}
],
"globalStyle": { ... }
}
4. 注意事项
-
路径规则:分包页面路径必须从分包根目录开始,不能跨分包引用资源。
-
资源加载:分包内的图片、组件等需放在分包目录内,避免主包过大。
-
兼容性 :在非小程序平台(如H5),分包配置可能被忽略,建议使用条件编译:
// #ifdef MP-WEIXIN // 分包相关代码 // #endif
-
优化建议 :
- 将低频页面放入分包。
- 使用
uni.preloadSubpackage
预加载分包(微信小程序支持)。
-
调试工具:使用微信开发者工具的分包分析功能,检查包大小和依赖。
常见问题解决
- 分包未生效 :检查
pages.json
语法是否正确,路径是否匹配实际目录。 - 包大小超限:优化图片、移除未使用代码,或拆分更多分包。
- 路由错误 :确保跳转路径包含分包根目录(如
/userCenter/profile
)。