段落用 p 标签,标题用 h 系列标签,边栏用 aside 标签,主要内容用 main 标签
对开发者:
对浏览器:
HTML5 现在已经不是 SGML(Standard Generalized Markup Language,标准通用标记语言) 的子集,1️⃣主要是关于图像,位置,存储,多任务等功能的增加(语义化标签):
2️⃣移除的元素:
3️⃣支持HTML5新标签:
4️⃣直接使用成熟的框架、比如HTML5shim
HTML5Shiv(或称为 HTML5Shim)是一个 JavaScript 文件,用于解决旧版 Internet Explorer(IE6-IE8)不支持 HTML5 新元素的问题。在 IE6-IE8 浏览器中,如果网页中出现了 HTML5 新元素,如 article、section、header、footer 等标签,这些元素会被视为未知元素并无法正常显示。HTML5Shiv 可以通过动态向 DOM 中添加所需元素来解决这个问题。
该工具由 Remy Sharp 创建,并于 2011 年发布。它的代码非常简单,只有几十行,核心实现原理即通过创建将 HTML5 新元素以 display:block; 显示的 HTML 元素,来触发 IE 的默认样式规则,从而让这些新元素可以正常显示。
使用 HTML5Shiv 的方法也很简单,只需要在 head 标签中添加如下代码即可:
该段代码的作用是判断当前浏览器是否为 IE9 及以上版本,如果是,则不加载 HTML5Shiv 文件,避免产生额外的性能消耗。如果是 IE6-IE8,则加载 HTML5Shiv 文件,以确保 HTML5 新元素能够正常显示。目前,HTML5Shiv 已经成为了 HTML5 开发的必备工具之一。
补充:如何区分HTML5: DOCTYPE声明新增的结构元素功能元素
HTML5 引入了一些新的结构元素和功能元素,通过 DOCTYPE 声明可以区分文档使用的 HTML 版本,从而正确地解释文档中使用的元素和属性。
回流必定会发生重绘,重绘不一定会引发回流。
回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流(必须重新计算网页的布局流程和所有相关元素的位置和大小)。
data-* 的值的获取和设置,2种方法:
(1)传统方法 getAttribute( ) 获取data-属性值; setAttribute( ) 设置data-属性值
getAttribute( ) 获取data-属性值;
setAttribute( ) 设置data-属性值
(2)HTML5新方法
例如 data-kerwin
dataset.kerwin 获取data-kerwin属性值
dataset.kerwin =“赵钱孙李” 设置data-kerwin属性值
⭕注意:
优势: 自定义的数据可以让页面拥有更好的交互体验 (不需要使用 Ajax 或去服务端查询数据)
- null 表示一个已定义但是没有值的对象,可以理解为变量被赋值为 null,表示该变量指向的对象不存在或者被清空了。一般来说,null 是由程序员主动赋值而来的,并且它是一个对象类型(object),使用 typeof 操作符返回值为 object,这也是 null 的一个特例。
- undefined 表示一个变量已经声明,但是尚未被初始化或者赋值,可以理解为变量还没有指向任何内存地址,或者虽然指向了内存地址,但是该内存地址的值为 undefined,也可以理解成该变量没有被分配内存空间。和 null 一样,undefined 也是一种数据类型,使用 typeof 操作符返回值为 undefined。
需要注意的是,在使用比较运算符时,null 和 undefined 的行为有所不同。例如,使用 == 运算符进行比较时,如果其中一个操作数为 null 或者 undefined,则会进行类型转换。在这个过程中,null、undefined 会被转换为布尔值 false,其他任何值都不会进行类似的转换。
①概念
②加载和渲染
③压缩和性能
综上所述,精灵图适用于需要渲染多个小图标的场景,可以减少HTTP请求次数和文件大小,提高渲染性能;Base64 适用于需要内联编码的小文件或者需要实现图片预览等功能的场景,可以避免引入外部文件。
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
将DOM树和CSSOM树结合,生成渲染树(Render Tree)
Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
Display:将像素发送给GPU,展示在页面上
毗邻的两个或多个 margin 会合并成一个margin,叫做外边距折叠。规则如下。
块级格式化上下文(Block Formatting Context,BFC)是 Web 页面中的一个渲染规范,是页面中盒子布局和浮动的计算规范,它可以看成是一个独立的渲染区域,规定了内部元素如何布局,并且与 BFC 外部毫不相干。BFC 中的元素布局不会影响到外部元素,同时也不受外部元素的影响。
在 BFC 中,每个盒子的左外边缘将触碰到容器的左侧边界,而不会出现浮动的情况。换言之,BFC 可以保证其中的元素垂直方向的位置不会发生重叠,当一个元素被设置为 BFC 后,它内部的元素就会按照一定的规则进行排列和布局,从而产生一种独立的渲染环境。
BFC 在页面中起着很重要的作用,它可以解决很多布局问题,例如:
- 清除浮动:当一个父元素包含了子元素的浮动时,将父元素设为 BFC 即可清除浮动。
- 防止 margin 重叠:当两个块级元素的 margin-top 和 margin-bottom 重叠时,将其中一个元素设为 BFC 即可避免 margin 重叠。
- 解决宽度塌陷问题:当一个浮动元素出现在一个没有设置宽度的父元素里,将父元素设为 BFC 即可防止宽度塌陷。
BFC 可以通过以下方式产生:
- 根元素或其他包含它的元素;
- 浮动 (float 不为 none);
- 绝对定位 (position 值为 absolute 或 fixed);
- 行内块元素 (display 值为 inline-block);
- 表格单元格(display 值为 table-cell);
- overflow 值不为 visible 的块级元素;
z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面,当脱离文档流内容较多,并且相互重叠的时候,就有可能发生本想完全显示的内容被其他内容遮挡的结果,这时就需要人为指定哪个层在上面,哪个在下面,z-index属性就是干这个用的。
⭕注意: Z-index 仅能在定位元素上奏效
在position的值是 relative、absolute、 fixed、sticky 时候可以触发。
box-sizing 属性允许您以特定的方式定义匹配某个区域的特定元素
语法: box-sizing: content-box | border-box | inherit:
(1)box-sizing:content-box; 普通盒模型 这是由 CSS2.1 规定的宽度高度行为。宽度和高度分别应用到元素的内容框。在 宽度和高度 之外绘制元素的 内边距 padding 和边框 border 。是默认值。如果你设置一个元素的宽为100px,那么这个元素的内容区会有100px 宽,并且任何 边框 和 内边距 的宽度 都会被增加到最后绘制出来的元素宽度中。
(2)box-sizing:border-box; IE盒模型(避免子盒子从浮动的父盒子中掉下) 为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制。告诉浏览器去理解你设置的边框和内边距的值是包含在width内的。也就是说,如果你将一个元素的width设为100px,那么这100px会包含其它的border和padding,内容区的实际宽度会是width减去border + padding 的计算值。大多数情况下这使得我们更容易的去设定一个元素的宽高。
(3)box-sizing:inherit; 规定应从父元素继承 box-sizing 属性的值
移动端初始视口的大小为什么默认是980px?
因为世界上绝大多数PC网页的版心宽度为 980px,如果网页没有专门做移动端适配,此时用手机访问网 页旁边刚好没有留白,不过页面缩放后文字会变得非常小。
为了解决页面缩放的体验问题,在网页代码的头部,加入一行viewport元标签。
这里的device-width告诉浏览器,将视口的宽度设置为设备宽度(这个宽度是人为预设的,不设的话就是980px)。 属性含义
initia-scale: 第一次进入页面的初始比例
minimum-scale: 允许缩小最小比例
maximum-scale: 允许放大最大比例
user-scalable: 允许使用者缩放,1 or 0 (yes or no)
img( max-width: 100%;} 此时图片会自动缩放,同时图片最大显示为其自身的100% (即最大只可以显示为自身那么大)
为什么不用 img{ width: 100%;}? 当容器大于图片宽度时,图片会无情的拉伸变形
为什么要媒体查询?
针对不用的设备提前为网页设定各种 CSS 样式CSS3中的Media Query模块,自动检测屏幕宽度,然后加载相应的CSS文件
语法举例 :
@media screen and (min-width:1200px){
body{
background-color: red;
}
}
当屏幕宽度大于1200px时,背景色变为红色。
为什么要用rem?
px、em、rem 有什么不同?
CSS3属性中关于制作动画的三个属性: Transform,Transition,Animation。
1️⃣transform: 描述了元素的静态样式,本身不会呈现动画效果,可以对元素进行旋转 rotate、扭曲 skew、缩放 scale和 移动 transate 以及 矩阵变形 matrix。
div{
transform:scale(2);
}
2️⃣transition 样式过渡,从一种效果逐渐改变为另一种效果(主动触发)
transition是一个合写属性
transition: transition-property transition-duration transition-timing-functiontransition-delay
从左到右分别是: css属性、过渡效果花费时间、速度曲线、过渡开始的延迟时间。
div{
width: 100px;
height:100px;
transition:transform 2s;
}
div: hover{
transform:rotate(180deg);
}
transition通常和hover等事件配合使用,需要由事件来触发过渡
我们知道 transition 虽然简单好用,但是我们会发现它受到各种限制。
(1)transition需要一个事件来触发,比如hover,所以没法在网页加载时自动发生
(2) transition是一次性的,不能重复发生,除非一再触发。
(3) transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态
(4)一条transition规则,只能定义一个属性的变化,不能涉及多个属性。
3️⃣animation动画 由@keyframes来描述每一帧的样式
div{
animation:myAnimation 5s infinite
}
@keyfirames myAnimation {
0% {left:0;transform:rotate(0);}
100%{left:200px;transform:rotate(180deg);}
}
区别:
animation属性类似于transition,他们都是随着时间改变元素的属性值,
其主要区别在于: transition需要触发一个事件才会随着时间改变其CSS属性;
animation在不需要触发任何事件的情况下,也可以显式的随时间变化来改变元素CSS属性,达到一种动画的效果
父元素的高度设置为 50vh,表示占据视口高度的一半。子元素的宽高不固定,内容也可能不确定,因此对 padding 进行了设置。通过应用上述 CSS 样式,就可以实现父元素和子元素的水平垂直居中,无论子元素宽高如何变化都能保持居中状态。
方法1:浮动
方法2:绝对定位
方法3: flexbox。移动开发里经常用到
1️⃣首先将容器的高度设置为100px。然后,将左栏设置为float:left并固定宽度为300px,中间栏通过设置margin-left和margin-right为300px来自适应宽度,并且由于浮动元素的存在,中间栏会自动填满剩余的宽度。最后,将右栏设置为float:right并固定宽度为300px。这样就可以实现左、中、右三栏布局,其中中间栏自适应宽度。
2️⃣首先将容器的高度设置为100px,并对其设置相对定位。然后,将左栏设置为position:absolute并固定宽度为300px,可以通过设置left值为0来让左栏靠左对齐。中间栏同样设置为position:absolute,通过设置left和right值为300px来占据剩余的宽度,从而实现自适应宽度。最后,将右栏设置为position:absolute并固定宽度为300px,通过设置right值为0来让右栏靠右对齐。
需要注意的是,当使用绝对定位布局时,必须为父元素设置position:relative以作为参照物。
首先将容器的高度设置为100px,并为其设置display:flex属性。然后,将左栏和右栏的宽度都固定为300px,中间栏通过设置flex:1来占据剩余的宽度,即自适应宽度。最后,分别对三栏元素设置不同的背景颜色。
需要注意的是,在使用flexbox布局时,父元素必须具有display:flex或display:inline-flex属性,而子元素则可以通过flex-grow、flex-shrink和flex-basis等属性来实现自适应布局。
是指使用 position 属性(如:position: relative/absolute/fixed/sticky)设置的元素所在的渲染区域,该区域相对于其最近的块级祖先元素进行定位。在定位流中,可以使用 top、bottom、left、right 属性控制元素的位置,以及 z-index 属性控制元素的层级关系。定位流的主要作用是实现页面中元素的绝对定位,即脱离文档流独立布局。
则是指使用 float 属性对元素进行浮动时所形成的渲染区域。在浮动流中,浮动元素会尽量出现在其包含块的顶部,并且不会覆盖行中的文本内容,以使页面排版更加合理。同时,浮动元素会影响父容器和其他兄弟元素的布局,需要适当清除浮动以避免造成布局混乱。浮动流的主要作用是实现页面中的多列布局、图片环绕文字等效果。
是 HTML 文档中元素默认的布局方式,也是 CSS 中最基本的布局模式之一。在普通流中,元素按照其默认的块级或行内级别排列,如块级元素会单独占据一行,行内元素会按照字母的顺序排列在一行。可以通过控制元素的 display 属性、float 属性以及 position 属性等来影响元素的位置和排列顺序。
FC(Formatting Context,格式化上下文)是页面渲染时的一种概念,它是 CSS 布局的基础模块之一,能够约束页面元素的排版和渲染方式。
在 HTML 页面中,所有的元素都是以层叠的方式排列在一起的,而 z-index 属性则用于控制元素的堆叠顺序。但是,在某些情况下,z-index 属性可能会失效,以下是几个常见的原因:
当对一个文档进行布局(layout)的时候,浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box)
一个盒子由四个部分组成:content
、padding
、border
、margin(如下图)
boreder
,即边框,围绕元素内容的内边距的一条或多条线,由粗细、样式、颜色三部分组成padding
,即内边距,清除内容周围的区域,内边距是透明的,取值不能为负,受盒子的background
属性影响margin
,即外边距,在元素外创建额外的空白,空白通常指不能放其他元素的区域三维视角如图:
标准盒子模型,是浏览器默认的盒子模型。
标准盒子模型的模型图如下图:
从上图可以看到:
也就是,width/height
只是内容高度,不包含 padding
和 border
值
也就是,width/height
包含了 padding
和 border
值
CSS 中的 box-sizing 属性定义了引擎应该如何计算一个元素的总宽度和总高度
box-sizing: content-box|border-box|inherit;
.container {
display: flex;
align-items: center; /*在交叉轴上居中*/
justify-content: center; /*在主轴上居中*/
}
- display: flex; 将容器设置为 Flex 布局
- align-items 属性将子元素在竖直方向上居中对齐
- justify-content 属性将子元素在水平方向上居中对齐。
⭕注意:父容器必须具有宽度和高度,子元素的宽度和高度最好是固定的,否则可能会出现不一致的排列方式。
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- 将父容器设置为相对定位(position: relative;)
- 使子元素的定位基准点为父元素而不是文档流。
- 将子元素设置为绝对定位(position: absolute;)
- top: 50%; 和 left: 50% 将子元素放置于父元素的中心点。
- 使用 transform 属性的 translate(-50%, -50%) 参数将子元素向左上方移动自身宽度和高度的一半,使其居中显示。
⭕注意:在使用绝对定位实现垂直水平居中时,父容器必须要有宽度和高度,否则无法保证子元素的准确位置。此外,使用绝对定位将会使元素脱离普通的文档流,可能会对其他元素造成影响,因此在使用时要选择合适的场景。
将子元素设置为 display:table-cell;、vertical-align:middle; 以及父元素设置为 display: table;。这种方法需要保证各个单元格的宽度相等,因此适用范围较窄。
.container {
display: table;
}
.child {
display: table-cell;
vertical-align: middle;
}
将容器设置为 Grid 布局,然后将子元素放置于网格的中心点。⭕注意:这里不能使用 align-items 和 justify-content 属性,而是需要使用 grid-template-columns、grid-template-rows 和 grid-column、grid-row 属性。
.container {
display: grid;
}
.child {
grid-column: 1 / 2; /* 放置于第一列 */
grid-row: 1 / 2; /* 放置于第一行 */
justify-self: center; /* 在水平方向上居中 */
align-self: center; /* 在垂直方向上居中 */
}
将容器设置为 Grid 布局,然后将子元素放在了网格的左上角(第一行第一列),并使用 justify-self 和 align-self 将子元素在水平和垂直方向上居中。需要注意的是,父容器和子元素都必须具有固定的宽度和高度,否则无法保证位置的准确性。
(特殊情况)将子元素的 line-height 属性设置为与其父元素相同的值,然后将子元素的 vertical-align 属性设置为 middle。
.parent {
height: 200px;
line-height: 200px;
text-align: center; /* 为了使子元素水平居中 */
}
.child {
display: inline-block;
vertical-align: middle;
}
将父容器的高度和 line-height 属性都设置为 200px,然后将文本对齐方式设为居中(text-align: center;)。接着,将子元素设置为 inline-block 元素,并使用 vertical-align: middle; 将其在垂直方向上居中。这种方法只适用于单行文本的情况。
将父容器设置为相对定位,将子元素设置为绝对定位,并使用 translate 属性将其移动到父容器的正中央。 将父容器设置为相对定位,将子元素设置为绝对定位,并使用 top: 50%; 和 left: 50%; 将其放置于父元素的中心点。然后,使用 transform 属性的 translate(-50%, -50%) 将子元素向左上方移动自身宽度和高度的一半,使其居中显示。这种方法需要保证子元素具有固定的宽度和高度。
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
⭕ 注意:在使用 transform 属性实现垂直水平居中时,父容器必须要有宽度和高度,否则无法保证子元素的准确位置。此外,使用绝对定位将会使元素脱离普通的文档流,可能会对其他元素造成影响,因此在使用时要选择合适的场景。
JS是单线程
执行任务的模式: 同步 异步
Javascript 语言的执行环境是“单线程”(一次只能完成一件任务。如果有多个任务就必须排队,前面一个任务完成,再执行后面一个任务,以此类推)。
“单线程” 这种模式的好处是实现起来比较简单,执行环境相对单纯; 坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行 (比如死循环)导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,Javascript语言将任务的执行模式分成两种: 同步和异步。
JavaScript 异步编程是指在程序执行过程中,某些操作不会阻塞后续代码的执行,以提高程序的运行效率和用户体验。常见的异步编程方式包括回调函数、Promise 和 async/await。其中,回调函数是最早也是最基础的异步编程方式,但由于回调函数嵌套多层容易引起代码可读性、维护性等问题,因此后来出现了 Promise、async/await 等更加优雅的方式来处理异步编程。
Promise 是 ECMAScript 6 中引入的一种处理异步操作的机制。它的基本用法是通过 Promise 构造函数来创建一个 Promise 实例,然后通过 then() 方法来指定成功(resolve)和失败(reject)时的回调处理函数,或通过 catch() 方法来指定处理异常情况时的回调函数。Promise 的两个主要优点是:
Promise 缺点:例如无法取消 Promise、无法立即得到 Promise 的返回结果等。
Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的缩写,它是一种无需重新载入整个页面而实现局部更新的技术。工作原理是通过 XMLHttpRequest 对象向服务器发送请求,接收服务器返回的数据并更新页面内容,从而实现异步局部更新的效果。
使用 AJAX 可以减少页面加载时间,提高用户体验。在使用 AJAX 时,通常需要用到的 API 包括:XMLHttpRequest 对象、readyState 属性、onreadystatechange 事件、open() 方法、send() 方法和 responseText 属性等。
1️⃣分片上传:
2️⃣断点续传: 服务端返回,从哪里开始 浏览器自己处理
在大文件上传时,需要考虑上传进度、断点续传、并发上传等问题。如果没有实现断点续传,上传过程中如果网络出现故障,就需要重新上传整个文件。因此,如果要上传较大的文件,最好是使用第三方库或者插件来完成,例如使用Axios、fetch-upload等库来进行大文件上传。这些库能够更好地对上传的请求进行管理,从而提高上传的稳定性和速度。
Token(令牌)是指在网络通信中为了确认请求方的身份而进行的身份验证方式,通过在用户进行登录认证之后,获取到一个特定权限的 Token,来代表该用户的身份信息。Token 由服务器颁发,客户端收到后可以存储在本地,之后每次发送请求时都需要携带该 Token 以进行身份验证。
Token 的优点包括:
- 提高系统的安全性:采用 Token 鉴权可以降低攻击风险,并保证了数据的隐私性和完整性。
- 提高用户体验:不需要频繁输入用户名和密码,也不需要每次都进行重复的认证操作,用户可以更快速地完成登录以及频繁的操作。
- 可扩展性:可以通过 Token 携带用户身份信息以外的数据,如权限、过期时间等,并且可以基于 Token 构建多种开放平台和 API 第三方接口。
1️⃣type 是用来定义类型别名(Type Aliases)的关键字,它可以将一个已有的类型起一个新的名字,从而使得代码更加易读和易维护,例如:
type Name = string;
type Age = number;
type Person = {
name: Name;
age: Age;
};
使用 type 定义了三个类型别名,分别是 Name、Age 和 Person。这样在后续的代码中,我们就可以使用这些别名来表示相应的数据类型,而不必每次都写出完整的类型声明。
2️⃣interface 则是用来定义接口(Interface)的关键字,它描述了一个对象的结构和属性,可以用来约束对象的形状和方法的参数和返回值类型。例如:
interface IUser {
name: string;
age: number;
sayHello: () => void;
}
使用 interface 定义了一个 IUser 接口,它包含了 name、age 和 sayHello 三个属性。这样在后续的代码中,我们就可以使用 IUser 接口来限制对象的结构,例如:
function greet(user: IUser) {
console.log(`Hello, ${user.name}!`);
}
const user = { name: 'Tom', age: 18, sayHello() {} };
greet(user);
定义了一个函数 greet,它的参数类型为 IUser 接口,这样就可以确保传递的对象包含了 name 和 sayHello 属性,并且 sayHello 必须是一个函数。
function isEmptyObject(obj) {
return Object.keys(obj).length === 0;
}
使用 Object.keys() 方法获取对象的所有属性名,并返回一个包含所有属性名的数组。然后我们可以通过数组的 length 属性来判断属性的数量是否为 0,如果为 0,则说明该对象为空。
function isEmptyObject(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
使用 for...in 循环遍历对象的所有属性,并通过 hasOwnProperty() 方法来判断该属性是不是该对象自己的属性(而不是从原型链继承来的)。如果存在至少一个属性,则说明该对象不为空。
function isEmptyObject(obj) {
return JSON.stringify(obj) === '{}';
}
使用 JSON.stringify() 方法将对象序列化成一个字符串,并检查该字符串是否等于 '{}'。如果相等,则说明该对象为空。
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {
console.log(key + ': ' + obj[key]);
}
使用 for...in 循环可以遍历对象的所有属性,包括从原型链中继承来的属性。需要注意的是,在使用 for...in 循环遍历对象时,最好使用 hasOwnProperty() 方法来判断该属性是不是该对象自己的属性,而不是从原型链继承来的。
const obj = { a: 1, b: 2, c: 3 };
Object.keys(obj).forEach(function (key) {
console.log(key + ': ' + obj[key]);
});
使用 Object.keys() 方法可以获取对象的属性名组成的数组,然后可以使用 forEach() 方法来遍历该数组,从而遍历对象的所有属性。
const obj = { a: 1, b: 2, c: 3 };
Object.values(obj).forEach(function (value) {
console.log(value);
});
使用 Object.values() 方法可以获取对象的属性值组成的数组,然后可以使用 forEach() 方法来遍历该数组,从而遍历对象的所有属性值。
⭕注意:使用 for...in 循环遍历对象的坏处是,它会遍历对象的所有可枚举属性,包括从原型链中继承来的属性。而且,如果对象本身有一些不希望被遍历的属性,比如通过 Object.defineProperty() 方法定义的属性,那么这些属性也会被遍历到,导致遍历结果不准确。此外,使用 for...in 循环还存在一些性能问题,因为它需要遍历整个原型链,比较耗时。
Map 和 Object 都是 JavaScript 中用来存储键值对的数据结构,主要区别:
在 JavaScript 中,使用 const 声明的变量不能被重新赋值,但是如果这个变量是一个对象,则对象本身的属性是可以修改的。
const obj = { a: 1, b: 2 };
obj.a = 3;
console.log(obj); // { a: 3, b: 2 }
虽然 obj 是用 const 声明的,但仍然可以通过给其属性重新赋值的方式修改对象本身。
⭕如果使用 const 声明的对象本身被重新赋值,那么就会报错,例如:
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
obj1 = obj2; // 报错 "Uncaught TypeError: Assignment to constant variable."
如果尝试将 obj1 的值替换为 obj2,就会报错,因为 const 声明的变量不允许被重新赋值。
function Person(name, age) {
this.name = name;
this.age = age;
}
let person = new Person('Tom', 25);
console.log(person); // 输出:{name: "Tom", age: 25}
上述代码定义了一个构造函数 Person,并且在使用 new 操作符时传递了两个参数 "Tom" 和 25。new Person('Tom', 25) 这行代码的执行流程如下:
- 创建一个空对象 {};
- 将这个空对象的原型指向 Person.prototype,也就是 __proto__ 属性指向 Person.prototype;
- 执行 Person 构造函数,并将 {} 作为上下文(this)传递进去;
- 在该构造函数的执行过程中给 this 绑定两个属性 name 和 age,此时 {name: "Tom", age: 25} 对象就已经初始化完成了;
- 如果构造函数返回一个对象,则返回这个对象;否则返回第一步创建的新对象。在本例中,构造函数没有显式返回任何值,因此会返回新创建的对象 {name: "Tom", age: 25}。
async/await 是 ES2017 (ES8)中新增的异步编程语法糖,它们提供了一种更加简单、直观的方式来处理异步操作。
1️⃣async 关键字定义了一个函数,该函数返回一个基于 Promise 的异步操作,例如:
async function fetchData() {
const data = await fetch('/api/data');
return data.json();
}
fetchData 函数前面使用了 async 关键字,表明该函数是一个异步函数。函数体内部可以使用 await 关键字等待异步操作执行完成,然后返回结果。在这个例子中,fetchData 函数包含两个异步操作:调用 fetch API 发起一个网络请求,以及对服务器响应数据进行解析的 data.json() 操作。
2️⃣await 是一个暂停异步函数的执行,等待一个 Promise 对象产生结果,它只能在 async 函数内部使用。await 表达式在产生结果时会恢复异步函数的执行。如果 await 后面的表达式是一个 Promise 对象,则表示异步操作需要等待该 Promise 对象的状态变为 resolved,并返回 resolve 的结果。例如:
async function fetchData() {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
}
将 fetch 返回的 Response 对象存储在 response 变量中,然后使用 await 等待 response.json() 的执行结果,并将返回的 JSON 数据赋值给 data 变量。最终在控制台中打印出解析出来的数据。
Promise 是 ES6 标准中新增加的一个异步编程解决方案。在前端项目中,Promise 可以用于处理异步请求、数据的处理和处理多个异步请求之间的依赖关系等方面,主要应用于以下几个方面:
Promsie.all 是一个常用的 Promise 方法,它可以接收一个 Promise 数组作为参数,返回一个新的 Promise 对象,当所有的 Promise 都成功时,返回的 Promise 对象会变成 fulfilled 状态,此时它的返回值是一个包含所有成功 Promise 返回值的数组。如果其中一个 Promise 出现了错误,则返回的 Promise 对象会变成 rejected 状态,此时它的错误信息是第一个被 reject 的 Promise 的错误信息。
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
let count = 0;
const results = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
count++;
results[i] = res;
if (count === promises.length) {
resolve(results);
}
}).catch(err => {
reject(err);
});
}
});
}
创建了一个新的 Promise 对象,然后遍历传入的 Promise 数组,在每个 Promise 对象上使用 then() 方法注册一个回调函数,当 Promise 对象成功时将返回值存入结果数组中,并递增计数器 count,当所有 Promise 对象都成功后,判断计数器是否等于 Promise 数组的长度,如果是则调用 resolve() 方法,返回结果数组;反之则不处理。另外,当一个 Promise 对象出现错误时,则直接调用 reject() 方法,将错误信息返回给 Promise.all 的调用者。
⭕注意:实现 Promise.all 时需要考虑到一些边界情况,例如传入的参数不是一个数组、数组是空的、Promise 对象数量为0 等情况。同时也需要注意异步操作中的错误处理,防止出现未捕获的异常导致程序崩溃。
JWT(JSON Web Token)是一种轻量级的跨域身份验证方案,它通过在用户和服务器之间传输加密的 JSON 数据来实现认证授权操作。JWT 由三部分组成:头部、载荷和签名。其中,头部包含了令牌的元数据,例如令牌类型和加密算法;载荷则包含了包含用户信息等有意义的数据;签名则通过密钥进行加密,用来保证数据不被篡改。
JWT 的使用流程通常如下:
⭕注意:由于 JWT 是基于 token 的认证机制,所以需要特别注意令牌的安全性,避免泄露加密密钥或遭到 CSRF 攻击等安全问题。同时也要注意令牌过期时间的设置,及时更新令牌以保证身份验证的安全性。
Promise 是一种用于解决 JavaScript 异步编程的技术。在原始的回调函数模式下,容易出现回调地狱、难以维护等问题,而 Promise 可以将异步操作以更优雅的方式组织起来,提高可读性和可维护性。
Promise 的内部原理主要包括以下三个部分:
Promise 的优点:
Promise 的缺点:
1️⃣call() 和 apply() 的作用是一样的,都是在指定的 this 指向的情况下调用函数。区别在于传参的方式不同:call() 的参数是一个一个传递的,而 apply() 的参数是以数组的形式传递的。
func.call(thisObj, arg1, arg2, ...);
func.apply(thisObj, [arg1, arg2, ...]);
2️⃣bind() 方法与 call() 和 apply() 的作用类似,但是它不会立即执行函数,而是返回一个新的函数,并将 this 的指向绑定到第一个参数中。
let newFunc = func.bind(thisObj, arg1, arg2, ...);
bind() 除了能改变 this 的指向外,还可以在新函数被调用时,指定一些默认参数,这在一些场景下非常实用。
内存泄漏指的是在应用程序中,对象已经不再被使用,但是由于某些原因还是占用了内存空间,导致系统的可用内存逐渐减少,最终可能会导致系统崩溃或者应用程序崩溃的情况。
在前端开发中,一些常见的导致内存泄漏的原因包括:
为了避免内存泄漏,需要注意以下几点:
哪些方法会改变原数组?
typeof 操作符是最基本、最常用的一种检测数据类型的方式,它返回一个字符串,表示变量的数据类型。例如:
typeof 123 // 返回 "number"
typeof 'hello' // 返回 "string"
typeof true // 返回 "boolean"
typeof null // 返回 "object"
typeof undefined // 返回 "undefined"
typeof [1,2,3] // 返回 "object"
typeof {name:'Tom'} // 返回 "object"
typeof function(){} // 返回 "function"
⭕注意:typeof 对 null 值的检测会返回 "object",这是一个历史遗留问题
instanceof 操作符用于检测某个变量是否属于指定的对象类型,它的语法如下:
obj instanceof Object
obj 表示要检测的变量,Object 表示指定的对象类型。例如:
var arr = [1,2,3];
arr instanceof Array // 返回 true
arr instanceof Object // 返回 true
var date = new Date();
date instanceof Date // 返回 true
date instanceof Object // 返回 true
function fn(){}
fn instanceof Function // 返回 true
fn instanceof Object // 返回 true
⭕注意:instanceof 只能用于检测对象类型,不能用于检测基本数据类型
constructor 属性指向对象的构造函数,通过它可以判断一个对象的类型。例如:
var arr = [1,2,3];
arr.constructor === Array // 返回 true
arr.constructor === Object // 返回 false
var date = new Date();
date.constructor === Date // 返回 true
date.constructor === Object // 返回 false
function fn(){}
fn.constructor === Function // 返回 true
fn.constructor === Object // 返回 false
var num = 123;
num.constructor === Number // 返回 true
⭕注意:constructor 属性只能用于检测对象类型,不能用于检测基本数据类型
Object.prototype.toString() 方法可以返回一个对象的类型,它比 typeof 更加准确。例如:
Object.prototype.toString.call(123) // 返回 "[object Number]"
Object.prototype.toString.call('hello') // 返回 "[object String]"
Object.prototype.toString.call(true) // 返回 "[object Boolean]"
Object.prototype.toString.call(undefined) // 返回 "[object Undefined]"
Object.prototype.toString.call(null) // 返回 "[object Null]"
Object.prototype.toString.call([1, 2, 3]) // 返回 "[object Array]"
Object.prototype.toString.call({name: 'Tom', age: 18}) // 返回 "[object Object]"
Object.prototype.toString.call(function () {}) // 返回 "[object Function]"
Object.prototype.toString.call(new Date()) // 返回 "[object Date]"
⭕注意:需要将 Object.prototype.toString() 方法与 call() 或者 apply() 方法一起使用
工厂模式是一种用于创建对象的设计模式,它可以将具体的对象创建逻辑从客户端代码中分离出去,从而提高应用程序的灵活性和可维护性。
用一个函数来创建实例,返回 new 创建的实例 (隐藏了new关键字) 场景:
function $(...rest){ //无论多少参数都一并解构在这里
return new Foo(...rest)
}
$("xxx")
单例模式是一种用于保证对象仅被实例化一次的设计模式,它在需要频繁创建对象的场景中可以减少系统开销,提高应用程序的性能表现。
是一个全局的,唯一的实例。
class Dog{
constructor(){}
static getInstance(){
return Dog._instance
}
}
let dl = new Dog();
let d2 = new Dog();
console.log(d1===d2); // false
let d3 = Dog.getInstance(); // 这样才能创建单例模式的对象
let d4 = Dog.getInstance();
console.log(d3===d4); // true
模态窗口是一种常见的前端界面设计,它可以在弹出窗口后阻止用户对其他窗口的访问,同时强制用户完成窗口内的操作。模态窗口通常用于提示信息、交互式表单、登录窗口等场景。
实现模态窗口通常有两种方法:
- 使用 JavaScript 和 CSS 实现:通过 JavaScript 控制 CSS 样式的变化,实现弹出窗口的显示和隐藏,并使用绝对定位将其居中显示。为了阻止用户对其他窗口的访问,可以设置一个遮罩层,将弹出窗口放置在其上方,并将遮罩层的 z-index 属性设置为较高的值,使其占据整个视图并阻止用户对其他元素的操作。
- 使用框架组件实现:现代前端框架通常都提供了模态窗口的组件,例如 Bootstrap 中的 Modal 组件、Ant Design 中的 Modal 组件等。使用这些组件可以快速地实现模态窗口,并且组件通常已经包含了大多数常见的需求,例如自动居中显示、遮罩层的添加等。
Proxy 访问一个对象属性之前先做一个拦截(做一些额外的业务或者逻辑操作)。
Vue3响应式原理
let obj = {
name:"Vue"
age:9
}
let obj2 = new Proxy(obj,{
get(target,property)
// 这个get函数 什么时候执行? 访问obj2的某个属性的时候就来执行
// target 就是obj这个对象
// property 就是访问的这个属性
return target[property]
}
set(target,property,newVa1){
// 这个set函数 什么时候执行? 修改obj2属性的时候执行
// 数据的劫持/拦截
//target 就是obj这个对象
//property就是访问的这个属性
//newVal 接收新的值
target[property] = newVal;
}
})
obj2.age=10 // 触发set
console.1og(obj2.age)
观察者模式是一种基于事件驱动的设计模式,它主要用于处理对象之间的消息通信。观察者模式可以将消息的发送者和接收者解耦,从而提高应用程序的可复用性和可扩展性。
一个主题,一个观察者。主题发生变化,就触发了观察者的执行(没有媒介)。
btn.addEventListener('click',()=>{})
发布订阅者模式是一种常见的设计模式,用于实现多个组件之间的松耦合通信。在这种模式中,发布者向订阅者发送消息,订阅者可以根据自己的需要订阅感兴趣的消息,从而实现针对性的消息处理。
针对不同的场景和需求,可以使用不同的实现方式来实现发布订阅者模式,例如自定义事件、Vue.js 中的事件处理、Redux 中的状态管理等。
发布者 订阅者 第三方
function fn1(){
//事件函数1
}
function fn2(){
// 事件函数2
}
//第三方 eventBus
//开启自定义事件监听
eventBus.on("eventName",fn1); //订阅者1
eventBus.on("eventName",fn2); //订阅者2
// 触发自定义事件eventName,从而执行fn1函数
eventBus.$emit('eventName');// 发布者$emit
// 删除自定义事件,防止内存泄漏。 在声明周期中的销毁阶段
eventBus.off('eventName',fn1);
保证原有函数功能不变的同时,增加一个新的功能(AOP面向切面编程)。
用于动态地给对象添加新的行为或修改原有行为。
装饰器是一种包装对象的方式,它可以增强对象的功能,同时又不会对原有对象造成任何改变。 装饰器模式可以应用于很多场景,例如实现日志记录、性能监控、数据埋点、缓存数据、权限控制等。应用于 Vue.js 中的 Mixin 混入、React 中的高阶组件(HOC)等
场景:
function deco(){
return function(){
console.log("给fn函数增加新的功能!")
}
}
@deco()
function fn(){
console.log("这是我正常的功能函数")
}
fn()
在任何一个前端项目中,访问服务器获取数据都是很常见的事情,但是如果相同的数据被重复请求了不止一次,那么多余的请求次数必然会浪费网络带宽,以及延迟浏览器渲染所要处理的内容,从而影响用户的使用体验。如果用户使用的是按量计费的方式访问网络,那么多余的请求还会隐性地增加用户的网络流量资费。因此考虑使用缓存技术对已获取的资源进行重用,是一种提升网站性能与用户体验的有效策略。
缓存的原理是在首次请求后保存一份请求资源的响应副本,当用户再次发起相同请求后,如果判断缓存命中则拦截请求,将之前存储的响应副本返回给用户,从而避免重新向服务器发起资源请求
缓存的技术种类有很多,比如代理缓存、浏览器缓存、网关缓存、负载均衡器及内容分发网络等,它们大致可以分为两类:共享缓存和私有缓存。共享缓存指的是缓存内容可被多个用户使用,如公司内部架设的Web代理;私有缓存指的是只能单独被用户使用的缓存,如浏览器缓存。
HTTP 缓存应该算是前端开发中最常接触的缓存机制之一,它又可细分为强制缓存和协商缓存,二者最大的区别在于判断缓存命中时,浏览器是否需要向服务器端进行询问以协商缓存的相关信息,进而判断是否需要就响应内容进行重新请求。下面就来具体看HTTP缓存的具体机制及缓存的决策策略。
在任何一个前端项目中,访问服务器获取数据都是很常见的事情,但是如果相同的数据被重复请求了不止一次,那么多余的请求次数必然会浪费网络带宽,以及延迟浏览器渲染所要处理的内容,从而影响用户的使用从验。如果用户使用的是按量计费的方式访问网络,那么多余的请求还会隐性地增加用户的网络流量资费。因此考虑使用缓存技术对已获取的资源进行重用,是一种提升网站性能与用户体验的有效策略。
缓存的原理是在首次请求后保存一份请求资源的响应副本,当用户再次发起相同请求后,如果判断缓存命中则拦截请求,将之前存储的响应副本返回给用户,从而避免重新向服务器发起资源请求。
缓存的技术种类有很多,比如代理缓存、浏览器缓存、网关缓存、负载均衡器及内容分发网络等,它们大致可以分为两类:共享缓存和私有缓存。共享缓存指的是缓存内容可被多个用户使用,如公司内部架设的Web代理; 私有缓存指的是只能单独被用户使用的缓存,如浏览器缓存。
HTTP 缓存应该算是前端开发中最常接触的缓存机制之一,它又可细分为强制缓存和协商缓存,二者最大的区别在于判断缓存命中时,浏览器是否需要向服务器端进行询问以协商缓存的相关信息,进而判断是否需要就响应内容进行重新请求。下面就来具体看HTTP缓存的具体机制及缓存的决策策略
浏览器缓存(Brower Caching) 是浏览器在本地磁盘对用户最近请求过的文档进行存储时,浏览器就可以直接从本地磁盘加载文档。
浏览器是如何判断是否使用缓存的:
浏览器会比较服务器返回的响应头中的“Expires”字段(指定了缓存过期时间)和“Cache-Control”字段(指定了缓存的验证方式和缓存过期时间)与本地缓存中的相应信息,来决定是否使用缓存。如果响应头中的缓存控制信息与本地缓存中的匹配,则浏览器可以直接从缓存中获取资源,否则就需要重新向服务器发起请求获取最新资源。
另外,浏览器还可以通过发送“If-Modified-Since”或“If-None-Match”等条件请求头来验证缓存是否仍然有效,从而避免重复下载资源,提高页面加载速度。
浏览器缓存的优点有
浏览器缓存主要有两类: 缓存协商 和 彻底缓存,也有称之为协商缓存和强缓存。
1.强制缓存: 不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中可以看到该请求返回 200的状态码
2.协商缓存: 在使用本地缓存之前,需要向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源; 协商缓存可以解决强制缓存的情况下,资源不更新的问题。
⭕两者的共同点:都是从客户端缓存中读取资源,区别是强缓存不会发请求,协商缓存会发请求。
Expires: response header里的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存。
Cache-Control: 当值设为 max-age=300 时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
cache-control 除了该字段外,还有下面几个比较常用的设置值:
Expires: 设置以分钟为单位的绝对过期时间,设置相对过期时间,max-age指明以秒为单位的缓存时间.
Expires优先级比Cache-Control低,同时设置Expires和Cache-Control则后者生效
跨域指的是在浏览器中,当一个网页的脚本在访问另一个网页的内容时,如果这两个网页的协议、域名或端口号有任何不同,都会触发跨域问题。
当前页面URL | 被请求页面URL | 是否跨域 | 原因 |
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源 (协议、域名、端口号相同) |
http://www.test.com/ | https://www.testcom/index.html | 跨域 | 协议不同 (http/https) |
http://www.test.com/ | 百度一下,你就知道 | 跨城 | 主域名不同 (test/baidu) |
http://ww.test .com/ | http://blog.test.com/ | 跨城 | 子域名不同 (www/blog) |
http://www.testcom:8080/ | http//www.test.com:7001/ | 跨城 | 端口号不同 (80807001) |
浏览器的同源策略 是造成 跨域 的主要原因。
同源策略是浏览器中的一种安全策略,保证当两个URL的协议、域名、端口有任何不同时,限制它们之间的交互不加限制则可能导致:
恶意网页获取其它网页的数据、修改DOM、操作DOM获取用户输入的值、读取Cookie、发送Ajax请求。
let document2 = opener.document;
document2.body.style.display = "none";
JSON with Padding 不受同源策略影响的标签: 、
设置响应头中的 Access-Control-Allow-Origin 字段,该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
res.header('Access-Control-Allow-Origin'.'*')
// res.header('Access-Control-Allow-Origin'http://localhost:3000')
同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。
反向代理指的是客户端与服务器之间存在一个代理服务器,客户端通过向代理服务器发送请求,代理服务器再将请求转发给实际的服务器进行处理,并将处理结果返回给客户端。在这个过程中,由于客户端始终是向同源的代理服务器发送请求,因此不会出现跨域问题,同时代理服务器也可以利用自身的能力对请求进行拦截、缓存、负载均衡等操作,提高系统的可靠性和性能。
在使用反向代理解决跨域问题时,通常需要在服务器端配置相应的代理规则。例如,在使用 Nginx 作为代理服务器时,可以通过配置 Nginx 的 location、proxy_pass、proxy_set_header 等指令,将客户端的请求转发到实际的服务器中,并支持对响应结果的加工和修改。
⭕注意:反向代理虽然可以有效地解决跨域问题,但也会带来额外的系统开销和维护成本。
vue.config.js 中的配置
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
proxy:{
'/api':{
target:'http://localhost:3000',
changeOrigin: true,// 是否开启跨域,值为 true 就是开启,false 不开启
pathRewrite: {
'^/api':''
}
}
}
}
})
把实际请求转到了本地运行的服务,再转发到后端地址,从而跳过了浏览器及其同源策略,也就不会跨域。
server{
listen 5000;
server_name localhost;
location /api {
add header Access-Control-Allow-Origin 'http://localhost:3000';
proxy_pass http://localhost:4000:
}
}
postMessage 是 HTML5 中新增的跨文档消息传递机制,是为数不多可以跨域操作的 window属性之一,它可用于解决以下方面的问题:
那前端用的是什么语言呢?
2️⃣ 什么是后端?
那后端用的是什么语言呢?
Java,PHP,Python,Go,JavaScript等等
3️⃣前后端关系
那前端代码和后端代码放哪里呢?
既然前端代码放哪都行,所以一般情况,前后端代码都放在服务器 (不同的位置)
⭕注意 代码运行:
key值 的主要作用:给元素添加一个唯一标识符, 用于提高Vue渲染性能。
⭕key值为什么不能是下标:
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。
状态提升:指将组件间共享的状态提升到它们的最近的公共祖先组件中,然后通过 props 将状态传递给兄弟组件,从而实现兄弟组件之间的通信。
事件总线(Event Bus):被看作是一个共享的中央事件管理器,组件都可以向该事件管理器注册事件监听器和触发事件,从而实现组件之间的通信。
事件总线可以通过一个单独的 Vue 实例来创建,这个实例可以作为一个中央事件管理器,用于分发事件和接收事件。在一个 Vue 实例中,通过 Vue.prototype.$bus 来创建事件总线,并将其挂载到 Vue 的原型上:
Vue.prototype.$bus = new Vue()
可以在任何组件中访问 $bus 实例,例如可以通过 $bus.$on() 方法注册事件监听器,通过 $bus.$emit() 方法触发事件:
{{ message }}
当 childA 组件中的按钮被点击时,它会通过 $bus.$emit() 方法触发 send-message 事件,并向 Brother 组件发送一条消息。在 childB 组件中,可以通过 $bus.$on() 方法监听 send-message 事件,并更新页面上的消息内容。
⭕注意:在事件总线中注册的事件监听器可能会引起命名冲突和内存泄漏等问题。
情况分类:
provide 可以在父组件中声明一个对象、函数或者属性,使其子孙组件(不管嵌套多深)可以通过 inject 获取到这些内容,并在组件中直接使用。
⭕注意:provide 和 inject 并不是响应式的,只能用于单向数据流的情况下,也就是父组件提供的数据不能够在子组件中修改。如果想要实现双向数据流,则需要使用 v-model 或者事件触发等其他方式。
发布-订阅模式,因此可以在任意组件中发送和接收事件。在 Vue.js 的组件中,可以在 mounted() 钩子函数中注册事件的监听器,并在 beforeDestroy() 钩子函数中注销监听器,以避免出现内存泄漏的问题。
① Vuex 的设计思想基于 Flux 模式,通过单向数据流的方式来管理应用状态。它将 Vue.js 应用中的状态抽象成一个个 store,每个 store 包含了一些状态(state)、状态的修改方法(mutation)和异步修改状态的方法(action)。同时,Vuex 还提供了 getter 来获取派生状态(derivative state)。
在使用 Vuex 时,需要先创建一个 Vuex store 实例,并将其传递给根组件。然后,在组件中可以通过 $store.state 访问状态、通过 $store.commit() 提交 mutation、通过 $store.dispatch() 触发 action、通过 $store.getters 获取派生状态。
② Pinia 设计思想基于 Vue.js 3 的新特性,使用了 Reactivity API 和 Composables 的概念,并采用了类似于 Vuex 的 store 架构来管理应用的状态。Pinia 在语法和使用方式上都比 Vuex 更加简洁、灵活,同时具有更好的性能、类型安全和开发体验。
在使用 Pinia 时,需要先创建一个 Pinia store 实例,并将其传递给根组件。然后,在组件中可以通过 useStore() 函数来获取 store 实例,并访问它的状态、提交 mutation 和 action、获取派生状态。
总结:Vuex 更加成熟、稳定,具有较好的生态和社区支持,更适合于大型应用的状态管理;而 Pinia 更加轻量、灵活,具有更好的 TypeScript 支持和开发体验,更适合于小型、中型应用的状态管理。选择哪个状态管理库需要根据实际情况进行权衡和取舍。
1️⃣功能上:Object.defineProperty 只能劫持对象的属性,它需要遍历对象属性进行劫持,对于新增、删除属性不够友好;而 Proxy 可以劫持整个对象,包括内部属性,同时还支持新增和删除属性,因此具有更好的灵活性。(Object.defineProperty 主要用于定义对象属性的特性,例如:可读、可写、枚举等;而 Proxy 可以劫持对象的底层操作,包括 get、set、has、deleteProperty 等多种操作。)
2️⃣性能上:由于 Object.defineProperty 需要遍历对象属性进行劫持,所以当对象属性较多时,遍历的开销也会增加,影响代码的性能;而 Proxy 则不需要这些操作,它处理数据的性能比 Object.defineProperty 更快。
3️⃣兼容性:Object.defineProperty 出现较早,但是在某些浏览器中不被支持(如 IE8),所以在开发时需要考虑兼容性问题;而 Proxy 是 ES6 标准中新增的功能,如果需要支持旧版浏览器,就需要使用 polyfill 进行兼容。
4️⃣动态特性:Object.defineProperty 只能对已有的属性进行更改或监听,并且需要显式地为属性设置特性;而 Proxy 可以动态地针对对象上的任何操作进行拦截,并且可以根据自定义逻辑实现更为灵活和高效的代理。
①Object.defineProperty 是 JavaScript 中一个用于定义对象属性的方法,它可以给一个对象定义一个新属性或修改一个已有属性,并控制对这个属性的访问和操作。
其本质是通过代理模式来实现的,当我们调用 Object.defineProperty 方法时,它会在目标对象上创建一个新的属性或修改一个已有属性,并返回目标对象本身。
在定义属性时,可以使用一些特殊的描述符来控制属性的特性,例如:value、writable、enumerable 和 configurable 等。其中,value 表示属性的值,writable 表示属性是否可写,enumerable 表示属性是否可枚举,configurable 表示属性是否可删除或修改特性。
当定义一个属性时,Object.defineProperty 方法会将该属性转化为 getter 和 setter 形式,从而实现控制对属性的访问和操作。当我们读取属性时,会调用 getter 方法;当我们修改属性时,会调用 setter 方法。
需要注意的是,由于 Object.defineProperty 方法只能监听到对象的属性变化,而不能监听到对象本身的变化,因此它无法对对象的新增或删除操作进行监听。如果需要监听对象的新增或删除操作,应该使用 Proxy 对象。
②Proxy 是 ES6 中新增的一个对象,它可以用于拦截对象的底层操作,并在拦截时执行自定义的逻辑。本质是一种元编程机制,在目标对象之间创建一个代理层,并拦截目标对象上的底层操作。当我们对代理对象进行读取、赋值、方法调用等操作时,该操作会被拦截到,进而触发相应的拦截器函数。这些拦截器函数包括了多种操作,例如:get、set、has、deleteProperty、apply 等。
过程为:使用 Proxy 对象时,需要传入一个目标对象和一个处理程序对象。处理程序对象中包含了一些拦截器函数,用于拦截该目标对象上的不同操作。当对目标对象进行操作时,Proxy 会自动调用相应的拦截器函数,并以该操作为参数来调用该函数,从而实现对目标对象的拦截。
⭕注意:由于 Proxy 对象是在目标对象之间创建代理层,因此使用 Proxy 可能带来一些性能损失。另外,由于 Proxy 对象是 ES6 新增的特性,不是所有浏览器都支持,因此在使用时需要注意兼容性。
Vue组件 是一个可复用、独立、有相对完整功能的 UI 组件。在 Vue 中,组件可以看做是拥有自己作用域和状态(数据)的 Vue 实例。每个组件都包含了模板、脚本、样式和各种功能逻辑,在应用中可以被反复使用。
Vue 组件的优点包括:
Vite采用ESM原生模块作为开发模式,在编译前不会将所有代码打包成一个文件,而是以“即时编译”的方式运行,仅编译当前正在编辑的文件及其相关依赖模块,从而提高了项目的启动速度和开发效率。在生产模式下,Vite会将所有需要的模块打包为一个或多个文件。另外,Vite还内置了对.vue文件的支持,并使用了Rollup作为构建工具以提供更快的构建速度。
Webpack则相对复杂一些,它采用CommonJS模块,需要通过配置文件来进行打包构建,所有模块都会被打包成一个或多个文件,因此启动时间较长,但它支持的功能和插件非常丰富,能够实现许多高级的构建场景和优化策略。另外,Webpack还支持热更新、Code Splitting等功能,能够实现更多的构建需求。
综上所述,Vite适合开发小型项目、快速迭代和H5开发等场景,而Webpack适合更加复杂的项目,它能够通过各种插件和配置文件来实现更加灵活和强大的构建功能。
assets 目录和 static 目录都是存放静态资源文件的目录,但
⭕区别:
相同点:都可以控制元素的显示和隐藏
区别:
1.createApp() —> 创建一个应用实例
说明: 等于vue2的—>new Vue()
使用场景: 写插件(封装全局组件会使用)
2.provide / inject —> 依赖注入
说明: 其实就是传值
使用场景: 某一个父组件传值 到后代组件,如果层级过多传递麻烦,所以使用缺点:不好维护和查询数据来源
3.directive
说明:自定义指令
场景:后台管理系统中的按钮权限控制( 一个用户拥有某些权限,但是只能查看和修改,不能删除)
4.mixin 将一组复用的选项合并成一个对象,并注入到组件中的方式
5.app.config.globalProperties
说明:获取vue这个全局对象的属性和方法
场景:自己封装插件的时候需要把方法添加到对象中
6.nextTick
说明: 等待下一次 DOM 更新刷新的工具方法: nextTick返回一个Pormise,回调函数是放在Promise中的,所以是异步执行的
场景: 就是把dom要更新,那么Vue是数据驱动dom,所以数据的赋值就要在nextTick进行
7.computed
8. reactive、ref
说明: 来定义数据的和Vue2的data类似
9.watch
说明: 监听 (Vue3不需要深度监听)
10.markRaw()
说明: 不被 new Proxy 代理,说白了就是静态的数据
11. defineProps()
说明: 父组件传递的值,子组件使用 setup 的形式,需要用 defineProps 接收
12. defineEmits()
当前组件使用setup形式,自定义事件需要使用defineEmits
13. slot 一种用于插入和分发组件内容的机制。它可以让父组件向子组件中动态地插入内容,从而实现更加灵活和可复用的组件设计。
(1)Vue2 和 Vue3 双向绑定 方法不同
(2)$set 在 Vue3 中没有,因为 new Proxy 不需要
(3) v-if 和 v-for 优先级不同了
(4)关于写法
(5)ref 和 $children 也不同
- 作用对象不同:ref 用于在组件之间建立父子关系或在组件内部访问子组件,而 $children 只能访问当前组件的直接子组件。
- 使用方式不同:ref 通过在组件或元素上设置 ref 属性来访问该组件或元素,可以通过在 this.$refs 中使用组件的 ref 名称来获取对应的组件或元素;而 $children 是当前组件的一个属性,可以直接在 this.$children 中访问其子组件列表。
- 执行时机不同:ref 是在组件渲染后才能访问到对应的组件或元素,因为它们还没有生成对应的 DOM 元素;而 $children 是在组件挂载期间就能访问到其所有子组件。
- ⭕注意:由于 $children 只能访问当前组件的直接子组件,因此在嵌套较深的组件结构中使用 $children 可能会比较麻烦,而 ref 则不受这种限制,可以在任意层级的组件之间进行访问。
Vue3做了如下更新:
- ref 的使用方式发生了变化。在 Vue3 中,ref 被用作一种新的数据类型,用于对普通数据类型进行包装并提供响应式效果,而不再用于访问 DOM 元素。在使用 ref 创建 ref 对象时,需要通过 .value 访问其内部原始值,并且可以使用新的函数 reactive 和 toRefs 来替代部分使用 ref 的场景。
- $children 的使用方式没有太大变化。在 Vue3 中,它仍然是当前组件的一个属性,用于访问其直接子组件列表。但需要注意的是,在 Vue3 中,$children 会在组件挂载完成后再生成,因此在组件实例的 created 生命周期钩子中无法访问到它。(
子组件可能会依赖父组件的状态或 props 来进行渲染。如果 $children 的访问时间太早,那么子组件有可能获取不到它所需要的数据,导致渲染错误。
因此,在 Vue3 中,$children 的生成被调整到了组件挂载完成后。这样就可以在组件的 mounted 生命周期钩子函数中安全地访问 $children,确保我们访问到的子组件都已经正确地渲染和挂载了。
)
VUE 2-3 知识框架_星辰大海1412的博客-CSDN博客Vue.js 是一个由尤雨溪创建的渐进式 JavaScript 框架,是一款轻量级、高效、易用的前端开发框架。它采用了响应式数据绑定和组件化视图构建的方式,使得页面开发更加简单、直观,同时也提高了代码的可重用性和可维护性。无论是 Vue.js 2.x 还是 Vue.js 3.x 版本,都具有优秀的性能、良好的开发体验和强大的生态系统,是前端开发中不可或缺的重要工具。所以这篇文章也用于复习这些知识。https://blog.csdn.net/m0_61662775/article/details/131089903?spm=1001.2014.3001.5501
是基于Vue的应用框架,关注的是渲染,可以开发服务端渲染应用的配置,帮助开发者快速构建 SSR 应用程序和静态站点。
(1)SSR: 服务端渲染(Server-Side Rendering)
⭕优点:
(2)SEO 搜索引擎优化(Search Engine Optimization)
(3)SPA 单页应用(Single Page Application) 的应用不利于搜索引擎SEO的操作
1️⃣在传统的浏览器端渲染(Client-Side Rendering)中,Vue 组件的渲染是通过 JavaScript 在浏览器中动态生成的。这种方式可以使得用户体验更加流畅,因为组件的渲染和更新都是在客户端完成的。但是,由于首次加载时需要下载并执行大量的 JavaScript 代码,相比于传统的服务器端渲染,这会导致页面加载速度较慢,以及 SEO 和首屏渲染等问题。
而应用 SSR 技术后,当服务器收到请求时,它会使用 Vue 的 SSR 引擎将组件在服务器上预渲染成 HTML 字符串,然后将此字符串直接发送给浏览器。这样就可以在浏览器中直接呈现出完整的 HTML 内容,从而减少了页面加载所需的时间和所需的 JavaScript 数据量,提高了网页的可访问性和搜索引擎优化效果。
SSR 可以让 Vue 应用程序具有快速的首屏加载体验,提高应用程序的可访问性,并有助于提升搜索引擎优化。但是,它也有一些注意事项和限制,例如需要考虑服务端渲染和客户端渲染之间的差异,以及需要更多的负载均衡和缓存策略等。
2️⃣SEO 的目的是通过对目标受众、竞争关键词、网站结构、页面内容、外部链接等进行分析和调整,使得网站在搜索引擎中的排名更加靠前,提高网站的曝光度和流量。
SEO 可以分为内部优化和外部优化两种方式。内部优化主要包括网站结构、URL 设计、页面内容、关键词密度、图片优化、站点地图等方面;外部优化则主要包括外部链接引用和社交媒体等方面。
3️⃣SPA,即单页应用(Single Page Application),是一种使用 JavaScript 等技术在单个页面上实现多个视图的应用程序。相比于传统的多页应用(MPA),SPA 不需要每次加载新的页面,而是动态地更新 DOM 元素来呈现不同的视图。
在 SPA 中,首先通过浏览器请求加载一个基础的 HTML、CSS 和 JavaScript 文件,然后在这些文件的基础上,根据用户的操作和应用程序的状态动态更新 DOM 元素,实现不同的视图。由于只有一次完整的页面加载,因此可以提高应用程序的性能和响应速度,并更好地使用现代 Web 技术,例如 AJAX、Web 套接字等。
SPA 能够提供良好的用户体验,并且具有较高的性能表现,但是也有一些缺点,例如:
- 首屏加载时间可能会较长,需要预加载或延迟加载的优化手段;
- 对 SEO 不友好,需要使用预渲染等技术来解决;
- 复杂的状态管理和路由控制可能需要使用第三方框架进行支持。
Nuxt.js 具有以下特点:
使用 Nuxt.js 可以带来如下优势:
CDN,即内容分发网络(Content Delivery Network),是一种基于互联网的分布式网络技术,通过将静态资源文件(如图片、音视频等)缓存到分布在全球各地的节点服务器上,在用户请求时选择最近的节点进行响应,从而提高用户访问速度和体验。
传统的 Web 服务器部署只有一台或少数几台,当用户请求流量过大时,可能会导致 Web 服务器瘫痪或响应时间变慢。而 CDN 可以通过缓存和就近访问等技术,有效地分担 Web 服务器的流量压力,加速用户的访问速度和降低 Web 服务器负载。
CDN 的工作原理如下:
- CDN 首先会对静态资源进行缓存,缓存的位置通常是距离用户最近或比较重要的节点服务器上。
- 用户请求访问静态资源时,CDN 的服务节点会根据用户的地理位置和网络条件等,自动选择最近或最优的服务器节点响应请求。
- 如果 CDN 的缓存中没有用户请求的资源,它会向源站请求获取资源后缓存并返回给用户。源站也可以主动更新缓存中的资源。
- CDN 通过多层缓存和智能路由等方案,为用户提供稳定、快速的访问服务。
- CDN 的优点是可以提高访问速度、降低流量压力、保护源站的安全等。但它也需要一定的成本,同时对于动态资源如 HTML、PHP 等,CDN 的优化效果较差。因此在使用 CDN 时,需要根据具体情况进行选择和发挥其优势。
⭕数据响应式方面
1️⃣watch
可以监测数据的变化并触发回调函数。通过 watch 可以实现对某个数据进行监听,当这个数据变化时,可以触发相关操作,实现数据的同步和响应。
watch 的使用场景主要包括:
watch 的本质是使用 Object.defineProperty 或者 Proxy 监听数据的变化,在数据变化时触发相应的回调函数。当我们使用 watch 监听一个数据时,Vue 内部会自动为这个数据创建一个 watcher,并将其添加到依赖列表中。当数据发生变化时,watcher 会自动被调度执行,从而实现数据的响应式更新。
watch 使用时可以通过 immediate 和 deep 选项来控制其行为:
- immediate:当 immediate 为 true 时,watcher 会在初始值被设置后立即执行回调函数,不需要等到数据发生变化后再执行。
- deep:当 deep 为 true 时,watcher 会递归遍历对象或数组的所有子属性,监听它们的变化。
除了这两个选项外,watch 还可以接收一个 handler 函数和一个 options 对象作为参数。handler 函数接收两个参数,分别是新值和旧值;options 对象包含了一些其他选项,例如:immediate、deep、flush 等。
⭕注意:由于 watch 使用了 Object.defineProperty 或者 Proxy 来监听数据的变化,因此它只能监听到数据的变化,而不能监听到数据的新增或删除操作。如果需要监听数组中元素的新增或删除操作,应该使用 Vue.set 或者 splice 方法来修改数组,以触发数据的响应式更新。
2️⃣computed
主要是用于计算衍生数据。computed 中的属性值可以访问其他观察的数据属性,当这些属性变化时,computed 中的属性也会重新求值。
computed 的使用场景主要包括:
computed 的本质是 getter 函数,在定义 computed 属性时,我们需要指定一个 getter 函数,该函数根据需要的计算逻辑来返回相应的计算结果。
computed 的主要作用有:
- 计算属性:将一些复杂的计算逻辑封装到计算属性中,使得模板中的代码更简洁,易于理解和维护。
- 缓存:computed 的计算结果会被缓存起来,在下次使用该计算属性时,如果依赖的数据没有发生变化,就会立即返回缓存中的计算结果,避免了重复计算和不必要的性能浪费。
- 依赖追踪:computed 能够自动追踪数据依赖关系,只要依赖的数据发生了变化,computed 就会重新计算新值,保证了组件数据的响应式更新。
使用 ref 指令定义输入框元素的标识符 myInput,然后在 JS 实例中使用 this.$refs.myInput 访问这个元素,并调用原生 focus() 方法将光标自动聚焦到这个输入框中。
{{ message }}
当点击“更新消息”按钮时,会将 message 数据更新为“新消息”。实际上,这个更新操作并不会立即更新 DOM,而是等到下一个事件循环周期。因此,如果我们需要获取更新后的 DOM 内容,就需要使用 $nextTick 方法。
在实现方式上,$nextTick 接收一个回调函数作为参数,在 DOM 更新完成后执行该回调函数。在回调函数中可以访问更新后的 DOM 内容,或者执行其他需要在 DOM 更新后才能执行的逻辑。
总结:jQuery 作为一个纯粹的 JavaScript 库,可以快速地对页面进行操作和交互,但其门槛较低,适用于基础的网页开发。Vue.js 和 React 则注重组件化开发,提供了更加高效和灵活的开发方式,并且支持响应式数据绑定、虚拟 DOM 等高级特性,适用于构建大型、复杂的 Web 应用程序。
①虚拟 DOM 所提供的最主要的优势是减少 DOM 操作的次数,从而减少浏览器的重绘和重新排版,达到提高性能的目的。当我们使用 React、Vue 等框架时,它们都会通过 diff 算法比较新旧两个虚拟 DOM 的差异,并且只更新发生变化的部分,而不是每次都重新渲染整个页面。这就避免了大量的无效 DOM 操作,从而提高了应用的性能。
②虚拟 DOM 还可以跨平台使用,因为它并不依赖于具体的浏览器实现,而是通过 JavaScript 实现一个轻量、可移植的 DOM 模型。这使得我们可以在 Web、移动端、桌面端等多个平台上开发相同的代码,提高了效率和一致性。
⭕ 虚拟 DOM 缺点:
需要在 JavaScript 中构建和比较虚拟 DOM 树,这可能会占用一定的 CPU 资源和内存,从而对于特别大的应用或低端设备可能会存在性能问题。此外,虚拟 DOM 也需要学习一些新的概念和技术,入门门槛较高。
在 Vue2 中,data 选项是一个函数,而不是一个普通的对象。这是因为在组件中,如果直接使用一个对象作为 data 选项,那么所有使用该组件的实例都会共享同一个数据对象,这在某些情况下可能会导致数据混乱的问题。
为了避免这种情况的发生,Vue2 引入了“组件作用域”的概念,即每个组件实例都有自己独立的作用域。因此,为了保证每个组件实例都有自己独立的数据,我们需要将 data 选项改成一个函数。这个函数返回一个新的对象,用于作为当前实例的数据。
⭕ 注意:如果 data 选项返回的是一个对象,而不是一个函数,Vue2 会给出一个警告提示。
Pinia 是一个基于 Vue 3 Composition API 的状态管理库,它具有轻量、简单易用、类型安全等特点。它主要适用于以下场景:
Vue 中的 nextTick 方法是用来在 DOM 更新之后执行回调函数的函数。在 Vue 中对数据进行修改时,由于 Vue 是基于异步渲染的,所以数据更新并不会立即同步到 DOM 上,而是在下一个 Event Loop 中进行处理。因此,我们有时候需要等待 Vue 完成 DOM 更新后再执行一些操作,这时候就可以使用 nextTick 方法。
✍案例:假设要在 Vue 实例的某个数据变化后获取某个元素的高度,但是由于数据还未同步到 DOM 上,此时直接获取元素高度是无法得到正确结果的。这时,就可以在 nextTick 回调函数中进行操作。nextTick 方法提供了两种使用方式:
①调用 Vue 实例对象上的 $nextTick 方法,传入回调函数作为参数:
this.$nextTick(() => {
// 获取元素的高度
const height = this.$refs.myElement.clientHeight;
console.log(`Element's height is ${height}px`);
});
② 使用 Promise 返回值,通过 then 方法实现回调函数:
Vue.nextTick().then(() => {
// 获取元素高度
const height = this.$refs.myElement.clientHeight;
console.log(`Element's height is ${height}px`);
});
⭕在 React 中,类似的机制是使用 Hook 来实现,其中 useEffect Hook 可以在组件渲染完成后执行副作用(effect),可以用来监听 DOM 的更新状态。例如,我们可以使用 useEffect 来在组件渲染后获取元素的高度:
import { useState, useEffect, useRef } from 'react';
function MyComponent() {
const [height, setHeight] = useState(0);
const myElementRef = useRef(null);
useEffect(() => {
// 获取元素的高度
const height = myElementRef.current.clientHeight;
setHeight(height);
}, []); // 第二个参数表示依赖项为空数组,只有在组件挂载后执行一次
return (
This is a component.
The height of this component is {height}px.
);
}
使用 useRef Hook 来获取组件中的 DOM 元素,然后在 useEffect 中获取元素的高度并更新状态。useEffect 的第二个参数是一个数组,用于指定依赖项,如果依赖项中的值发生变化,useEffect 会重新执行。在这个示例中,由于依赖项为空数组,所以只会在组件挂载后执行一次。通过 useEffect,我们可以实现类似 Vue 中的 nextTick 的效果,等待组件渲染完成后再执行回调函数。
MVVM、MVC 和 MVP 是常见的软件架构模式,用于组织和管理软件的代码和逻辑。
1️⃣MVC(Model-View-Controller):
2️⃣MVP(Model-View-Presenter):
3️⃣MVVM(Model-View-ViewModel):
总结:
1️⃣Mixin(混入): Mixin 是一种将类的功能注入到另一个类中的机制。通过 mixin,一个类可以复用其他类的方法和属性,而无需继承整个类层次结构。
Mixin 的使用场景:
2️⃣Extends(继承):Extends 是一种类之间的关系,子类继承父类的属性和方法,并可以在子类中进行修改和扩展。
Extends 的使用场景:
总结:
在 Vue Router 中,路由拦截可以通过导航守卫(Navigation Guards)来实现。导航守卫是一组回调函数,用于控制路由导航的行为,包括在跳转前、跳转后、跳转取消等不同的阶段执行相应的逻辑。
Vue Router 提供了三种类型的导航守卫:
1️⃣全局守卫:全局守卫会在整个路由跳转过程中被触发。包含三个导航守卫:beforeEach、beforeResolve 和 afterEach。
2️⃣路由独享守卫:路由独享守卫只对指定的路由生效,它们定义在路由配置中的 beforeEnter 字段中。
3️⃣组件内守卫:组件内守卫分别在组件被复用时、路由参数发生变化时以及组件被销毁时触发。包含三个导航守卫:beforeRouteEnter、beforeRouteUpdate 和 beforeRouteLeave。
通过使用这些导航守卫,你可以在不同的阶段对路由进行拦截,执行相应的逻辑控制。例如,可以在 beforeEach 守卫中判断用户是否有权限访问某个页面,如果没有则取消跳转;或者在 beforeRouteLeave 守卫中提示用户是否放弃未保存的内容等。
⭕注意:在导航守卫中,你可以通过调用 next 方法来控制路由的行为。next 方法接受一个参数,可以指定跳转的路径或取消导航。通过使用不同的参数,你可以实现不同的路由跳转控制逻辑。
Vue-Router 动态展示路由_星辰大海1412的博客-CSDN博客Vue Router 是 Vue.js 官方提供的路由管理器,它能够实现单页面应用(SPA)中的前端路由功能。其中的动态展示路由是指根据用户的操作或其他条件,动态地加载和展示相应的页面组件。这篇文章将详细地介绍相关的操作。https://blog.csdn.net/m0_61662775/article/details/131411487?spm=1001.2014.3001.5501
1️⃣Loader 是 Webpack 中用来对模块进行预处理的工具,它允许对不同类型的文件进行转换,以满足项目所需。在 Webpack 的执行流程中,loader 会在编译阶段执行,在加载模块时将源代码转换为可执行的 JavaScript 代码。
module.exports = function(source) {
// 对模块源代码进行处理
const result = someTransformation(source);
// 返回处理后的结果
return result;
};
2️⃣Webpack 的插件(Plugin)则是用来扩展 Webpack 功能的一种机制,它可以监听 Webpack 构建过程中的各种事件,执行自定义的逻辑,实现一些特殊的需求功能。
Plugin 通常会包含一个 apply 方法,它接受一个 Compiler 对象作为参数,在 Compiler 对象的生命周期事件中注册相应的钩子函数。当 Webpack 在执行过程中触发了这些事件后,对应的钩子函数将被依次执行。通过插件机制,我们可以在 Webpack 构建过程中对各个阶段进行干预,完成一些定制化的操作,例如代码压缩、资源优化、添加环境变量等等。
class MyPlugin {
apply(compiler) {
compiler.hooks.done.tap('MyPlugin', stats => {
// 构建完成后执行的逻辑
console.log('Build is done!');
});
}
}
定义了一个名为 MyPlugin 的插件类,它注册了 Compiler 的 done 钩子函数,在构建完成后执行一些简单的输出逻辑。在使用插件时,只需要将其实例化并添加到 plugins 配置数组中即可使用。例如:
const webpackConfig = {
// ...
plugins: [new MyPlugin()],
};