对包装的深入了解
最近在试着用vite重新构建vue项目
看到index里面有一行
<link link rel="modulepreload" href="./assets/vendor.2a8f1291.js">
没见过此种用法,翻译一篇文章Preloading modules
浏览器最近已经原生的支持JS modules, 包括静态和动态的import
,这意味着现在们可以在浏览器内直接基于模块编写JS不用编译和打包。
基于模块的开发在可缓存性方面提供了一些真正的优势,帮助你减小代码体积。通过让您对应用程序中的关键代码进行优先级排序,代码的更细粒度也有助于解决加载过程。
但是,模块依赖项会带来加载问题,因为浏览器需要先等待模块加载,然后才能找到其依赖项。解决此问题的一种方法是预加载依赖项,以便浏览器提前知道所有文件并可以保持连接。
到目前为止,还没有真正好的方法以声明方式预加载模块。Chrome 64附带“实验性Web平台功能”标志后的<link rel =“ modulepreload”>。 <link rel =“ modulepreload”>是<link rel =“ preload”>特定于模块的版本,它解决了后者的许多问题。
what what's <link rel="preload">
Chrome在50版中添加了对<link rel =“ preload”>的支持,这是一种在浏览器需要资源之前以声明方式提前请求资源的方法。
<head>
<link rel="preload" as="style" href="critical-styles.css">
<link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>
这尤其适用于字体等资源,这些资源通常隐藏在CSS文件中,有时深度很深。当浏览器本来可以利用这段时间开始下载并利用全部连接带宽时,它必须等待多次往返才能发现它需要获取大字体文件。
及其等效的HTTP标头提供了一种简单的声明式方法,使浏览器可以立即了解当前导航的一部分所需的关键文件。当浏览器看到预加载时,它将开始对该资源的高优先级下载,因此,在实际需要时,它已经被获取或部分存储在其中。为什么 <link rel =“ preload”>
对module不生效?
这是棘手的。源有多种凭证模式,为了获得高速缓存命中,它们必须匹配,否则最终将获取资源两次。不用说,双重获取是不好的,因为它浪费用户的带宽并使他们等待更长的时间,这没有充分的理由。
对于<script>
和<link>
标签,可以使用crossorigin属性设置凭据模式。但是,事实证明,没有crossorigin属性的<script type =“ module”>不具有凭据模式,对于<link rel =“ preload”>.这意味着您必须将
此外,获取文件只是实际运行代码的第一步。首先,浏览器必须解析和编译它。理想情况下,这也应该提前发生,以便在需要模块时,代码就可以运行了。但是,V8(Chrome的JavaScript引擎)解析和编译模块的方式与其他JavaScript不同.<link rel =“ preload”>没有提供任何方式表明正在加载的文件是模块,因此浏览器所能做的就是加载文件并将其放入缓存。使用<script type =“ module”>标记加载脚本(或由其他模块加载)后,浏览器将代码解析并编译为JavaScript模块。
那么对于模块来说,<link rel =“ modulepreload”>只是<link rel =“ preload”>吗?
简而言之,是的。通过为预加载模块使用特定的链接类型,我们可以编写简单的HTML,而不必担心我们使用的是哪种凭据模式。默认值可以正常工作。
<head>
<link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">
而且由于Chrome现在知道您要预加载的是模块,因此它可以很聪明地在完成获取操作后立即解析并编译该模块,而不必等到它尝试运行为止。
但是模块的依赖关系又如何呢?
有趣的你应该问!实际上,我们还没有讨论过:递归。
实际上,<link rel =“ modulepreload”>规范允许选择加载的不仅是请求的模块,还包括其所有依赖项树。浏览器不必这样做,但是可以。
那么,由于需要完整的依赖关系树来运行应用程序,因此预加载模块及其依赖关系树的最佳跨浏览器解决方案是什么?
选择以递归方式预加载依赖项的浏览器应具有可靠的模块重复数据删除功能,因此通常,最佳实践是声明模块及其依赖项的平面列表,并信任浏览器不要两次提取相同的模块。
<head>
<!-- dog.js imports dog-head.js, which in turn imports
dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
<link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
<link rel="modulepreload" href="dog-head-mouth.mjs">
<link rel="modulepreload" href="dog-head.mjs">
<link rel="modulepreload" href="dog.mjs">
</head>
预加载模块是否有助于提高性能?
通过向浏览器告知需要获取的内容,预加载可以帮助最大程度地利用带宽,从而避免在长途往返期间无所事事。
如果您正在试验模块并由于深度依赖树而遇到性能问题,那么创建平面预加载列表肯定会有所帮助!
也就是说,模块性能仍在研究之中,因此请确保使用开发人员工具仔细查看应用程序中发生的情况,并考虑同时将应用程序捆绑为几个块。
推荐阅读
-
社交媒体之谜深入了解 Facebook 的内容战略
-
深入了解 CSS 灵活框中的灵活框
-
微信 "扫一扫 "物联网,全面揭秘 "扫一扫 "背后的扫盲技术!-1.1 扫一扫感知物体是做什么的? 1.1 微信扫一扫是做什么的? 扫一扫识物是指以图片或视频(商品图片:鞋/包/美妆/服饰/家电/玩具/图书/食品/珠宝/家具/其他商品)为输入媒介,挖掘微信内容生态中的有价值信息(电商+百科+资讯,如图1所示),并展示给用户。这里的电商基本涵盖了微信小程序覆盖上亿SKU的全量优质电商,可以支持用户货比N家并直接下单购买,百科和资讯则聚合了微信内的头部自媒体如搜狗、搜搜、百度等,向用户展示和分享拍摄商品相关的内容资讯。 图 1 扫一扫识别功能示意图 欢迎大家更新iOS新版微信→扫一扫→识货,亲自体验,也欢迎大家通过识货界面的反馈按钮向我们提交反馈意见。 扫一扫识物实景图展示 1.2 扫一扫识物有哪些使用场景? 扫一扫识物的目的是为用户访问微信内部生态内容开辟一个新窗口,以用户扫图片为输入形式,为用户提供微信生态内容中的百科、资讯、电商等作为展示页面。除了用户熟悉的扫一扫操作外,我们还将进一步拓展长按操作,让用户更方便地进行扫一扫操作。"扫一扫知事 "的落地场景主要涵盖三大部分: a. 科普知识: a.科普知识。用户通过扫一扫,可以在微信生态圈中获取该对象的百科、资讯等常识或趣闻,帮助用户更好地了解该对象; b.购物场景。同样的搜索功能支持用户看到喜欢的商品立即检索到微信小程序电商中的同款商品,支持用户即扫即购; c.广告场景。扫一扫识别物体可以辅助公众号文章、视频更好地理解其中蕴含的图片信息,从而更好地投放匹配广告,提高点击率。 1.3 Sweep Sense 为 Sweep 家族带来了哪些新技术? 对于扫一扫来说,大家耳熟能详的应该就是扫一扫二维码、扫一扫小程序码、扫一扫条形码、扫一扫翻译了。无论是各种形式的编码还是文字字符,都可以看作是图片的一种特定编码形式,而物的识别则是对自然场景图片的识别,这对于扫一扫家族来说是一个质的飞跃,我们希望从物的识别入手,进一步拓展扫一扫对自然场景图片的理解能力,比如扫酒、扫车、扫植物、扫人脸等服务,如下图3所示。 图 3 Sweep 家族
-
Java面试 "带你一次性了解面试中的必要问题,谈谈你对ES的理解。
-
深入了解 Open CASCADE 中的 SelectMgr_EntityOwner 和高亮功能
-
深入了解 PHP 函数的调用顺序
-
一种结构设计模式,允许在对象中动态添加新行为。它通过创建一个封装器来实现这一目的,即把对象放入一个装饰器类中,然后把这个装饰器类放入另一个装饰器类中,以此类推,形成一个封装器链。这样,我们就可以在不改变原始对象的情况下动态添加新行为或修改原始行为。 在 Java 中,实现装饰器设计模式的步骤如下: 定义一个接口或抽象类作为被装饰对象的基类。 公共接口 Component { void operation; } } 在本例中,我们定义了一个名为 Component 的接口,该接口包含一个名为 operation 的抽象方法,该方法定义了被装饰对象的基本行为。 定义一个实现基类方法的具体装饰对象。 公共类 ConcreteComponent 实现 Component { public class ConcreteComponent implements Component { @Override public void operation { System.out.println("ConcreteComponent is doing something...") ; } } 定义一个抽象装饰器类,该类继承于基类,并将装饰对象作为一个属性。 公共抽象类装饰器实现组件 { protected Component 组件 public Decorator(Component component) { this.component = component; } } @Override public void operation { component.operation; } } } 在这个示例中,我们定义了一个名为 Decorator 的抽象类,它继承了 Component 接口,并将被装饰对象作为一个属性。在操作方法中,我们调用了被装饰对象上的同名方法。 定义一个具体的装饰器类,继承自抽象装饰器类并实现增强逻辑。 公共类 ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component 组件) { super(component); } } public void operation { super.operation System.out.println("ConcreteDecoratorA 正在添加新行为......") ; } } 在本例中,我们定义了一个名为 ConcreteDecoratorA 的具体装饰器类,它继承自装饰器抽象类,并实现了操作方法的增强逻辑。在操作方法中,我们首先调用被装饰对象上的同名方法,然后添加新行为。 使用装饰器增强被装饰对象。 公共类 Main { public static void main(String args) { Component 组件 = new ConcreteComponent; component = new ConcreteDecoratorA(component); 组件操作 } } 在这个示例中,我们首先创建了一个被装饰对象 ConcreteComponent,然后通过 ConcreteDecoratorA 类创建了一个装饰器,并将被装饰对象作为参数传递。最后,调用装饰器的操作方法,实现对被装饰对象的增强。 使用场景 在 Java 中,装饰器模式被广泛使用,尤其是在 I/O 中。Java 中的 I/O 库使用装饰器模式实现了不同数据流之间的转换和增强。 让我们打开文件 a.txt,从中读取数据。InputStream 是一个抽象类,FileInputStream 是专门用于读取文件流的子类。BufferedInputStream 是一个支持缓存的数据读取类,可以提高数据读取的效率,具体代码如下: @Test public void testIO throws Exception { InputStream inputStream = new FileInputStream("C:/bbb/a.txt"); // 实现包装 inputStream = new BufferedInputStream(inputStream); byte bytes = new byte[1024]; int len; while((len = inputStream.read(bytes)) != -1){ System.out.println(new String(bytes, 0, len)); } } } } 其中 BufferedInputStream 对读取数据进行了增强。 这样看来,装饰器设计模式和代理模式似乎有点相似,接下来让我们讨论一下它们之间的区别。 第三,与代理模式的区别: 代理模式的目的是控制对对象的访问,它在对象外部提供一个代理对象来控制对原对象的访问。代理对象和原始对象通常实现相同的接口或继承相同的类,以确保两者可以相互替换。 装饰器模式的目的是动态增强对象的功能,而这是通过对象内部的包装器来实现的。在装饰器模式中,装饰器类和被装饰对象通常实现相同的接口或继承自相同的类,以确保两者可以相互替代。装饰器模式也被称为封装器模式。 在代理模式中,代理类附加了与原类无关的功能。
-
对包装的深入了解
-
深入了解低代码平台中的依赖关系管理和异步批量更新
-
深入了解 blob 和 arrayBuffer 中 xhr 的 responseType