跳转到主要内容

面试题库

Web 网页面试题

Web 网页、组件通信、事件总线等,共 72 题。

  • 前端
  • 面试

Web 网页面试题

来源:resource/御码IT教育-Java+Python智能体-MySQL+前端+场景+人事.pdf

二、Web网页

1、HTML5

1、HTML5的新特性

HTML5 主要新增:语义化标签、音视频原生支持、Canvas、本地存储、增强表单、地理定位、Web Workers、离线应用,让 Web 更强大、语义更清晰、性能更好、体验接近原生 APP。

2、HTML4、HTML5、XML、XHTML它们之间有什么区别

XML:自定义标签、存数据、语法极严 XHTML:按 XML 规则写 HTML,太严现在不用 HTML4:旧版网页语言,功能少 HTML5:当前主流,功能强、语法友好、语义化

3、DOCTYPE(⽂档类型) 的作用

DOCTYPE(Document Type Declaration,文档类型声明)是 HTML 文档的 “身份标识”,其核心作用 是:

  1. 告知浏览器解析规则: 声明当前文档遵循的 HTML 或 XML 标准(如 HTML4、HTML5、XHTML),让浏览器以对应模式 渲染页面,避免因规则不明确导致的显示异常。
  2. 触发浏览器的渲染模式: 标准模式(Standard Mode):严格遵循 W3C 标准解析 CSS 和布局。 怪异模式(Quirks Mode):兼容旧浏览器的非标准解析方式(仅在 DOCTYPE 缺失或格式错误时 触发)

4、img标签的title属性与alt属性的区别是什么

2、CSS

1、CSS3新特性

CSS3 主要新增:更强选择器、圆角阴影、渐变、transform、transition、animation、flex/grid 布局、 自定义字体、响应式媒体查询,让页面更美观、交互更流畅、布局更简单。

2、解决父元素高度塌陷

父元素高度塌陷是因为子元素浮动,脱离文档流。 解决方法有四种:

  1. 父元素 overflow:hidden 触发 BFC;
  2. 末尾加空元素 clear:both;
  3. 使用伪元素 ::after 清除浮动(最优);
  4. 父元素浮动或设置 inline-block。

特性

块元素(block)

行内元素(inline)

独占一 行 ✅独占一行 ❌不独占,和其他行内元素同行 宽高设 置 ✅可设置 width、height ❌不能直接设置宽高 内外边 距 ✅上下左右 margin/padding 都生 效 ✅左右生效,上下不占空间 默认宽 度 父元素 100% 内容本身宽度 可包含 可包含块元素 & 行内元素 只能包含文本 / 行内元素,不能嵌套块元

素

常见标 签 div、p、h1~h6、ul、li、section span、a、img、input、button

3、伪类和伪元素的区别

伪类是单冒号,用于选中元素的状态;伪元素是双冒号,用于创建不存在的虚拟元素。最常见的伪类 是:hover,最常见的伪元素是::before 和::after。 伪类:用于选中元素的某个状态 :hover :active :focus :first-child :nth-child 伪元素:用于创建不存在的虚拟元素 ::before ::after ::first-line ::first-letter

4、什么是CSS盒模型

CSS 盒模型就是把元素看成矩形盒子,包含 content、padding、border、margin 四部分。 分为标准盒模型(content-box,width = 内容)和怪异盒模型(border-box,width = 内容 + 内边距 + 边框),实际开发中常用 border-box。

5、封装过哪些组件?

我主要封装过后台管理系统最常用的:通用表格、查询表单、弹窗、权限按钮、上传等组件。目的是减 少重复代码、统一交互风格、提高开发效率,让业务页面只关注业务逻辑。

6、块元素和行内元素有什么区别

块元素独占一行、可设宽高、默认父元素 100% 宽;行内元素不换行、不能直接设宽高、靠内容撑开。

7、CSS 预处理器 SASS/LESS 有何用途

Sass/Less 是 CSS 预处理器,通过变量、嵌套、混合、继承、运算等功能,CSS 预处理器就是给 CSS 加 了编程能力,让 CSS 更好维护、更高效、更易复用。 主要用途: 变量:把颜色、尺寸、字体等定义成变量,统一修改,方便换肤、统一风格。 嵌套写法:层级结构清晰,代码更直观,不用重复写选择器。 混合(Mixin):封装重复样式(如圆角、阴影、居中),直接调用,减少冗余。 继承(Extend):让多个选择器共用一套样式,减少 CSS 体积。 运算、函数、逻辑:支持加减乘除、颜色计算、循环、判断,实现动态样式。

8、rgba和opacity的透明效果有什么不同

