发现这个React 内存泄露问题是某一天的晚上一直开着直播页,直播页用的 react 版本是 16.8.6,到了早上跳到这个页面的时候,控制台有点卡,怀疑是有内存泄露,于是就开始分析这个直播页面。
打开控制台 performance
面板点击开始录制,如下:
从上图可以发现在这时间内, nodes 节点一直在增长,很有可能发生了内存泄露。
我们来到 memory
面板分析内存变化:
注:上图的蓝色线条表示在时间轴的最后该对象依旧存在,灰色线条则说明对象在时间轴内被分配,但是已经被gc(垃圾回收)了。
上图都是点击 gc 再进行记录的,但是上图还有很多蓝色线条,而且内存一直往上涨,很明显的内存泄露问题,那会是什么导致内存泄露的呢?
很快发现这里有个 bi 的东西居然占了 31%的大小:
这个 bi 是用来干嘛的?展开 bi ,鼠标悬浮在 bi 某处:
发现这个节点是直播间里的进房消息,里面是 react 存的 FiberNode
节点,观察了一下里面这些 bi 基本上都是消息元素。
选择 summary
,选出Detached
(分离)的元素:
Detached HTMLDivElement
居然有41429个,展开,鼠标悬浮:
还是消息信息,说明是这个导致 bi 增加的。我们继续展开:
从上图看 react 会保留消息的上下兄弟节点的引用,而且保留的引用层级有点深,各个节点嵌套依赖,导致一直存在内存里:
仔细查下了项目消息相关代码,发现并不会存在有内存泄露的操作。这里简单说下渲染消息的逻辑,消息有进房消息,聊天消息,礼物消息等等,消息展示会根据 messages 数组里面的类型去渲染不同的消息。messages 数组不会无限增长,控制在 100 个,超过就删掉第一个元素,保证维持100个元素渲染消息,但是从上图来看,这些分离的元素并没有被 react 完全删除,还保存在内存里,查了下 React 的 Issue,并查了相关文章,发现有不少人遇到这个问题:
React 核心成员 Dan 给出的解决办法是升级到 16.9.0。
这里将项目 React 和 React-dom 版本升级16.9.0,
发现 FiberNode 节点还是会一直增加。。。
继续查看Issue 发现,React 版本 0.0.0-241c4467e
修复了这个问题。
Bug: Detached DOM node memory leak · Issue #18066 · facebook/react · GitHub
这个版本的 mr 已经合并在 React master 上
Null stateNode after unmount by bvaughn · Pull Request #17666 · facebook/react · GitHub
这个 mr 是 2019 年 12 月 20 号合并的,2019 年 12 月 20 号之后的版本是 16.13.0,这里将项目直接升级到 16.13.1,然后查看 node 节点:
发现 node 节点可以降下来了,查看 memory
面板:
对比Snapshot 5
,分离的元素没有新增,说明这个版本基本修复了这个问题。
React 16.8.6 (16.2.5到16.12.0 可能会有,这些版本没有验证,但是 issue 里面有人遇到)的版本会存在内存泄露问题,建议升级 React 到 16.13.0 以上。
]]>最近团队在用 WASM + FFmpeg 打造一个 WEB 播放器。我们是通过写 C 语言用 FFmpeg 解码视频,通过编译 C 语言转 WASM 运行在浏览器上与 JavaScript 进行通信。默认 FFmpeg 去解码出来的数据是 yuv,而 canvas 只支持渲染 rgb,那么此时我们有两种方法处理这个yuv,第一个使用 FFmpeg 暴露的方法将 yuv 直接转成 rgb 然后给 canvas 进行渲染,第二个使用 webgl 将 yuv 转 rgb ,在 canvas 上渲染。第一个好处是写法很简单,只需 FFmpeg 暴露的方法将 yuv 直接转成 rgb ,缺点呢就是会耗费一定的cpu,第二个好处是会利用 gpu 进行加速,缺点是写法比较繁琐,而且需要熟悉 WEBGL 。考虑到为了减少 cpu 的占用,利用 gpu 进行并行加速,我们采用了第二种方法。
在讲 YUV 之前,我们先来看下 YUV 是怎么获取到的:
由于我们是写播放器,实现一个播放器的步骤必定会经过以下这几个步骤:
- 将视频的文件比如 mp4,avi,flv等等,mp4,avi,flv 相当于是一个容器,里面包含一些信息,比如压缩的视频,压缩的音频等等, 进行解复用,从容器里面提取出压缩的视频以及音频,压缩的视频一般是 H265、H264 格式或者其他格式,压缩的音频一般是 aac或者 mp3。
- 分别在压缩的视频和压缩的音频进行解码,得到原始的视频和音频,原始的音频数据一般是pcm ,而原始的视频数据一般是 yuv 或者 rgb。
- 然后进行音视频的同步。
可以看到解码压缩的视频数据之后,一般就会得到 yuv。
对于前端开发者来说,YUV 其实有点陌生,对于搞过音视频开发的一般会接触到这个,简单来说,YUV 和我们熟悉的 RGB 差不多,都是颜色编码方式,只不过它们的三个字母代表的意义与 RGB 不同,YUV 的 “Y” 表示明亮度(Luminance或Luma),也就是灰度值;而 ”U” 和 ”V” 表示的则是色度(Chrominance或Chroma),描述影像色彩及饱和度,用于指定像素的颜色。
为了让大家对 YUV 有更加直观的感受,我们来看下,Y,U,V 单独显示分别是什么样子,这里使用了 FFmpeg 命令将一张火影忍者的宇智波鼬图片转成YUV420P:
|
|
在 GLYUVPlay
软件上打开 test.yuv
,显示原图:
Y分量单独显示:
U分量单独显示:
V 分量单独显示:
由上面可以发现,Y 单独显示的时候是可以显示完整的图像的,只不过图片是灰色的。而U,V则代表的是色度,一个偏蓝,一个偏红。
常见的YUV的采样有YUV444,YUV422,YUV420:
注:黑点表示采样该像素点的Y分量,空心圆圈表示采用该像素点的UV分量。
- YUV 4:4:4采样,每一个Y对应一组UV分量。
- YUV 4:2:2采样,每两个Y共用一组UV分量。
- YUV 4:2:0采样,每四个Y共用一组UV分量。
YUV的存储格式有两类:packed(打包)和 planar(平面):
举个例子,对于 planar 模式,YUV 可以这么存 YYYYUUVV,对于 packed 模式,YUV 可以这么存YUYVYUYV。
YUV 格式一般有多种,YUV420SP、YUV420P、YUV422P,YUV422SP等,我们来看下比较常见的格式:
YUV420P(每四个 Y 会共用一组 UV 分量):
YUV420SP(packed,每四个 Y 会共用一组 UV 分量,和YUV420P不同的是,YUV420SP存储的时候 U,V 是交错存储):
YUV422P(planar,每两个 Y 共用一组 UV 分量,所以 U和 V 会比 YUV420P U 和 V 各多加一行):
其中YUV420P和YUV420SP根据U、V的顺序,又可分出2种格式:
YUV420P
:U前V后即YUV420P
,也叫I420
,V前U后,叫YV12
。
YUV420SP
:U前V后叫NV12
,V前U后叫NV21
。
数据排列如下:
|
|
至于为啥会有这么多格式,经过大量搜索发现原因是为了适配不同的电视广播制式和设备系统,比如 ios 下只有这一种模式NV12
,安卓的模式是 NV21
,比如 YUV411
、YUV420
格式多见于数码摄像机数据中,前者用于NTSC
制,后者用于 PAL
制。至于电视广播制式的介绍我们可以看下这篇文章【标准】NTSC、PAL、SECAM三大制式简介
以YUV420P存储一张1080 x 1280图片为例子,其存储大小为 ((1080 x 1280 x 3) >> 1)
个字节,这个是怎么算出来的?我们来看下面这张图:
以 Y420P 存储那么 Y 占的大小为 W x H = 1080x1280
,U 为(W/2) * (H/2)= (W*H)/4 = (1080x1280)/4
,同理 V为(W*H)/4 = (1080x1280)/4
,因此一张图为 Y+U+V = (1080x1280)*3/2
。
由于三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储,那么YUV的存储位置如下(PS:后面会用到):
|
|
简单来说,WebGL是一项用来在网页上绘制和渲染复杂3D图形,并允许用户与之交互的技术。
在 webgl 世界中,能绘制的基本图形元素只有点、线、三角形,每个图像都是由大大小小的三角形组成,如下图,无论是多么复杂的图形,其基本组成部分都是由三角形组成。
着色器是在GPU上运行的程序,是用OpenGL ES着色语言编写的,有点类似 c 语言:
具体的语法可以参考着色器语言 GLSL (opengl-shader-language)入门大全,这里不在多加赘述。
在 WEBGL 中想要绘制图形就必须要有两个着色器:
其中顶点着色器的主要功能就是用来处理顶点的,而片元着色器则是用来处理由光栅化阶段生成的每个片元(PS:片元可以理解为像素),最后计算出每个像素的颜色。
一、提供顶点坐标
因为程序很傻,不知道图形的各个顶点,需要我们自己去提供,顶点坐标可以是自己手动写或者是由软件导出:
在这个图中,我们把顶点写入到缓冲区里,缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。接着我们创建并编译顶点着色器和片元着色器,并用 program 连接两个着色器,并使用。举个例子简单理解下为什么要这样做,我们可以理解成创建Fragment 元素: let f = document.createDocumentFragment()
,
所有的着色器创建并编译后会处在一种游离的状态,我们需要将他们联系起来,并使用(可以理解成 document.body.appendChild(f)
,添加到 body,dom 元素才能被看到,也就是联系并使用)。
接着我们还需要将缓冲区与顶点着色器进行连接,这样才能生效。
二、图元装配
我们提供顶点之后,GPU根据我们提供的顶点数量,会挨个执行顶点着色器程序,生成顶点最终的坐标,将图形装配起来。可以理解成制作风筝,就需要将风筝骨架先搭建起来,图元装配就是在这一阶段。
三、光栅化
这一阶段就好比是制作风筝,搭建好风筝骨架后,但是此时却不能飞起来,因为里面都是空的,需要为骨架添加布料。而光栅化就是在这一阶段,将图元装配好的几何图形转成片元(PS: 片元可以理解成像素)。
四、着色与渲染
着色这一阶段就好比风筝布料搭建完成,但是此时并没有什么图案,需要绘制图案,让风筝更加好看,也就是光栅化后的图形此时并没有颜色,需要经过片元着色器处理,逐片元进行上色并写到颜色缓冲区里,最后在浏览器才能显示有图像的几何图形。
总结
WEBGL 绘制流程可以归纳为以下几点:
由于每个视频帧的图像都不太一样,我们肯定不可能知道那么多顶点,那么我们怎么将视频帧的图像用 webgl 画出来呢?这里使用了一个技巧—纹理映射。简单来说就是将一张图像贴在一个几何图形表面,使几何图形看起来像是有图像的几何图形,也就是将纹理坐标和 webgl 系统坐标进行一一对应:
如上图,上面那个是纹理坐标,分为 s 和 t 坐标(或者叫 uv 坐标),值的范围在【0,1】之间,值和图像大小、分辨率无关。下面那张图是webgl坐标系统,是一个三维的坐标系统,这里声明了四个顶点,用两个三角形组装成一个长方形,然后将纹理坐标的顶点与 webgl 坐标系进行一一对应,最终传给片元着色器,片元着色器提取图片的一个个纹素颜色,输出在颜色缓冲区里,最终绘制在浏览器里(PS:纹素你可以理解为组成纹理图像的像素)。但是如果按图上进行一一对应的话,成像会是反的,因为 canvas 的图像坐标,默认(0,0)是在左上角:
而纹理坐标则是在左下角,所以绘制时成像就会倒立,解决方法有两种:
对纹理图像进行 Y 轴翻转,webgl 提供了api:
|
|
纹理坐标和 webgl 坐标映射进行倒转,举个栗子🌰,如上图所示,本来的纹理坐标(0.0,1.0)
对应的是webgl 坐标(-1.0,1.0,0.0)
,(0.0,0.0)
对应的是(-1.0,-1.0,0.0)
,那么我们倒转过来,(0.0,1.0)
对应的是(-1.0,-1.0,0.0)
,而(0.0,0.0)
对应的是(-1.0,1.0,0.0)
,这样在浏览器成像就不会是反的。
详细步骤
着色器部分
|
|
创建并编译着色器,将顶点着色器和片段着色器连接到 program,并使用:
|
|
创建缓冲区,存顶点和纹理坐标(PS:缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用)。
|
|
初始化并激活纹理单元(YUV)
|
|
渲染绘制(PS:由于我们获取到的数据是YUV420P,那么计算方法可以参考刚才说的计算方式)。
|
|
上述那些步骤最终可以绘制成这张图:
完整代码:
|
|
最后我们来看下效果图:
在实际开发过程中,我们测试一些直播流,有时候渲染的时候图像显示是正常的,但是颜色会偏绿,经研究发现,直播流的不同主播的视频宽度是会不一样,比如在主播在 pk 的时候宽度368,热门主播宽度会到 720,小主播宽度是 540,而宽度为 540 的会显示偏绿,具体原因是 webgl 会经过预处理,默认会将以下值设置为 4:
|
|
这样默认设置会每行 4 个字节 4 个字节处理,而 Y分量每行的宽度是 540,是 4 的倍数,字节对齐了,所以图像能够正常显示,而 U,V 分量宽度是 540 / 2 = 270
,270 不是4 的倍数,字节非对齐,因此色素就会显示偏绿。目前有两种方法可以解决这个问题:
第一个是直接让 webgl 每行 1 个字节 1 个字节处理(对性能有影响):
|
|
第二个是让获取到的图像的宽度是 8 的倍数,这样就能做到 YUV 字节对齐,就不会显示绿屏,但是不建议这样做, 转的时候CPU占用极大,建议采取第一个方案。
图像视频编码和FFmpeg(2)——YUV格式介绍和应用 - eustoma - 博客园
YUV pixel formats
https://wiki.videolan.org/YUV/
使用 8 位 YUV 格式的视频呈现 | Microsoft Docs?redirectedfrom=MSDN)
IOS 视频格式之YUV - 简书
图解WebGL&Three.js工作原理 - cnwander - 博客园
写这个插件的原因是最近要对比一下页面的 cpu 和内存占用的性能,本来是想找看看有没有什么软件能够去可视化一下当前标签页的cpu和内存占用,但是发现却找不到这种软件,mac 上有个活动监视器,但是当你开很多标签页的话并不很好的监听当前标签页的 cpu 和内存占用,能看到谷歌浏览器的 rendered 进程,但是谷歌浏览器的 rendered 进程很多你并不知道是哪个:
而且也没有可视化进行查看平均的 cpu 和内存占用,后来看到谷歌浏览器有个任务管理器可以查看当前标签页的 cpu 占用和内存占用,于是想到有没有人已经写了这种插件,但是遗憾的是并没有,后面仔细搜索了谷歌浏览器插件开发文档确定想要的功能能实现,于是这个插件就诞生了😀
对当前标签页点击插件图标,会开始对当前的标签页也就是页面的CPU和内存进行监控,并生成对应的变化折线图和平均值和表格,平均值如果超过一定范围会有颜色变化。
有人可能会问页面开启扩展后会对当前页面统计造成影响,其实是不会的,谷歌扩展是独立的进程,不会对当前的页面的 cpu 和内存占用造成影响
由于使用了谷歌浏览器的实验特性,因此插件需要运行在谷歌浏览器开发者版,可以在这里下载谷歌浏览器开发者版
==> 插件地址
]]>我们都知道,WebP 是 Google 推出的 WebP 图片格式,它是一种支持有损压缩和无损压缩的图片文件格式,根据Google测试,相同的图片,WebP 格式的图片均能比 PNG,JPG 格式的图片节约不少体积,但是其兼容性不是很好,如下:
因此我们需要做一些兼容处理,那么如何判断浏览器支持 webp 呢?下面有几种方法可供参考。
使用 canvas 的 toDataURL 进行判断
toDataURL方法在MDN解释如下:
HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
toDataURL方法将图片转化为包含dataURI的DOMString,通过 base64 编码前面的图片类型值是image/webp进行判断。
比如在谷歌浏览器使用toDataURL方法转成image/webp:
在 Safari 浏览器使用toDataURL方法转成image/webp:
可以发现在不支持 webp 的浏览器进行toDataURL,得到的图片类型并不是 webp,因此我们可以通过这个进行判断。
实现方法:
|
|
在服务端根据请求header信息判断浏览器是否支持webp
谷歌浏览器上请求图片 header是这样的:
IE 浏览器请求图片 header是这样的:
在图片请求发出的时候,Request Headers 里有 Accept,服务端可以根据Accept 里面是否有 image/webp 进行判断。
通过加载一张 webp 图片进行判断
|
|
此方法会加载一张 1x1 的白色的正方形背景图,用来测试浏览器是否支持 webp。
在 Google 测试代码:
在 Firefox 测试代码:
在 Safari 测试代码:
Google官方文档是这样处理的(先加载一个WebP图片,如果能获取到图片的宽度和高度,就说明是支持WebP的,反之则不支持):
|
|
原文:The differences between Object.freeze() vs Const in JavaScript
作者:Bolaji Ayodeji
本文经授权翻译转载,版权归原作者所有!
自ES6发布以来,ES6给JavaScript带来了一些新特性和方法。对于JavaScript开发者来说,这些特性能够很好地改善了我们的工作流程以及工作效率,其中的特性就包括 Object.freeze()
方法和 const
。
一些开发人员特别是新手们会认为这两个功能的工作方式是一样的,但其实并不是。 让我来告诉你Object.freeze()
和 const
是如何不同的。
const
和 Object.freeze()
完全不同。
const
的行为像 let
。它们唯一的区别是, const
定义了一个无法重新分配的变量。 通过 const
声明的变量是具有块级作用域的,而不是像 var
声明的变量具有函数作用域。Object.freeze()
接受一个对象作为参数,并返回一个相同的不可变的对象。这就意味着我们不能添加,删除或更改对象的任何属性。可变对象的属性能够进行更改,而不可变对象在创建对象后不能更改其属性。
|
|
这个例子会抛出一个Uncaught TypeError,因为我们正在尝试重新分配使用const关键字声明的变量user,这样做是无效的。
这个例子中使用 let 或者 var 声明是能够正常工作的,但是使用 const 并不能。
使用const声明的对象仅能阻止其重新分配,但是并不能使其声明的对象具有不可变性(能够阻止更改其属性)。
参考以下代码,我们使用const关键字声明了一个变量,并为其分配了一个名为user的对象。
|
|
尽管我们无法重新分配这个名为 user 的变量,但是我们仍然可以改变其对象本身。
|
|
我们肯定希望对象具有无法修改或删除的属性。 const
无法实现这样的功能,但是 Object.freeze
可以。
要禁用对象的任何更改,我们需要使用 Object.freeze()
。
|
|
Object.freeze
只是做了层浅冻结,当遇到具有嵌套属性的对象的时候,我们需要递归Object.freeze
来冻结具有嵌套属性的对象。
|
|
因此,当具有嵌套属性的对象时, Object.freeze()
并不能完全冻结对象。
要完全冻结具有嵌套属性的对象,您可以编写自己的库或使用已有的库来冻结对象,如Deepfreeze 或 immutable-js
const
和 Object.freeze()
并不同, const
是防止变量重新分配,而 Object.freeze()
是使对象具有不可变性。
感谢阅读,干杯!
]]>有时候我们发现 commit 写的不好,想要更改当前分支最近一次的 commit 的内容,我们可以使用:
|
|
既然可以更改最近一次的 commit,那可能有些人就想要在当前分支下前几次的 commit 内容进行更改或者进行 commit 合并,那我们需要怎么做呢?方法如下:(注意此操作最好是在自己维护的分支上弄,多人开发的分支就不太适合)
使用git rebase -i xxxx
,这里的 i 指的是交互的意思
现在项目 commit 如下
我现在想要更改第二个 commit 的内容,那我需要知道当前要修改的 commit 的父亲的 id,也就是 init game 的 commit id 1801cf5,然后git rebase -i 1801cf5
:
当前是 pick,我们需要使用 reword 来更改第二个 commit 的内容
保存退出,出现
修改 commit 内容,然后退出,出现
git log —oneline
查看
成功添加.号
现在我们想要把第二个 commit 和第一个 commit 这两个连续的 commit 进行合并,我们可以这样:
同样使用git rebase -i 1801cf5
,将第二个 pick 改为 s(squash),这样可以合并到上一个commit
保存退出
修改成
保存退出
git log —oneline
查看
成功合并。
现在有人又有个疑惑,如果我是相隔的 commit,但是我想将相隔的 commit 的内容进行合并,那又需怎么做呢?
还是以下面这个为例子
现在我们想将第一个 commit 的内容和第三个内容进行合并,这里你可能你比较疑惑,第三个 commit 的上一个 commit 是没有的,那怎么用 rebase 呢?我们需要记下第三个 commit 的 id,输入git rebase -i 1801cf5
目前只有两个,没有 init game 的 commit id,我们需要将这个 commit id 添加上去(我们可以不用写commit 的内容):
将相关要合并的 commit 写在一起:
保存退出。按照之前的步骤即可。
]]>平常调试 node 打 log 打习惯了,突然发现一个问题就是打印对象的时候,尤其这个对象里面有很多属性的时候,在终端上得一直往上拉才能看到,因此打算使用 vscode 来打断点调试程序。
这里的例子是使用 koa ,express 类似。我是使用阿里巴巴的飞冰快速搭建一个后台和前台的项目。
npm run client
这个时候前端项目就运行起来了。client/pages/UserLogin/actions.js
,将 import {login} from '../../api/user';
改为 import {login} from '../../api/index';
就可以了。最后打开页面,地址终端里面有说明。VsCode左侧第四个按钮是调试按钮,默认是『没有配置』。点击右侧的齿轮状图标,选择Node.js 会在项目根目录下创建 .vscode 的文件夹及 launch.json 文件。launch.json 内容如下:
|
|
默认会访问server下的 index.js 文件,但是这个项目的入口文件是 app.js,因此需要将index.js
改为app.js
。
在launch.json中会使用到一些预定变量,这里说明一下:
${workspaceRoot}
:VSCode中打开文件夹的路径${workspaceRootFolderName}
:VSCode中打开文件夹的路径, 但不包含”/“${file}
:当前打开的文件${fileBasename}
: 当前打开文件的文件名, 不含扩展名${fileDirname}
: 当前打开文件的目录名${fileExtname}
当前打开文件的扩展名${cwd}
:当前执行目录当我们在单步调试程序的时候,会进入node_modules里面,通常情况下我们并不需要去关心里面的逻辑实现,只关心项目本身的代码。在这个时候,我们就需要使用skipFiles:
|
|
我们还想要自动重启的功能,安装 nodemon
或者 node-dev
:
|
|
修改lanuch.json:
|
|
这里新增了三个字段,分别是:
这里在 server/controller/user.js
的 login 打了个断点:
启动调试,如下:
vscode 集成终端打印如下:
在前端页面点击登录,会跳到这里:
我们就能看到变量的信息啦😄(注意:如果此时终止了调试,nodemon 还是会运行,得在集成终端终止)
平时在使用 nodejs 去 require 的时候是不是有使用绝对路径或者相对路径去引用,那么 nodejs 解析路径顺序是怎么样的呢?接下来我会讲一下 nodejs 路径解析顺序
假设有一个文件路径为 /root/src/moduleA.js
,包含了一个导入var x = require("./moduleB");
, 也就是导入了一个相对路径的一个模块,那么Node.js以下面的顺序解析这个导入:
/root/src/moduleB.js
这个路径是否存在,如果不存在进入下一步。/root/src/moduleB
目录是否包含一个package.json
文件,且package.json
文件指定了一个”main”模块,比如 ,Node.js发现文件 /root/src/moduleB/package.json
包含了 { "main": "lib/mainModule.js" }
,那么 nodejs 就会去 /root/src/moduleB/lib/mainModule.js
/root/src/moduleB
目录是否包含一个 index.js
文件。 这个文件会被隐式地当作那个文件夹下的”main”模块。假设有一个文件路径为/root/src/moduleA.js
,里面包含了一个导入var x = require("moduleB");
,也就是绝对路径的一个模块,那么Node.js以下面的顺序解析这个导入:
/root/src/node_modules/moduleB.js
/root/src/node_modules/moduleB/package.json
(里面指定了 main 字段,跟上面相对路径是一样的)/root/src/node_modules/moduleB/index.js
/root/node_modules/moduleB.js
/root/node_modules/moduleB/package.json
/root/node_modules/moduleB/index.js
/node_modules/moduleB.js
/node_modules/moduleB/package.json
/node_modules/moduleB/index.js
在一些前端框架中,例如
angular
,vue
都有数据双向数据绑定的功能,这个功能极大的方便我们操作数据。那么接下来我会讲解一下双向数据绑定的4种实现方式。
手动触发绑定的主要思路是通过在数据对象定义 get 和 set 方法(可以使用其他的命名方法),调用时手动去触发 get 和 set 方法去获取数据,修改数据,改变数据后会主动去触发 get 和 set 函数中视图层的重新渲染。
简单的手动触发绑定代码如下:
|
|
数据劫持的基本思路是使用 Object.defineProperty 对 ViewModel 数据对象进行 get 和 set 的监听,当有数据变动的时候扫描元素节点,然后去运行对应节点上的指令(directive)。
代码如下:
|
|
利用Proxy ,它可以目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此可以对外界的访问进行过滤和改写,实现数据双向数据绑定和上一个类似。
代码如下:
|
|
脏检查的基本原理是在 ViewModel 对象的某个属性值发生变化的时候找到与这个属性值相关的所有元素,然后去比较数据变化,如果变化就用 directive 指令调用,对这个元素进行重新渲染。
简单的脏检查代码如下:
|
|
前些天使用react-native 写了个项目,遇到的问题挺多的,在这里记录下来📝,避免忘记。本篇文章会不定期更新!
出现Remote debugger is in a background tab which may cause apps to perform slowly黄色警报
解决方法
把那个chrome的Tab页保持最前,窗口不要最小化就好了。。。。
出现connection to http://localhost:8081 红色错误
解决方法
这个很神奇。遇到了多按 ⌘R几下或者把模拟器上的项目删除之后重新加载一般就会解决。
bad news: metro v0.29.0 won’t work with RN 0.54-0.55 because it introduced a new config param that RN is not handling yet (we’re working on improving configuration compatibility between RN and metro).
good news: the actual fix needed to solve this issue is in the RN repo (7be3d1c), so cherry-picking it into the 0.54 and 0.55 branches and releasing a RN minor version will fix this. (cc @hramos, @grabbou ).
或者使用下面命令可以解决问题
|
|
出现Runtime is not ready for debugging红色错误
两种解决方法
出现Unrecognized font family 红色报警
解决方法
在终端输入
|
|
然后重新启动即可
React Native不支持自动计算Image等View的大小
react-native-interactable 出现 Invariant Violation红色警报
解决方法
降级:将 react-native 版本降到0.53.0 就行了
参考 wix/react-native-interactable#185
Build后遇到’No bundle URL present’ error
解决方法
关闭SS,VPN这类的服务,重新 react-native run-ios
即可。很神奇。。。
官方也有这个 issue
出现regeneratorRuntime is not defined
解决方法
|
|
最终原因是因为一个组件没删干净🤣
出现:CFBundleIdentifier”, Does Not Exist 错误
解决方法
打开 xcode 运行项目出现
在这里面有个 libInteractable.a 删除掉就行了
参考:rebeccahughes/react-native-device-info#251
删除包注意事项
首先 react-native unlink <lib name>
然后 yarn remove <lib name>
一定要这样做不然会有问题。。。
出现Invariant Violation: View config not found for name 红色警报
解决方法
https://stackoverflow.com/questions/46750477/react-native-invariant-violation-view-config
Button 组件无法直接使用 style定宽度和高度等等
解决方法
If this button doesn’t look right for your app, you can build your own button using TouchableOpacity or TouchableNativeFeedback.
也就是说可以使用 TouchableOpacity
或者 TouchableNativeFeedback
组件代替
使用TouchableWithoutFeedback 出现错误
解决方法
TouchableWithoutFeedback,这个组件必须至少有一个child,如果是多个组件,必须以view来包装。写成这样就可以了
|
|
xcode出现Showing All Messages Code signing is required for product type ‘Unit Test Bundle’ in SDK ‘iOS 11.2’
在Xcode上
即可
react-navigation TabNavigator点击切换反应迟钝。
在真机上调试react-navigation的TabNavigator,点击tab总感觉反应很慢,试了好久都是这样,大概有0.5秒之后才会切换体验很差。
解决方法
关闭debug模式。。。。
React Navigation TabNavigator 一个帧的延迟
当页面加载时,下面的 tab 图标从第一个到第二个图标有一个帧的延迟
解决方法
定义initialLayout,用以防止react-native-tab-view渲染中一个帧的延迟
参考:https://github.com/react-native-community/react-native-tab-view#avoid-one-frame-delay
出现timed out waiting for 红色警报
解决方法
重启模拟器。。。
react native 没有\
组件换行
解决方法
可以在 Text 组件里写 {‘\n’},如:
react native checkbox 原生组件只适合安卓
解决方法
可以使用这个 https://github.com/crazycodeboy/react-native-check-box
react-navigation的headerRight添加点击事件
解决方法
首先需要在componentDidMount(){}中动态的添加点击事件
|
|
然后
|
|
接下来就可以通过params方法来获取点击事件了(记住先要判断navigation.state.params是否存在,不然会报错。。。)
|
|
react-navigation tab 点击 StatusBar 颜色问题
解决方法
详情 https://reactnavigation.org/docs/status-bar.html
Image 标签不支持 http 问题
解决方法
ios 9 以上,默认是Https请求,如需支持Http,修改info.plist文件添加键值对就好了
react-native-swiper 动态数据渲染,翻页出现错乱
出现
一开始,使用静态的数据没问题,但是使用动态加载数据就出现问题,经过一些调试发现,可能是 index 的问题,在 github 库里搜 issue 果然有人遇到过这个问题 https://github.com/leecade/react-native-swiper/issues/720
解决方法
添加 key 值是你获取数据的长度
react-native-swiper 跳转索引 bug 问题。
一开始
在翻页的时候,出现索引随机变化的问题,当时看了一下 api 是没有问题的,一直定位到
将标题不显示就发现索引没有问题了,不会随机翻页。。。,接着在某一页看到题目比较大,发生了抖动,结果造成了随机翻页,再一看里面有数字和文字,大小不一样,设置一下字体就好了。。。。个人认为是抖动的时候可能触发了react-native-swiper的翻页,结果造成随机翻页。。。
神坑的 bug,找了4,5个小时。。。。😡
ListView, FlatList, Sections and VirtualizedList paddingBottom 无效的问题。
ListView, FlatList, Sections and VirtualizedList 继承了 ScrollView
所以导致都有这个问题
解决方法
|
|
出现 could not connect to development server红色警报
解决方法
关掉 vpn ,或者不要开全局模式。。。很神奇
前些天,线上笔试的时候,发现需要浏览器同意开启摄像头,感觉像是 js 调用的,由于当时笔试,也就没想到这么多🤣。今天闲来无事,看了下自己的 todo,发现有这个调用摄像头的todo,才想到😂。网上查了一下,果然 js 有调用摄像头的 api,为此自己写一个 demo ,避免忘记。
一共有两种实现方式,一种是使用navigator.getUserMedia
(该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性),前面一种已经从 Web 标准中删除,仅为了向后兼容而存在,第二种是使用navigator.mediaDevices.getUserMedia
(推荐使用),这两种方法 Safari 貌似都不支持。。。。
navigator.getUserMedia
用法详见 mdn ,代码如下:
|
|
navigator.mediaDevices.getUserMedia
用法详见mdn。navigator.mediaDevices.getUserMedia
其实和第一种差不多,主要第二种返回是一个 Promise 对象,代码如下:
|
|
思路是设置一个标志变量 videoPlaying 看看是否 video 有在 play,监听拍照按钮的点击事件,如果videoPlaying 为 true ,使用一个canvas 获取 video 的宽高(默认 canvas 是不显示的),然后使用 canvas 的drawImage,然后使用 canvas 的 toDataURL返回一个 data url,将这个 url,设置在一个 img 标签上即可😀
navigator.getUserMedia
实现代码:
|
|
第二种navigator.mediaDevices.getUserMedia
实现方法:
|
|
从3月底到4月中旬,经过半个月的面试,终于在4月17号顺利拿到了阿里的 offer,感谢这段时间内面试官、家人和朋友的帮助和鼓励。下面我会总结一些企业关注的点和面试的一些技巧。
我认为大企业招聘技术实习生或者是应届生时关注有以下几点:
在面试时,这些方面都会有所体现。
比如问你一些常规性问题(考察你对当前应聘岗位的知识储备)。
问你一些计算机基础问题(考察你的计算机基础)。
HR 问你一些关于性格优缺点和一些其他问题(考察你的性格和你的价值观)。
问你一些场景题(考察你的项目经验)。
问你最近看什么书或者你常逛什么技术社区(考察你是否能够主动去学习新的东西和你对技术是否有热情)等等。
首先,在面试中,一定要自信不要紧张。遇到不会的问题不要慌张,可以将不会的问题转化为自己会的问题。我举个例子,比如面试官问你前端的async
和 await
,那么此时如果你这个不会,那么你可以问一下面试官这个主要是干嘛的,面试官一般都会跟你解释,他告诉你这是异步,那么你就可以往异步的方面靠,这样会让面试官觉得你掌握的还行🙂。
其次,就是简历的项目经验和技能一定要写好,因为面试官会一直问项目的实现,难点,和项目产生的背景。然后会问你在简历上的一些相关技能。个人认为如果不太会的技能就不要写上去。比如我写了了解前端安全和性能优化,然后很多面试官就问这个。。。一直问。。。还好准备的比较充分,不然就 gg 了🤣
最后,在面试时,千万不要抱怨你的队友,即便队友很水。我举个反例,有人在 hr 面试时,hr 问他对队友看法,然后他说队友很水,整个项目都是他在写,然后当天就挂了。你想,企业会要背后说别人闲话的人吗?
最后的最后,面试一定要保持积极的心态,就当面试是在自我学习的过程,每次面试都自我反省一下,到底哪些问题回答不好,然后去查缺补漏,这样一定会收获到一个自己满意的 offer 😀
]]>最近重构了我之前项目 qq 音乐移动端,使用的技术是 vue,vuex,vue-router,和 typescript,在这期间,遇到的问题还是蛮多的,一会儿我会把我遇到的问题以及解决方法列出来,避免忘记。
重构完成的项目 ===> vue-qq-music
TypeScript与Vue全家桶的配置可以参考以下两篇文章(在这里由衷感谢两位作者):
为什么我要将TypeScript
和 Vue
集成呢?因为TypeScript 有以下几个优势:
ts 无法识别$ref
解决方法
① 直接在 this.$refs.xxx 后面申明类型如:
|
|
② 在export default class xxx extends Vue
里面声明全部的$ref 的类型
|
|
ts 无法识别 require
解决方法
安装声明文件
|
|
运行npm run build
出现
解决方法
You can fix this by using the most recent beta version of
uglifyjs-webpack-plugin
. Our team is working to remove completely the UglifyJsPlugin from within webpack, and instead have it as a standalone plugin.If you do
yarn add uglifyjs-webpack-plugin@beta --dev
ornpm install uglifyjs-webpack-plugin@beta --save-dev
you should receive the latest beta which does successfully minify es6 syntax. We are hoping to have this released from beta extremely soon, however it should save you from errors for now!
也就是说升级你的uglifyjs-webpack-plugin版本:yarn add uglifyjs-webpack-plugin@beta --dev
vue-property-decorator 装饰器写法不对。当时我是要把 mixins,注入到组件里,我就这样写:
ts提示找不到 mixin。我就很纳闷为什么找不到名字,由于官网vue-property-decorator例子太少,只好一步一步摸索😂
解决方法
把mixins写在@Component里面…,像这样:
Property '$xxx' does not exist on type 'App'
的话,那么可以在vue-shim.d.ts
增加
|
|
经过几天的折腾,终于把项目重构完成,我个人认为加上 TypeScript
,确实效率挺高了许多,不过 Vue+TypeScript
还是没 Angular
支持那么完善,相信之后 vue 对于 ts 的集成会更加完善!
一直想写这篇文章,无奈由于要考试的原因,一直在复习,拖延到现在才写🤣,之前用 node 的 express 框架写了个小项目,里面有个上传图片的功能,这里记录一下如何实现(我使用的是 ejs)📝
首先,当用户点击上传头像,更新头像的时候,将头像上传到项目的一个文件夹里面(我是存放在项目的public/images/img
里面),并且将图像名重命名(可以以时间戳来命名)。
同时图片在项目的路径插入到用户表的当前用户的 userpicturepath
里面
picture
属性里面<img>
的 src
获取到当前用户的session里面的 picture
的值,最后动态刷新页面头像就换成了用户上传的头像了ejs部分
|
|
js部分
|
|
路由部分,使用formidable
,这是一个Node.js模块,用于解析表单数据,尤其是文件上传
|
|
近日,在写 ejs 文件时,我发现用 vscode 没有啥提示,因此换成 webStrom ,但是用 webStrom 将 es6 编译成 es5 的时候出现了一些问题😭,经过一番搜索, 最后终于成功解决,这里记录一下🖊
首先建立一个新的工程,点击设置
在设置里面,把JavaScript language version改成ECMAScript 6
然后在js文件里写一段ES6代码
现在IDE会出现一个File watcher
提示条
此时先别点Add watcher!
在终端切换到项目的路径,输入以下命令
|
|
|
|
现在可以去点Add watcher,点完之后会弹出一个框
下面第三行,Program
那一项,填
|
|
然后点OK,这个时候你就会发现左边多出来一个新文件
但是现在还没搞定!现在只是搞定了自动转换的功能,系统默认把ES6 编译成了ES6…
打开终端,输入:
|
|
再次打开设置,在搜索框输入file watchers
,点击babel
在 Arguments 里面将 env
改为 =es2015
,点击ok
在根目录下新建一个.babelrc
文件(就是babel在当前项目的配置文件),写上:
|
|
在安装electron的时候,一直卡在node install.js不动😭,翻了墙也不行,于是在 github 上搜索终于找到解决方法,为此记录下来📝
install.js
,里面的下载是依赖于electron-download
这个模块
搜索electron-download发现:👇
打开终端,输入vi ~/.npmrc
,在里面添加
|
|
再试一次npm install electron -g
,这次就成功了😄
自动添加HTML / XML关闭标签(必备)
自动重命名配对的HTML / XML标签(必备)
格式化javascript,JSON,CSS,Sass,和HTML
包含Bootstrap 4&Font awesome 的代码片段
颜色识别匹配括号
智能提示HTML class =“”属性(必备)
非常强大的一款插件,能够运行多种语言的代码片段或代码文件:C,C ++,Java,JavaScript,PHP,Python,Perl,Ruby,Go等等,安装完成后,右上角出现:
点击这个按钮就可以运行你的文件了(必备)
能够查看CSS ID和类的字符串作为HTML文件中相应的CSS定义(必备)
使用方法:将光标放在class里面的属性,右击
查文档必备,搭配 dash(不过似乎只有 mac 版)😁
快捷键
ctrl + h
它根据你当前选中的语言查找 dash 里面的文档
让 vscode 映射 chrome 的 debug功能,使静态页面都可以用 vscode 来打断点调试
简单使用:戳我
添加注释块
设置:
|
|
快捷键: 按两次Ctrl+alt+d
EsLint可以帮助我们检查Javascript编程时的语法错误。比如:在Javascript应用中,你很难找到你漏泄的变量或者方法。EsLint能够帮助我们分析JS代码,找到bug并确保一定程度的JS语法书写的正确性。
配置:戳我
用于 html 的Font-awesome代码片段
在底部状态栏显示当前文件大小,点击后还可以看到详细创建、修改时间
以图表的形式查看git日志
使用 command+shift+p(Ctrl+shift+p) 输入git log就可以看到了
git 日志插件
在 html 标签上写class 智能提示当前项目所支持的样式(必备)
html 代码片段(必备)
html代码检测
可以在选中HTML标签中外面套一层标签
使用:选择一大段代码,然后按“Alt + W”
突出目前的缩进深度
智能提示 css 的 class 名
鼠标移到路径里显示图像预览
es6代码片段(必备)
js代码片段(必备)
jQuery 代码片段
实时编译 sass ,不过需要配置,附上我的配置
|
|
使用
markdown 语法检查
可以在导入语句中自动完成JavaScript / TypeScript模块。
在导入语句中自动填充npm模块,跟Node.js Modules Intellisense差不多
当前的 html 文件用浏览器打开,类似 webstorm 的那四个小浏览器图标功能,前提条件html 文件必须保存
快捷键alt+b
输出提示的文字颜色有一些变化,方便获取关键信息
路径自动补全(必备)
格式化JavaScript / TypeScript / CSS 。
工程项目过多时,shift+cmd+p(shift+ctrl+p) 然后输入project,第一次选择edit Project编辑自己的工程项目,之后就可以直接选择open打开你的项目
写 sass 必备
生成假数据,地址,电话,图片等等
打开方式shift+cmd+p(shift+ctrl+p)) 然后输入faker 就可以选择了
实时观看 javascript 的变量的变化
使用:先shift+cmd+p (ctrl+shift+p)输入 quokka 选择 new javascript 就行了😀
测试正则的插件
检查typescript编程时的语法错误语法
自动搜索工作区文件中的TypeScript定义,并将所有已知符号作为完成项,以允许代码完成。
目录树图标
react代码片段,下载人数超多😉
格式化 javascript, JSX, typescript, TSX 文件
语法高亮、智能感知
vue代码片段
vue的 typescript 代码片段
vue 2代码片段
个人最喜欢的主题,应该是最好看的主题之一😀
这个也好看😄
这个和 One Dark Pro差不多,One Dark Pro颜色主题多一些
里面包含黑色和白色主题,这个白色主题感觉挺好看的
大家还有什么好的插件推荐吗🤓
]]>最近在看 underscore 的源码时发现,作者好多都用 call,而用 apply 比较少,比如说下面这段:👇
|
|
既然 call 和 apply 都能用,那为什么只用 call 而不用 apply 呢?
经过网上的搜索发现,call 比 apply 速度快,在 console运行如下代码:
|
|
console的结果:
可以发现 call 比 apply 快了10ms 左右,那是什么原因造成这样的呢?
因为 apply 运行前要对作为参数的数组进行一系列检验和深拷贝,而 call 则没有
我们看一下 ECMAScript 是怎么写的:
由ECMAScript 标准发现 apply 比 call 的步骤多了好多,这就是 call 比 apply 执行速度快的原因!
]]>最近用了谷歌插件vimium,发现非常好用,牛逼神器一个😄,下面记一下常用的快捷键
打开 vimium help
快捷键 shift+/
观察者模式也称”发布-订阅”模式,它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态
举个生活比较常见常见的例子,比如你去面试之后,面试官看你表现不错,最后会跟你要联系方式,以便之后可以联系你。在这角色扮演当中,你就是“订阅者”,面试官就是“发布者”。
那么发布订阅模式是咋实现的呢?
思路
|
|
测试:
|
|