Websocket详解
更新中—
更新中—
1 | function defineReactive(obj, key) { |
defineProperty利用闭包, 对_val进行set和get,称为数据劫持。
由此可见defineProperty的响应式粒度是key级别的。也就是说,已知的属性才有办法劫持其getter和setter,所以其根本没有办法对未知的key劫持做响应。
pnpm、npm、yarn 包管理工具『优劣对比』及『环境迁移』 - 掘金 (juejin.cn)
v2 早期递归依赖,导致重复安装问题
v3 扁平化解决递归依赖, 扁平化依赖算法耗时长,下载需要更长时间
v5 package-lock.json解决扁平化耗时长问题:
package-lock.json 的作用
- 记录项目所有依赖的精确版本号
- 提供生成确切的依赖树,可视化
- 优化依赖安装过程,跳过重复依赖
- 防止依赖意外更新
pnpm env use --global [version]
HTML中每个元素都可视为一个盒子,在浏览器解析时
回流必将引起重绘,而重绘不一定会引起回流
回流一般在于元素的几何信息(大小、位置)发生变化时触发。
除此之外一些获取几何信息的属性也会触发回流
offsetTop offsetLeft offsetWidth offsetHeight
scroll-
client-
getBoundingClientRect() getComputedStyle()
修改文本排列方向是会回流的,修改对齐方向不回流
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列
当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据
因此浏览器不得不清空队列,触发回流重绘来返回正确的值
主流webpack需要把整个项目编译好后再交给dev server,因此大型项目devserver启动时间很长,HMR也需要很长时间。
因此,Vite应运而生
当我们使用webpack启动项目时,webpack会根据我们配置文件中的入口文件,
在Webpack中,构建过程大概分为这么几个阶段初始化Init、构建Make、生成Seal
描述得比较简单, 实际完成这些过程是比较复杂的
每次修改一个模块,webpack要遍历整个依赖图来找出所有依赖于该模块的其他模块。再进行局部的重新编译。
三个阶段:
webpack4以前是通过jsonp来拉取更新模块的,webpack4以及以后通过ws通信+ajax请求
原理是通过在内存中创建虚拟文件系统来提供开发服务器功能。它监听文件变化并通过 WebSocket 与浏览器通信,以实现HMR,提供高效的开发环境。
cache: true
thread-loader
底层
基于esbuild和rollup,利用浏览器对ESM的支持
ESM
ESM === ES module, 大部分浏览器都支持<script type='module'>
在<script type="module">
中,浏览器遇到内部的import引用时,会自动发起http请求,去加载对应的模块。
ESM 支持编译时就能确定模块的依赖关系
node_modules/.vite
下。注意:
按需编译,编译了:
typescript、vue、jsx、css预处理语言、静态资源等。实际上都转换为了浏览器可以解析的html/js/css,但是由于SouceMap方便开发者,我们在F12网络看到的资源依然是index.ts这样的后缀。
当修改一个文件时vite精确定位到该模块,使其失活。vite重新编译该模块后再发送给浏览器。浏览器利用动态导入import()
来替换。
原理:利用websocket连接浏览器,建立连接后监听文件变化。服务器向浏览器注入代码用于处理ws消息(重新请求模块、刷新页面)
重新请求模块依然走ajax请求,而不是ws;目的是为了保证ws只用于通信上。
基于rollup打包,rollup轻量、打包更快。
从几方面回答:
大部分算法可以看作对于二叉树的操作
二叉树解题的思维模式分两类:
1、是否可以通过遍历一遍二叉树得到答案 ?如果可以,用一个 traverse
函数配合外部变量来实现,这叫「遍历」的思维模式。
2、是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案 ?如果可以,写出这个递归函数的定义,并充分利用这个函数的返回值,这叫「分解问题」的思维模式。
无论使用哪种思维模式,你都需要思考:
如果单独抽出一个二叉树节点,它需要做什么事情?需要在什么时候(前/中/后序位置)做 ?其他的节点不用你操心,递归函数会帮你在所有节点上执行相同的操作。
1 | void sort(int[] nums, int lo, int hi) { |
1 | void sort(int[] nums, int lo, int hi) { |
前中后序是遍历二叉树过程中处理每一个节点的三个特殊时间点
- 前序位置的代码在刚刚进入一个二叉树节点的时候执行;
- 后序位置的代码在将要离开一个二叉树节点的时候执行;
- 中序位置的代码在一个二叉树节点左子树都遍历完,即将开始遍历右子树的时候执行。
js中实现异步有三大方式: 回调
Promise
generator
回调
Promise
Generator
现在我们来详细聊聊generator实现异步
场景
我要读一个文件dir.txt 这是一部目录,里面记录了 Lzy.json , XXX.json …. 等等 用户文件。
然后我们要读取Lzy.json 的信息。
用 async/await
方式解决
1 | async function read() { |
用 generator
方式解决
1 | function* read() { |
这样只执行了第一步,而我们得到第一步的PromiseResult之后要继续递归的运行next(PromiseResult) 这样才能得到第二步。
所以我们需要一个执行器
co
,
1 | let finalRes= co(read()) |
实现 co
1 | function co(gen) { |
context 就是上一步yield后面的表达式完成时的PromiseResult
例如: const dir = yield readFile('./txt/dir.txt', 'utf8');
就是把context 赋值给了dir
创建一个worker对象,可以在浏览器中创建一个新线程不阻塞UI线程渲染。
1 | <body> |
worker.js
1 | onmessage = function(e) { |
通过new Worker(‘./worker.js’) 的worker.js 文件处于 DedicatedWorkerGlobalSpace
作用域下,可以用 self
访问到这个全局对象。
所以以上三个方法就是对全局对象的事件处理函数的重写。
1 | const btn = document.querySelector('#btn'); |
1 | let counter = 0; // 计时器 |
注意