以下内容是2023年7月17~10月16日自己每周学习的记录
第29周 7.17-7.23
这周在项目中使用了一种新的布局。
就是下图所示的布局效果。

在过去,这种图片固定比例的,宽度自适应的布局都至少只要2层以上的标签,这一次,使用了容器元素和全新的cqw单位,可以一层标签即实现最终的效果。
相关实现已经整理成文,有兴趣可以访问这里:"CSS高宽不等图片固定比例布局的三重进化"。
第30周 7.24-7.30
本周研究了了一个新的CSS属性text-wrap,目前仅Chrome浏览器支持,兼容性如下图所示:

其中名为balance的属性值很有趣,可以让div元素中的内联元素尽可能宽度一致排列,布局差异如下所示:

这个属性其实很有用,例如类似朋友圈点赞的那种布局效果,目前的实现都是优先第一行显示的。

有了text-wrap:balance属性后,就可以是下图所示的布局效果,体验会更好。

更多内容可以访问这里了解。
第31周 7.31-8.6
在做项目的时候,偶然看到有同事的CSS代码中对samp标签样式进行了定义,然后研究了下这个已经没用过的HTML标签,发现还是一个系列,可以用来帮助代码文本的语义化呈现,具体如下:
-
kbd: 键盘输入的文字内容
-
samp: 范例输出
-
code: 计算机代码输出
-
var: 变量与自变量实例
平时只用过 code 标签 ,结果没想到还有其他类似HTML属性,现代浏览器全部都支持。
更详细的内容我已经这里成文章了,有兴趣的可以访问这里了解。
第32周 8.7-8.13
本周学习了两个HTML全局属性,nonce和popover属性。
nonce与安全相关 ,配合meta元素的CSP设置,可以决定哪个内联JS执行,哪个内联<script›
元素不执行。
假设页面有如下所示的代码:
xml
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-NDhkODkxMzYtNGUxZS00N2NjLTk1YTItNWMyOTM4YzdhZGJj'">
<script nonce="NDhkODkxMzYtNGUxZS00N2NjLTk1YTItNWMyOTM4YzdhZGJj">// ...</script>
则此时,只有nonce值有设置,且值匹配的
popover属性是个比较新的属性。
可以让任意的元素点击后直接出现浏览器层面的popover弹出层效果。
例如下面的代码:
xml
<button popovertarget="imgPopover">点击我</button>
<div popover id="imgPopover"><img src="1.jpg" /></div>
此时,点击按钮,就会出现图片显示浮层。
点击空白自动隐藏。
关于这两个HTML属性更多的内容,可以参见我撰写的这篇文章:"我仍未知道的HTML nonce和popover属性"。
第33周 8.14-8.20
本周学习了非常多的东西,因为要专业分享,要讲HTML部分,所以梳理了下HTML相关的内容,发现了很多之前遗漏的知识。
包括:
1. 浏览器的referrerpolicy策略
document.referrer可以返回当前页面的来源页面,而究竟显示哪些信息,是完整地址,还是空,亦或是只有域名,都是可以设置的。
例如:
-
no-referrer不发送任何Referer信息
-
no-referrer-when-downgrade表示协议安全级别下降的时候不发送Referer信息
-
origin是只显示域信息
-
origin-when-cross-origin表示跨域的时候只显示域信息
-
same-origin表示域名相同,否则Referer信息是空
-
strict-origin表示仅当协议安全级别保持不变时发送Referer信息
-
strict-origin-when-cross-origin是默认值(之前默认值不是这个),如果域名和安全协议相同或更高的时候,Referer信息是完整地址,如果跨域,则Referer信息是原始的域信息,如果安全协议降低,Referer信息为空
-
unsafe-url表示就算URL地址不安全,也发送完整的Referer信息
2. 返回顶部
原来<a href="#top">返回顶部</a>
也可以返回顶部,以前只知道<a href="#">返回顶部</a>
。
3. li元素的父元素
<li>
元素的父元素除了<ul>``<ol>
,还可以是<menu>
,这个之前也没注意到。
4. Open Graph协议
也就是开放内容协议,有利于社交分享,以微软官网源码示意:

5. <caption>
元素
<caption>
元素只能用在<table>
表单中,此限制也是最近知道。
6. autocapitalize属性
autocapitalize属性可以控制手机软键盘呼起时候,字母的大小写设置。
7. 其他...
还有很多其他内容,例如exportparts属性,<dfn>
和<abbr>
的使用场景,以及上周介绍的几个属性等,篇幅原因,不一一展示了。
第34周 8.21-8.27
这周好好研究了下User Timing API,相比于Date.now()或者performance.now()方法,此API可以在自己希望记录的位置打下标记,然后和浏览器的性能工具配合,可以看到整个的渲染时长、时机等过程,更加方便我们定位调试,以及理解Web渲染的过程。
以及还可以和其他时间线API一起使用,例如PerformanceObserver。
PerformanceObserver可以对各项性能指标进行观察,而浏览器支持的性能指标非常多,尤其是Chrome浏览器。
我们可以使用PerformanceObserver.supportedEntryTypes这个语句获取,例如在Mac OS X系统的Chrome浏览器下。