opacity opacity是一个属性。opacity属性的值,可以被其子元素继承,给父级div设置opacity属性,那么 所有子元素都会继承这个属性,并且,该元素及其继承该属性的所有子元素的所有内容透明度都会 改变。 rgba(0,0,0,0.5) rgba是一个属性值。rgba设置的元素,只对该元素的背景色有改变,并且,该元素的后代不会继承 该属性。 补冲:rgba只是一个属性值,在background 里用改变背景色,在color里是改字体颜色,shadow 里是改阴影色,不止是能够改元素的背景色,要看具体是在哪个属性上用

9、display:none与visibility:hidden的区别

display:none 让元素彻底消失、不占空间、触发重排;visibility:hidden 只是隐藏视觉,元素仍占据布 局空间,只触发重绘。

$color: #ff0000;
.box {
.title { color: red; }
}

特性

display: none

visibility: hidden

是否可见 ❌完全看不见 ❌看不见 是否占据空间 ❌不占位置 ✅还占原来位置 是否重排重绘 触发重排 + 重绘 只触发重绘 子元素 子元素会一起消失

子元素设visibility:visible可以重新显示

事件触发 无法触发点击等事件 无法触发事件 性能 较差 较好

10、CSS3选择器及其优先级

对于选择器的优先级: 标签选择器、伪元素选择器:1 类选择器、伪类选择器、属性选择器:10 id 选择器:100 内联样式:1000 注意事项: !important声明的样式的优先级最高; 如果优先级相同,则最后出现的样式生效; 继承得到的样式的优先级最低; 通用选择器(*)、子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,所以它们的权值 都为 0 ; 样式表的来源不同时,优先级顺序为:内联样式 > 内部样式 > 外部样式 > 浏览器用户自定义样式 > 浏览器默认样式

11、display的block、inline和inline-block的区别

① block:会独占一行,多个元素会另起一行,可以设置width、height、margin和padding属性; ② inline:元素不会独占一行,设置width、height属性无效。但可以设置水平方向的margin和 padding属性,不能设置垂直方向的padding和margin; ③ inline-block:将对象设置为inline对象,但对象的内容作为block对象呈现,之后的内联对象会被排 列在同一行内。

3、JavaScript

1、JSONP原理

JSONP 的原理是利用

对比项

sessionStorage

localStorage

存储大小

4KB 左右

5MB 左右

5MB 左右

生命周期 可设置过期时间,默认会 话关闭失效 仅当前会话,关闭标签 / 窗口即销毁 永久存储,手动清除 才失效 与服务端 通信

每次请求自动携带在请求

头

仅在浏览器,不自动发送 仅在浏览器,不自动

发送

作用域 同源窗口共享 同源但不同标签页不共享 同源窗口共享 API 易用 性 原生难用,需自己封装 简单易用:setItem /getItem 简单易用:setItem /getItem 主要用途 登录状态、会话标识、跟 踪 临时页面数据、表单暂存 长期本地缓存、配置 信息

2、cookie、sessionStorage、localStorage的区别

3、什么是事件传播?

事件传播是事件在 DOM 节点间传递的过程,分为捕获、目标、冒泡三个阶段。浏览器默认使用冒泡机 制,即从子元素向父元素传递,可通过 stopPropagation 阻止传播。

4、解释 JavaScript 中“this”的工作原理

new→新对象 call/apply/bind→指定对象 对象.方法 ()→该对象 直接调用→ window/undefined 箭头函数→继承外层 this

5、解释原型继承如何工作

JS 每个对象都有原型对象,通过 proto 指向它。访问属性时,自身没有就沿着原型链往上找,这就是原

型继承。方法定义在 prototype 上可被所有实例共享,最终所有对象都继承自 Object.prototype。

  1. 当你访问对象的一个属性 / 方法时: 先在对象自身找 找不到→去它的__proto__ (原型对象)找 还找不到→继续往原型的原型找 直到null (最顶层)
  2. 这条链就是原型链。
3. 所有对象最终都继承自Object.prototype 。

6、null、未定义或未声明的变量之间有什么区别?

null 是主动赋值的空对象;undefined 是已声明未赋值的默认值;未声明变量是从未定义过,直接使用 会报错。null == undefined 为 true,全等为 false,typeof 未声明变量不会报错而是返回 undefined。

7、什么是闭包,以及如何/为什么使用闭包?

闭包就是内部函数引用外部函数变量,使变量不被回收。

作用是私有化变量、缓存状态、保存上下文。

优点是能持久化状态,缺点是可能造成内存泄漏。

作用:让外部函数的变量不会被垃圾回收,长期保存在内存中。

8、call和apply有什么区别

