面试题库
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 文档的 “身份标识”,其核心作用 是:
- 告知浏览器解析规则: 声明当前文档遵循的 HTML 或 XML 标准(如 HTML4、HTML5、XHTML),让浏览器以对应模式 渲染页面,避免因规则不明确导致的显示异常。
- 触发浏览器的渲染模式: 标准模式(Standard Mode):严格遵循 W3C 标准解析 CSS 和布局。 怪异模式(Quirks Mode):兼容旧浏览器的非标准解析方式(仅在 DOCTYPE 缺失或格式错误时 触发)
4、img标签的title属性与alt属性的区别是什么
2、CSS
1、CSS3新特性
CSS3 主要新增:更强选择器、圆角阴影、渐变、transform、transition、animation、flex/grid 布局、 自定义字体、响应式媒体查询,让页面更美观、交互更流畅、布局更简单。
2、解决父元素高度塌陷
父元素高度塌陷是因为子元素浮动,脱离文档流。 解决方法有四种:
- 父元素 overflow:hidden 触发 BFC;
- 末尾加空元素 clear:both;
- 使用伪元素 ::after 清除浮动(最优);
- 父元素浮动或设置 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 的原理是利用
对比项
cookie
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。
- 当你访问对象的一个属性 / 方法时: 先在对象自身找 找不到→去它的__proto__ (原型对象)找 还找不到→继续往原型的原型找 直到null (最顶层)
- 这条链就是原型链。
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 组件,避免组件反复创建与销毁,保留组件状态,提升页面切换性能。 核心作用
- 缓存组件实例,组件切换时不被销毁
- 避免重复创建、销毁,大幅提升性能
- 保留组件状态、数据、滚动位置 生命周期(独有的两个)
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,使代码更易维护。
- Model(数据层) 后端接口、本地数据、业务数据。
- View(视图层) 页面、DOM、用户看到的界面。
- 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实例作为事件总线。 组件通过事件总线监听和触发事件来传递数据。
事件总线的实现步骤
- 创建事件总线: 你可以在一个单独的.js 文件中创建一个新的Vue实例,并将其导出为事件总线。 或者,你也可以在Vue项目的入口文件(如main.js )中,将事件总线挂载到Vue的原型上, 使其成为全局可用的。
- 在组件中引入事件总线: 使用import 语句将事件总线引入到你需要使用它的组件中。
- 触发和监听事件: 使用$emit 方法在组件中触发事件,并传递相关数据。 使用$on 方法在另一个组件中监听这个事件,并定义回调函数来处理接收到的数据。
- 移除事件监听: 在组件销毁之前,使用$off 方法移除所有不再需要的事件监听,以防止内存泄漏。
事件总线的例子
- 创建事件总线(event-bus.js):
- 在组件中引入事件总线并使用: 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 事件的监听,以防止内存泄漏。