通过设置需要指定观察的类型,我们就可以看到对应的性能指标了,例如'paint'类型,我们可以看到页面内容初次渲染的时间。代码示意:
ini
const observer = new PerformanceObserver(entryList => {
for (const entry of entryList.getEntries()) {
console.dir(entry);
}
});
observer.observe({
entryTypes: ['paint']
});
浏览器返回:

startTime就可以示意首次渲染时间。
PerformanceObserver的相关知识非常多,这里就不具体展开,有兴趣的可以参考我撰写的"狠狠地研究了下 PerformanceObserver API"一文。
额外的思考
User Timing API非常庞杂,想要全部通透,需要非常多的时间精力,而相关知识的应用价值其实有限,如今的Web页面的性能都非常强悍了,很少遇到需要打开控制台排查性能的机会。
所以,对于大部分而言,花非常多的时间学习相关的内容是不换算的,我觉得了解个大概就足够了,至少那些基本的概念需要知道。
例如时间线是什么,时间线中的mark和measure是什么意思,first-input和largest-contentful-paint的含义是什么等?
对于这类非热门的前端知识,通常业内深入介绍的文章较少,文档呢,也通常会省略很多细节,此时,要想有个全面且大概的了解,一定要亲自实践,而不是自己脑补。
largest-contentful-paint渲染的元素究竟是哪个?如果不是自己预期的元素,会是什么原因造成的呢?
通透了这些原因,你对这些API的理解自然就比别人深刻一些,专业技术也就比别人更加扎实了。
第35周 8.28-9.3
这周研究了下registerProtocolHandler()方法。
第一眼看到registerProtocolHandler()方法的时候,说实话,我还是挺兴奋的。
这可是个有趣的东西啊,居然可以自定义协议地址,并且可以像正常链接一样访问。
然而,亲自体验了之后,发现没有想象的那么美好,限制较多,最大的问题,在Web端并不能简化URL,还需要对协议内容进行分析处理。
通常的自定义协议都是,桌面软件注册,浏览器URL呼起。
而registerProtocolHandler()方法是浏览器注册,浏览器呼起......总之比较鸡肋。
有兴趣可以访问这篇文章"一言难尽的registerProtocolHandler()方法"。
其他学习
之前对外分享的时候,多次提到,要节约时间,要学会坚持,重复做一件事情,后来发现效果一般,听众没什么感触。
这周看到了路长全的分享,讲得比我好------什么是吃苦?吃苦的本质是自律,心不苦,则智不开(有兴趣的可以搜相关视频)。
和日常的辛苦相对比,更有说服力些,可以借鉴下。
第36周 9.4-9.10
- reset与chang事件
<form>
元素在执行reset()方法的时候,虽然里面的输入框元素的值变了,但是并不会触发change事件,这个现象有时候会对我们的开发带来麻烦。
那有没有什么办法打个补丁呢?
有,正好reset事件执行时机是在输入框value值变化之前触发的,因此,我们可以检测此value前后有没有变化,以此来决定是否触发change事件。
具体代码如下,在页面任意位置引入即可:

2. 本地环境跨域
本地开发域名经常是localhost,而请求地址往往是域名,此时会有跨域的问题。
之前的做法都是让运维配置运行跨域的请求头,但这个比较麻烦,后来我发现了一个更简单的解决方法。
那就是安装个Chrome插件Access Control Allow Origin,安装后,只要开启就没有跨域的烦恼了。

第37周 9.11-9.17
本周学习了这些。
1. HTML elementtiming属性
抽空研究了下elementtiming属性,在性能监控的需求中,这个属性很实用。
给任意的有图文内容的DOM元素添加此属性,并自定义标志量作为属性值,则使用PerformanceObserver API进行观察的时候,就可以知道此元素的渲染起始时间,如果是图片等元素,还可以知道图片的加载时间。

这对于排查页面中哪个元素的渲染最耗时非常有用。
详见我整理的这篇文章:"HTML elementtiming属性初体验记录"。
2. 复制图片到剪切板
上周做了个在线PNG/JPG优化的小工具,但是有个问题,如果我想直接粘贴到编辑器中,或者在其他地方进行上传,就必须先将优化的图片下载到本地,然后再进行处理。
但这样的操作显然有些啰嗦,最好可以直接复制优化的图片进入剪切板。
浏览器提供了原生的API可以满足此需求,代码示意:
ini
// blob是图片二进制数据
const data = [newClipboardItem({ ['image/png']: blob })];
navigator.clipboard.write(data);
然而,此API只能复制PNG图片,复制其他格式的图片数据会报错。