call 和 apply 都能改变函数的 this 指向并立即执行,区别仅在于参数形式:call 接收参数列表,apply 接收数组参数。 call:参数一个个传 apply:参数放在数组里

9、CORS 代表什么以及它解决什么问题

CORS(Cross-Origin Resource Sharing)是跨域资源共享,是解决浏览器跨域请求限制的官方标准方 案。通过后端设置响应头,允许指定域名访问接口,支持所有请求方式和 Cookie,比 JSONP 更安全强 大。

工作原理:

浏览器发送跨域请求时,自动在请求头带上Origin (当前域名)。

functionouter(){
let count=0; // 被闭包“包住”的变量
returnfunctioninner(){
count++;
console.log(count);
};
}
const fn =outer();
fn(); // 1
fn(); // 2
func.call(thisArg, arg1, arg2, arg3...)
func.apply(thisArg, [arg1, arg2, arg3...])

特性

var

let

const

作用域

函数级作用域

块级作用域

块级作用域

变量提升 有,值为 undefined 有,但暂时性死区 同 let 重复声明 允许 不允许 不允许 重新赋值 可以 可以

不可以

声明可不赋值 可以 可以

必须赋值

对比项

数组 Array

集合 Set

是否允许重复

允许重复

自动去重

是否有序

有序,按插入顺序排列 有序(ES6+),但无索引

访问方式

通过下标arr[i]

不能通过下标访问

查找元素

includes / indexOf慢 O (n)

has()极快O(1)

占用内存

相对较低 相对较高

常用方法

push、pop、splice、map、filter
add、delete、has、clear

后端在响应头里配置允许的域、方法、请求头。 浏览器检查响应头,允许则正常返回数据,否则拦截报错。

后端配置:

Access-Control-Allow-Origin :允许哪些域名(最重要)

Access-Control-Allow-Methods :允许的请求方法(GET、POST、PUT 等)

Access-Control-Allow-Headers :允许的请求头
Access-Control-Allow-Credentials :是否允许携带 Cookie

10、解释let、var和const之间的区别

var:函数作用域、可重复声明、无块级、有变量提升。 let:块级作用域、不可重复声明、有暂时性死区、可修改。 const:块级作用域、声明必须赋值、不可重新赋值,适合常量。

11、集合与数组有什么区别

数组有序、可重复、支持下标访问;集合无重复、无下标、查找更快。Set 常用于去重和快速判断存 在,Array 用于需要顺序和下标的场景。

12、你能举一个解构对象或数组的例子吗

解构就是快速从数组或对象中取出值,赋值给变量,代码更简洁。 数组解构 对象结构 函数参数解构(面试高频)

13、ES2015 和 ES6 有什么区别

ES6 和 ES2015 是同一个标准的两种叫法,ES6 是版本号,ES2015 是官方年份名称,之后的版本都改用 年份命名,比如 ES2016、ES2017。

14、“use strict”有什么作用?

use strict 是开启 JS 严格模式,让语法检查更严格 ①禁止意外创建全局变量 不声明直接赋值会报错。 ②禁止删除变量、函数

delete obj不允许。

// 普通写法

letarr= [10, 20, 30];
leta=arr[0];
letb=arr[1];

// 解构写法

let [a, b, c] = [10, 20, 30];
console.log(a); // 10
console.log(b); // 20
letuser= { name: "张三", age: 20 };

// 普通写法

letname=user.name;
letage=user.age;

// 解构写法

let { name, age } =user;
console.log(name); // 张三
console.log(age);  // 20
functionprint({ name, age }){
console.log(name, age);
}
print({ name: "李四", age: 25});

对比项

GET

POST

参数位置

在URL里 在请求体 body里

数据大小

小,受 URL 长度限制 大,理论无限制

安全性

低,参数可见 相对高,参数不可见

缓存

会被浏览器缓存 默认不缓存

书签 / 历史

可收藏、保留历史 不可以

编码类型

只支持 URL 编码 支持多种(form-data、json 等)

后退 / 刷新

无害 数据会重新提交

语义

获取数据

提交 / 修改数据

③禁止重复参数名

function(a,a){}报错。

④禁止八进制前缀 0

let num = 0123;报错。

⑤禁止this指向 window

普通函数中this  = undefined 。
⑥不允许使用with 、callee 、caller等废弃语法。

15、HTTP GET 和 POST 请求有什么区别

16、使用回调、promise、await 和 async 处理异步调用。使用每

种方法来处理异步调用有何优缺点

回调:最原始,简单但易陷入回调地狱,错误处理混乱。 Promise:解决回调地狱,支持链式调用和并发,但仍有 then 链。 async/await:基于 Promise,写法像同步,最优雅、最易维护,是目前最佳方案。

