自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(78)
  • 收藏
  • 关注

原创 22_快速diff算法

当这段代码运行完毕之后,j 的值为 1,此时我们还需要处理相同的后置节点,思路也是一样的。在这个例子中,p-1 在新旧两组子节点中,就属于相同的前置节点,p-2、p-3 就属于相同的后置节点。此时就可以看到,预处理之后,新旧两组子节点都存在未处理的节点,这种情况就需要判断一个节点是否需要移动,如果不需要移动,则判断是需要被添加还是被移除的节点。这里为什么说是相对索引呢,source 是从 0 开始的,而未处理的节点就未必是从 0 开始的,所以记录一个相对索引。

2024-11-01 10:09:21 711

原创 21_双端 diff 算法

简单 diff 算法的问题在于,它的移动操作并不是最优的,我们还是使用上一节的例子来看,如图:在这个例子中,我们使用简单 diff 算法来更新需要进行两次 DOM 移动,将 p-1 移动到 p-3 后面,将 p-2移动到 p-1 后面。但是其实只需要一次移动 DOM 即可,就是将 p-3 移动到 p-1 的前面即可。这一点想要实现,是简单 diff 算法无法做到的,而使用双端 diff 算法就可以。顾名思义,双端 diff 算法是一种同事对新旧两组子节点的两个端点进行比较的算法,因此我们需要四个索引值,分别

2024-11-01 10:06:36 784

原创 20_简单的diff算法

取新的子节点的第二个节点 p-1,它的 key 为 1,尝试在旧的子节点中找一个可复用的节点,找到了,并且找到的复用节点在旧的子节点中索引为 0,此时变量 lastIndex 的值为 2,取新的子节点的第二个节点 p-2,它的 key 为 2,尝试在旧的子节点中找一个可复用的节点,找到了,并且找到的复用节点在旧的子节点中索引为 1,此时变量 lastIndex 的值为 2,在前文,我们针对新旧vnode 的 children 都是数组时,采用的方案是,先卸载旧节点的子元素,在重新挂在新节点的子元素。

2024-10-29 11:24:40 1018

原创 19_文本、注释、Fragment节点

类似的组合还有 select 和 options 这两个标签,而 vue3 中支持多个根节点模板,所以不存在这个问题,而 vue3 中是如何描述这个多根节点模板呢?Fragment 是 vue3 新增的一个 vnode 类型,在实现这个类型之前,我们可以先聊一下为什么需要这个类型。而 Fragment 本身并不会渲染任何内容,因此只会渲染 Fragment 的子节点。而 Items 组件负责渲染一组。

2024-10-29 11:19:22 677

原创 18_更新子节点

简单实现vue3更新子节点的操作

2024-10-25 10:33:24 191

原创 17_事件的处理

是创建浏览器上下文的时间,在这里可以简单理解为一个只会增加的时间,页面刷新就会重置重新计算,所以首次渲染之后,点击 p 元素,触发点击事件,将 bol 的值改为 true,此时会执行 effect 然后绑定事件,并记录下绑定的时间,重点来了,此时的 e.timeStamp 触发的时间是 p 事件触发的时间,而非这个冒泡到 div 的事件触发时间,所以这个 timeStamp 一定比 div 的事件绑定时间要早,而比这个早就会被。这样就解决了我们的问题。解决初次绑定,那么如果是更新事件应该如何处理呢?

2024-10-25 10:31:16 1244

原创 16_区分vnode的类型

在前文中,我们进行了一个简单的打补丁的操作,但是这个操作是有一些前提的,比如新旧 vnode 描述的内容相同,比如首次渲染的 vnode 的 type 是一个 div,二次渲染时,这个 vnode 的 type 改为了一个 p,而这种类型都不同的 vnode,其实就没有打补丁的意义,毕竟元素标签都不同了。

2024-10-24 00:29:58 215

原创 15_卸载操作

正确的卸载方式应该是,根据 vnode 对象获取对应与其相关联的真实 DOM 元素,然后使用原生 DOM 操作方式将该元素移除。在之前我们就提到,首次渲染之后,后续如果再调用 render 函数时,传递的 vnode 为 null 则表示是卸载。

2024-10-24 00:07:51 654

原创 14_挂载子节点和元素的属性

