<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: icy0307</title>
    <description>The latest articles on DEV Community by icy0307 (@icy0307).</description>
    <link>https://dev.to/icy0307</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F863908%2Fda17f8e6-0476-449e-b2b4-8168e96caff0.png</url>
      <title>DEV Community: icy0307</title>
      <link>https://dev.to/icy0307</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/icy0307"/>
    <language>en</language>
    <item>
      <title>浏览器兼容性代码</title>
      <dc:creator>icy0307</dc:creator>
      <pubDate>Tue, 22 Aug 2023 09:21:07 +0000</pubDate>
      <link>https://dev.to/icy0307/liu-lan-qi-jian-rong-xing-dai-ma-4a39</link>
      <guid>https://dev.to/icy0307/liu-lan-qi-jian-rong-xing-dai-ma-4a39</guid>
      <description>&lt;p&gt;如何处理浏览器的兼容性代码和包大小息息相关。&lt;/p&gt;

&lt;p&gt;polyfill的通常意义, 在wiki上是这么说的.&lt;/p&gt;

&lt;p&gt;💡 In web development, a polyfill is code that implements a feature on web browsers that do not natively support the feature. Most often, it refers to a JavaScript library that implements an HTML5 or CSS web standard, either an established standard on older browsers, or a proposed standard on existing browsers.&lt;/p&gt;

&lt;p&gt;然而在现实世界上，领域不同还有不同的jargon（黑话）。了解这些黑话和不同的黑话对于领域范围对处理包大小的实际问题非常有意义。&lt;/p&gt;

&lt;h2&gt;
  
  
  JS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  当我们在讨论js相关的polyfill的时候，实际在说什么？
&lt;/h3&gt;

&lt;p&gt;js的兼容性代码总共需要考虑两大块内容，ECMAScript和web standards api。其中ECMAScript又分为两大部分: 语言的syntax和语言所指的功能。另外浏览器，还有web api提供的能力.&lt;/p&gt;

&lt;p&gt;语言的syntax： 如 arrow function, spread operator,  async await , generator&lt;/p&gt;

&lt;p&gt;语言的feature:• 如&lt;a href="https://github.com/zloirock/core-js#ecmascript-reflect"&gt;ECMAScript: Reflect&lt;/a&gt;,   &lt;a href="https://github.com/zloirock/core-js#arrayprototypeincludes"&gt;Array.prototype.includes&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;web standard: 如 &lt;a href="https://github.com/zloirock/core-js#settimeout-and-setinterval"&gt;&lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dLXQw26X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybd3446hyz1ymr2myqc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dLXQw26X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybd3446hyz1ymr2myqc7.png" alt="Image description" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  这对工具有什么影响？
&lt;/h3&gt;

&lt;p&gt;工具分为两大类：compiler 和 polyfill library&lt;/p&gt;

&lt;p&gt;compiler如： Babel, TypeScript, esbuild(vite和webpack会处理关于module类的syntax)&lt;/p&gt;

&lt;p&gt;polyfill library如：core-js, es-shim, tslib等&lt;/p&gt;

&lt;p&gt;compiler 负责 让modern syntax 在现代浏览器工作。他们将浏览器不支持的syntax, 通过一些helper function（@bable/helpler, tslib），转成浏览器能认识的样子。&lt;/p&gt;

&lt;p&gt;polyfill library的任务是让native function能工作。es-shim如他的名字一样，支持 ECMAScript里的feature. 而core-js除此之外还支持&lt;a href="https://github.com/zloirock/core-js/issues/846#issuecomment-1219209987"&gt;部分web api&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GTLtm9b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zz0aa465wtq0ezxpim1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GTLtm9b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zz0aa465wtq0ezxpim1.png" alt="Image description" width="800" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;babel一类的compiler可以对代码进行分析，并自动根据对应的浏览器，通过polyfill provider选择import合适的polyfill library,如&lt;code&gt;core-js&lt;/code&gt;或es-shim ，将依赖注入。&lt;/p&gt;

&lt;h3&gt;
  
  
  如何打包这些polyfill
&lt;/h3&gt;

&lt;h4&gt;
  
  
  polyfill and ponyfill
&lt;/h4&gt;

&lt;p&gt;在详述如何打包polyfill之前，我们要先解释一个概念： ponyfill&lt;/p&gt;

&lt;p&gt;💡 A &lt;em&gt;ponyfill&lt;/em&gt;&lt;br&gt;
 is almost the same as a polyfill, but not quite. Instead of patching functionality for older browsers, a ponyfill provides that functionality as a standalone module you can use&lt;/p&gt;

&lt;p&gt;根据polyfill和ponyfill的分类，我们就有两种打包方式。其中polyfill指的是指global context的，可能会污染全局。ponyfill指的是不会污染全局没有副作用的版本。&lt;/p&gt;

&lt;p&gt;一个典型的例子就是 core-js和 core-js-pure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;core-js/actual/promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="c1"&gt;// 含有副作用的全局polyfill&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;core-js-pure/actual/promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//无副作用的局部polyfill.如果引用不存在，这部分会被dead code remove&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  如何打包
&lt;/h4&gt;

&lt;h5&gt;
  
  
  For App
&lt;/h5&gt;

&lt;p&gt;对于APP来说。 一般来说我们可能不怕全局污染（当作为微前端应用的一部分就要重新考虑)。&lt;/p&gt;

&lt;h6&gt;
  
  
  最原始的做法
&lt;/h6&gt;

&lt;p&gt;最原始的做法是： 所有的polyfill的直接在应用入口文件定义。&lt;br&gt;
例如在&lt;a href="https://polyfill.io/v3/url-builder"&gt;polyfill-io&lt;/a&gt;上，选择你需要的polyfill,生成一个“polyfill.min.js”，并在html所有script开始前引用它。&lt;br&gt;
或者使用babel，将要用的功能插件，例如"&lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;/plugin-transform-spread”枚举出来。&lt;br&gt;
这个做法的问题在于，靠人工制定哪个环境下需要哪些功能。需要人工知道哪些浏览器支持ECMA的哪些标准，并不方便放弃或增加对于浏览器的支持。如果node_modules里有人用了什么不知道的feature,代码就没有经过polyfill.&lt;/p&gt;
&lt;h5&gt;
  
  
  改进的做法
&lt;/h5&gt;