17、“Object.create”和“new”有什么区别?

Object.create和new之间的区别在于它们如何创建和初始化对象。Object.create(proto)使用

指定的原型对象创建一个新对象,而不调用构造函数。 另一方面,“new Constructor()”创建一个新对象,调用构造函数,将新创建的对象设置为构造函数中 “this”的值,并返回新创建的对象。

特性

浅拷贝

深拷贝

拷贝层级 仅第一层 所有层级(递归) 深层对象 共享引用 完全独立 修改深层 互相影响 互不影响 性能 快 较慢 适用 扁平数据 嵌套深、复杂对象

对比项

普通函数

箭头函数

this 指向

由调用方式决定,动态可变 继承外层作用域的 this,无法修改

构造函数

可以用new

不能 new,会报错

arguments

有

没有

prototype

有

没有

yield

可作为 Generator 函数

不能使用 yield

写法

完整繁琐 简洁,单行可省略{}和return

18、深拷贝与浅拷贝

19、JS基础类型和复杂类型

JS数据基础类型有: String、Number、Boolean、Null、undefined五种基本数据类型,加上新增的两种ES6的类型 Symbol、BigInt JS有三种复杂类型(引用数据类型): Object(对象)、Array(数组)、function(函数)

20、箭头函数与普通函数的区别

21、JS垃圾回收机制

JS 通过垃圾回收自动管理内存,主流用标记清除法:标记可达对象,清除不可达对象;需避免意外全局 变量、未清理定时器、闭包滥用等造成内存泄漏。

22、js列举和数组操作相关的方法(常用)

添加元素

push(): 在数组的末尾添加一个或多个元素,并返回新的长度。 unshift(): 在数组的开头添加一个或多个元素,并返回新的长度。

删除元素

pop(): 删除数组的最后一个元素,并返回那个元素。 shift(): 删除数组的第一个元素,并返回那个元素。 splice(): 通过删除、替换或添加新元素来改变数组的内容。

查找元素

indexOf(): 查找元素在数组中的索引。 lastIndexOf(): 从数组的末尾开始查找元素。 find(): 查找第一个满足测试函数的元素。 findIndex(): 查找第一个满足测试函数的元素的索引。

排序和翻转

sort(): 对数组元素进行排序。 reverse(): 颠倒反转数组中元素的顺序。

遍历和过滤

forEach(): 对数组的每个元素执行一次提供的函数。 map(): 创建一个新数组,其结果是该数组中的每个元素都调用一次提供的函数后的返回值。 filter(): 创建一个新数组,包含通过所提供函数实现的测试的所有元素。 reduce(): 对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返 回值。 reduceRight(): 类似reduce(),但是从数组的末尾开始累加。

归并数组

concat(): 合并两个或多个数组。 slice(): 提取原数组的一部分,返回一个新数组。

其它方法

join(): 将数组的所有元素连接成一个字符串并返回这个字符串。 toString(): 返回一个表示数组内容的字符串。 split(): 把字符串转换成数组。 toLocaleString(): 返回一个表示数组内容的本地化字符串。

23、JS中 “==“和“===“的区别详解

===:三个等号我们称为等同符,当等号两边的值为相同类型的时候,直接比较等号两边的值,值相同 则返回 true,若等号两边的值类型不同时直接返回 false。也就是说三个等号既要判断值也要判断类型 是否相等。 ==:两个等号我们称为等值符,当等号两边的值为相同类型时比较值是否相同,类型不同时会发生类型 的自动转换,转换为相同的类型后再作比较。也就是说两个等号只要值相等就可以。

24、如何用原生 JS给一个按钮绑定两个 onclick 事件

25、谈谈你对Webpack的理解

Webpack 是前端工程化的核心构建工具,负责把项目里的 JS、CSS、图片、字体等各种资源,模块化解 析、编译转换、压缩优化,最终打包成浏览器能运行的静态资源。

核心概念

①Entry:打包入口 ②Output:打包出口 ③Loader:编译转换非 JS 资源 ④Plugin:扩展打包功能

核心能力

①支持 ES6、TypeScript、Sass 等编译 ②代码分割、懒加载、Tree Shaking ③压缩、混淆、缓存优化 ④开发热更新(HMR)

26、javascript获取元素的值

<buttonid="btn">点击</button>
<script>

//通过事件监听绑定多个事件