虽然可以通过canvas将JPG图片转为PNG,但那就是无损图了,而不是压缩图。
后来,曲线救国,直接复制图片的base64信息,然后再在上传页面,或者预览页面进行二次处理。
具体可以参见我整理的文章:"又get到了,JS复制图片到剪切板"。
第38周 9.18-9.24
本周比较忙碌,没有学习,只有思考。
基于JSON数据和JS在Web端模拟视频播放功能上周已经弄好了。
不过周末想了下,需要重新改变下算法实现。
原因:
之前算法是,先提前对每个tts音频和背景音乐进行合并,然后再控制播放,这样实现的好处是解析器编写简单。
但是有一个问题,由于浏览器的限制,这种合成需要用户至少点击一次页面,无法默默地完成。
而根据实际运行的结果,此合成会造成几百毫秒的卡顿,如果在用户点击后执行,会有体验问题。
所以,需要换一种实现,就是时间轴运行到哪个tts,就播放哪个tts音频,之前的xx工具就是这么处理的,虽然麻烦了点,不过因为即点即播,用户体验不受影响。
另外还有个好处,tts和背景音乐分离后,背景音乐的音量设置也可以比较容易地反馈出来。
至于音频的合成,就留在视频导出的时候完成,因为此时会专门做一个loading效果,即使有延时和卡顿,用户也不会觉得有什么问题。
不过,这里还有个技术小缺失,基于canvas图片序列+音频=>MP4视频的技术我这边是没问题的,demo已经跑通,但是合并的时候,需要控制某一个音频的音量我还不知道具体的细节,这个需要花时间研究下,可能需要额外的半天时间。
第39周 9.25-10.1
因为某某工作的原因,重新拾起了对<html>
和<body>
元素的关注,然后以前一些模模糊糊的理解现在终于理清楚了。
默认情况下,body元素的高度是0,但是此时设置background背景,会发现满屏显示。
例如:
body { background-color: black; }

但,如果此时html元素也设置个背景色,例如:
html { background-color: skyblue; }
此时,body设置的背景色消失了,明明 body 是子元素。
还有奇怪的,如果body设置的是线性渐变,例如:
body { background: linear-gradient(black, white); }
发现背景是个8px像素平铺的水平条纹。

8px哪里来的,为何是水平条纹?
很多人并不理解这种现象。
这其实是body、html元素的某种特异性。
即:
-
如果body设置了背景,但html没设置,则body的背景等同于设置在 html 元素上,否则,body按照普通的标签元素渲染。
-
html背景的渲染高度至少一屏。
这就可以理解上述所有的现象了。
尤其是最后的水平条纹。
由于body元素默认有8px的margin大小,考虑到margin合并,因此,html元素的默认高度是8px,于是渐变背景高度也是8px。
但是html元素的背景至少要一屏高,于是8px水平平铺了起来。
overflow属性也有类似的现象。
单纯如下body overflow是无法隐藏div元素的,哪怕div元素的高度比body明显高,例如:
css
body { height: 30px; overflow: hidden; }
body > div { height: 300px; background-color: #cd0000; }
此现象出现原因和背景色类似。
-
如果html元素没有设置overflow属性,那么body元素的设置等同于设置在html元素上,否则,body元素的overflow属性按照普通元素渲染。
-
html元素的overflow剪裁高度至少一个屏幕高度。
所以,上面CSS代码,只需要html元素设置任意不是 visible 值的overflow属性,都能触发body元素剪裁div,哪怕html元素的高度很高。
例如:
css
html { height: 400px; overflow: scroll; }
此时body overflow生效。
于是,下面的现象也就理解了,即如果div高度很高,例如3000px,可以看到div元素在一屏高度的地方隐藏了:
css
body > div { height: 3000px; background-color: #cd0000; }
这是因为html元素的overflow生效高度至少1屏。
第40周 10.2-10.9
学什么学,国庆假期,浪起来!
钓鱼钓了个昏天暗地......
第41周 10.9-10.15
本周学习围绕音频处理展开,两个需求点。
1. 改变原始音频的音量
如果仅仅是调整播放的音量,那很简单。
如果你使用的是<audio>
元素,直接改变volume属性就好了。
ini
audio.volume = 0.5;
如果没有audio元素,而是使用的Web Audio API,希望在 AudioBuffer 层面设置播放的音量,则可以使用GainNodes实现。
您可以狠狠地点击这里体验效果:使用gainNode改变音频播放音量demo
如果希望从原始文件层面改变音频的音量大小,怎么办呢?
可以解析音频文件,然后遍历采样点数据,然后根据音量设置进行调整。
由于代码较长,这里就不放出来了,有兴趣可以访问这篇文章:"JS改变AudioBuffer音量并下载为新audio音频"。
2. 音频的拼接与合并
拼接:
合并:
操作过程和改变音量大小的实现大同小异。ArrayBuffer转AudioBuffer,然后读取音频信息,对采样信息进行处理。
同样,篇幅原因,这里不展示源码,有兴趣的可以访问:"纯JS实现多个音频的拼接或者合并"
如果你对原生实现不怎么感兴趣,也可以使用开源项目。
比方说这个叫做crunker的项目:github.com/jaggad/crun...
如果你已经有了 AudioBuffer 资源,上面的 fetchAudio() 方法也是可以省略的。
另外,此JS还支持使用静音填充视频。
OK,以上就是最近三个月的学习记录。
然后我专门建了个学习记录的专栏,每个季度更新一下。
欢迎关注哈。