通过这个现象可以发现,用户对文本框的修改并不会影响 el.getAttribute(‘value’) 的返回值,这个现象蕴含着 HTML Attributes 所代表的意义,实际上,HTML Attributes 的作用是设置与之对应的 DOM Properties 的初始值,一旦值改变,那么 DOM Properties 始终存储着当前值,而通过 getAttribute 函数得到的值仍然是初始值。而如果用户修改了文本框的值,那么 el.value 的值就是当前文本框的值。

2024-10-23 12:05:34 1185

原创 13_渲染器的设计

观察这个对象,我们使用 type 来表示一个 vnode 的类型,不同类型的 type 属性值可以描述多种类型的 vnode。当 type 为属性值为字符串时,表示一个普通的 html 标签,并使用 type 属性的属性值作为标签的名称。// 创建一个渲染器// 调用 render 函数渲染该 vnode// 在这里编写逻辑if(vnode){} else {// 卸载return {render// 如果 n1 不存在,则执行挂载,使用 mountElement 函数完成挂载。

2024-10-23 12:03:58 1116

原创 12_实现 ref

这个 ref 内部的 value 的值需要根据 isShallow 来进行判断,因为我们还会实现一个 shallowRef 方法,来作为浅层次的 ref,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。这里出于严谨考虑,并没有采用上一节的,直接使用一个字符串,而是换成了 symbol,当然,目前这里只有根据标识符判断,而没有添加,稍后会进行添加。在上一节中,我们初步实现了一个 ref,可以使用,但是实现结构不够好,本节我们仿照 Vue3 源码重新实现一下。

2024-10-22 11:11:07 666

原创 11_原始值的响应式方案-ref

可以看到,我们利用 proxy 的 get 拦截器作为插入代码逻辑的地方,在这里我们通过判断一个值是否是 ref 数据,如果是在自动的在这里读取 refVal.value。有了自动脱 ref 的能力之后,就可以降低用户在使用时的心智负担,无需关心那个属性是 ref 那个属性是普通数据或者是 reactive。因此,我们需要脱 ref 能力,所谓自动脱 ref,就是指的属性访问行为,即 ref 数据无需通过 xx.value 来访问。然而,这样书写就会失去响应式,当我们修改数据时,不会触发渲染,这是为什么?

2024-10-22 11:10:05 1834

原创 10_实现readonly

我们虽然在创建 readonly 函数时,给 createReactiveObject 的第二个参数是 false,表示是深响应的,但从结果可以看到,没有被拦截,而且依然被修改了。在某些时候,我们希望定义一些数据是只读的,不允许被修改,从而实现对数据的保护,即为 readonly。

2024-10-19 17:29:10 382

原创 09_实现reactive之代理 Set 和 Map

而 sProxy.delete 是一个方法,sProxy.delete 是访问状态,并没有真的执行,只有当 sProxy.delete(1) 才是真正执行了,但是这个执行是外部的用户操作的,因此就算我们一样设置 Reflect.get(target, key, target),也不行,因为 JavaScript 中 this 的指向通常需要函数调用那一刻才能确定,所以最后还是变成 sProxy 来调用。至此,我们还要一项缺陷没有补充完成,那就是与值的响应式联系,与 for…in 不同,for…

2024-10-19 17:27:48 1241

原创 08_实现 reactive

本章主要实现了 reactive 对 Object 和 Array 的数据代理

2024-10-17 18:27:08 1352

原创 07_理解 Proxy 和 Reflect

这里的 this 表示其实是 obj 这个原始对象,而非 objProxy 这个代理对象,因为我们在 get 拦截函数中,是直接使用 targt[key] 这样来返回值的,这就等于是 obj.xx 进行访问属性,自然 this 就表示是 obj 这个原始对象,而原始对象自然不会进行依赖的收集,所以只触发了一次。,所以直接使用这种括号的方式是无法指定这个 this 了,但是我们可以通过调用这个 Reflect.get 方法,来完成这个指定 this 的操作。是用于获取当前执行上下文中的。

2024-10-17 18:16:51 343

原创 06_实现watch

watch 本质上就是监听一个响应式数据,这个响应式数据发生变化的时候,进行通知并触发相应的回调函数。

2024-10-16 10:27:58 1410

原创 05_计算属性 computed 和 lazy

可以看到修改值之后,并没有在重新打印 effect 的副作用函数,那么体现实际 Vue 中,即计算属性改变了值,但是模板没有重新渲染,而导致出现这个问题的原因也简单,fullName.value 触发 get value,get value 中第一次会调用 computed 内部的 effectFn 函数来返回 getter 的值,而 getter 也是被一个 effect(即 effect(getter))包裹的;这一文中有讲到,**外部 effect 不会被内部 effect 中的响应式数据收集。