letbtn=document.getElementById('btn')
btn.addEventListener('click', one)
btn.addEventListener('click', two)
functionone() {
alert('第一个')
},
functiontwo() {
alert('第二个')
},
</script>
<inputtype="text"id="inp1"/>
<divid="box">
内容部分
</div>
<script>
constval=document.querySelector('#inp1').value
constval2=document.querySelector('#box').innerHTML
constval3=document.querySelector('#box').innerText
</script>

27、jquery的ajax请求属性

4、VUE

1、v-show 与 v-if 的区别

v-show 指令是通过修改元素的display 的CSS 属性让其显示或者隐藏; v-if 指令是直接销毁和重建DOM 达到让元素显示和隐藏的效果; 使用v-show 会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if 更加合理。

2、keep-alive 的作用是什么

keep-alive 用于缓存 Vue 组件,避免组件反复创建与销毁,保留组件状态,提升页面切换性能。 核心作用

  1. 缓存组件实例,组件切换时不被销毁
  2. 避免重复创建、销毁,大幅提升性能
  3. 保留组件状态、数据、滚动位置 生命周期(独有的两个)
activated :组件激活时触发
deactivated :组件失活时触发

常用属性 include :只缓存哪些组件 exclude :不缓存哪些组件 max :最大缓存实例数

varformData=newFormData();
formData.append("param1", "value1");
formData.append("param2", "value2");

// 例如添加文件

// formData.append("file", fileInput.files[0]);
$.ajax({
url: "yourserver.com/api/data",
type: "POST",
data: formData,
processData: false,  // 告诉jQuery不要处理发送的数据
contentType: false,  // 告诉jQuery不要设置Content-Type请求头
success: function(response) {
console.log(response);
}
});

3、Vue2.0和Vue3.0的区别

我给你整理成最常考、最清晰的要点,逻辑非常顺:

1. 响应式原理不同

Vue2:Object.defineProperty ,劫持对象属性

Vue3:Proxy代理整个对象 支持监听新增 / 删除属性 支持数组索引、length 性能更好

2. 生命周期变化

Vue3 支持Composition API(setup) 生命周期改名:

beforeCreate  / created→用setup替代

beforeMount→onBeforeMount mounted→onMounted beforeUpdate→onBeforeUpdate updated→onUpdated beforeUnmount→onBeforeUnmount unmounted→onUnmounted

3. 编写方式(最大区别)

Vue2:Options API(data、methods、computed 分开) Vue3:Composition API(setup 组合式,按逻辑聚合) 代码更好维护、复用、类型推导友好

4. 支持 TypeScript

Vue2:对 TS 支持弱 Vue3:TS 从头开发,类型提示极好

5. 虚拟 DOM & 性能

Vue3 对 diff 算法优化:编译时标记静态节点 打包体积更小,Tree-shaking 友好

6. 新组件 / 新功能

<Teleport>传送门
<Suspense>异步组件加载

多根节点:Fragment

7. 指令与插槽

v-model语法统一

函数式组件、自定义指令写法简化

4、Vue3带来了什么改变

性能的提升:

①打包大小减少41% ②初次渲染快55%, 更新渲染快133% ③内存减少54%

源码的升级:

①使用Proxy代替defineProperty实现响应式 ②重写虚拟DOM的实现和Tree-Shaking ③拥抱TypeScript ④ Vue3可以更好的支持TypeScript

新的特性:

① setup配置 ② ref与reactive ③ watch与watchEffect ④ provide与inject

新的内置组件:

① Fragment ② Teleport ③ Suspense

其他改变:

①新的生命周期钩子 ② data 选项应始终被声明为一个函数 ③移除keyCode支持作为 v-on 的修饰符

5、MVVM

MVVM 是 Model-View-ViewModel 的架构模式,通过 ViewModel 实现数据与视图的双向绑定,让数据 变化自动更新视图、视图变化自动同步数据,无需手动操作 DOM,使代码更易维护。

  1. Model(数据层) 后端接口、本地数据、业务数据。
  2. View(视图层) 页面、DOM、用户看到的界面。
  3. ViewModel(视图模型) Vue 实例本身,桥梁: 数据变→视图自动更新 视图变→数据自动更新

双向数据绑定 + 关注点分离

View 和 Model 不直接通信 不用手动操作 DOM 代码更清晰、好维护、好测试

6、Vue实现双向数据绑定的4个方法

1.使用 v-model 指令实现双向数据绑定

使用 v-model 指令可以很方便地实现双向数据绑定。以下是使用 v-model 指令实现双向数据绑定的步 骤: 在 Vue 实例中定义一个数据属性。 在表单元素上使用 v-model 指令来绑定数据。

2. 使用 .sync 修饰符:

.sync 是 Vue 提供的修饰符,用于实现父子组件之间的双向数据绑定。它可以简化父组件向子组件传递 数据并接收子组件修改后的数据的过程。 父组件传递数据到子组件: 子组件内部修改数据并通知父组件: 在子组件中,使用.sync修饰符将父组件传递的值绑定到子组件的属性上,并通过$emit方法触发 update:前缀的事件来更新父组件的数据。

<template>
<inputv-model="message"type="text">
<p>{{ message }}</p>
</template>
<script>
exportdefault {
data() {
return {
message: ''// 定义一个数据属性
};
}
};
</script>
<inputv-model="message"type="text">
<template>
<child-component:value.sync="data"></child-component>
</template>
<template>
<inputtype="text":value="value"@input="$emit('update:value',
$event.target.value)">
</template>

3.使用自定义事件

可以通过自定义事件在组件之间实现双向数据绑定。父组件通过 props 传递数据给子组件,子组件在修 改数据时,通过自定义事件将修改后的数据传递给父组件。 父组件传递数据到子组件: 子组件内部修改数据并通知父组件: 在子组件中,通过$emit方法触发input事件,并将修改后的数据传递给父组件。

4.使用全局状态管理(如 Vuex)

Vuex 是 Vue 的官方状态管理库,它提供了一个集中式的数据存储,用于管理共享的状态。通过在 Vuex 中定义状态和 mutations,可以实现全局的双向数据绑定。 状态定义和修改: 组件中使用和修改状态:

<template>
<child-component:value="data"@input="data = $event"></child-component>
</template>
<template>
<inputtype="text":value="value"@input="$emit('input',
$event.target.value)">
</template>
// 在 Vuex 的 state 中定义数据
state: {
value: ''
},
// 在 mutations 中修改数据
mutations: {
updateValue(state, newValue) {
state.value=newValue;
}
}
<template>
<inputtype="text":value="value"@input="updateValue">
</template>
<script>
import { mapState, mapMutations } from'vuex';
exportdefault {
computed: {
...mapState(['value'])
},
methods: {
...mapMutations(['updateValue'])
}

在组件中使用mapState将状态映射到组件的计算属性中,使用mapMutations将 mutations 映射到 组件的方法中,从而实现对共享状态的双向数据绑定。

7、Vue组件传参的八种方式

1、Props

Props是Vue中组件之间传递数据的一种常见方式。父组件可以通过props将数据传递给子组件,子组件 通过props选项来接收这些数据。 使用方式: 父组件中定义要传递的数据,并在使用子组件时以属性的形式传递。 子组件中通过props选项来声明要接收的数据。 示例:

}
</script>
<!-- 父组件 -->
<template>
<div>
<child-component:message="parentMessage"></child-component>
</div>
</template>
<script>
importChildComponentfrom'./ChildComponent.vue';
exportdefault {
components: {
ChildComponent
},
data() {
return {
parentMessage: '这是来自父组件的消息'
};
}
};
</script>
<!-- 子组件 -->
<template>
<div>{{ message }}</div>
</template>
<script>
exportdefault {
props: ['message']
};
</script>

2、$emit

emit是Vue中组件之间通过事件进行数据传递的一种方式。子组件可以通过emit方法触发一个自定义事 件,并将数据传递给父组件。父组件可以通过在子组件标签上监听这个自定义事件来接收数据。 使用方式: 子组件中定义要触发的事件,并通过$emit方法传递数据。 父组件中在子组件标签上监听该事件,并定义处理函数来接收数据。 示例:

3、Provide/Inject

Provide/Inject是Vue中组件之间通过依赖注入进行数据传递的一种方式。父组件可以通过provide选项 提供数据,子组件(包括跨层级的子孙组件)可以通过inject选项注入这些数据。 使用方式: 父组件中通过provide选项提供数据。

<!-- 子组件 -->
<template>
<button@click="sendMessage">发送消息给父组件</button>
</template>
<script>
exportdefault {
methods: {
sendMessage() {
this.$emit('childMessage', '这是来自子组件的消息');
}
}
};
</script>
<!-- 父组件 -->
<template>
<div>
<child-component@childMessage="handleChildMessage"></child-component>
</div>
</template>
<script>
importChildComponentfrom'./ChildComponent.vue';
exportdefault {
components: {
ChildComponent
},
methods: {
handleChildMessage(message) {
console.log(message);
}
}
};
</script>

子组件中通过inject选项注入所需的数据。 示例:

4、attrs和listeners

$attrs:

概述:$attrs 是一个对象,它包含了父作用域中没有被prop接收的所有属性(不包含class和 style属性)。可以通过v-bind=“$attrs” 直接将这些属性传入内部组件,实现父组件隔代向孙组 件传值。 示例:父组件将name 和age 属性传递给子组件,子组件通过v-bind=“$attrs” 将这些属性(以及 可能的其他未声明的属性)传递给孙组件。孙组件通过props 接收这些属性。

