作为一名前端爱好者, 笔者利用空余时间研究了几个国外网站的源码,发现不管是库,还是业务代码,都会用到了一些比较有意思的API,虽然平时在工作中部分接触过,但是经过这次的研究,觉得很有必要总结一下,毕竟已经2020年了,是时候更新一下技术储备了,本文主要通过实际案例来带大家快速了解以下几个知识点:
我会对部分API做一些比较有意思的案例,那么开始我们的学习吧~
Observer是浏览器自带的观察者,它主要提供了Intersection, Mutation, Resize, Performance这四类观察者, 这里笔者重点介绍Intersection Observer.
IntersectionObserver提供了一种异步观察目标元素与其祖先元素交叉状态的方法。当一个IntersectionObserver对象被创建时,其被配置为监听根中一段给定比例的可见区域,并且无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,我们可以在同一个观察者对象中配置监听多个目标元素。
说简单点就是该api可以异步监听目标元素在根元素里的位置变动,并触发响应事件.我们可以利用它来实现更为高效的图片懒加载, 无限滚动以及内容埋点上报等.接下来我们通过一个例子来说明一下它的使用步骤.
// 1.定义观察者及观察回调
const intersectionObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
console.log(entry)
// ...一些操作
});
},
{
root: document.querySelector('#root'),
rootMargin: '0px',
threshold: 0.5
}
)
// 2. 定义要观察的目标对象
const target = document.querySelector(“.target”);
intersectionObserver.observe(target);
复制代码
以上代码就实现了一个基本的Intersection Observer,虽然已有代码中还体现不出什么实质性功能. 接下来介绍一下代码中使用到的参数的含义:
其中rootBounds表示根元素的位置信息, boundingClientRect表示目标元素的位置信息,intersectionRect表示叉部分的位置信息, intersectionRatio表示目标元素的可见比例.
当我们设置rootMargin为10px时,我们的root会增大影响范围,但目标元素移动到淡红色区域就会被监听到,当然我们还可以设置rootMargin为负值来减少影响区域.其支持的值为百分比和px,如下:
rootMargin: '10px'
rootMargin: '10%'
rootMargin: '10px 0px 10px 10px'
复制代码
thresholds可以如下图理解:
由上图所示,当我们设置阈值为[0.25, 0.5]时, 目标元素的25%和50%进入根元素的影响范围时都会触发回调.利用这个特性我们往往可以实现位差动画,或者更根据目标元素的位置变化做不同的交互. 当然Intersection还提供了以下几个方法来控制观察对象:
了解了使用方法和api之后,我们来看看一个实际应用--实现图片懒加载:
<img src="loading.gif" data-src="absolute.jpg">
<img src="loading.gif" data-src="relative.jpg">
<img src="loading.gif" data-src="fixed.jpg">
<script>
let observerImg = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
// 替换为正式的图片
entry.target.src = entry.target.dataset.src;
// 停止监听
observer.unobserve(entry.target);
});
},
{
root: documennt.getElementById('scrollView'),
threshold: 0.3
}
);
document.querySelectorAll('img').forEach(img => { observerImg.observe(img) });
</script>
复制代码
以上代码就实现了一个图片懒加载功能, 当图片的30%进入根元素时才加载真实的图片,这又让我想起了之前在某条做广告埋点上报时使用react-lazyload的画面.大家还可以利用它实现无限滚动, H5视差动画等有意思的交互场景.
Mutation Observer主要用来实现dom变动时的监听,同样也是异步触发,对监听性能非常友好. Resize Observer主要用来监听元素大小的变化,相比于每次窗口变动都触发的window.resize事件, Resize Observer有更好的性能和对dom有更细粒度的控制,它只会在绘制前或布局后触发调用. 以上两个api的使用和Intersection使用非常类似,官方资料也写得很全,大家可以好好研究一下.
这个问题主要是之前有朋友问过我,当时的想法就是简单的认为script内的代码执行完之后以及与dom绑定了,存放在了浏览器内存中,最近查了很多资料发现有一个有点意思的解释,放出来大家可以感受一下:
JavaScript解释器在执行脚本时,是按块来执行的,也就是说浏览器在解析HTML文档流时,如果遇到一个script标签,javascript解释器会等待这个代码块都加载完了,才进行预编译,然后才执行。所以,当开始执行这个代码块的代码时,这个代码段已经被解析完了。这时再从DOM中删去也就不影响代码的执行了。
Proxy/Reflect虽然是es6的api,出现也已经有几年了,但是在项目中用的还是比较少,如果是做底层架构方面的工作,还是建议大家多去使用,毕竟vue/react这种框架源码把这些api玩的如火纯青,还是很有必要掌握一下的。
其实我们认真看mdn的介绍或者阮一峰老师的文章,还是很好理解这些api的用法的,接下来我们详细介绍一下这两个api以及应用场景.
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy在很多场景中都会和Reflect一起使用. 用法也很简单,我们看看Proxy的基本用法:
const obj = {
name: '徐小夕',
age: '120'
}
const proxy = new Proxy(obj, {
get(target, propKey, receiver) {
console.log('get:' + propKey)
return Reflect.get(target, propKey, receiver)
},
set(target, propKey, value, receiver) {
console.log('set:' + propKey)
return Reflect.set(target, propKey, value, receiver)
}
})
console.log(proxy.name) // get:name 徐小夕
proxy.work = 'frontend' // set:work frontend
复制代码
以上代码拦截了obj对象,并重新定义了读写(get/set)方法,这样我们就可以在访问对象时进行额外的操作了.
Proxy还有apply(拦截 Proxy 实例作为函数调用的操作)和construct(拦截 Proxy 实例作为构造函数调用的操作)等属性可以使用,我们可以在对象操作的不同阶段进行拦截,这里我就不一一样举例了.接下来看看Proxy的实际应用场景.
我们一般操作数组大多数都是正向操作的,不能通过指定负数来逆向查找数组,如下图: 我们不能通过arr[-1]来拿到数组的尾部元素(字符串同理),这个时候我们就可以用Proxy来实现这一功能,这是我们的结构有点像环状:
这种实现的好处是如果我们想访问数组的最后一个元素时,我们不需要先拿到长度,再通过索引访问了:
// 原始写法
arr[arr.length -1]
// 通过proxy改造后写法
arr[-1]
复制代码
实现代码如下:
function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver);
}
};
let target = [];
target.push(...elements);
return new Proxy(target, handler);
}
复制代码
我们可以发现以上代码使用proxy来代理数组的读取操作,在内部封装了支持负值查找的功能,当然我们也可以不用proxy来实现同样的功能,这里实现参考阮一峰老师的实现.
一般我们在做表单校验的时候会写一些if else或者switch判断来实现对不同属性值的校验,同样我们也可以用proxy来优雅的实现它,代码如下:
const formData = {
name: 'xuxi',
age: 120,
label: ['react', 'vue', 'node', 'javascript']
}
// 校验器
const validators = {
name(v) {
// 检验name是否为字符串并且长度是否大于3
return typeof v === 'string' && v.length > 3
},
age(v) {
// 检验age是否为数值
return typeof v === 'number'
},
label(v) {
// 检验label是否为数组并且长度是否大于0
return Array.isArray(v) && v.length > 0
}
}
// 代理校验对象
function proxyValidator(target, validator) {
return new Proxy(target, {
set(target, propKey, value, receiver) {
if(target.hasOwnProperty(propKey)) {
let valid = validator[propKey]
if(!!valid(value)) {
return Reflect.set(target, propKey, value, receiver)
}else {
// 一些其他错误业务...
throw Error(`值验证错误${propKey}:${value}`)
}
}
}
})
}
复制代码
有了以上实现模式,我们就可以实现对表单中某个值进行设置时进行校验了,用法如下:
let formObj = proxyValidator(formData, validators)
formObj.name = 333; // Uncaught Error: 值验证错误name:f
formObj.age = 'ddd' // Uncaught Error: 值验证错误age:f
复制代码
以上代码中当设置了不合法的值时,控制台将会剖出错误,如果在实际业务中,我们可以给用户做出适当的提醒.
以上几点笔者在之前的文章中也写过,所以这里不在详细介绍了.大家也可以根据实际情况自己实现更加灵活的拦截操作.当然Proxy提供的API远远不止这几个,我们可以在MDN或者其他渠道了解更多高级用法.
Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API,更多的应用场景是配合proxy一起使用,在上文中已经用到了.可以将Object对象的一些明显属于语言内部的方法放到Reflect对象上,并修改某些Object方法的返回结果. Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
CustomEvent API是个非常有意思的api, 而且非常实用, 更重要的是学起来非常简单,而且被大部分现代浏览器支持.我们可以让任意dom元素监听和触发自定义事件,只需要如下操作:
// 添加一个适当的事件监听器
dom1.addEventListener("boom", function(e) { something(e.detail.num) })
// 创建并分发事件
var event = new CustomEvent("boom", {"detail":{"num":10}})
dom1.dispatchEvent(event)
复制代码
我们来看看CustomEvent的参数介绍:
我们可以通过dispatchEvent来触发自定义事件.其实他的用途有很多,比如创建观察者模式, 实现数据双向绑定, 亦或者在游戏开发中实现打怪掉血,比如下面的例子: 笔者上面画了一个打boss的草图, 现在的场景是两个玩家一起打boss, 我们可以在玩家发动攻击的时候触发dispatch掉血的自定义事件, boss监听到事件后将血量自动扣除, 至于不同角色的伤害值,我们可以存放在detail中,然后通过策略模式去分发伤害.笔者曾今在学校开发的H5游戏时就大量采用类似的模式,还是非常有意思的.
File API使得我们在浏览器端可以访问文件的数据,比如预览文件,获取文件信息(比如文件名,文件内容,文件大小等), 并且可以在前端实现文件下载(可以借助canvas和 window.URL.revokeObjectURL的一些能力).当然我们还可以实现拖拽上传文件这样高用户体验的操作.接下来我们来看看几个实际例子.
function previewFiles(files, previewBox) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /^image\//;
if (!imageType.test(file.type)) {
continue;
}
var img = document.createElement("img");
previewBox.appendChild(img); // 假设"preview"就是用来显示内容的div
var reader = new FileReader();
reader.onload = (function(imgEl) {
return function(e) { imgEl.src = e.target.result; };
})(img);
reader.readAsDataURL(file);
}
}
复制代码
以上代码可以在reviewBox容器中显示已上传好的图片,当然我们还可以基于此来扩展,利用canvas将图片画到canvas上,然后进行图片压缩,最后再把压缩后的图片上传到服务器.这种方式其实目前很多工具型网站都在用,比如在线图片处理网站,提供的批量压缩图片,批处理水印等功能,套路都差不多,感兴趣的朋友可以尝试研究一下.
这块笔者之前也写过详细的文章,这里就不一一举例了, 文章地址:
全屏API主要是让网页能在电脑屏幕中全屏显示,它允许我们打开或者退出全屏模式,以便我们根据需要进行对应的操作,比如我们常用的网页图形编辑器或者富文本编辑器, 为了让用户专心于内容设计,我们往往提供切换全屏的功能供用户使用.由于全屏API比较简单,这里我们直接上代码:
// 开启全屏
document.documentElement.requestFullscreen();
// 退出全屏
document.exitFullscreen();
复制代码
以上代码的document.documentElement也可以换成任何一个你想让其全屏的元素.默认情况下我们还可以通过document.fullscreenElement来判断当前页面是否处于全屏状态,来实现屏幕切换的效果.如果是react开发者,我们也可以将其封装成一个自定义hooks来实现与业务相关的全屏切换功能.
URL API是URL标准的组成部分,URL标准定义了构成有效统一资源定位符的内容以及访问和操作URL的API。
我们利用URL组件可以做很多有意思的事情.比如我们有个需求需要提取url的参数传给后台,传统的做法是自己写一个方法来解析url字符串,手动返回一个query对象.但是利用URL对象,我们可以很方便的拿到url参数,如下:
let addr = new URL(window.location.href)
let host = addr.host // 获取主机地址
let path = addr.pathname // 获取路径名
let user = addr.searchParams.get("user") // 获取参数为user对应的值
复制代码
以上代码可知,我们如果将url转化为URL对象,那么我们就可以很方便的通过searchParams提供的api来拿到url参数而无需自己再写一个方法了.
另一方面,如果网站安全性比较高,我们还可以对参数进行自然数排序然后再加密上传给后端.具体代码如下:
function sortMD5WithParameters() {
let url = new URL(document.location.href);
url.searchParams.sort();
let keys = url.searchParams.keys();
let params = {}
for (let key of keys) {
let val = url.searchParams.get(key);
params[key] = val
};
// ...md5加密
return MD5(params)
}
复制代码
地理位置 API 通过 navigator.geolocation 提供, 这个浏览器API也比较实用, 我们在网站中可以用此方式确定用户的位置信息,从而让网站有不同的展现,增强用户体验.
举几个有意思的例子可以让大家感受一下:
其实应用远远不止如此,程序员可以发挥想象来实现更有意思的事情,让自己的网站更智能.接下来笔者就基于promise写一段获取用户位置的代码:
function getUserLocation() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject()
} else {
navigator.geolocation.getCurrentPosition(success, error);
}
function success(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
resolve({latitude, longitude})
}
function error() {
reject()
}
})
}
复制代码
使用方式和结果如下图所示:
我们基于获取到的经纬度调用第三方api(比如百度,高德)就可以获取用户所在为精确位置信息了.
Notifications API 允许网页或应用程序在系统级别发送在页面外部显示的通知;这样即使应用程序空闲或在后台,Web应用程序也会向用户发送信息。
我们举个实际的例子,比如我们网站内容有更新,通知用户,效果如下:
相关代码如下:
Notification.requestPermission( function(status) {
console.log(status); // 仅当值为 "granted" 时显示通知
var n = new Notification("趣谈前端", {body: "从零搭建一个CMS全栈项目"}); // 显示通知
});复制代码
当然浏览器的Notification还给我们提供了4个事件触发api方便我们做更全面的控制:
有了这样的事件监听,我们就可以控制当用户点击通知时, 跳转到对应的页面或者执行相关的业务逻辑.如下代码所示:
Notification.requestPermission( function(status) {
console.log(status); // 仅当值为 "granted" 时显示通知
var n = new Notification("趣谈前端", {body: "从零搭建一个CMS全栈项目"}); // 显示通知
n.onshow = function () {
// 消息显示时执行的逻辑
console.log('show')
}
n.click = function () {
// 消息被点击时执行的逻辑
history.push('/detail/1232432')
}
n.close = function () {
// 消息关闭时执行的逻辑
console.log('close')
}
});
复制代码
当然我们在使用前需要获取权限,方式也很简单,大家可以在mdn上学习了解.
Battery Status API提供了有关系统充电级别的信息并提供了通过电池等级或者充电状态的改变提醒用户的事件。这个可以在设备电量低的时候调整应用的资源使用状态,或者在电池用尽前保存应用中的修改以防数据丢失。
之前的版本中Battery Status API提供了几个事件监听函数来监听电量的变化以及监听设备是否充电,但是笔者看文档时这些api都已经废弃,如下:
虽然以上几个看似有用的api已经被弃用,但是笔者亲测谷歌还是可以正常使用的,但是为了让自己代码更可靠,我们可以用其他方式代替,比如用定时器定期去检测电量情况,进而对用户做出不同的提醒.接下来我们看看基本的用法:
navigator.getBattery().then(function(battery) {
console.log("是否在充电? " + (battery.charging ? "是" : "否"));
console.log("电量等级: " + battery.level * 100 + "%");
console.log("充电时间: " + battery.chargingTime + " s");
console.log("放电时间: " + battery.dischargingTime + "s");
});
复制代码
我们可以通过getBattery拿到设备电池信息,这个api非常有用,比如我们可以在用户电量不足时禁用网站动画或者停用一些耗时任务,亦或者是对用户做适当的提醒,改变网站颜色等,对于webapp中播放视频或者直播时,我们也可以用css画一个电量条,当电量告急时提醒用户.作为一个优秀的网站体验师,这一块还是不容忽视的.
如果想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。
以上便是本次分享的全部内容,希望对你有所帮助^_^
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/65obZjlGgkMmPPxgn1AIJw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。