&lt;p&gt;工业化的第二个改进，是引入类似&lt;a href="https://github.com/browserslist/browserslist"&gt;browserlist&lt;/a&gt;的声明。在package.json或其配置文件里声明需要对哪些浏览器起效。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// package.json&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browserslist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;last 1 version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt; 1%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not dead&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;在入口处引入polyfill.例如“import "core-js";”.  然后compiler根据browserlist里的声明。将目标浏览器下缺失的每个功能列出来。全局polyfill, 使得后面的代码无需引入polyfill也能直接引用。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;core-js/modules/es.string.pad-start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;core-js/modules/es.string.pad-end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;这就是babel/preset-env的&lt;strong&gt;&lt;code&gt;useBuiltIns: 'entry'&lt;/code&gt;&lt;/strong&gt;的或babel-polyfills系列的&lt;code&gt;"method": "entry-global"&lt;/code&gt;行为。&lt;br&gt;
在入口文件根据目标浏览器，把可能用到的每一个ECMAScript feature引入。这样的好处是，只需要在入口处声明一次，并且方便更改浏览器支持。如果某个包没有参与app的编译也无所谓。因为所有的功能都在入口文件列出了。&lt;br&gt;
但这样也同样带来了问题，由于没有分析代码用到了哪些feature，所以需要把所有功能在入口列出。&lt;br&gt;
这样虽然根据浏览器裁剪了部分polyfill，但任然不符合模块化的要求，代码可能很后面才用到或者压根用不到，造成首屏和整体包体积膨胀。譬如core-js，gzip后任然有70kB。这些polyfill是全局副作用，还会占据首屏运行时间。&lt;/p&gt;
&lt;h6&gt;
  
  
  最好的做法
&lt;/h6&gt;

&lt;p&gt;最佳的做法是用到什么引入什么。babel这样的compiler分析这个库的所有代码(包括node_modules)。根据分析结果, 将引用插入每个文件开头。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;
    &lt;span class="c1"&gt;// 源文件&lt;/span&gt;
    &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="c1"&gt;// babel-plugin-polyfill-corejs3 usage-pure 转换后, 不污染全局&lt;/span&gt;
    &lt;span class="nx"&gt;_flatMapInstanceProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="c1"&gt;// babel-plugin-polyfill-corejs3 usage-global&lt;/span&gt;
    &lt;span class="c1"&gt;// 或preset-env useBuiltIns: 'usage'转换后 污染全局&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;core-js/modules/es.array.flat-map.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;这样减少了文件执行时间和文件大小。由于是app, 我们可以选择在全局进行polyfill。&lt;/p&gt;

&lt;h6&gt;
  
  
  babel该如何配置
&lt;/h6&gt;