<!-- 父组件 -->
<template>
<div>
<child-component></child-component>
</div>
</template>
<script>
importChildComponentfrom'./ChildComponent.vue';
exportdefault {
components: {
ChildComponent
},
provide() {
return {
message: '这是通过provide/inject传递的消息'
};
}
};
</script>
<!-- 子组件 -->
<template>
<div>{{ message }}</div>
</template>
<script>
exportdefault {
inject: ['message']
};
</script>
<!-- 父组件(Parent.vue) -->
<template>
<div>
<h1>父组件</h1>
<ChildComponent:name="parentName":age="parentAge"/>
</div>
</template>

$listeners:

概述:$listeners 是一个对象,它包含了父组件中所有的v-on事件监听器(不包含.native修饰器 的)。可以通过v-on=“$listeners” 将这些事件监听器传入内部组件,实现孙组件隔代向父组件 传值。 举例: 在上述例子中,如果孙组件需要向父组件发送事件,可以通过$emit 触发事件,并在子组件中使 用v-on=“$listeners” 将这些事件传递给父组件。然而,需要注意的是,$listeners 通常用于 孙组件向隔代的父组件发送事件,而不是直接用于父子组件间的通信。在实际应用中,父子组件间 的通信更多地使用$emit 和v-on 。

<script>
importChildComponentfrom'./ChildComponent.vue';
exportdefault {
components: { ChildComponent },
data() {
return {
parentName: 'Tom',
parentAge: 30
};
}
};
</script>
<!-- 子组件(ChildComponent.vue) -->
<template>
<div>
<h2>子组件</h2>
<GrandChildComponentv-bind="$attrs"/>
</div>
</template>
<script>
importGrandChildComponentfrom'./GrandChildComponent.vue';
exportdefault {
components: { GrandChildComponent }
};
</script>
<!-- 孙组件(GrandChildComponent.vue) -->
<template>
<div>
<h3>孙组件</h3>
<p>父组件传递的名字:{{ name }}</p>
<p>父组件传递的年龄:{{ age }}</p>
</div>
</template>
<script>
exportdefault {
props: ['name', 'age']
};
</script>

5、parent和children

parent和children是Vue中组件之间通过访问父组件和子组件实例进行数据传递的一种方式。子组件可以 通过parent属性访问父组件实例,父组件可以通过children属性访问子组件实例(注意:$children是一 个数组,包含了所有子组件的实例,但不保证顺序)。 使用方式: 通过parent或children访问相应的组件实例。 直接在组件实例上访问或修改数据。

示例

注意:通常不建议直接使用$children 进行组件间的通信,因为它可能导致代码难以维护和理 解。如果需要访问子组件的数据或方法,更推荐使用ref 或provide/inject 等机制。

<!--父组件(Parent.vue)-->
<template>
<div>
<h1>父组件</h1>
<ChildComponent />
</div>
</template>
<script>
import ChildComponent from'./ChildComponent.vue';
export default{
components: { ChildComponent },
data(){
return{
parentMessage: 'Hello from Parent'
};
},
methods: {
parentMethod(){
console.log('Parent method called');
}
}
};
</script>
<!--子组件(ChildComponent.vue)-->
<template>
<div>
<h2>子组件</h2>
<button @click="callParentMethod">调用父组件方法</button>
</div>
</template>
<script>
export default{
methods: {
callParentMethod(){
this.$parent.parentMethod();
}
}

6、Vuex

Vuex是Vue中一种专门用于状态管理的插件。通过在Vuex中定义全局的状态,并在组件中使用getter和 mutation来访问和修改状态,可以实现组件之间的数据传递和共享。 使用方式: 安装Vuex并创建store。 在store中定义状态、mutation、action等。 在组件中通过this.$store访问store,并使用getter获取状态,使用mutation或action修改状态。

示例

};
</script>
<!-- 父组件(Parent.vue) -->
<template>
<div>
<h1>父组件</h1>
<ChildComponent/>
</div>
</template>
<script>
import { mapState, mapMutations } from'vuex';
importChildComponentfrom'./ChildComponent.vue';
exportdefault {
components: { ChildComponent },
computed: {
...mapState(['counter'])
},
methods: {
...mapMutations(['increment'])
}
};
</script>
<!-- 子组件(ChildComponent.vue) -->
<template>
<div>
<h2>子组件</h2>
<p>计数器:{{ counter }}</p>
<button@click="increment">增加</button>
</div>
</template>
<script>
import { mapState, mapMutations } from'vuex';
exportdefault {
computed: {
...mapState(['counter'])
},
methods: {
...mapMutations(['increment'])

7、插槽(Slot)

插槽是一种让父组件能够向子组件指定内容插入点的机制。通过插槽,父组件可以将自己的模板内容 传递给子组件,并在子组件的指定位置渲染出来。 插槽分为默认插槽、具名插槽和作用域插槽三种类型。 使用方式: 在子组件中定义插槽。 在父组件中使用子组件时,通过插槽向子组件传递内容。

示例

默认插槽

默认插槽是最基本的插槽类型,用于在组件内传递和显示任意内容。如果没有给插槽命名, Vue会将内容传递到默认插槽中。

}
};
</script>
<!-- Vuex Store(store.js) -->
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
counter: 0
},
mutations: {
increment(state) {
state.counter++;
}
}
});