2024-10-16 10:24:45 909

原创 04_实现调度执行

什么是可调度?所谓可调度,指的是当 trigger 动作触发副作用函数重新执行的时候,有能力决定副作用函数执行的时机、次数以及方式。

2024-10-15 16:38:36 878

原创 03_处理响应式系统的边界情况

objProxy.a++ 可以划分为 objProxy.a = objProxy.a + 1,这里 objProxy.a 进行了获取,然后又执行了 objProxy.a 的 赋值,也就说进行了读的操作之后,就又进行了写的操作,写的操作又会导致执行副作用函数,而上一次的副作用函数还没执行完成,就立马进行了下一次的执行,而这个下一次的执行又会重新执行这个步骤,这样无限的递归调用自己,就会导致栈溢出。要解决这一点其实也简单,只要每次重新执行依赖之前,就把之前旧的依赖清除掉即可。

2024-10-15 16:36:40 660

原创 02_响应式数据的基本实现

结果也正如我们所期望的那样,既然实现了拦截,那么此时就可以植入我们需要的代码逻辑,在 get 时收集执行的函数,set 时在触发,此时我们会发现一个问题,我 get 触发的时候,怎么才能得到当前正在执行的函数呢,如果不能得到这个函数,我如何进行存储呢?的时候进行拦截,插入我们需要执行的代码逻辑,比如读的时候就可以把这个 effect 函数收集起来,放到一个数组里面,当进行写的时候,就从这个数组里面取出 effect 函数,在重新执行。**case3:**在不同的副作用函数中读取了两个不同对象的不同属性。

2024-10-14 12:06:54 1433

原创 01_响应式数据与副作用函数

副作用函数:函数执行会直接或间接影响其他函数的执行,比如说,修改全局变量,修改传入的参数,修改 DOM,发送网络请求等。来看一下下面这段代码:当 effect 函数执行的时候,就会修改 body 的文本,但是这个修改 body 文本的操作,确并不是 effect 函数的专属,其他函数都可以读取或者设置 body 的文本,这就导致了 effect 函数的执行,对其他函数产生了影响,所以 effect 函数就是一个副作用函数。响应式数据:数据的变化会直接或间接影响其他数据的执行,比如说,修改一个数据,会触发

2024-10-14 12:04:19 185

原创 Vue: Module “vue“ has no exported member xxx

不过算是有心栽花花不开,无心插柳柳成荫,碰巧解决了,也不知道是不是这个原因,记录一下吧,说不定会给同样困惑的人一点解决方式。这个问题让我困扰了好一会儿,我询问了 chatgpt 和各种网站社区,尝试了切换依赖的版本,清除缓存等等,依然没有解决。在你当前项目的声明文件,即 xxx.d.ts 中插入下面这句导入。当然,前提你需要安装了这个依赖。

2024-06-19 14:44:16 1336

原创 资源提示关键词

上图可以看出,因为提前建立了链接,所以后续的请求直接发送即可,就不用在建立连接了,加上了这个 preconnect 之后,浏览器就知道我们将要连接这个网站,跳转也好、请求资源也好,它会做好早期的连接工作。prefetch 加载的资源可以获取非当前页面所需要的资源(即一般是加载除开首屏的其他页面资源),并将其放入缓存中至少 5 分钟(无论资源是否可以缓存),并且,当页面跳转时,未完成的 prefetch 请求不会中断。),这最大限度的减少了延迟,因为一旦用户单击链接就已经进行了 DNS 查找。

2024-05-11 16:19:44 811

原创 用户页面触发点击事件和 js 执行点击事件的区别

两者不一致的原因其实也简单,用户点击触发的时候,并不在执行栈中发生具体的代码执行,仅仅是发送了一个点击事件而已,所以在第一个侦听器触发时,会打印。其实这个谜底揭开之后,第一反应都是,哦~,非常简单,但是细节决定成败,我被这个细节毁掉了,所以仅以此篇记录我这次的“折戟沉沙”通过 js 触发的点击事件,首先会在栈中加入一个 btn.click 的任务,执行侦听器1打印。此时由于执行栈为空,那么就会立即取出微队列的任务进行执行,即输出。加入微队列,此时执行栈是存在任务的,所以不会直接取出微队列执行。