&lt;ol&gt;
&lt;li&gt;使用&lt;strong&gt;&lt;code&gt;@babel/preset-env&lt;/code&gt;&lt;/strong&gt;避免手工枚举plugin, 注意proposal需要单独声明出来。&lt;/li&gt;
&lt;li&gt;如果需要全局polyfill,使用&lt;strong&gt;&lt;code&gt;@babel/preset-env&lt;/code&gt;&lt;/strong&gt;的 &lt;strong&gt;&lt;code&gt;useBuiltIns: 'usage'&lt;/code&gt;&lt;/strong&gt;或&lt;code&gt;[babel-plugin-polyfill-corejs3](https://github.com/babel/babel-polyfills/blob/main/packages/babel-plugin-polyfill-corejs3)&lt;/code&gt;的usage-global。后者是前者最新用法的代替。区别在于原来只能使用corejs作为polyfill。现在可以自己指定polyfill-provider。譬如你还可以使用&lt;code&gt;[babel-plugin-polyfill-es-shims](https://github.com/babel/babel-polyfills/blob/main/packages/babel-plugin-polyfill-es-shims)/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;如果需要局部ponyfill,使用&lt;code&gt;[babel-plugin-polyfill-corejs3](https://github.com/babel/babel-polyfills/blob/main/packages/babel-plugin-polyfill-corejs3)&lt;/code&gt;的usage-global。&lt;/li&gt;
&lt;li&gt;使用@babel/plugin-transform-runtime来 避免helper代码重复。一定要注意&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt;与所使用的runtime版本匹配。这部分会造成非常可观的代码膨胀。&lt;strong&gt;&lt;a href="https://babeljs.io/docs/babel-plugin-transform-runtime#version"&gt;&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  作为库作者如何打包
&lt;/h5&gt;

&lt;p&gt;最好的方式莫过于不打包。不去预设用户需要哪些polyfill。因为知道用户需要什么的只有用户自己。一旦预设的polyfill超出了用户的需要，就会造成不需要的代码被下载。库应该参与项目的构建。&lt;/p&gt;

&lt;p&gt;不过现实是大部分为了使用者方便，很多项目polyfill提前引用或打包。虽然这是不被推荐的行为。如果真的需要这么做的话，仍然要注意：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;使用ponyfill而不是polyfill，不要污染全局。&lt;/li&gt;
&lt;li&gt;使用&lt;a href="https://babeljs.io/docs/babel-plugin-transform-runtime"&gt;babel/plugin-transform-runtime&lt;/a&gt;来，并把 &lt;code&gt;@babel/runtime&lt;/code&gt;, core-js &lt;strong&gt;声明为dependency&lt;/strong&gt;(不是peer dep或者dev dep), 并注意 &lt;strong&gt;不要打包&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;库一般会提供多个target, 如cjs，es5,es2015等等，shippedProposals, esm等选项都要做出相应的改变。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt;字段与你使用的runtime版本匹配。因为默认值是@babel/&lt;a href="mailto:runtime@7.0.0"&gt;runtime@7.0.0&lt;/a&gt;，会造成版本不匹配，进而导致本该inject helper的地方，代码直接内连。例如regenerator的boilerplate代码就特别多。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;一个完整的例子：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//babel.config.js&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/preset-env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 根据browser list进行polyfill&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;targets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firefox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;useESModules&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commonjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="c1"&gt;//根据你的构建产物判断是否使用esm&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plugins&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/transform-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// 不是每个文件都插入代码，而是从@babel/runtime/helpers import&lt;/span&gt;
            &lt;span class="nx"&gt;useESModules&lt;/span&gt; &lt;span class="c1"&gt;//根据你的构建产物判断是否使用esm&lt;/span&gt;
            &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^7.4.4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 不要使用默认版本，避免无法inject造成代码膨胀&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;polyfill-corejs3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usage-pure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// 用ponyfill&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ECMAScript syntax
&lt;/h3&gt;

&lt;p&gt;打包方式基本和上文说的一致，需要区分app和library&lt;/p&gt;

&lt;p&gt;typescript, babel都可以进行转换。&lt;/p&gt;

&lt;p&gt;需要注意的是，babel/runtime或tslib都需要显示的声明成production dependencies. 并使用上文提到的@babel/plugin-transform-runtime来避免重复代码&lt;/p&gt;

&lt;h3&gt;
  
  
  WEB API
&lt;/h3&gt;

&lt;p&gt;core-js提供一部分web-api的实现。&lt;/p&gt;

&lt;p&gt;但web-api的问题在于，这部分功能大多数需要浏览器支持，不支持可能就是没有办法实现。所以当你用一个功能的时候，先在&lt;a href="https://caniuse.com/"&gt;CANIUSE&lt;/a&gt;上判断您的大多数用户是否可以使用，并提供降级方案。&lt;/p&gt;

&lt;h2&gt;
  
  
  Css
&lt;/h2&gt;

&lt;p&gt;不同的浏览器下，对css的支持也有差异。通常用prefix来区分。例如 “&lt;code&gt;-moz&lt;/code&gt;”代表mozilla旗下的fire-fox, &lt;code&gt;-webkit&lt;/code&gt;代表苹果使用的webkit. 分清哪些场景使用哪些属性，单靠手工在&lt;a href="https://caniuse.com/"&gt;caniuse&lt;/a&gt;查询依然十分不可靠。我们可以使用css中的polyfill： &lt;a href="https://github.com/postcss/autoprefixer"&gt;autoprefixer&lt;/a&gt; 同样根据browserlist自动完成。&lt;/p&gt;

&lt;h2&gt;
  
  
  Browserlist的角色和问题
&lt;/h2&gt;

&lt;p&gt;browserlist可以方便我们更新支持的browser，并且所有工具链都可以根据统一的target来统一完成对目标浏览器的适配。&lt;/p&gt;

&lt;p&gt;例如，defaults的默认值是&amp;gt; 0.5%, last 2 versions, Firefox ESR, not dead&lt;/p&gt;

&lt;p&gt;browserlist的数据来自于caniuse-lite。可以使用&lt;code&gt;[update-browserslist-db](https://github.com/browserslist/update-db)&lt;/code&gt;来定期更新。&lt;/p&gt;

&lt;p&gt;所以我们需要提交lock, 指出browserlist依赖的caniuse-lite版本，否则不同构建产生的polyfill都可能不一样。&lt;/p&gt;

&lt;p&gt;另外一个应用可能含有很多仓库。对于同一个项目，使用&lt;a href="https://github.com/browserslist/browserslist#shareable-configs"&gt;shared-config&lt;/a&gt;来让各个项目统一。&lt;/p&gt;

&lt;h3&gt;
  
  
  ref:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Polyfill_(programming)"&gt;https://en.wikipedia.org/wiki/Polyfill_(programming)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://es6-features.org/#StatementBodies"&gt;http://es6-features.org/#StatementBodies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/babel/babel/issues/10008#user-content-annex-b"&gt;https://github.com/babel/babel/issues/10008#user-content-annex-b&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/babel/babel/discussions/14443"&gt;https://github.com/babel/babel/discussions/14443&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/babel/babel/issues/9853"&gt;https://github.com/babel/babel/issues/9853&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://es6-features.org/#BinaryOctalLiteral"&gt;http://es6-features.org/#BinaryOctalLiteral&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/zloirock/core-js/labels/web%20standards"&gt;https://github.com/zloirock/core-js/labels/web standards&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/"&gt;https://caniuse.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://babeljs.io/docs/babel-plugin-transform-runtime"&gt;https://babeljs.io/docs/babel-plugin-transform-runtime&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>关键资源加载实践</title>
      <dc:creator>icy0307</dc:creator>
      <pubDate>Tue, 22 Aug 2023 05:49:16 +0000</pubDate>
      <link>https://dev.to/icy0307/guan-jian-zi-yuan-jia-zai-shi-jian-3a41</link>
      <guid>https://dev.to/icy0307/guan-jian-zi-yuan-jia-zai-shi-jian-3a41</guid>
      <description>&lt;p&gt;作为开发人员，您知道哪些文件最重要。你可以提前标记这些关键资源，加快加载过程。&lt;/p&gt;

&lt;p&gt;本文介绍了如何达成此目的。&lt;/p&gt;

&lt;p&gt;包括：&lt;/p&gt;

&lt;p&gt;预加载资源在真实世界的应用场景。&lt;/p&gt;

&lt;p&gt;何时以及如何使用preload。&lt;/p&gt;

&lt;p&gt;如何在你的构建工具中标记这些关键资源，以及现有方案的不足。&lt;/p&gt;

&lt;h2&gt;
  
  
  发现问题
&lt;/h2&gt;

&lt;p&gt;在一个应用里，我们发现当前页面真实能响应用户交互的时间非常晚。&lt;/p&gt;

&lt;p&gt;让我们用一张图视图化这个问题&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zI3d1vYn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9uromwyti9ucxbfl4bg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zI3d1vYn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9uromwyti9ucxbfl4bg.png" alt="Image description" width="800" height="221"&gt;&lt;/a&gt;&lt;br&gt;
为了使的client side渲染进行快速响应，渲染和响应交互的事件分了包，当用户渲染的脚本开始执行时，才会触发响应用户交互资源的加载。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onclick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.real-click-response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// This is the real time to interactive that user feel&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;/&amp;gt;
render(button);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;而这些代码里，可能会有其他响应交互所需的资源被进一部分包。这部分资源，在前面下载的资源实际开始执行的时候才被发现。&lt;/p&gt;

&lt;p&gt;这个被称为&lt;strong&gt;sequential network requests&lt;/strong&gt;问题。&lt;/p&gt;

&lt;p&gt;在真实世界里，网络环境越差，造成的结果就越严重。这个问题可以在performance面板被发现。典型的特征是，主线程是空的，没有要处理的内容。空闲时间和下载时间正好好差不多。并且下一个任务正是 evaluate刚刚下载的script.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QXWu5zRo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9ff79fkzghz1nocluov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QXWu5zRo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9ff79fkzghz1nocluov.png" alt="Image description" width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  怎么做预加载
&lt;/h2&gt;

&lt;p&gt;我们期望可以达成这样的目标。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cEZ1NNrW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/de3egnu78oq9omgzllo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cEZ1NNrW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/de3egnu78oq9omgzllo5.png" alt="Image description" width="800" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在网络请求有余力的时候，提早下载接下来的重要资源。&lt;/p&gt;

&lt;h3&gt;
  
  
  错误的做法❌
&lt;/h3&gt;

&lt;p&gt;实践中，有些人可能会在浏览器空闲的时候，尝试await import马上需要的资源。在应用的开头加入如下的话&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;requestIdleCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./resource-need-pretty-soon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;这个错误的原因在：&lt;/p&gt;

&lt;p&gt;我们期望的是触发他的下载，而不是执行。import会触发文件的evaluation。执行写在该文件top level的语句。这可能会非常耗时，取决于你引入的module在top level写了多少自执行代码。实际观察中，最差可能会出现500ms以上。&lt;/p&gt;

&lt;p&gt;client side render 以及应用初始化的时候，可能会产生很多微任务。我们不期望待会才需要的文件的evaluation会发生在 client side render的任何任务之前。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;预加载只应该占用网络线程的空闲资源而不是主线程的资源。&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  没那么好的做法
&lt;/h3&gt;

&lt;p&gt;自定义preload implementation，如fetch API。&lt;/p&gt;

&lt;p&gt;fetch需要被调用它的script在download, parse, compile, evaluate后才能被看见，实际上也产生了&lt;strong&gt;sequential network requests。&lt;/strong&gt;即使inline了script，仍然需要compile 和执行。这仍然没有我们接下来要介绍的做法效率高。&lt;/p&gt;

&lt;h3&gt;
  
  
  正确的做法
&lt;/h3&gt;

&lt;p&gt;幸运的是，浏览器提供了&lt;strong&gt;[rel=preload&lt;/strong&gt;的](&lt;a href="https://web.dev/preload-critical-assets/)%E6%A0%87%E7%AD%BE%EF%BC%8C%E5%8E%BB%E8%A7%A6%E5%8F%91%E8%B5%84%E6%BA%90%E7%9A%84%E4%B8%8B%E8%BD%BD%E3%80%82"&gt;https://web.dev/preload-critical-assets/)标签，去触发资源的下载。&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;虽然名字里有个load，但他只会触发下载和缓存，并不会真正执行。&lt;/p&gt;

&lt;p&gt;具体可以被preload的资源，和该标签支持的属性可以&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload"&gt;参考mdn的描述&lt;/a&gt;，在此不加以赘述。&lt;/p&gt;

&lt;p&gt;但是关于preload，以下有一些你需要注意的事：&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Don’t preload everything
&lt;/h4&gt;

&lt;p&gt;只preload那些你知道接下来绝对会使用到的资源。把不需要的资源preload只会浪费用户带宽。另外一次性preload过多的资源，可能会造成资源竞争。&lt;/p&gt;

&lt;p&gt;如果资源下载下来后，大概三秒没有用到，浏览器会出以下告警。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dtpuBMQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0tsuq4ccs42dxbg3u4n1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtpuBMQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0tsuq4ccs42dxbg3u4n1.png" alt="Image description" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果下一个页面或者navigation才需要的资源，您可能需要的是&lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Prefetch"&gt;prefetch&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. 有必要preload在html的script吗？
&lt;/h4&gt;

&lt;p&gt;在html嵌入的资源我们称之为initial assets，有些资源是动态加载的(譬如await import的资源)。我们称其为 async assets.&lt;/p&gt;

&lt;p&gt;script标签有可能会出现在body的末尾，我们需要对这些 initial assets 增加preload标签吗？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;通常不需要。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rtby8VoB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhusc3qh7vgkn0c4qbh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rtby8VoB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhusc3qh7vgkn0c4qbh4.png" alt="Image description" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;现代浏览器的html parser 在阻塞资源面前的确会暂停。 例如不是async，defer或者module的script标签，或是link下载的stylesheet。&lt;/p&gt;

&lt;p&gt;但是同时他会有一个另一个html parser: &lt;strong&gt;&lt;a href="https://web.dev/preload-scanner/"&gt;preload scanner&lt;/a&gt;&lt;/strong&gt;，提前发现当前页面里需要的资源。所以刚刚说到的， 放在body末尾的script标签也会被发现，即使前面有阻塞资源。所以是没必要对这些资源加preload标签的。&lt;/p&gt;

&lt;p&gt;然而 ，目前的preload scanner只作用于html。假设你的css会触发字体下载，或者需要背景图片。那即使这些是渲染关键资源的确也发现不了。如果这是阻碍您性能的关键问题，的确可以尝试preload这些资源。&lt;/p&gt;

&lt;p&gt;如果你的关键渲染资源被异步加载。用script去加载另一些js和css资源，preloads scanner就会不起作用。如果执行加载的script前面有block资源（例如首屏css), &lt;strong&gt;sequential network request&lt;/strong&gt;依然存在。即使你尝试使用preload标签，这些加载preload标签的script也只会在blocking resource执行完才开始运行。&lt;/p&gt;

&lt;p&gt;这种&lt;strong&gt;sequential network request&lt;/strong&gt;被称为 &lt;a href="https://developer.chrome.com/zh/docs/lighthouse/performance/critical-request-chains/"&gt;critical request chains&lt;/a&gt;, 我们应该避免它。&lt;/p&gt;

&lt;p&gt;所以比起preload标签，请先确认一定要用script去下载关键渲染资源吗？为什么不是直接把它们inline到html里？&lt;/p&gt;

&lt;h4&gt;
  
  
  3. 会重复下载吗
&lt;/h4&gt;

&lt;p&gt;会，如果你不足够小心。&lt;/p&gt;

&lt;p&gt;preload会下载，并且放入缓存。但是有时，preload资源和下载的资源并不会复用，触发&lt;strong&gt;double-fetch。&lt;/strong&gt;这不仅代表你做的preload优化没用，还代表浪费了用户的带宽。&lt;/p&gt;

&lt;p&gt;这就是仔细阅读preload属性的必要时候了&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一定要带上&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attributes"&gt;as&lt;/a&gt;属性。as描述了要被下载的资源类型，会影响Accept request header, request匹配等等。更重要的是 as还确定了preload 资源的fetch priority。&lt;/p&gt;

&lt;p&gt;关于preload资源的priority详细可以参看 &lt;a href="https://web.dev/fetch-priority/#browser-priority-and-fetchpriority"&gt;此表&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;预加载的字体如过没用crossorigin attribute 会被加载两次&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果使用了integrity，一些浏览器下不支持&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Addy Osmani关于double fetch问题有&lt;a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf"&gt;非常出色的文章&lt;/a&gt;。&lt;/p&gt;

&lt;h4&gt;
  
  
  4.  import preloads 只应该在下载他们的资源开始被import的时候才开始触发。
&lt;/h4&gt;

&lt;p&gt;import preload, 包括 dynamic &lt;code&gt;import()&lt;/code&gt; 或 &lt;code&gt;modulepreload&lt;/code&gt;，preload资源的时机应该在加载他们的script被加载之后。确保加载他们的script顺序是在前面的。script A 加载script B。只要scriptA被加载的时候scriptB才被需要。 尤其是modulepreload除了下载缓存以外，还会compile指定的资源。&lt;/p&gt;

&lt;h2&gt;
  
  
  如何在工程里使用preload
&lt;/h2&gt;

&lt;p&gt;在现代项目里，我们通常会使用打包工具将项目里的module组合成更大的文件，也就是bundle。所以在源码中，构建过程还未完成，我们是不可能自己加link标签的。所以我们需要借助打包工具将我们需要预加载的资源用link preload预加载。&lt;/p&gt;

&lt;p&gt;对于bundler原理其实很相似，但值得注意的是vite会有auto preload的功能。不过问题依旧是相似的。&lt;/p&gt;

&lt;p&gt;以下将用webpack 做一下说明：&lt;/p&gt;

&lt;p&gt;什么是关键资源其实需要我们自己决定, webpack利用事先定义好的comment来约定用户如何告诉webpack这是需要被preload的文件。也可以告诉webpack他的优先级是什么&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;
&lt;span class="cm"&gt;/* webpackFetchPriority: "high"*/&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CriticalChunk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;在构建的时候，他会解析到这个comment，明白用户想要加载这个module。于是这个module所属于的chunk group都会被预加载。&lt;/p&gt;

&lt;p&gt;不仅包含这个module本身，也包含这个module直接依赖的其他资源，如css，依赖的其他module等。&lt;/p&gt;

&lt;p&gt;加载的时机是由webpack runtime，在require引入preload的资源的文件的时机加载， 也就是加载他们的script被加载时。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;只在需要的时候preload&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;所以以下场景，不应该加preload标志&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Don't do this&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someRuntimeCondition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CriticalChunkA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CriticalChunkB&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;由于bundler只能静态分析，他并不知道RuntimeCondition在runtime的值是什么。所以他无法判断需要preload哪个文件，只好两个都下载，这会极大程度浪费用户带宽.&lt;/p&gt;

&lt;p&gt;在这个场景下我们该怎么做？&lt;/p&gt;

&lt;p&gt;it depends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;对于关键资源，可以在服务端触发主动下载。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果我们可以不依赖客户端知道这个condition是什么，并且我们可以确定这个资源关键关键路径一定会用到。&lt;/p&gt;

&lt;p&gt;我们可以事先把构建工具产生的bundle信息上传，在服务器预先判断好条件，通过不同条件下发不同bundle。&lt;/p&gt;

&lt;p&gt;这时会有两种方式: Server push 和 html 上附加&lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;Server push是http2 的一个新功能。他的一般做法是，在服务器上在html头部添加&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/push.css&amp;gt;; as=style; rel=preloa&lt;/span&gt;&lt;span class="err"&gt;d
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;如果server支持它，就会把他看作要push的对象。否则客户端就会把它视为一个preload assets。如果你使用了cdn，cdn也要支持。&lt;/p&gt;

&lt;p&gt;另一个做法是下发html的 上拼接&lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这两个做法的关键区别在：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;server push不需要依赖preload scanner. 所以原理上他会快一点点（但preload scanner本身也不慢）&lt;/li&gt;
&lt;li&gt;server push 的资源很容易被浪费。如果他已经push下来，但还没有被任何请求认领这个http session就被关闭的话，push缓存就没有了。如果这个请求已经在客户端缓存的话，server是没有办法知道的，依然push下来，造成带宽的浪费。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;另外，即使不在服务器上，也可以在浏览器运行的任何时间被添加。&lt;/p&gt;

&lt;p&gt;综上所述，可能大部分时间拼接&lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt;是个比较好的方法。&lt;/p&gt;

&lt;h3&gt;
  
  
  在entrypoint不起效
&lt;/h3&gt;

&lt;p&gt;下面这段代码不会生效，这和webpack的生态有关。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;awaitImportByInitialChunk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./await-imported-by-intial-chunk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;awaitImportByInitialChunk&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;webpack runtime只会去&lt;a href="https://medium.com/webpack/link-rel-prefetch-preload-in-webpack-51a52358f84c"&gt;加载被其他dynamic import的文件&lt;/a&gt;await import 的资源。&lt;/p&gt;

&lt;p&gt;正如上文所说，被preload的资源下载的时机，应该是加载它的文件被触发加载之后。而initial chunk是在html上通过script标签下载的。所以这种的位置就应该在这个script标签之后，html上。&lt;/p&gt;

&lt;p&gt;在webpack的生态里，这是由其插件&lt;a href="https://github.com/jantimon/html-webpack-plugin"&gt;https://github.com/jantimon/html-webpack-plugin&lt;/a&gt; 完成。&lt;/p&gt;

&lt;p&gt;遗憾的是，这个功能还没有被实现。不过html-webpack-plugin本身是支持插件的，所以我们可以&lt;a href="https://github.com/principalstudio/html-webpack-inject-preload"&gt;自己实现&lt;/a&gt;它。（还未发布）&lt;/p&gt;

&lt;h3&gt;
  
  
  避免会触发资源竞争的写法
&lt;/h3&gt;

&lt;p&gt;在实际生产中，还发现一种会让自动添加可能是负效果的写法。&lt;/p&gt;

&lt;p&gt; 假设当前的应用分几个关键的stage加载自身。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// entry.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstStageResources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondStageResources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./11&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./20&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thirdStageResources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./21&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./22&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* webpackPreload: true */&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./30&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preloadResources&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstStageResources&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondStageResources&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondStageResources&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;我们假设想要预下载其中的文件，对每个import()都做了preload标记，会发现由于所有的动态import其实都由一个文件加载。 当这个文件是entry，也就是html加载的initial chunk的时候，所有资源会一起被preload下来。&lt;/p&gt;

&lt;p&gt;本身&lt;a href="https://web.dev/fetch-priority/#browser-priority-and-fetchpriority"&gt;优先级很低&lt;/a&gt;的标签，甚至高过页面本身就有的initial script 标签，造成和其他资源的竞争。如果不改变这种pattern继续preload, 在资源紧张的时候可能是种负优化。&lt;/p&gt;

&lt;h3&gt;
  
  
  我有serviceWorker了，preload link有效吗
&lt;/h3&gt;

&lt;p&gt;service worker 在fetch事件的时候，进行拦截。&lt;/p&gt;

&lt;p&gt;如果cache里面没有，依旧可以访问http cache(disk cache)。这时如果已经preload资源就会存在在http cache里。依旧避免了网络请求的时间。所以整体来说，这仍然有意义。&lt;/p&gt;

&lt;p&gt;另外根据Jake Archibald&lt;a href="https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/"&gt;这篇文章&lt;/a&gt;，preload有独立的cache, 优先级会更高。导致被preload的资源，在资源原本被请求的位置发出请求时，不会走到service worker。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf"&gt;Addy Osmani的文章&lt;/a&gt;也提到如果这个文件是cacheable将会放入http cache, 如果不是甚至会被提升到memory cache。memory cache将比从disk里读的效率更高。&lt;/p&gt;

&lt;p&gt;另外如果是 module preload，preload的资源甚至已经被compile好。&lt;/p&gt;

&lt;p&gt;所以无论从何种角度上来说，preload都能和service worker一起使用，并在sequential request的场景下，起到性能提升作用。&lt;/p&gt;

&lt;h2&gt;
  
  
  总结
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;不要使用import预加载，import实际会让代码执行， 使用自带的preload机制做预加载&lt;/li&gt;
&lt;li&gt;只预加载过会一定要用到的资源，预加载的时机是想要加载预加载模块的代码被加载时。&lt;/li&gt;
&lt;li&gt;首屏资源没有必有主动添加，让preload scanner帮助你&lt;/li&gt;
&lt;li&gt;没有办法使用preload scanner的情况，可以在构建工具里标注， 动态的内容可以在服务器帮助判断。如果entryPoint发现标注preload失效，可以使用我们提供的webpack插件&lt;/li&gt;
&lt;li&gt;避免double fetch， 或者触发资源竞争的写法&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ref:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/preload-scanner/"&gt;https://web.dev/preload-scanner/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/zh/docs/lighthouse/performance/critical-request-chains/"&gt;https://developer.chrome.com/zh/docs/lighthouse/performance/critical-request-chains/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/fetch-priority/#conclusion"&gt;https://web.dev/fetch-priority/#conclusion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf"&gt;https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sking7.github.io/articles/332630583.html"&gt;https://sking7.github.io/articles/332630583.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.keycdn.com/blog/http2-push"&gt;https://www.keycdn.com/blog/http2-push&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/webpack/link-rel-prefetch-preload-in-webpack-51a52358f84c"&gt;https://medium.com/webpack/link-rel-prefetch-preload-in-webpack-51a52358f84c&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Peer Dependencies in depth</title>
      <dc:creator>icy0307</dc:creator>
      <pubDate>Tue, 22 Aug 2023 05:27:49 +0000</pubDate>
      <link>https://dev.to/icy0307/peer-dependencies-in-depth-1o3b</link>
      <guid>https://dev.to/icy0307/peer-dependencies-in-depth-1o3b</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;peerDependencies&lt;/code&gt; field is not for single copy of a package.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;peerDependencies&lt;/code&gt; when your host app needs a compatible version of another package to work with the library you provide. If your library doesn't have such constraints, list them as &lt;code&gt;dependencies&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;peerDependencies&lt;/code&gt; field is meant to warn you of any incompatibilities, so resolve unmet peer errors instead of ignoring them.&lt;/li&gt;
&lt;li&gt;As the library author, make both your  &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;peerDependencies&lt;/code&gt; ranges as wide as possible.&lt;/li&gt;
&lt;li&gt;Do not overuse &lt;code&gt;peerDependencies&lt;/code&gt;. Otherwise, library users will have difficulty resolving compatibility problems or may have to resort to ignoring them altogether.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you have worked with node packages, you might have come across the term "peer dependency." &lt;/p&gt;

&lt;p&gt;However, I've noticed that some of my colleagues tend to think that &lt;strong&gt;&lt;em&gt;peer dependencies are only for singletons&lt;/em&gt;&lt;/strong&gt; or that &lt;em&gt;**they guarantee a single copy&lt;/em&gt;* in final bundles* so the final code size could be smaller. However, &lt;strong&gt;this isn't quite accurate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, we will explore what peer dependencies are, how they differ from dependencies, and how package managers like 'npm' work with them.  This post will also cover how to resolve peer dependencies conflict and best practice to de-duplicate package’s copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Peer Dependencies?
&lt;/h2&gt;

&lt;p&gt;💡 &lt;code&gt;peerDependencies&lt;/code&gt; field is designed to be used for declaring compatibility requirements between a library and other libraries used by the host app.&lt;/p&gt;

&lt;p&gt;In order to understand what peer dependencies are, we need to understand why they are needed in the first place and why &lt;code&gt;dependencies&lt;/code&gt; are not enough.&lt;/p&gt;

&lt;p&gt;So why &lt;code&gt;dependencies&lt;/code&gt; are not  good enough? &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dependencies&lt;/code&gt; work fine in terms of "compatibility". If a package you are building requires library &lt;code&gt;foo&lt;/code&gt; version &lt;code&gt;1.0.0&lt;/code&gt;, and your app depends on &lt;code&gt;foo@2.0.0&lt;/code&gt;, your code will work fine as there are two copy of foo.&lt;/p&gt;

&lt;p&gt;├── &lt;a href="mailto:foo@2.0.0"&gt;foo@2.0.0&lt;/a&gt;&lt;br&gt;
└─┬ &lt;a href="mailto:your-library@1.2.3"&gt;your-library@1.2.3&lt;/a&gt;&lt;br&gt;
       └── &lt;a href="mailto:foo@1.0.0"&gt;foo@1.0.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, certain packages depend on other packages that must be installed by the user in order to function properly. If a package requires the presence of other packages, it is likely designed to be used with specific versions of those packages. If the installed versions are not compatible, an error should be thrown to alert the user to choose a compatible version instead of silently installing multiple versions as a sub-folder (as the &lt;code&gt;dependencies&lt;/code&gt; field does). This ensures that the library can work correctly with the host package.&lt;/p&gt;

&lt;p&gt;For example, consider the following structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="err"&gt;└─┬&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;
  &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Your library is a UI component library made with React. In order to work in a DOM environment, the &lt;code&gt;react&lt;/code&gt; package needs to be rendered by &lt;code&gt;react-dom&lt;/code&gt;, which should be installed in the root project using a method like &lt;code&gt;root.render(&amp;lt;App /&amp;gt;)&lt;/code&gt;. Depending on your code, your UI component library may have some constraints on the version of &lt;code&gt;react-dom&lt;/code&gt;, even though &lt;code&gt;react-dom&lt;/code&gt; is not required in your library's source code. For instance, your library uses React hooks, so &lt;a href="https://legacy.reactjs.org/warnings/invalid-hook-call-warning.html" rel="noopener noreferrer"&gt;the &lt;code&gt;react-dom&lt;/code&gt; library that mounts it must above version 16.8.0&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;If only using &lt;code&gt;dependencies&lt;/code&gt; field, the  &lt;code&gt;react-dom&lt;/code&gt; would present inside your library folder. But this is futile because &lt;code&gt;react-dom@17.0.0&lt;/code&gt; is not the package that the host app uses to render your &lt;code&gt;react&lt;/code&gt; component.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="c1"&gt;// this is the package the host app use to provide 'render' API&lt;/span&gt;
&lt;span class="err"&gt;└─┬&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;
  &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;
  &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="c1"&gt;// this is futile, since this is not the react-dom actually being used.&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is when &lt;code&gt;react-dom&lt;/code&gt; should be a peer dependency.&lt;/p&gt;

&lt;p&gt;Let’s look into another scenario, when &lt;code&gt;react&lt;/code&gt; need to be a  a peer dependency.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt; 
&lt;span class="err"&gt;└─┬&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;
  &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="c1"&gt;// incompatible with host app version&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The project install your library has already has &lt;code&gt;react&lt;/code&gt;  as dependency.&lt;code&gt;react&lt;/code&gt; that your library uses needs to be the same version as the one installed by the user, so that &lt;a href="https://legacy.reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react" rel="noopener noreferrer"&gt;hooks can work properly&lt;/a&gt;. In order to use your library, the host app’s &lt;code&gt;react&lt;/code&gt; version must be updated. &lt;strong&gt;The user must be informed of this!!! Hence, the use of peer dependencies.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Are Peer Dependencies Different From Dependencies?
&lt;/h2&gt;

&lt;p&gt;As mentioned above, when deciding whether to list a package in &lt;code&gt;dependencies&lt;/code&gt; or &lt;code&gt;peerDependencies&lt;/code&gt;, &lt;strong&gt;the only thing that should be taken into consideration is whether it imposes compatibility constraints on other libraries that the host app is using or must install.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This means that the &lt;code&gt;peerDependencies&lt;/code&gt; field is not for a singleton or single copy in the final bundle(We will explore how to achieve this later on).&lt;/p&gt;

&lt;p&gt;Let's consider a question: should &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; be listed as &lt;code&gt;dependencies&lt;/code&gt; or &lt;code&gt;peerDependencies&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The answer is: it depends.&lt;/p&gt;

&lt;p&gt;Wait a second, did you just mention in the previous section that &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; are peer dependencies? Yes, but that assumption was based on the idea that you are building a React-based UI library that provides React components for the host app to render. What if your library is a UI library that doesn't require &lt;code&gt;react-dom&lt;/code&gt; to render and only adds rendered DOM to the outside DOM that the user provides? The use of React is relevant only to the library  and does not impose any constraints on other packages installed by the user. Furthermore, the host app does not need to install any other packages for the library to work. Therefore, &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; are &lt;code&gt;dependencies&lt;/code&gt; that matter only to you: the library author. You may change your implementation to &lt;code&gt;Vue&lt;/code&gt; or &lt;code&gt;Svelte&lt;/code&gt; at any time.&lt;/p&gt;

&lt;p&gt;Here is a little quiz:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Should &lt;code&gt;lodash&lt;/code&gt; be a peer dependency or dependency?&lt;/li&gt;
&lt;li&gt;Should &lt;code&gt;tslib&lt;/code&gt; be a peer dependency or dependency?&lt;/li&gt;
&lt;li&gt;Should &lt;code&gt;rollup&lt;/code&gt; be a peer dependency or dependency?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What about multiple copies of package?
&lt;/h2&gt;

&lt;p&gt;Does this mean that if we use the &lt;code&gt;dependencies&lt;/code&gt; field, we may end up with multiple copies of the same package installed?&lt;/p&gt;

&lt;p&gt;Yes, if the versions are mismatched. This is also true for &lt;code&gt;peerDependencies&lt;/code&gt;, except it won't resolve correctly by design.&lt;/p&gt;

&lt;p&gt;As a matter of fact, &lt;code&gt;react&lt;/code&gt; does support having multiple copies on the same page. This is important because having multiple copies ensures that all code works properly, even if the package version they rely on doesn't match. For example, if the host app is using a different version of &lt;code&gt;lodash&lt;/code&gt; than the library is using, and there are breaking changes between these two versions, two copies are needed to ensure the code runs correctly.&lt;/p&gt;

&lt;p&gt;Furthermore, &lt;a href="https://medium.com/dailyjs/webpack-4-splitchunks-plugin-d9fbbe091fd0" rel="noopener noreferrer"&gt;bundlers like webpack have some bundle rules that cause multiple copies even though there is only one copy of a package in node_modules tree.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhvcdtb8caz6ll7fsaf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhvcdtb8caz6ll7fsaf8.png" alt="Image description" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Library has no way to ensure there is only one copy of package in final bundles or that the object inside library is singleton by modifying package json. The only correct way to create a singleton object is by adding a flag in the global scope.The number of copies in the node_modules folder is not determined by &lt;code&gt;dependencies&lt;/code&gt; or &lt;code&gt;peerDependencies&lt;/code&gt;, but by the package versions of the packages.&lt;/p&gt;

&lt;p&gt;So make sure your packages follow the &lt;a href="https://docs.npmjs.com/about-semantic-versioning" rel="noopener noreferrer"&gt;semantic versioning&lt;/a&gt; rule for both&lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;peerDependencies&lt;/code&gt; , and make it as wide as possible like &lt;code&gt;"^1.0"&lt;/code&gt; or &lt;code&gt;"1.x"&lt;/code&gt;, instead of  &lt;code&gt;"~1.0.4"&lt;/code&gt; or &lt;code&gt;"1.0.4"&lt;/code&gt; .For packages with a version below &lt;code&gt;"1.0.0"&lt;/code&gt;, the number in the middle represents the possibility of changes that break backward compatibility. If you know that such changes will not occur, you may use &lt;code&gt;"&amp;lt;1.0.0"&lt;/code&gt; instead. Then the host app can use command like &lt;code&gt;dedupe&lt;/code&gt;  to deduplicate dependencies with overlapping ranges.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should we do when conflicts occur with peer dependencies?
&lt;/h2&gt;

&lt;p&gt;Should we ignore it?&lt;/p&gt;

&lt;p&gt;If you have read the previous section, you now understand that peer dependency is a way to warn you that the packages you installed may not work with other packages you installed. Of course, you should resolve it instead of ignoring it.&lt;/p&gt;

&lt;p&gt;Unfortunately, in npm versions 3 through 6, &lt;code&gt;peerDependencies&lt;/code&gt; were completely ignored when building a package tree. Even today, both &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;pnpm&lt;/code&gt; have the &lt;code&gt;strict-peer-deps&lt;/code&gt; flag set to false by default, which ignores indirect peers. This can cause packages to receive a peer dependency outside the range set in their package's &lt;code&gt;peerDependencies&lt;/code&gt; object, potentially leading to bugs.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;-force&lt;/code&gt; and &lt;code&gt;-legacy-peer-deps&lt;/code&gt; is a shameful last resort, as it might break your code.&lt;/p&gt;

&lt;p&gt;But how to resolve it exactly? &lt;/p&gt;

&lt;p&gt;Before diving into how to resolve it, check whether the library really should list the conflicting package as a peer dependency causing so much trouble.&lt;/p&gt;

&lt;p&gt;If not, ask the author to change it into the &lt;code&gt;dependencies&lt;/code&gt; field, and ask the author to make the version of the package as broad as possible.&lt;/p&gt;

&lt;p&gt;If it should be listed in &lt;code&gt;peerDependencies&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dependency is below what peerDependencies require:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkvu5k884swwx5dmlene.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkvu5k884swwx5dmlene.png" alt="Image description" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just update the required dependency.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dependency is above what peerDependencies require:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd40bn5ms5wtm9v8i74eu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd40bn5ms5wtm9v8i74eu.png" alt="Image description" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If possible, ask the library author to update its peer dependencies. Alternatively, you can downgrade the dependencies of the host application.  Asking author to update might be the only solution in some cases when versions really are incompatible and you cannot afford to downgrade.&lt;/p&gt;

&lt;p&gt;Sometimes a library may be unmaintained, but still working. In such cases, you may know that broadening the version of &lt;code&gt;peerDependencies&lt;/code&gt; would not cause any compatibility issues. Alternatively, you may realize that the &lt;code&gt;peerDependencies&lt;/code&gt; it requires should not even be a &lt;code&gt;peerDependencies&lt;/code&gt; in the first place, but making the author understand this may take forever. Fortunately, your package manager provides a convenient way to avoid such hassle.&lt;/p&gt;

&lt;p&gt;For npm above 8, you can use &lt;code&gt;overrides&lt;/code&gt; field in package.json &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$react"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For pnpm, there more choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://pnpm.io/package_json#pnpmpeerdependencyrulesallowedversions" rel="noopener noreferrer"&gt;pnpm.peerDependencyRules.allowedVersions&lt;/a&gt; or some other related flag in package.json&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pnpm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"peerDependencyRules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowedVersions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"17"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Pnpm installation hook: hooks.readPackage(pkg, context): pkg&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;peerDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;peerDependency1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;peerDependency2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;overrides&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rootPkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pnpm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;overridesPeerDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peerDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;peerDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peerDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peerDependencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;readPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// skipDeps(pkg);&lt;/span&gt;
            &lt;span class="nf"&gt;overridesPeerDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However &lt;strong&gt;&lt;code&gt;pnpm.overrides&lt;/code&gt;&lt;/strong&gt;  isn’t working now.&lt;/p&gt;

&lt;p&gt;For yarn, sadly there is no way to do it for the time being, you can track this &lt;a href="https://github.com/yarnpkg/berry/issues/4099" rel="noopener noreferrer"&gt;issue&lt;/a&gt; for future possibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Things You Should Know About Peer Dependencies
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;“missing peer” usually mean auto installation  has been turned off,  check out corresponding  package manager doc for detail.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;peer dependencies are transitive.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;packageX&lt;/code&gt; has a dependency on &lt;code&gt;packageY&lt;/code&gt;, and &lt;code&gt;packageY&lt;/code&gt; has a peer dependency on &lt;code&gt;packageZ&lt;/code&gt;, then &lt;code&gt;packageZ&lt;/code&gt; is also a peer dependency of &lt;code&gt;packageX&lt;/code&gt;. This chain can continue until some package acts as the host.&lt;/p&gt;

&lt;p&gt;These can cause implicit problems, such as: &lt;a href="https://pnpm.io/how-peers-are-resolved" rel="noopener noreferrer"&gt;having two copies of exact same version packages&lt;/a&gt; in pnpm monorepo, or &lt;a href="https://dev.to/arcanis/implicit-transitive-peer-dependencies-ed0"&gt;a mismatch between babel plugins and babel version due to babel preset&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;peer dependencies can be optional. See &lt;a href="https://docs.npmjs.com/cli/v9/configuring-npm/package-json#peerdependenciesmeta" rel="noopener noreferrer"&gt;this page&lt;/a&gt; for more details.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, peer dependencies are an essential part of creating and maintaining a library to ensure compatibility with other packages used in a host application. They are not meant for singleton or single copy in the final bundle, and their use should be carefully considered to avoid unnecessary conflicts and inconvenience for users. For users, it is crucial to resolve conflicts when they occur rather than ignoring them. For library authors, it is recommended to use peer dependencies only when necessary and be lenient **in version requirement. This post has become much longer that I intended to, so we will talk about how to resolve multiple  packages copies in future post.&lt;/p&gt;

&lt;p&gt;Ref:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.npmjs.com/cli/v9/configuring-npm/package-json/#peerdependencies" rel="noopener noreferrer"&gt;https://docs.npmjs.com/cli/v9/configuring-npm/package-json/#peerdependencies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.npmjs.com/about-semantic-versioning" rel="noopener noreferrer"&gt;https://docs.npmjs.com/about-semantic-versioning&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nodejs.org/en/blog/npm/peer-dependencies" rel="noopener noreferrer"&gt;https://nodejs.org/en/blog/npm/peer-dependencies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/arcanis/implicit-transitive-peer-dependencies-ed0"&gt;https://dev.to/arcanis/implicit-transitive-peer-dependencies-ed0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/64573177/unable-to-resolve-dependency-tree-error-when-installing-npm-packages" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/64573177/unable-to-resolve-dependency-tree-error-when-installing-npm-packages&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pnpm/pnpm/issues/4214" rel="noopener noreferrer"&gt;https://github.com/pnpm/pnpm/issues/4214&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yarnpkg/berry/issues/4099" rel="noopener noreferrer"&gt;https://github.com/yarnpkg/berry/issues/4099&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/arcanis/implicit-transitive-peer-dependencies-ed0"&gt;https://dev.to/arcanis/implicit-transitive-peer-dependencies-ed0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/npm/rfcs/blob/main/implemented/0025-install-peer-deps.md" rel="noopener noreferrer"&gt;https://github.com/npm/rfcs/blob/main/implemented/0025-install-peer-deps.md&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>packagemanager</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