//子组件

<template>
<divclass="my-component">
<slot></slot><!-- 默认插槽 -->
</div>
</template>

//父组件

<template>
<MyComponent>
<p>This is some default slot content!</p>
</MyComponent>
</template>

具名插槽

具名插槽允许我们在组件中定义多个插槽,每个插槽都有一个唯一的名称。这样可以在组件中 更精确地控制内容的显示位置。

在这个例子中,<h1>Header Content</h1> 将会显示在<slot name="header"></slot> 位置,
<p>Footer Content</p> 将会显示在<slot name="footer"></slot> 位置,而默认插槽中的内
容仍会显示在<slot></slot> 位置。

作用域插槽

作用域插槽是一种特殊类型的插槽,允许我们在父组件中访问子组件的数据。这在需要动态渲 染内容时特别有用。

//子组件(MyComponent.vue)
<template>
<divclass="my-component">
<header>
<slotname="header"></slot>
</header>
<main>
<slot></slot><!-- 默认插槽 -->
</main>
<footer>
<slotname="footer"></slot><!--具名插槽-->
</footer>
</div>
</template>

//父组件

<template>
<MyComponent>
<templatev-slot:header>
<h1>Header Content</h1>
</template>
<p>This is some default slot content!</p>
<templatev-slot:footer>
<p>Footer Content</p>
</template>
</MyComponent>
</template>
//子组件(MyComponent.vue)
<template>
<divclass="my-component">
<slot:data="someData"></slot>
</div>
</template>
<script>
exportdefault {
data() {
return {
someData: 'Hello from MyComponent!'
};
}

8、事件总线(Event Bus)

事件总线是一个设计模式,它充当一个中间人,负责监听各个组件发布的事件,并分发给订阅这些事件 的其他组件。在Vue中,事件总线通常是一个新的Vue实例,它提供了$emit 、$on 和$off 方法,分别 用于触发事件、监听事件和移除事件监听。 $emit:用于触发事件,并传递相关数据。 $on:用于监听事件,并定义事件触发时的回调函数。 $off:用于移除事件监听,防止内存泄漏。 使用方式: 创建一个空的Vue实例作为事件总线。 组件通过事件总线监听和触发事件来传递数据。

事件总线的实现步骤

  1. 创建事件总线: 你可以在一个单独的.js 文件中创建一个新的Vue实例,并将其导出为事件总线。 或者,你也可以在Vue项目的入口文件(如main.js )中,将事件总线挂载到Vue的原型上, 使其成为全局可用的。
  2. 在组件中引入事件总线: 使用import 语句将事件总线引入到你需要使用它的组件中。
  3. 触发和监听事件: 使用$emit 方法在组件中触发事件,并传递相关数据。 使用$on 方法在另一个组件中监听这个事件,并定义回调函数来处理接收到的数据。
  4. 移除事件监听: 在组件销毁之前,使用$off 方法移除所有不再需要的事件监听,以防止内存泄漏。

事件总线的例子

  1. 创建事件总线(event-bus.js):
  2. 在组件中引入事件总线并使用: ComponentA:
};
</script>

//父组件

<template>
<MyComponentv-slot:default="slotProps">
<p>{{ slotProps.data }}</p>
</MyComponent>
</template>
importVuefrom'vue';
exportconstEventBus=newVue();
<template>
<button@click="sendMessage">Send Message to ComponentB</button>
</template>

ComponentB: 在这个例子中,当ComponentA 中的按钮被点击时,它会通过事件总线触发一个名为message-from-a 的 事件,并传递一条消息。然后,ComponentB 会监听这个事件,并在接收到消息时更新其数据。最后, 在ComponentB 销毁之前,它会移除对message-from-a 事件的监听,以防止内存泄漏。