2024-05-08 12:24:11 629

原创 Promise魔鬼面试题

此时同步执行完成,只有两个 Promise 状态完成,这两个函数开始执行,首先就会输出 0,然后根据前面分析的规则,如果返回的是一个 Promise,就会调用这个 Promise的then方法,且是放入一个微队列中执行,这里的。我们回到题目本身,来分析一下输出,我们知道,一个 then 方法一定会返回一个 Promise,then 方法是同步的,then 方法里面的回调才是异步的,根据这个准则,我们可以先来确定一下初步的执行状态。而 p4.then(()=>完成p0),其实需要执行只是。

2024-05-08 03:06:25 1235

原创 什么是 JWT

因为 http 是无状态的,无法记住上一次访问的人是谁,也就表示登录之后,下一次在请求我还是不知道你是否登录了,所以前使用了 cookie 来实现,登录请求成功之后,服务器返回的 set-cookie:user_id=1;account=admin,会被浏览器的自动添加到 cookie 中,所以登录之后后续请求,携带这个 cookie ,服务器就知道你登录过了非浏览器环境,如何在令牌中设置过期时间解决方案:比如响应头响应的 cookie 中添加字段来表示过期时间如何防止令牌被伪造。

2024-05-06 03:17:53 1081

原创 HTTP 缓存/ HTTP 协商缓存

当客户端发出一个 get 请求到服务器,服务器可能有以下的内心活动:『你请求的这个资源,我很少会改动它,干脆你把它缓存起来吧,以后就不要来烦我了』所谓客户端缓存,顾名思义,是将某一次的响应结果保存在客户端(比如浏览器)中,而后续的请求仅需要从缓存中读取即可,极大的降低了服务器的处理压力。,那么非常简单,服务器在此给予一个正常的响应(响应码 200 带响应体),同时可以附带上新的缓存指令即可,这样一来,客户端缓存新的内容即可。这样一来,可以最大程度的减少网络传输,因为资源还有效,服务器不会传输消息体。

2024-05-06 03:12:22 759

原创 封装动画函数

至于具体需要利用这两个时机完成什么,或者做点什么,就看各自的业务需求了,还可以根据你自己的需求作出更多扩展,比如传递多个起始值可以执行多组动画。首先就是计时器的id:timer;然后运动的总次数:count;当前的运动次数:curCount;当前值:curValue。其实到现在,我们的这个插件已经实现的差不多了,只是缺少一些值的计算和停止条件。当然经过 gif 的转换和录屏本身的原因,会存在不小的抖动。我们还需要那些属性呢?

2024-01-10 17:04:48 960

原创 Vue-根据角色获取菜单动态添加路由

如果大家写过后台管理系统的项目,那么动态路由一定是绕不开的,如果想偷懒的话,就把所有路由一开始都配置好,然后只根据后端返回的菜单列表渲染就好菜单就好了,但是这样的隐患就是我在地址栏输入的地址的时候,也会进入这个页面,不偷懒的方法就是本文要介绍的,真动态路由了,当然不会仅仅只是介绍使用数据怎么换成动态路由添加就好了,会从登录获取token后请求菜单列表...最后注册完成,这一系列流程完整的实现一次,相信对于第一次接触这个案例的朋友会有帮助

2024-01-10 10:35:58 1704

原创 vueRouter 配合 keep-alive 不生效的问题

其实这个不生效的问题根本也不算一个问题,犯的错和写错单词差不多,但是也是一时上头没发现,所以记录一下,如果遇到同样的问题,也希望可以帮助你早点看到这个哭笑不得的错误,哈哈哈。现在我打需求是在切换页面的时候,可以让留言页面输入的内容进行缓存。我们在 app.vue 下是使用 keep-alive,看看效果。其余两个页面和本次问题关系不大,而且都很简单就不展示了。解决还是非常简单的,谨以此文记录我这次马虎的错误。首先看一下我写的简单 demo。这些我就不做赘述了,都非常简单。

2024-01-07 23:13:34 2246 1

原创 自定义事件总线

自定义事件总线属于一种观察着模式,其中包括三个角色发布者(Publisher):发出事件(Event)订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler)事件总线(EvnetBus):无论是发布者还是订阅者都是通过事件总线作为中台的根据上面的分析,我们可以选用 class 的形式来实现,on(){}emit(){}off(){}

2024-01-06 12:17:26 1007

原创 保持宽高比

在开发中,多少都会碰到一个需求,保持一个合适的宽高比,比如展示一些图片或者视频的时候,会需要保持合适的宽高比,比如 4:3 16:9 等等,本文介绍两种方式。使用这个 703 * 26% 是不是就和这个 182.77 的值相差无几啊,所以这个经过验证也是没有错的。当然一般情况下这个 box 和 container 的宽度应该是一致的,这里我是为了进行测试而书写的。这是一个比较新的属性,可以非常方便的设置宽高比,使用的方式也非常简单。利用 padding 实现这个需求的话,优点兼容性好,但是会复杂一点。

2024-01-04 11:34:29 1141

原创 实现vue加载指令 v-loading

{ loading:true, color: 'blue', text: '拼命加载中...' ... }通过这些配置来增强指令的效果,有兴趣的可以自己试试i < 12;i ++) {if(!if(!if (!},inset: 0;top: 0;width: 6px;inset: 0;

2024-01-03 17:38:23 2862

原创 CSS 命名规范-BEM

基于组件方式的web开发方法,基本思想是将用户界面分成独立的模块BEM 是一套针对 css 类样式的命名方法BlockElementModifier一个完整的 BEM 类名:block__element–modifier 例如轮播图的下方的点,用于切换展示的图片,其中被选中的点可以命名为 banner__dot–selected__通常用于连接元素,--通常链接不同的形态BEM 具体代表什么呢?block【块】!

2024-01-03 11:33:53 1444 1

原创 requestAnimationFrame 解析

requestAnimationFrame 是浏览器提供的一个方法,我们可以通过 requestAnimationFrame 来告诉浏览器我们需要执行一个动画,并且这个动画触发的时机是浏览器在下次重绘之前调用指定的回调函数更新动画该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行当然也有一些需要注意的地方:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用。因为是一次性的。

2023-12-31 23:45:40 1880

原创 使用递归实现深拷贝

我在测试中都是单独每一项数据进行测试的,是为了更好的观测,实际一个对象都包含这些数据的话也都是 ok的,需要的话可以自己测试,而且写下来就会发现,其实逻辑都是差不多的,可以根据你实际的情况进行增加或者减免,在日常的开发中使用 JSON 序列化一般也可满足我们的需求,不过知道不用和不知道还是存在本质的区别的,可能现在有些你学习的技术没有实际的意义,但是只有积累的足够多的时候你才能完成一些本质上的突破很多事情不是因为看到了希望才去坚持,而是因为坚持了才能看到希望})})if (![] : {}

2023-12-31 21:20:14 1649

原创 JSON 详解

stringify 和 parse 搭配使用的时候,可以完成深拷贝,不敢存在一些限制,比如函数,循环引用,symbol 等等一些清空都是无法使用这种方式完成深拷贝的。可以看到,obj 并没有因为修改了 obj2 的值而受到影响,在一些简单的 JSON 形式的数据的对象时,使用此方法是一种非常不错的选择。此时输出的结果,就不是单纯的一行字符串,而是经过美化的格式,在一些调试查看数据的时候,还是非常好用的。此时就可以发现,只转换了我们需要的部分,当我们存在这样的需求的时候,使用这个参数,是非常方便的。

2023-12-30 14:37:15 1124

原创 搜索关键字高亮

我个人觉得,在空闲的时候时不时写一个小功能,日积月累,当你以后遇到需要使用的时候,就可以直接拿来使用,当然了。在筛选数据的时候,就可以感觉到如果需要往下执行,我们还缺少一个条件,关键词匹配的条件,关键词匹配,我们可以想到什么?是不是正则表达式呢?然后我们就应该来思考一下,前置条件,在这里,我们明显只有一个条件,那就是有没有关键词,有的话如何,没有的话又如何,首先我们第一步是不是要通过这个有没有关键词进行筛选数据呢?这个数据的筛选,是不是需要,可以自己根据自己的需求来设计,这个案例整体是不是还是比较简单呢。

2023-12-30 10:27:35 1094

原创 基于element-ui table组件的二次封装

使用插槽之后,就已经可以满足日常的基本使用了,如果你还需要其他的需求,可以在此基础上进行扩展比如序列号,勾选状态等等,亦或者和其他组件配合,再次封装为一个组件,通过这种分层的思想,可以解决很多业务的问题。

2023-12-29 12:15:12 2897

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除