cainiaoblog 23.2.346 → 23.2.348

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/atom.xml CHANGED
@@ -6,7 +6,7 @@
6
6
  <link href="https://xuehuayu.cn/atom.xml" rel="self"/>
7
7
 
8
8
  <link href="https://xuehuayu.cn/"/>
9
- <updated>2023-07-20T03:42:31.824Z</updated>
9
+ <updated>2023-07-21T09:10:52.811Z</updated>
10
10
  <id>https://xuehuayu.cn/</id>
11
11
 
12
12
  <author>
@@ -21,7 +21,7 @@
21
21
  <link href="https://xuehuayu.cn/article/9c3c56c.html"/>
22
22
  <id>https://xuehuayu.cn/article/9c3c56c.html</id>
23
23
  <published>2023-03-06T15:35:33.000Z</published>
24
- <updated>2023-07-20T03:42:31.824Z</updated>
24
+ <updated>2023-07-21T09:10:52.811Z</updated>
25
25
 
26
26
  <content type="html"><![CDATA[<p>vue,react,webpack,babel 要点总结</p><span id="more"></span><p><a href="https://juejin.cn/post/7146973901166215176">掘金大牛:「历时8个月」10万字前端知识体系总结(基础知识篇)</a></p><h2 id="Vue"><a href="#Vue" class="headerlink" title="Vue"></a>Vue</h2><h3 id="基本使用"><a href="#基本使用" class="headerlink" title="基本使用"></a>基本使用</h3><h4 id="v-html"><a href="#v-html" class="headerlink" title="v-html"></a>v-html</h4><p>原始html内容,有xss防线</p><h4 id="computed"><a href="#computed" class="headerlink" title="computed"></a>computed</h4><ul><li>有缓存,data不变不会重新计算</li></ul><h4 id="watch"><a href="#watch" class="headerlink" title="watch"></a>watch</h4><p>监听引用类型,使用下面方法深度监听,但是<strong>拿不到<code>oldVal</code></strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">watch</span>:&#123;</span><br><span class="line"> <span class="attr">info</span>: &#123;</span><br><span class="line"> handler (oldVal, val) &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>((oldVal, val)</span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">deep</span>: <span class="literal">true</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="class-写法"><a href="#class-写法" class="headerlink" title="class 写法"></a>class 写法</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">data () &#123;</span><br><span class="line"> <span class="attr">isblack</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">isRed</span>: <span class="literal">false</span>,</span><br><span class="line"> </span><br><span class="line"> <span class="attr">black</span>: <span class="string">&quot;black&quot;</span>,</span><br><span class="line"> <span class="attr">red</span>: <span class="string">&quot;red&quot;</span></span><br><span class="line">&#125;</span><br><span class="line">:<span class="keyword">class</span>=&#123;<span class="attr">black</span>: isblack, <span class="attr">red</span>: isRed&#125;</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">class</span>=[black, red]</span><br></pre></td></tr></table></figure><h4 id="style"><a href="#style" class="headerlink" title="style"></a>style</h4><p>object 类型,驼峰写法</p><h4 id="v-if-和-v-show-区别"><a href="#v-if-和-v-show-区别" class="headerlink" title="v-if 和 v-show 区别"></a>v-if 和 v-show 区别</h4><p>v-if 是否渲染,更新不频繁使用</p><p>v-show 是通过 css 的 display 控制显示与隐藏,频繁切换使用</p><h4 id="v-for"><a href="#v-for" class="headerlink" title="v-for"></a>v-for</h4><ul><li>也可以遍历对象</li><li>key,不能乱写,尽量不用index,不用random</li><li><code>v-for</code> 优先级比<code>v-if</code>高</li></ul><h4 id="事件"><a href="#事件" class="headerlink" title="事件"></a>事件</h4><h5 id="event参数,自定义参数"><a href="#event参数,自定义参数" class="headerlink" title="event参数,自定义参数"></a>event参数,自定义参数</h5><ul><li>没有参数,直接可以获取</li><li>有参数,用<code>$event</code></li><li><code>event</code>是原生对象</li></ul><h5 id="事件修饰符,按键修饰符"><a href="#事件修饰符,按键修饰符" class="headerlink" title="事件修饰符,按键修饰符"></a>事件修饰符,按键修饰符</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@click.stop.prevent</span><br><span class="line">@click.ctrl.exact</span><br></pre></td></tr></table></figure><h5 id="!(观察)事件被绑定到哪里"><a href="#!(观察)事件被绑定到哪里" class="headerlink" title="!(观察)事件被绑定到哪里"></a>!(观察)事件被绑定到哪里</h5><p><code>event.target</code>的值表明事件是挂在<strong>当前元素</strong>上的</p><h4 id="表单"><a href="#表单" class="headerlink" title="表单"></a>表单</h4><h5 id="v-model"><a href="#v-model" class="headerlink" title="v-model"></a>v-model</h5><p>v-model.trim</p><p>v-model.lazy</p><p>v-model.number</p><h2 id="组件使用"><a href="#组件使用" class="headerlink" title="组件使用"></a>组件使用</h2><h3 id="props"><a href="#props" class="headerlink" title="props"></a>props</h3><p>简写</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">props</span>:[<span class="string">&#x27;list&#x27;</span>]</span><br></pre></td></tr></table></figure><p>复杂写法,常用,可以定义类型和默认值</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">props</span>: &#123;</span><br><span class="line"> <span class="attr">type</span>: <span class="title class_">Array</span>,</span><br><span class="line"> <span class="attr">default</span>:&#123;</span><br><span class="line"> <span class="keyword">return</span> []</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="emit"><a href="#emit" class="headerlink" title="$emit"></a>$emit</h3><p>父组件</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;<span class="title class_">Input</span> @add=<span class="string">&quot;clickhandler&quot;</span>/&gt;</span><br></pre></td></tr></table></figure><p>子组件</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">methods</span>:&#123;</span><br><span class="line"> <span class="title function_">addTitle</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.$emit(<span class="string">&#x27;add&#x27;</span>, title)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="组件通信-自定义事件(兄弟组件通信)"><a href="#组件通信-自定义事件(兄弟组件通信)" class="headerlink" title="组件通信 - 自定义事件(兄弟组件通信)"></a>组件通信 - 自定义事件(兄弟组件通信)</h3><p><strong>vue 具有自定义事件能力 $on、$off、$emit</strong></p><p><strong>组件卸载时,解除事件绑定,销毁子组件,定时器等</strong></p><h3 id="组件生命周期"><a href="#组件生命周期" class="headerlink" title="组件生命周期"></a>组件生命周期</h3><ul><li>挂载阶段</li><li>更新阶段</li><li>销毁阶段</li></ul><h4 id="单个组件"><a href="#单个组件" class="headerlink" title="单个组件"></a>单个组件</h4><p>created:vue实例化完成</p><p>mounted:渲染完成</p><h4 id="父子组件"><a href="#父子组件" class="headerlink" title="父子组件"></a>父子组件</h4><p>组件实例创建: 先父后子</p><p>组件渲染: 先子后父【子组件渲染完,才能挂载】</p><h3 id="高级特性"><a href="#高级特性" class="headerlink" title="高级特性"></a>高级特性</h3><h4 id="自定义-v-model"><a href="#自定义-v-model" class="headerlink" title="自定义 v-model"></a>自定义 v-model</h4><p>父组件中</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;CunstomVModal v-model=&#123;name&#125;&gt;</span><br></pre></td></tr></table></figure><p>子组件中 CunstomVModal</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line"> &lt;input type=&quot;text&quot; :value=&quot;txt&quot; @input=&quot;$emit(&#x27;chage&#x27;, $event.target.value)&quot;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line"> export default &#123;</span><br><span class="line"> model:&#123;</span><br><span class="line"> prop: &#x27;txt&#x27;,</span><br><span class="line"> event:&#x27;chage&#x27;</span><br><span class="line"> &#125;,</span><br><span class="line"> props:&#123;</span><br><span class="line"> txt: String,</span><br><span class="line"> default () &#123;</span><br><span class="line"> return &#x27;&#x27;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h4 id="nextTick、refs"><a href="#nextTick、refs" class="headerlink" title="$nextTick、refs"></a>$nextTick、refs</h4><ul><li>vue 是异步渲染</li><li>data 改变之后,dom不会like渲染</li><li>$nextTick 会在dom渲染之后被处罚,以获取最新的dom节点</li></ul><h4 id="slot"><a href="#slot" class="headerlink" title="slot"></a>slot</h4><ul><li>基本使用</li><li>作用于插槽</li><li>具名插槽</li></ul><h4 id="动态组件"><a href="#动态组件" class="headerlink" title="动态组件"></a>动态组件</h4><ul><li>:is&#x3D;”component-name”</li><li>需要根据数据,动态渲染的场景。即组件类型不确定</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line"> &lt;component :is=&quot;compName&quot;&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line"> import myComp from &#x27;./myComp&#x27;</span><br><span class="line"> components:&#123;</span><br><span class="line"> myComp</span><br><span class="line"> &#125;,</span><br><span class="line"> data () &#123;</span><br><span class="line"> return &#123;</span><br><span class="line"> compName: &#x27;myComp&#x27;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h4 id="异步组件"><a href="#异步组件" class="headerlink" title="异步组件"></a>异步组件</h4><ul><li>import()</li><li>按需加载,异步加载大组件</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">export default &#123;</span><br><span class="line"> components:&#123;</span><br><span class="line"> Demo: () =&gt; import(&#x27;./ImportDemo&#x27;)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="keep-alive"><a href="#keep-alive" class="headerlink" title="keep-alive"></a>keep-alive</h4><ul><li>缓存组件</li><li>频繁切换,不需要重复渲染</li><li>vue常见性能优化</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// 每个组件只渲染一次,且不会销毁。</span><br><span class="line">// 简单的用v-show就行,稍微复杂的用keep-alive</span><br><span class="line">&lt;keep-alive&gt;</span><br><span class="line"> &lt;A v-if=&quot;a&quot;&gt;&lt;/A&gt;</span><br><span class="line"> &lt;B v-if=&quot;b&quot;&gt;&lt;/B&gt;</span><br><span class="line"> &lt;C v-if=&quot;c&quot;&gt;&lt;/C&gt;</span><br><span class="line">&lt;/keep-alive&gt;</span><br></pre></td></tr></table></figure><h4 id="mixin"><a href="#mixin" class="headerlink" title="mixin"></a>mixin</h4><ul><li>多个组件有相同的逻辑,抽离出来</li><li>mixin并不是完美的解决方案,会有一些问题</li><li>vue 3 提出的 composition API 旨在解决这些问题</li></ul><hr><h5 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h5><ul><li>变量来源不明确,不利于阅读</li><li>多个mixin可能会造成命名冲突</li><li>mixin和组件可能会出现多对多的关系,复杂度较高</li></ul><hr><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&lt;sript&gt;</span><br><span class="line"> import Mixin from &#x27;./mixin&#x27;</span><br><span class="line"> export default &#123;</span><br><span class="line"> mixins: [Mixin],</span><br><span class="line"> data () &#123;</span><br><span class="line"> return &#123;&#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&lt;/sript&gt;</span><br></pre></td></tr></table></figure><h4 id="vuex-使用"><a href="#vuex-使用" class="headerlink" title="vuex 使用"></a>vuex 使用</h4><ul><li>基本概念、基本使用、API</li><li>state的数据结构设计</li></ul><h5 id="vuex基本概念"><a href="#vuex基本概念" class="headerlink" title="vuex基本概念"></a>vuex基本概念</h5><ul><li>state</li><li>gettters</li><li>action</li><li>mutation</li></ul><h5 id="用于vue组件"><a href="#用于vue组件" class="headerlink" title="用于vue组件"></a>用于vue组件</h5><ul><li>dispatch</li><li>commit</li><li>mapstate</li><li>mapGetters</li><li>mapActions</li><li>mapMutations</li></ul><h4 id="vue-router"><a href="#vue-router" class="headerlink" title="vue-router"></a>vue-router</h4><ul><li>路由模式:<ul><li>hash<ul><li>window.onhashchange</li><li>会出发网页跳转,</li><li>可前进后退,</li><li>不会提交到server端,强制刷新也不会</li><li>可刷新</li></ul></li><li>H5 history<ul><li>history.pushState</li><li>window.onpopstate</li><li>不可刷新(需要后端支持)</li></ul></li></ul></li><li>路由配置:<ul><li>动态路由 {path: ‘&#x2F;user&#x2F;:id?’, component: User}</li><li>懒加载 {path: ‘&#x2F;user&#x2F;:id?’, component: () &#x3D;&gt; import(‘.&#x2F;user’)}</li></ul></li></ul><h3 id="Vue原理"><a href="#Vue原理" class="headerlink" title="Vue原理"></a>Vue原理</h3><h4 id="如何里面MVVM"><a href="#如何里面MVVM" class="headerlink" title="如何里面MVVM"></a>如何里面MVVM</h4><ul><li>很久以前的视图<ul><li>asp jsp php 已经有组件化了</li><li>传统组件,只是静态渲染,更新依赖于操作dom</li></ul></li><li>数据驱动视图(vue MVVM,react setState)</li></ul><h4 id="vue-响应式"><a href="#vue-响应式" class="headerlink" title="vue 响应式"></a>vue 响应式</h4><ul><li>组件data的数据一旦变化,立刻出发视图更新</li><li>核心API - Object.defineProperty<ul><li>get</li><li>set</li></ul></li><li>Object.defineProperty 的一些缺点 (Vue3.0 使用 Proxy)</li><li>proxy 兼容性不太好,且无法 polyfill</li></ul><h5 id="Object-defineProperty-缺点"><a href="#Object-defineProperty-缺点" class="headerlink" title="Object.defineProperty 缺点"></a>Object.defineProperty 缺点</h5><ul><li>深度监听,需要递归到底,一次性计算量大</li><li>新增,删除 无法监听,需要用 Vue.set 和 Vue.delete</li><li>无法监听数组,需要专门处理</li></ul><h5 id="Proxy-实现响应式"><a href="#Proxy-实现响应式" class="headerlink" title="Proxy 实现响应式"></a>Proxy 实现响应式</h5><ul><li>基本使用</li><li>Reflect</li><li>实现响应式</li><li>对比 defineProperty 提高性能<ul><li>set第一层属性的时候,触发set</li><li>set第二层属性的时候触发第一层属性的get</li><li>所以在get的时候,返回一个 新的proxy对象即可 深度监听,而且是惰性监听</li><li>所以性能比defineProperty好</li></ul></li></ul><h4 id="虚拟DOM-和-diff"><a href="#虚拟DOM-和-diff" class="headerlink" title="虚拟DOM 和 diff"></a>虚拟DOM 和 diff</h4><ul><li>DOM操作非常耗性能,js是执行非常快的</li><li>vue和react是数据驱动视图</li></ul><h5 id="vdom"><a href="#vdom" class="headerlink" title="vdom"></a>vdom</h5><p>用js模拟dom结构,计算出最小的变更,操作dom</p><h5 id="snabbdom"><a href="#snabbdom" class="headerlink" title="snabbdom"></a>snabbdom</h5><p>h函数,返回vnode(js)<br>patch(container, vnode) 初次渲染<br>patch(oldVnode, newVnode) 更新,新旧vnode都有children,那么updateChildren<br>patch(oldVnode, null) 销毁清空</p><h5 id="diff"><a href="#diff" class="headerlink" title="diff"></a>diff</h5><ul><li>只比较同一层级,不跨级比较</li><li>tag不相同,直接删掉重建,不再深度比较</li><li>tag和key都相同,则认为是相同节点,不再深度比较</li></ul><h5 id="模板编译"><a href="#模板编译" class="headerlink" title="模板编译"></a>模板编译</h5><ul><li>vue将模板编译为render函数</li><li>执行render函数生成vnode,patch</li><li>触发响应式,监听data属性getter setter</li></ul><blockquote><ul><li>with语法</li><li>vue组件可以用render代替template</li></ul></blockquote><h6 id="初次渲染过程"><a href="#初次渲染过程" class="headerlink" title="初次渲染过程"></a>初次渲染过程</h6><ul><li>解析为render函数</li><li>触发响应式,监听data属性getter setter</li><li>执行render函数,生成vnode,patch</li></ul><h6 id="更新过程"><a href="#更新过程" class="headerlink" title="更新过程"></a>更新过程</h6><ul><li>修改data,触发setter,(此前在getter中已被监听)</li><li>重新执行render函数,生成newVnode</li><li>patch(oldVnode, newVnode)</li></ul><h5 id="组件异步渲染"><a href="#组件异步渲染" class="headerlink" title="组件异步渲染"></a>组件异步渲染</h5><ul><li>$nextTick</li><li>汇总data修改,一次性更新视图</li><li>减少dom操作次数,提高性能</li></ul><h4 id="Vue3"><a href="#Vue3" class="headerlink" title="Vue3"></a>Vue3</h4><h5 id="新功能"><a href="#新功能" class="headerlink" title="新功能"></a>新功能</h5><ul><li><p>createApp</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">// vue2</span><br><span class="line">const app = new Vue(&#123;&#125;)</span><br><span class="line">Vue.use()</span><br><span class="line">Vue.mixin()</span><br><span class="line">Vue.component()</span><br><span class="line">Vue.directive()</span><br><span class="line">// vue3</span><br><span class="line">const app = Vue.createApp(&#123;&#125;)</span><br><span class="line">app.use()</span><br><span class="line">app.mixin()</span><br><span class="line">app.component()</span><br><span class="line">app.directive()</span><br></pre></td></tr></table></figure></li><li><p>emits属性</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">emits: [&#x27;onClickhandler&#x27;] // 建议 onXxx</span><br></pre></td></tr></table></figure></li><li><p>生命周期</p><ul><li>beforeDestroy 改为 beforeUnmount</li><li>destroyed 改为 unmounted</li></ul></li><li><p>多事件处理, @click&#x3D;”one($event),two($event)”</p></li><li><p>Fragment, 模板不需要包在一个根组件中了,template下面可以写多个标签</p></li><li><p>移除.sync改为v-model参数</p></li><li><p>异步组件的引用方式, defindAsyncComponent</p></li><li><p>移除filter,如:v-if&#x3D;”boolA | boolB”</p></li><li><p>Teleport,<teleport to="body"></teleport></p></li><li><p>Suspense</p></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&lt;Suspense&gt;</span><br><span class="line"> &lt;template&gt;</span><br><span class="line"> &lt;Test1/&gt; &lt;!-- 是一个异步组件 --&gt;</span><br><span class="line"> &lt;/template&gt;</span><br><span class="line"> &lt;!-- 具名插槽 </span><br><span class="line"> 即:Suspense 组件内部,有两个slot,</span><br><span class="line"> 其中一个具名为 fallback</span><br><span class="line"> --&gt;</span><br><span class="line"> &lt;tempalte #fallback&gt;</span><br><span class="line"> Loading...</span><br><span class="line"> &lt;/tempalte&gt;</span><br><span class="line">&lt;/Suspense&gt;</span><br></pre></td></tr></table></figure><ul><li>Composition API<ul><li>reactive</li><li>ref toRef toRefs</li><li>readonly</li><li>computed</li><li>watch watchEffect</li><li>钩子函数生命周期</li></ul></li></ul><h5 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h5><ul><li>proxy 实现响应式<ul><li>对值类型无能为力,所以要用 ref</li></ul></li><li>编译优化<ul><li>PatchFlag 静态标记</li><li>hoistStatic 静态提升</li><li>cacheHandler 缓存事件</li><li>SSR 优化</li><li>Tree-shaking 优化</li></ul></li></ul><h5 id="Vue3-比-vue2-优势"><a href="#Vue3-比-vue2-优势" class="headerlink" title="Vue3 比 vue2 优势"></a>Vue3 比 vue2 优势</h5><ul><li>性能更好</li><li>体积更小</li><li>更好的ts支持</li><li>更好的代码组织</li><li>更好的逻辑抽离</li><li>更多的新功能</li></ul><h5 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h5><ul><li>beforeDestroy 改为 beforeUnmount</li><li>destroyed 改为 unmounted</li><li>其他沿用vue2的生命周期</li><li>setup等于 beforeCreate 和 created</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">import &#123; onBeforeMount &#125; from &#x27;vue&#x27;</span><br><span class="line"></span><br><span class="line">export default &#123;</span><br><span class="line"> &lt;!-- 其他代码 --&gt;</span><br><span class="line"> setup () &#123;</span><br><span class="line"> onBeforeMount(() =&gt; &#123;</span><br><span class="line"> console.log(&#x27;onBeforeMount&#x27;)</span><br><span class="line"> &#125;)</span><br><span class="line"> &#125;</span><br><span class="line"> &lt;!-- 其他代码 --&gt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="Composition-API-对比-Options-API"><a href="#Composition-API-对比-Options-API" class="headerlink" title="Composition API 对比 Options API"></a>Composition API 对比 Options API</h5><ul><li>更好的代码组织</li><li>更好的逻辑复用(options api 分散于生命周期中,代码越多越明显)</li><li>更好的类型推导(直接this.a 和 this.fn,不利于类型判断 )</li><li>不建议共用</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">export default &#123;</span><br><span class="line"> data () &#123;</span><br><span class="line"> return &#123;</span><br><span class="line"> a: 10</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;,</span><br><span class="line"> methods: &#123;</span><br><span class="line"> fn () &#123;</span><br><span class="line"> const a = this.a</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;,</span><br><span class="line"> mounted: &#123;</span><br><span class="line"> this.fn()</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="ref-toRef-toRefs"><a href="#ref-toRef-toRefs" class="headerlink" title="ref toRef toRefs"></a>ref toRef toRefs</h5><ul><li>toRef 将一个响应式对象中的某个属性变为ref</li><li>toRefs 将一个响应式对象变为普通对象,这个普通对象的每个属性变为ref</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line"> &lt;div&gt;&#123;&#123;name&#125;&#125;&#123;&#123;ageRef&#125;&#125;&lt;/div&gt;</span><br><span class="line"> &lt;div ref=&quot;eleRef&quot;&gt;我是文字&lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line"> import &#123; ref, toRef, toRefs, reactive, onMounted &#125; from &#x27;vue&#x27;</span><br><span class="line"></span><br><span class="line"> export default &#123;</span><br><span class="line"> name: &#x27;Ref&#x27;,</span><br><span class="line"> setup() &#123;</span><br><span class="line"> const eleRef = ref(null)</span><br><span class="line"> const ageRef = ref(20)</span><br><span class="line"> // 两者保持引用关系↓</span><br><span class="line"> // 不能返回解构的state,会丢失响应式</span><br><span class="line"> const state = reactive(&#123;</span><br><span class="line"> name: &#x27;张三&#x27;</span><br><span class="line"> &#125;)</span><br><span class="line"> const nameRef = toRef(state, &#x27;name&#x27;)</span><br><span class="line"> nameRef.value = &#x27;李四&#x27; // state中的name也会改变</span><br><span class="line"> state.name = &#x27;王五&#x27; // nameRef的值也会改变</span><br><span class="line"> // 两者保持引用关系 ↑</span><br><span class="line"> ageRef.value = 30</span><br><span class="line"> onMounted(() =&gt; &#123;</span><br><span class="line"> console.log(eleRef.value.innerHTML)</span><br><span class="line"> &#125;)</span><br><span class="line"></span><br><span class="line"> // const stateRefs = toRefs(state)</span><br><span class="line"> // const &#123;name: nameRef&#125; = stateRefs</span><br><span class="line"> // return stateRefs // toRefs的返回</span><br><span class="line"></span><br><span class="line"> // 其他返回</span><br><span class="line"> return &#123;</span><br><span class="line"> ageRef,</span><br><span class="line"> eleRef</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&lt;/script&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="为什么要用-Ref"><a href="#为什么要用-Ref" class="headerlink" title="为什么要用 Ref"></a>为什么要用 Ref</h5><ul><li>返回 值类型,会丢失响应式</li><li>如在setup、computed、合成函数,都有可能返回 值类型</li><li>vue如果不定义ref。用户将自造ref,反而更混乱</li></ul><h5 id="为什么要-value"><a href="#为什么要-value" class="headerlink" title="为什么要.value"></a>为什么要.value</h5><ul><li>ref 是个对象(不丢失响应式),value存储值</li><li>通过.value属性的get和set实现响应式</li><li>用于模板、reactive时,(vue可以自己控制)不需要.value,其他情况都需要</li></ul><h5 id="Composition-API-实现逻辑复用"><a href="#Composition-API-实现逻辑复用" class="headerlink" title="Composition API 实现逻辑复用"></a>Composition API 实现逻辑复用</h5><ul><li>抽离逻辑代码到一个函数</li><li>函数命名约定为 useXxx 格式</li><li>setup中引用 useXxx 函数</li></ul><h5 id="Proxy-实现响应式-1"><a href="#Proxy-实现响应式-1" class="headerlink" title="Proxy 实现响应式"></a>Proxy 实现响应式</h5><ul><li>基本使用</li><li>Reflect</li><li>实现响应式</li><li>对比 defineProperty 提高性能<ul><li>set第一层属性的时候,触发set</li><li>set第二层属性的时候触发第一层属性的get</li><li>所以在get的时候,返回一个 新的proxy对象即可 深度监听,而且是惰性监听</li><li>所以性能比defineProperty好</li></ul></li></ul><h5 id="watch-和-watchEffect-的区别"><a href="#watch-和-watchEffect-的区别" class="headerlink" title="watch 和 watchEffect 的区别"></a>watch 和 watchEffect 的区别</h5><ul><li>两者都可以监听data属性变化</li><li>watch 需要明确监听哪个属性</li><li>watchEffect会根据其中的属性,自动监听其变化</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">// watch 默认不会立即执行</span><br><span class="line">// 监听ref</span><br><span class="line">watch(nameRef, (newVal, oldVal) =&gt; &#123;&#125;, [options])</span><br><span class="line">// 监听响应式</span><br><span class="line">watch(() =&gt; state.name, (newState, oldState) =&gt; &#123;&#125;, [options])</span><br><span class="line"></span><br><span class="line">watchEffect(() =&gt; &#123;</span><br><span class="line"> // 初始化就会执行一次,收集监听的数据</span><br><span class="line"> console.log(&#x27;watchEffect&#x27;)</span><br><span class="line">&#125;)</span><br><span class="line">watchEffect(() =&gt; &#123;</span><br><span class="line"> // 写了 state.name 就监听, 不写的不会监听</span><br><span class="line"> console.log(state.name)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h5 id="setup中如何获取组件实例"><a href="#setup中如何获取组件实例" class="headerlink" title="setup中如何获取组件实例"></a>setup中如何获取组件实例</h5><ul><li>在setup 和 Composition API 中没有this</li><li>可通过 getCurrentInstance 获取当前实例</li><li>若使用 options API 可照常使用 this</li></ul><h5 id="vue3-为何比-vue2-快"><a href="#vue3-为何比-vue2-快" class="headerlink" title="vue3 为何比 vue2 快"></a>vue3 为何比 vue2 快</h5><ul><li>Proxy 响应式</li><li>PatchFlag &#x2F;&#x2F; 标记节点类型</li><li>hoistStatic &#x2F;&#x2F; 缓存静态节点,多个节点合并</li><li>cacheHandler &#x2F;&#x2F;</li><li>SSR 优化</li><li>tree-shaking</li></ul><h6 id="PatchFlag"><a href="#PatchFlag" class="headerlink" title="PatchFlag"></a>PatchFlag</h6><ul><li>编译模板时,动态节点做标记</li><li>标记分为不同类型,如 TEXT PROPS</li><li>diff算法时,可以区分静态节点,以及不同类型的节点</li></ul><h6 id="hoistStatic"><a href="#hoistStatic" class="headerlink" title="hoistStatic"></a>hoistStatic</h6><ul><li>将静态节点的定义,提升到父作用域,缓存起来</li><li>多个相邻的静态节点,会被合并起来</li><li>典型的拿空间换时间的优化策略</li></ul><h6 id="cacheHandler"><a href="#cacheHandler" class="headerlink" title="cacheHandler"></a>cacheHandler</h6><ul><li>将事件缓存起来</li></ul><h5 id="vite-ES6-module"><a href="#vite-ES6-module" class="headerlink" title="vite - ES6 module"></a>vite - ES6 module</h5><h2 id="React"><a href="#React" class="headerlink" title="React"></a>React</h2><h3 id="基本使用-1"><a href="#基本使用-1" class="headerlink" title="基本使用"></a>基本使用</h3><h4 id="setState"><a href="#setState" class="headerlink" title="setState"></a>setState</h4><ul><li>不可变值,为了SCU</li><li>可能异步可能同步</li><li>可能会被合并</li></ul><h5 id="是同步还是异步"><a href="#是同步还是异步" class="headerlink" title="是同步还是异步"></a>是同步还是异步</h5><ul><li>直接使用是异步的</li><li>在setTimeout或者自定义的dom事件中是同步的</li></ul><h5 id="何时会合并"><a href="#何时会合并" class="headerlink" title="何时会合并"></a>何时会合并</h5><ul><li>对象形式,会被合并</li><li>函数形式,不会合并</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">this.setState((state, props) =&gt; &#123;&#125;)</span><br></pre></td></tr></table></figure><ul><li>直接修改state的值,影响性能</li></ul><h4 id="事件为何要bind-this"><a href="#事件为何要bind-this" class="headerlink" title="事件为何要bind this"></a>事件为何要bind this</h4><ul><li>class生成的实例中,this是undefined</li></ul><h4 id="Reaact-高级特性"><a href="#Reaact-高级特性" class="headerlink" title="Reaact 高级特性"></a>Reaact 高级特性</h4><ul><li>函数组件</li><li>非受控组件</li><li>Portals ReactDOM.createPortals()</li><li>context</li><li>异步组件,import() React.lazy React.Suspense</li><li>性能优化,SCU 核心问题是 不可变值</li><li>高阶组件HOC</li><li>render props</li></ul><h5 id="jsx-本质是什么"><a href="#jsx-本质是什么" class="headerlink" title="jsx 本质是什么"></a>jsx 本质是什么</h5><ul><li>React.createElement 函数,执行返回 vnode</li></ul><h5 id="事件合成机制"><a href="#事件合成机制" class="headerlink" title="事件合成机制"></a>事件合成机制</h5><ul><li>div 冒泡至 document,合成统一的react event,dispatchEvent事件派发交由对应的处理器执行</li><li>更好的兼容性和跨平台</li><li>挂载到根节点,减少内存消耗,避免频繁解绑</li><li>方便统一管理(如事务机制)</li></ul><h5 id="transaction-事务机制"><a href="#transaction-事务机制" class="headerlink" title="transaction 事务机制"></a>transaction 事务机制</h5><p>Transaction类的主要作用使用提供的包装(Wrapper)来包装一个函数。<br>Transaction会接受一个方法 func,和一组Wrapper。Transaction会在func执行之前,执行一组Wrapper中的initialize方法。而后执行func方法,在func方法执行完了之后,执行Wrapper提供的所有close方法。</p><h5 id="fiber"><a href="#fiber" class="headerlink" title="fiber"></a>fiber</h5><ul><li>props state</li><li>render 生成 vnode</li><li>patch(ele, vnode)<ul><li>reconciliation 执行diff算法,纯js计算<ul><li>进行任务拆分</li><li>DOM需要渲染时暂停,空闲时恢复</li><li>window.requestIdleCallback</li></ul></li><li>commit 阶段,将diff结果渲染到dom中</li></ul></li></ul><h5 id="react-性能优化"><a href="#react-性能优化" class="headerlink" title="react 性能优化"></a>react 性能优化</h5><ul><li>渲染列表key</li><li>及时销毁事件</li><li>合理使用异步组件</li><li>减少 bind 次数</li><li>合理使用 scu pure memo</li><li>合理使用 immutablejs</li><li>打包webpack优化</li><li>前端通用优化 图片懒加载等</li><li>SSR</li></ul><h5 id="React-和-Vue-区别"><a href="#React-和-Vue-区别" class="headerlink" title="React 和 Vue 区别"></a>React 和 Vue 区别</h5><p>1、共同</p><ul><li>支持组件化</li><li>数据驱动视图</li><li>vdom操作dom</li></ul><p>2、不同</p><ul><li>React 使用 jsx 拥抱js(文件都是.js结尾),Vue使用模板拥抱html(文件是.vue结尾)</li><li>React 函数式编程,Vue 声明式编程</li><li>React 更多需要自力更生,Vue 把想要的都给你</li></ul><h2 id="Webpack"><a href="#Webpack" class="headerlink" title="Webpack"></a>Webpack</h2><h3 id="升级webpack5-级周边插件后。需要做的调整"><a href="#升级webpack5-级周边插件后。需要做的调整" class="headerlink" title="升级webpack5 级周边插件后。需要做的调整"></a>升级webpack5 级周边插件后。需要做的调整</h3><ul><li>package.json的dev-server命令改了,<ul><li>4: “dev”:”webpack-dev-server –config build&#x2F;webpack.dev.js”</li><li>5: “dev”:”webpack serve –config build&#x2F;webpack.dev.js”</li></ul></li><li>webpack-merge<ul><li>4: const { smart } &#x3D; require(‘webpack-merge’)</li><li>5: const { merge } &#x3D; require(‘webpack-merge’)</li></ul></li><li>CleanWebpackplugin<ul><li>4: const CleanWebpackplugin &#x3D; require(‘clean-webpack-plugin’)</li><li>5: const { CleanWebpackplugin } &#x3D; require(‘clean-webpack-plugin’)</li></ul></li><li>modules.rules<ul><li>4: loader: [‘xxx-loader’]</li><li>5: use: [‘xxx-loader’]</li></ul></li><li>filename hash h小写<ul><li>4: filename: ‘bundle.[contentHash:8].js’</li><li>5: filename: ‘bundle.[contenthash:8].js’</li></ul></li></ul><h3 id="基本配置"><a href="#基本配置" class="headerlink" title="基本配置"></a>基本配置</h3><ul><li><p>拆分配置 和 merge</p></li><li><p>启动本地服务 webpack-dev-server</p></li><li><p>处理 ES6</p> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">modules.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="attr">entry</span>: path.<span class="title function_">join</span>(srcPath, <span class="string">&#x27;index&#x27;</span>),</span><br><span class="line"> <span class="attr">modules</span>: &#123;</span><br><span class="line"> <span class="attr">rules</span>: [</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/\.js$/</span>,</span><br><span class="line"> <span class="attr">loader</span>: [<span class="string">&#x27;babel-loader&#x27;</span>],</span><br><span class="line"> <span class="attr">include</span>: srcPath,</span><br><span class="line"> <span class="attr">exclude</span>: <span class="regexp">/node_modules/</span></span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;presets&quot;: [&quot;@babel/preset-env&quot;],</span><br><span class="line"> &quot;plugins&quot;: []</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>处理样式</p></li><li><p>处理图片</p><ul><li>file-loader 将图片变为url形式</li><li>url-loader 将图片变为base64形式,减少http请求,很小没必要再进行http请求</li></ul></li><li><p>模块化</p></li></ul><h3 id="高级配置"><a href="#高级配置" class="headerlink" title="高级配置"></a>高级配置</h3><h4 id="多入口"><a href="#多入口" class="headerlink" title="多入口"></a>多入口</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">entry</span>: &#123;</span><br><span class="line"> <span class="attr">index</span>: path.<span class="title function_">join</span>(srcPath, <span class="string">&#x27;index.js&#x27;</span>),</span><br><span class="line"> <span class="attr">other</span>: path.<span class="title function_">join</span>(srcPath, <span class="string">&#x27;other.js&#x27;</span>),</span><br><span class="line">&#125;,</span><br><span class="line"><span class="attr">output</span>: &#123;</span><br><span class="line"> <span class="attr">filename</span>: <span class="string">&#x27;[name].[contentHash:8].js&#x27;</span>,</span><br><span class="line"> <span class="attr">path</span>: distPath</span><br><span class="line">&#125;</span><br><span class="line"><span class="attr">plugins</span>: [</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">HtmlWebpackPlugin</span>(&#123;</span><br><span class="line"> <span class="attr">template</span>: path.<span class="title function_">join</span>(srcPath, <span class="string">&#x27;index.html&#x27;</span>),</span><br><span class="line"> <span class="attr">filename</span>: <span class="string">&#x27;index.html&#x27;</span>,</span><br><span class="line"> <span class="attr">chunks</span>: [<span class="string">&#x27;index&#x27;</span>] <span class="comment">// 要引入什么js文件</span></span><br><span class="line"> &#125;),</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">HtmlWebpackPlugin</span>(&#123;</span><br><span class="line"> <span class="attr">template</span>: path.<span class="title function_">join</span>(srcPath, <span class="string">&#x27;other.html&#x27;</span>),</span><br><span class="line"> <span class="attr">filename</span>: <span class="string">&#x27;other.html&#x27;</span>,</span><br><span class="line"> <span class="attr">chunks</span>: [<span class="string">&#x27;other&#x27;</span>]</span><br><span class="line"> &#125;)</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h4 id="抽离css文件"><a href="#抽离css文件" class="headerlink" title="抽离css文件"></a>抽离css文件</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">coonst <span class="title class_">MiniCssExtracctplugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;mini-css-extract-plugin&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">TerserJSPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;terser-webpack-plugin&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">OptimizeCssAssetsplugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;optimize-css-assets-webpack-plugin&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="attr">module</span>: &#123;</span><br><span class="line"> <span class="attr">rules</span>: [</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/\.css$/</span>,</span><br><span class="line"> <span class="attr">loader</span>: [</span><br><span class="line"> <span class="title class_">MiniCssExtracctplugin</span>.<span class="property">loader</span>,</span><br><span class="line"> <span class="string">&#x27;css-loader&#x27;</span>,</span><br><span class="line"> <span class="string">&#x27;postcss-loader&#x27;</span></span><br><span class="line"> ]</span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">MiniCssExtracctplugin</span>(&#123;</span><br><span class="line"> <span class="attr">filename</span>: <span class="string">&#x27;css/main.[contentHash:8].css&#x27;</span></span><br><span class="line"> &#125;)</span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">optimization</span>: &#123;</span><br><span class="line"> <span class="attr">minimizer</span>: [<span class="keyword">new</span> <span class="title class_">TerserJSPlugin</span>(&#123;&#125;), <span class="keyword">new</span> <span class="title class_">OptimizeCssAssetsplugin</span>(&#123;&#125;)]</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="压缩-optimization"><a href="#压缩-optimization" class="headerlink" title="压缩 optimization"></a>压缩 optimization</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">TerserJSPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;terser-webpack-plugin&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">OptimizeCssAssetsplugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;optimize-css-assets-webpack-plugin&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> &lt;!-- 其他配置 --&gt;</span><br><span class="line"> <span class="attr">optimization</span>: &#123;</span><br><span class="line"> <span class="attr">minimizer</span>: [<span class="keyword">new</span> <span class="title class_">TerserJSPlugin</span>(&#123;&#125;), <span class="keyword">new</span> <span class="title class_">OptimizeCssAssetsplugin</span>(&#123;&#125;)]</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="抽离公共代码"><a href="#抽离公共代码" class="headerlink" title="抽离公共代码"></a>抽离公共代码</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> &lt;!-- 其他配置 --&gt;</span><br><span class="line"> <span class="attr">optimization</span>: &#123;</span><br><span class="line"> <span class="attr">minimizer</span>: [],</span><br><span class="line"> <span class="attr">splitChunks</span>: &#123;</span><br><span class="line"> <span class="comment">// initial 不处理异步</span></span><br><span class="line"> <span class="comment">// async 只处理异步</span></span><br><span class="line"> <span class="comment">// all 全部</span></span><br><span class="line"> <span class="attr">chunks</span>: <span class="string">&#x27;all&#x27;</span>,</span><br><span class="line"> <span class="attr">cacheGroups</span>: &#123;</span><br><span class="line"> <span class="comment">// 引用关系 关联 HtmlWebpackPlugin chunks 配置</span></span><br><span class="line"> <span class="comment">// 第三方模块</span></span><br><span class="line"> <span class="attr">vendor</span>: &#123;</span><br><span class="line"> <span class="attr">name</span>: <span class="string">&#x27;vendor&#x27;</span>, <span class="comment">// chunk 名称</span></span><br><span class="line"> <span class="attr">priority</span>: <span class="number">1</span>, <span class="comment">// 权限更高,有限抽离,重要!</span></span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/node_modules/</span>, <span class="comment">// </span></span><br><span class="line"> <span class="attr">minSize</span>: <span class="number">0</span>, <span class="comment">// 大小限制,实际开发不要写0</span></span><br><span class="line"> <span class="attr">minChunks</span>: <span class="number">1</span>, <span class="comment">// 最少复用过几次</span></span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">vendor</span>: &#123;</span><br><span class="line"> <span class="attr">name</span>: <span class="string">&#x27;common&#x27;</span>, <span class="comment">// chunk 名称</span></span><br><span class="line"> <span class="attr">priority</span>: <span class="number">0</span>, <span class="comment">// 权限更高,有限抽离,重要!</span></span><br><span class="line"> <span class="attr">minSize</span>: <span class="number">0</span>, <span class="comment">// 大小限制</span></span><br><span class="line"> <span class="attr">minChunks</span>: <span class="number">2</span>, <span class="comment">// 最少复用过几次</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="异步加载"><a href="#异步加载" class="headerlink" title="异步加载"></a>异步加载</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义 chunk</span></span><br><span class="line"><span class="keyword">import</span>(<span class="string">&#x27;./dynamic-data.js&#x27;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(res.<span class="property">default</span>.<span class="property">message</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="module-chunk-bundle-区别"><a href="#module-chunk-bundle-区别" class="headerlink" title="module chunk bundle 区别"></a>module chunk bundle 区别</h3><ul><li>module:就是写的代码,只要可以引入的都是module</li><li>chunk:多模块合并成的,比如:index.js以及它引入的其他文件的集合</li><li>bundle:最终的输出文件</li></ul><h3 id="性能优化,构建速度"><a href="#性能优化,构建速度" class="headerlink" title="性能优化,构建速度"></a>性能优化,构建速度</h3><ul><li>优化babel-loader</li><li>IgnorePlugin</li><li>noParse</li><li>happypack</li><li>parallelUglifyPlugin</li><li>自动刷新</li><li>热更新</li><li>DllPlugin</li></ul><h4 id="优化babel-loader"><a href="#优化babel-loader" class="headerlink" title="优化babel-loader"></a>优化babel-loader</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/\.js$/</span>,</span><br><span class="line"> <span class="attr">use</span>: [<span class="string">&#x27;babel-loader?cacheDirectory&#x27;</span>], <span class="comment">// 开启缓存</span></span><br><span class="line"> <span class="attr">include</span>: path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;src&#x27;</span>), <span class="comment">// 明确范围</span></span><br><span class="line"> <span class="comment">// 排除范围, 两者选一即可</span></span><br><span class="line"> <span class="comment">// exclude: path.resolve(__dirname, &#x27;node_modules&#x27;)</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="IgnorePlugin,直接不引入,代码中没有"><a href="#IgnorePlugin,直接不引入,代码中没有" class="headerlink" title="IgnorePlugin,直接不引入,代码中没有"></a>IgnorePlugin,直接不引入,代码中没有</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 不引入所有语言包</span></span><br><span class="line"><span class="keyword">new</span> webpack.<span class="title class_">IgnorePlugin</span>(<span class="regexp">/\.\/locale/</span>, <span class="regexp">/moment/</span>)</span><br><span class="line"><span class="comment">// 手动引入中文</span></span><br><span class="line">moment.<span class="title function_">locale</span>(<span class="string">&#x27;zh-cn&#x27;</span>)</span><br></pre></td></tr></table></figure><h4 id="noParse,引入,但不打包"><a href="#noParse,引入,但不打包" class="headerlink" title="noParse,引入,但不打包"></a>noParse,引入,但不打包</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">modules.<span class="property">exports</span>=&#123;</span><br><span class="line"> <span class="attr">module</span>: &#123;</span><br><span class="line"> <span class="attr">noParse</span>: [<span class="regexp">/react\.min\.js/</span>],</span><br><span class="line"> <span class="attr">rules</span>: [</span><br><span class="line"> &#123;&#125;</span><br><span class="line"> ]</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="happyPack-多线程打包"><a href="#happyPack-多线程打包" class="headerlink" title="happyPack 多线程打包"></a>happyPack 多线程打包</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">HappyPack</span> = <span class="built_in">require</span>(<span class="string">&#x27;happypack&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="attr">modules</span>: &#123;</span><br><span class="line"> <span class="attr">rules</span>: [</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/\.js$/</span>,</span><br><span class="line"> <span class="attr">use</span>: [<span class="string">&#x27;happypack/loader?id=babel&#x27;</span>],</span><br><span class="line"> <span class="attr">include</span>: srcPath</span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Happypack</span>(&#123;</span><br><span class="line"> <span class="attr">id</span>: <span class="string">&#x27;babel&#x27;</span>,</span><br><span class="line"> <span class="attr">loaders</span>: [<span class="string">&#x27;babel-loader?cacheDirectory&#x27;</span>]</span><br><span class="line"> &#125;)</span><br><span class="line"> ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="ParallelUglifyPlugin-多进程压缩js"><a href="#ParallelUglifyPlugin-多进程压缩js" class="headerlink" title="ParallelUglifyPlugin 多进程压缩js"></a>ParallelUglifyPlugin 多进程压缩js</h4><p>new ParallelUglifyPlugin({<br> uglifyJS: {<br> &#x2F;&#x2F; 还是使用 uglifyjs 压缩,只是开启了多进程<br> output: {<br> beautify: false, &#x2F;&#x2F; 最紧凑的输出<br> comments: false &#x2F;&#x2F; 删除所有注释<br> },<br> compress: {<br> &#x2F;&#x2F; 删除所有 console 语句,可兼容ie<br> drop_console: true,<br> }<br> }<br>})</p><h4 id="自动刷新"><a href="#自动刷新" class="headerlink" title="自动刷新"></a>自动刷新</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="comment">// 开启之后, webpack-dev-server 会自动开启刷新浏览器</span></span><br><span class="line"> <span class="attr">watch</span>: <span class="literal">true</span>, <span class="comment">// 默认false </span></span><br><span class="line"> <span class="attr">watchOptions</span>: &#123;</span><br><span class="line"> <span class="attr">ignored</span>: <span class="regexp">/node_modules/</span>, <span class="comment">// 忽略</span></span><br><span class="line"> <span class="attr">aggregateTimeout</span>: <span class="number">300</span>, <span class="comment">// 监听变化后等300ms再执行,防止太快</span></span><br><span class="line"> <span class="attr">poll</span>: <span class="number">1000</span> <span class="comment">// 每隔1000ms 询问一次</span></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="DllPlugin"><a href="#DllPlugin" class="headerlink" title="DllPlugin"></a>DllPlugin</h4><ul><li>前端框架如 vue react , 体积大,构建慢</li><li>较稳定,不常升级版本</li><li>同一个版本只构建一次即可,不用每次都重新构建</li><li>webpack 内置</li><li>打包出dll文件</li><li>DllReferencePlugin - 使用dll文件</li></ul><p>1、打包dll配置 webpacl.dll.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="attr">mode</span>: <span class="string">&#x27;development&#x27;</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">entry</span>: &#123;</span><br><span class="line"> <span class="comment">// 把 React 相关模块的放到一个单独的动态链接库</span></span><br><span class="line"> <span class="attr">react</span>: [<span class="string">&#x27;react&#x27;</span>, <span class="string">&#x27;react-dom&#x27;</span>]</span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">output</span>: &#123;</span><br><span class="line"> <span class="attr">filename</span>: <span class="string">&#x27;[name].dll.js&#x27;</span>,</span><br><span class="line"> <span class="attr">path</span>: distPath,</span><br><span class="line"> <span class="attr">library</span>: <span class="string">&#x27;_dll_[name]&#x27;</span></span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">DllPlugin</span>(&#123;</span><br><span class="line"> <span class="attr">name</span>: <span class="string">&#x27;_dll_[name]&#x27;</span>,</span><br><span class="line"> <span class="attr">path</span>: path.<span class="title function_">join</span>(distPath, <span class="string">&#x27;[name].mainfest.json&#x27;</span>),</span><br><span class="line"> &#125;)</span><br><span class="line"> ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>2、修改index.html模板</p><p>添加</p><script src="./react.dll.js"></script><p>3、配置webpack.dev.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="attr">moduls</span>: &#123;</span><br><span class="line"></span><br><span class="line"> <span class="attr">rules</span>: [</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/\.js$/</span>,</span><br><span class="line"> <span class="attr">loader</span>: [<span class="string">&#x27;babel-loader&#x27;</span>],</span><br><span class="line"> <span class="attr">include</span>: srcPath,</span><br><span class="line"> <span class="attr">exclude</span>: <span class="regexp">/node_modules/</span> <span class="comment">// 使用dll,所以不需要转换了</span></span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line">&#125;,</span><br><span class="line"></span><br><span class="line"><span class="attr">plugins</span>: [</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">DllReferencePlugin</span>(&#123;</span><br><span class="line"> <span class="attr">mainfest</span>: <span class="built_in">require</span>(path.<span class="title function_">join</span>(distPath, <span class="string">&#x27;react.mainfest.json&#x27;</span>))</span><br><span class="line"> &#125;)</span><br><span class="line"></span><br><span class="line">]</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="webpack-优化构建速度-(可用于生产环境)"><a href="#webpack-优化构建速度-(可用于生产环境)" class="headerlink" title="webpack 优化构建速度 (可用于生产环境)"></a>webpack 优化构建速度 (可用于生产环境)</h4><ul><li>优化 babel-loader</li><li>IgnorePlugin</li><li>noParse</li><li>happyPack</li><li>parellelUglifyPlugin</li></ul><h4 id="webpack-优化构建速度-(不可用于生产环境)"><a href="#webpack-优化构建速度-(不可用于生产环境)" class="headerlink" title="webpack 优化构建速度 (不可用于生产环境)"></a>webpack 优化构建速度 (不可用于生产环境)</h4><ul><li>自动刷新</li><li>热更新</li><li>DllPlugin</li></ul><h4 id="webpack性能优化-产出代码"><a href="#webpack性能优化-产出代码" class="headerlink" title="webpack性能优化 - 产出代码"></a>webpack性能优化 - 产出代码</h4><ul><li><p>体积更小</p></li><li><p>合理分包,不重复引用</p></li><li><p>速度更快,内损使用更少</p></li><li><p>小图片base64编码</p></li><li><p>bundle + hash</p></li><li><p>懒加载 import 语法</p></li><li><p>提取公共代码 splitChunks</p></li><li><p>IgnorePlugin</p></li><li><p>cdn加速</p></li><li><p>使用 production</p></li><li><p>Scope Hosting 改变打包作用域</p></li></ul><h5 id="使用-production"><a href="#使用-production" class="headerlink" title="使用 production"></a>使用 production</h5><ul><li>自动开启代码压缩</li><li>Vue React 等会自动删除调试代码(如开发环境的warning)</li><li>启动 Tree-Shaking</li></ul><h5 id="Scope-Hosting"><a href="#Scope-Hosting" class="headerlink" title="Scope Hosting"></a>Scope Hosting</h5><ul><li>代码体积更小</li><li>创建函数作用域更少</li><li>代码可读性更好</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">ModuleConcatenationPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;webpack/lib/optmize/ModuleConcatenationPlugin&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"></span><br><span class="line"> <span class="attr">resolve</span>: &#123;</span><br><span class="line"> <span class="comment">// 针对npm中的第三方模块有限采用 jsnext:main 中指向的es6 模块化语法的文件</span></span><br><span class="line"> <span class="attr">mainFields</span>: [<span class="string">&#x27;jsnext:main&#x27;</span>, <span class="string">&#x27;browser&#x27;</span>, <span class="string">&#x27;main&#x27;</span>]</span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="comment">// 开启 Scope Hosting</span></span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">ModuleConcatenationPlugin</span>()</span><br><span class="line"> ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="babel"><a href="#babel" class="headerlink" title="babel"></a>babel</h2><ul><li>环境搭建 , 基本配置<ul><li>.babelrc 配置</li><li>presets 和 plugins</li></ul></li><li>babel-polyfill</li><li>babel-runtime</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;presets&quot;: [</span><br><span class="line"> [</span><br><span class="line"> &quot;@babel/preset-env&quot;,</span><br><span class="line"> &#123;</span><br><span class="line"> &quot;useBuiltins&quot;: &quot;usage&quot;, // 按需引入</span><br><span class="line"> &quot;corejs&quot;: 3 // 版本3</span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line"> ],</span><br><span class="line"> plugins: []</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="babel-polyfill"><a href="#babel-polyfill" class="headerlink" title="babel-polyfill"></a>babel-polyfill</h3><ul><li>babel 7.4 之后弃用 babel-polyfill</li><li>推荐直接使用 core-js 和 regenerator</li></ul><h3 id="babel-polyfill-问题"><a href="#babel-polyfill-问题" class="headerlink" title="babel-polyfill 问题"></a>babel-polyfill 问题</h3><ul><li>污染全局环境,使用babel-runtime解决</li></ul><p>。babelrc中</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">plugins: [</span><br><span class="line"> [</span><br><span class="line"> &quot;@babel/plugin-transform-runtime&quot;,</span><br><span class="line"> &#123;</span><br><span class="line"> &quot;absoluteRuntime&quot;: false,</span><br><span class="line"> &quot;corejs&quot;: 3,</span><br><span class="line"> &quot;helpers&quot;: true,</span><br><span class="line"> &quot;regenerator&quot;: true,</span><br><span class="line"> &quot;useESModules&quot;: false</span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line">]</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
27
27
 
@@ -38,7 +38,7 @@
38
38
  <link href="https://xuehuayu.cn/article/84c9ccd6.html"/>
39
39
  <id>https://xuehuayu.cn/article/84c9ccd6.html</id>
40
40
  <published>2022-08-29T14:22:29.000Z</published>
41
- <updated>2023-07-20T03:42:31.824Z</updated>
41
+ <updated>2023-07-21T09:10:52.811Z</updated>
42
42
 
43
43
  <content type="html"><![CDATA[<p><code>本文转载自https://github.com/ascoders/weekly/blob/master/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF/255.%E7%B2%BE%E8%AF%BB%E3%80%8ASolidJS%E3%80%8B.md</code><br><a href="https://github.com/solidjs/solid">SolidJS</a> 是一个语法像 React Function Component,内核像 Vue 的前端框架,本周我们通过阅读 <a href="https://www.loginradius.com/blog/engineering/guest-post/introduction-to-solidjs/">Introduction to SolidJS</a> 这篇文章来理解理解其核心概念。</p><span id="more"></span><p>为什么要介绍 SolidJS 而不是其他前端框架?因为 SolidJS 在教 React 团队正确的实现 Hooks,这在唯 React 概念与虚拟 DOM 概念马首是瞻的年代非常难得,这也是开源技术的魅力:任何观点都可以被自由挑战,只要你是对,你就可能脱颖而出。</p><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>整篇文章以一个新人视角交代了 SolidJS 的用法,但本文假设读者已有 React 基础,那么只要交代核心差异就行了。</p><h3 id="渲染函数仅执行一次"><a href="#渲染函数仅执行一次" class="headerlink" title="渲染函数仅执行一次"></a>渲染函数仅执行一次</h3><p>SolidJS 仅支持 FunctionComponent 写法,无论内容是否拥有状态管理,也无论该组件是否接受来自父组件的 Props 透传,都仅触发一次渲染函数。</p><p>所以其状态更新机制与 React 存在根本的不同:</p><ul><li>React 状态变化后,通过重新执行 Render 函数体响应状态的变化。</li><li>Solid 状态变化后,通过重新执行用到该状态代码块响应状态的变化。</li></ul><p>与 React 整个渲染函数重新执行相对比,Solid 状态响应粒度非常细,甚至一段 JSX 内调用多个变量,都不会重新执行整段 JSX 逻辑,而是仅更新变量部分:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params">&#123; var1, var2 &#125;</span>) =&gt; (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;&gt;</span></span></span><br><span class="line"><span class="language-xml"> var1: &#123;console.log(&quot;var1&quot;, var1)&#125;</span></span><br><span class="line"><span class="language-xml"> var2: &#123;console.log(&quot;var2&quot;, var2)&#125;</span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;/&gt;</span></span></span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>上面这段代码在 <code>var1</code> 单独变化时,仅打印 <code>var1</code>,而不会打印 <code>var2</code>,在 React 里是不可能做到的。</p><p>这一切都源于了 SolidJS 叫板 React 的核心理念:<strong>面相状态驱动而不是面向视图驱动</strong>。正因为这个差异,导致了渲染函数仅执行一次,也顺便衍生出变量更新粒度如此之细的结果,同时也是其高性能的基础,同时也解决了 React Hooks 不够直观的顽疾,一箭 N 雕。</p><h3 id="更完善的-Hooks-实现"><a href="#更完善的-Hooks-实现" class="headerlink" title="更完善的 Hooks 实现"></a>更完善的 Hooks 实现</h3><p>SolidJS 用 <code>createSignal</code> 实现类似 React <code>useState</code> 的能力,虽然看上去长得差不多,但实现原理与使用时的心智却完全不一样:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;()</span> =&gt;</span> setCount(count() + 1)&#125;&gt;&#123;count()&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们要完全以 SolidJS 心智理解这段代码,而不是 React 心智理解它,虽然它长得太像 Hooks 了。一个显著的不同是,将状态代码提到外层也完全能 Work:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;()</span> =&gt;</span> setCount(count() + 1)&#125;&gt;&#123;count()&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这是最快理解 SolidJS 理念的方式,即 SolidJS 根本没有理 React 那套概念,SolidJS 理解的数据驱动是纯粹的数据驱动视图,无论数据在哪定义,视图在哪,都可以建立绑定。</p><p>这个设计自然也不依赖渲染函数执行多次,同时因为使用了依赖收集,也不需要手动申明 deps 数组,也完全可以将 <code>createSignal</code> 写在条件分支之后,因为不存在执行顺序的概念。</p><h3 id="派生状态"><a href="#派生状态" class="headerlink" title="派生状态"></a>派生状态</h3><p>用回调函数方式申明派生状态即可:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">doubleCount</span> = (<span class="params"></span>) =&gt; <span class="title function_">count</span>() * <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;()</span> =&gt;</span> setCount(count() + 1)&#125;&gt;&#123;doubleCount()&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这是一个不如 React 方便的点,因为 React 付出了巨大的代价(在数据变更后重新执行整个函数体),所以可以用更简单的方式定义派生状态:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// React</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">useState</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">const</span> doubleCount = count * <span class="number">2</span>; <span class="comment">// 这块反而比 SolidJS 定义的简单</span></span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;()</span> =&gt;</span> setCount((count) =&gt; count + 1)&#125;&gt;</span></span><br><span class="line"><span class="language-xml"> &#123;doubleCount&#125;</span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"> );</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>当然笔者并不推崇 React 的衍生写法,因为其代价太大了。我们继续分析为什么 SolidJS 这样看似简单的衍生状态写法可以生效。原因在于,SolidJS 收集所有用到了 <code>count()</code> 的依赖,而 <code>doubleCount()</code> 用到了它,而渲染函数用到了 <code>doubleCount()</code>,仅此而已,所以自然挂上了依赖关系,这个实现过程简单而稳定,没有 Magic。</p><p>SolidJS 还支持衍生字段计算缓存,使用 <code>createMemo</code>:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">doubleCount</span> = (<span class="params"></span>) =&gt; <span class="title function_">createMemo</span>(<span class="function">() =&gt;</span> <span class="title function_">count</span>() * <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;()</span> =&gt;</span> setCount(count() + 1)&#125;&gt;&#123;doubleCount()&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>同样无需写 deps 依赖数组,SolidJS 通过依赖收集来驱动 <code>count</code> 变化影响到 <code>doubleCount</code> 这一步,这样访问 <code>doubleCount()</code> 时就不用总执行其回调的函数体,产生额外性能开销了。</p><h3 id="状态监听"><a href="#状态监听" class="headerlink" title="状态监听"></a>状态监听</h3><p>对标 React 的 <code>useEffect</code>,SolidJS 提供的是 <code>createEffect</code>,但相比之下,不用写 deps,是真的监听数据,而非组件生命周期的一环:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="title function_">createEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">count</span>()); <span class="comment">// 在 count 变化时重新执行</span></span><br><span class="line"> &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这再一次体现了为什么 SolidJS 有资格 “教” React 团队实现 Hooks:</p><ul><li>无 deps 申明。</li><li>将监听与生命周期分开,React 经常容易将其混为一谈。</li></ul><p>在 SolidJS,生命周期函数有 <code>onMount</code>、<code>onCleanUp</code>,状态监听函数有 <code>createEffect</code>;而 React 的所有生命周期和状态监听函数都是 <code>useEffect</code>,虽然看上去更简洁,但即便是精通 React Hooks 的老手也不容易判断哪些是监听,哪些是生命周期。</p><h3 id="模板编译"><a href="#模板编译" class="headerlink" title="模板编译"></a>模板编译</h3><p>为什么 SolidJS 可以这么神奇的把 React 那么多历史顽疾解决掉,而 React 却不可以呢?核心原因还是在 SolidJS 增加的模板编译过程上。</p><p>以官方 <a href="https://playground.solidjs.com/">Playground</a> 提供的 Demo 为例:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Counter</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">increment</span> = (<span class="params"></span>) =&gt; <span class="title function_">setCount</span>(<span class="title function_">count</span>() + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;button&quot;</span> <span class="attr">onClick</span>=<span class="string">&#123;increment&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"> &#123;count()&#125;</span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"> );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>被编译为:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> _tmpl$ = <span class="comment">/*#__PURE__*/</span> <span class="title function_">template</span>(<span class="string">`&lt;button type=&quot;button&quot;&gt;&lt;/button&gt;`</span>, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Counter</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">increment</span> = (<span class="params"></span>) =&gt; <span class="title function_">setCount</span>(<span class="title function_">count</span>() + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (<span class="function">() =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">const</span> _el$ = _tmpl$.<span class="title function_">cloneNode</span>(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"> _el$.$$click = increment;</span><br><span class="line"></span><br><span class="line"> <span class="title function_">insert</span>(_el$, count);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> _el$;</span><br><span class="line"> &#125;)();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先把组件 JSX 部分提取到了全局模板。初始化逻辑:将变量插入模板;更新状态逻辑:由于 <code>insert(_el$, count)</code> 时已经将 <code>count</code> 与 <code>_el$</code> 绑定了,下次调用 <code>setCount()</code> 时,只需要把绑定的 <code>_el$</code> 更新一下就行了,而不用关心它在哪个位置。</p><p>为了更完整的实现该功能,必须将用到模板的 Node 彻底分离出来。我们可以测试一下稍微复杂些的场景,如:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;button&gt;</span><br><span class="line"> <span class="attr">count</span>: &#123;<span class="title function_">count</span>()&#125;, count+<span class="number">1</span>: &#123;<span class="title function_">count</span>() + <span class="number">1</span>&#125;</span><br><span class="line">&lt;/button&gt;</span><br></pre></td></tr></table></figure><p>这段代码编译后的模板结果是:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> _el$ = _tmpl$.<span class="title function_">cloneNode</span>(<span class="literal">true</span>),</span><br><span class="line"> _el$2 = _el$.firstChild,</span><br><span class="line"> _el$4 = _el$2.<span class="property">nextSibling</span>;</span><br><span class="line">_el$4.<span class="property">nextSibling</span>;</span><br><span class="line"></span><br><span class="line">_el$.$$click = increment;</span><br><span class="line"></span><br><span class="line"><span class="title function_">insert</span>(_el$, count, _el$4);</span><br><span class="line"></span><br><span class="line"><span class="title function_">insert</span>(_el$, <span class="function">() =&gt;</span> <span class="title function_">count</span>() + <span class="number">1</span>, <span class="literal">null</span>);</span><br></pre></td></tr></table></figure><p>将模板分成了一个整体和三个子块,分别是字面量、变量、字面量。为什么最后一个变量没有加进去呢?因为最后一个变量插入直接放在 <code>_el$</code> 末尾就行了,而中间插入位置需要 <code>insert(_el$, count, _el$4)</code> 给出父节点与子节点实例。</p><h2 id="精读"><a href="#精读" class="headerlink" title="精读"></a>精读</h2><p>SolidJS 的神秘面纱已经解开了,下面笔者自问自答一些问题。</p><h3 id="为什么-createSignal-没有类似-hooks-的顺序限制?"><a href="#为什么-createSignal-没有类似-hooks-的顺序限制?" class="headerlink" title="为什么 createSignal 没有类似 hooks 的顺序限制?"></a>为什么 createSignal 没有类似 hooks 的顺序限制?</h3><p>React Hooks 使用 deps 收集依赖,在下次执行渲染函数体时,因为没有任何办法标识 “deps 是为哪个 Hook 申明的”,只能依靠顺序作为标识依据,所以需要稳定的顺序,因此不能出现条件分支在前面。</p><p>而 SolidJS 本身渲染函数仅执行一次,所以不存在 React 重新执行函数体的场景,而 <code>createSignal</code> 本身又只是创建一个变量,<code>createEffect</code> 也只是创建一个监听,逻辑都在回调函数内部处理,而与视图的绑定通过依赖收集完成,所以也不受条件分支的影响。</p><h3 id="为什么-createEffect-没有-useEffect-闭包问题?"><a href="#为什么-createEffect-没有-useEffect-闭包问题?" class="headerlink" title="为什么 createEffect 没有 useEffect 闭包问题?"></a>为什么 createEffect 没有 useEffect 闭包问题?</h3><p>因为 SolidJS 函数体仅执行一次,不会存在组件实例存在 N 个闭包的情况,所以不存在闭包问题。</p><h3 id="为什么说-React-是假的响应式?"><a href="#为什么说-React-是假的响应式?" class="headerlink" title="为什么说 React 是假的响应式?"></a>为什么说 React 是假的响应式?</h3><p>React 响应的是组件树的变化,通过组件树自上而下的渲染来响应式更新。而 SolidJS 响应的只有数据,甚至数据定义申明在渲染函数外部也可以。</p><p>所以 React 虽然说自己是响应式,但开发者真正响应的是 UI 树的一层层更新,在这个过程中会产生闭包问题,手动维护 deps,hooks 不能写在条件分支之后,以及有时候分不清当前更新是父组件 rerender 还是因为状态变化导致的。</p><p>这一切都在说明,React 并没有让开发者真正只关心数据的变化,如果只要关心数据变化,那为什么组件重渲染的原因可能因为 “父组件 rerender” 呢?</p><h3 id="为什么-SolidJS-移除了虚拟-dom-依然很快?"><a href="#为什么-SolidJS-移除了虚拟-dom-依然很快?" class="headerlink" title="为什么 SolidJS 移除了虚拟 dom 依然很快?"></a>为什么 SolidJS 移除了虚拟 dom 依然很快?</h3><p>虚拟 dom 虽然规避了 dom 整体刷新的性能损耗,但也带来了 diff 开销。对 SolidJS 来说,它问了一个问题:为什么要规避 dom 整体刷新,局部更新不行吗?</p><p>对啊,局部更新并不是做不到,通过模板渲染后,将 jsx 动态部分单独提取出来,配合依赖收集,就可以做到变量变化时点对点的更新,所以无需进行 dom diff。</p><h3 id="为什么-signal-变量使用-count-不能写成-count?"><a href="#为什么-signal-变量使用-count-不能写成-count?" class="headerlink" title="为什么 signal 变量使用 count() 不能写成 count?"></a>为什么 signal 变量使用 <code>count()</code> 不能写成 <code>count</code>?</h3><p>笔者也没找到答案,理论上来说,Proxy 应该可以完成这种显式函数调用动作,除非是不想引入 Mutable 的开发习惯,让开发习惯变得更加 Immutable 一些。</p><h3 id="props-的绑定不支持解构"><a href="#props-的绑定不支持解构" class="headerlink" title="props 的绑定不支持解构"></a>props 的绑定不支持解构</h3><p>由于响应式特性,解构会丢失代理的特性:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params">props</span>) =&gt; <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;props.userName&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br><span class="line"><span class="comment">// ❎</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params">&#123; userName &#125;</span>) =&gt; <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;userName&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br></pre></td></tr></table></figure><p>虽然也提供了 <code>splitProps</code> 解决该问题,但此函数还是不自然。该问题比较好的解法是通过 babel 插件来规避。</p><h3 id="createEffect-不支持异步"><a href="#createEffect-不支持异步" class="headerlink" title="createEffect 不支持异步"></a>createEffect 不支持异步</h3><p>没有 deps 虽然非常便捷,但在异步场景下还是无解:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = <span class="title function_">createSignal</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="title function_">createEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">wait</span>(<span class="number">1000</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">count</span>()); <span class="comment">// 不会触发</span></span><br><span class="line"> &#125;</span><br><span class="line"> <span class="title function_">run</span>();</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>SolidJS 的核心设计只有一个,即让数据驱动真的回归到数据上,而非与 UI 树绑定,在这一点上,React 误入歧途了。</p><p>虽然 SolidJS 很棒,但相关组件生态还没有起来,巨大的迁移成本是它难以快速替换到生产环境的最大问题。前端生态想要无缝升级,看来第一步是想好 “代码范式”,以及代码范式间如何转换,确定了范式后再由社区竞争完成实现,就不会遇到生态难以迁移的问题了。</p><p>但以上假设是不成立的,技术迭代永远都以 BreakChange 为代价,而很多时候只能抛弃旧项目,在新项目实践新技术,就像 Jquery 时代一样。</p><blockquote><p>讨论地址是:<a href="https://github.com/dt-fe/weekly/issues/438">精读《SolidJS》· Issue #438 · dt-fe&#x2F;weekly</a></p></blockquote><p><strong>如果你想参与讨论,请 <a href="https://github.com/dt-fe/weekly">点击这里</a>,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。</strong></p><blockquote><p>关注 <strong>前端精读微信公众号</strong></p></blockquote><img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg"><blockquote><p>版权声明:自由转载-非商用-非衍生-保持署名(<a href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh">创意共享 3.0 许可证</a>)</p></blockquote>]]></content>
44
44
 
@@ -59,7 +59,7 @@
59
59
  <link href="https://xuehuayu.cn/article/53803e6.html"/>
60
60
  <id>https://xuehuayu.cn/article/53803e6.html</id>
61
61
  <published>2022-06-17T10:03:49.000Z</published>
62
- <updated>2023-07-20T03:42:31.824Z</updated>
62
+ <updated>2023-07-21T09:10:52.811Z</updated>
63
63
 
64
64
  <content type="html"><![CDATA[<p>git提交注释规范</p><span id="more"></span><h3 id="git-提交注释规范"><a href="#git-提交注释规范" class="headerlink" title="git 提交注释规范"></a>git 提交注释规范</h3><p><strong>git commit -m ‘type: (scope:) subject’</strong><br>&#x2F;空行&#x2F;<br><strong>(body)</strong></p><ul><li><p>type 必需,用于说明 commit 的类别,只允许使用下面8个标识,并且为小写</p><ul><li>br: 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况</li><li>feat:新功能(feature)</li><li>fix:修补bug</li><li>docs:文档(documentation)</li><li>style: 格式(不影响代码运行的变动,空白、格式、缺少符号等)</li><li>refactor:重构(即不是新增功能,也不是修改bug的代码变动)</li><li>test:增加测试</li><li>chore:构建过程、辅助工具、gitignore、库(如文档生成)等的变动</li><li>pref: 改进性能的变更</li><li>revert: feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)</li></ul></li><li><p>scope 可选, 用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。</p></li><li><p>subject 必须,是 commit 目的的简短描述,不超过50个字符。</p></li><li><p>body 可选,部分是对本次 commit 的详细描述,可以分成多行。</p></li></ul>]]></content>
65
65
 
@@ -80,7 +80,7 @@
80
80
  <link href="https://xuehuayu.cn/article/a4145266.html"/>
81
81
  <id>https://xuehuayu.cn/article/a4145266.html</id>
82
82
  <published>2022-05-30T16:43:13.000Z</published>
83
- <updated>2023-07-20T03:42:31.824Z</updated>
83
+ <updated>2023-07-21T09:10:52.811Z</updated>
84
84
 
85
85
  <content type="html"><![CDATA[<p>前端数组拍平flat一行代码</p><span id="more"></span><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, [<span class="number">2</span>, [<span class="number">3</span>, [<span class="number">4</span>, <span class="number">5</span>]]]]</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">flat</span> (arr) &#123;</span><br><span class="line"> <span class="keyword">return</span> arr.<span class="title function_">reduce</span>(<span class="function">(<span class="params">p, c</span>) =&gt;</span> <span class="title class_">Array</span>.<span class="title function_">isArray</span>(c) ? p.<span class="title function_">concat</span>(<span class="title function_">flat</span>(c)) : p.<span class="title function_">concat</span>(c), [])</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">flat</span>(arr))</span><br></pre></td></tr></table></figure>]]></content>
86
86
 
@@ -101,7 +101,7 @@
101
101
  <link href="https://xuehuayu.cn/article/bedea419.html"/>
102
102
  <id>https://xuehuayu.cn/article/bedea419.html</id>
103
103
  <published>2022-05-27T15:51:37.000Z</published>
104
- <updated>2023-07-20T03:42:31.824Z</updated>
104
+ <updated>2023-07-21T09:10:52.811Z</updated>
105
105
 
106
106
  <content type="html"><![CDATA[<p>前端this面试题,着急了多想想就好了</p><span id="more"></span><p>谁调用方法,this就指向谁</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line"> <span class="attr">length</span>: <span class="number">10</span>,</span><br><span class="line"> log () &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">length</span>)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">obj.<span class="title function_">log</span>() <span class="comment">//10,obj直接调用</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> length = <span class="number">20</span></span><br><span class="line"><span class="keyword">const</span> fn = obj.<span class="property">log</span></span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>() <span class="comment">// 0</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将log赋值给fn,相当于直接运行log方法</span></span><br><span class="line"><span class="comment"> * 这里有个坑,好吧我的坑,以为是 20,忘了打印的是 this.length 了</span></span><br><span class="line"><span class="comment"> * ES6 新特性,方法的 length 返回 参数的个数, 所以此处是 0</span></span><br><span class="line"><span class="comment"> * */</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="number">30</span>, obj.<span class="property">log</span>]</span><br><span class="line"></span><br><span class="line">arr[<span class="number">1</span>]() <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 这个和 直接执行 obj.log 挺像的,数组就是 特殊的 对象,</span></span><br><span class="line"><span class="comment"> * console.dir(arr) 得到一个特殊的对象</span></span><br><span class="line"><span class="comment"> * 所以数组执行方法 this.length 指向的是arr的length属性,</span></span><br><span class="line"><span class="comment"> * 数组的 length 为 2</span></span><br><span class="line"><span class="comment"> * */</span> </span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
107
107
 
@@ -122,7 +122,7 @@
122
122
  <link href="https://xuehuayu.cn/article/bf7e7421.html"/>
123
123
  <id>https://xuehuayu.cn/article/bf7e7421.html</id>
124
124
  <published>2022-03-12T11:53:01.000Z</published>
125
- <updated>2023-07-20T03:42:31.824Z</updated>
125
+ <updated>2023-07-21T09:10:52.811Z</updated>
126
126
 
127
127
  <content type="html"><![CDATA[<p>使用git的时候,有没有遇到过这种情况,配置了公司的gitlab的ssh-key之后,又要配置github、bitbucket、gitee等这些时候,就不知道怎么办了?</p><span id="more"></span><p>其实不必烦恼,git是支持配置多账号的。废话不多说,直接上干货。</p><h4 id="第一步:"><a href="#第一步:" class="headerlink" title="第一步:"></a>第一步:</h4><p>通过 ssh-keygen 命令生成多个账号的key,<br>注意:回车之后,有一步是填写密钥文件的路径和名称,此时可修改路径和名称,建议路径不要改,只改名字,以便区分。默认是 C:&#x2F;Users&#x2F;用户名&#x2F;.ssh&#x2F;id_rsa</p><figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -t rsa -c &quot;你的邮箱&quot;</span><br></pre></td></tr></table></figure><h4 id="第二步:"><a href="#第二步:" class="headerlink" title="第二步:"></a>第二步:</h4><p>在C:\Users\用户名.ssh 文件夹下面创建 config 文件,注意,这个文件没有后缀,然后用记事本打开,然后写配置,怕麻烦的就复制下面的,改吧改吧就行了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"># 配置 github.com</span><br><span class="line"> Host github.com </span><br><span class="line"> HostName github.com</span><br><span class="line"> IdentityFile C:\\Users\\用户名\\.ssh\\github_rsa</span><br><span class="line"> PreferredAuthentications publickey</span><br><span class="line"> User np</span><br><span class="line">​</span><br><span class="line"># 配置 bitbucket</span><br><span class="line"> Host bitbucket.org</span><br><span class="line"> HostName bitbucket.org</span><br><span class="line"> IdentityFile C:\\Users\\用户名\\.ssh\\bitbucket_rsa</span><br><span class="line"> PreferredAuthentications publickey</span><br><span class="line"> User np</span><br><span class="line">​</span><br><span class="line"># 配置 gitee</span><br><span class="line"> Host gitee.com</span><br><span class="line"> HostName gitee.com</span><br><span class="line"> IdentityFile C:\\Users\\用户名\\.ssh\\gitee_rsa</span><br><span class="line"> PreferredAuthentications publickey</span><br><span class="line"> User np</span><br><span class="line">​</span><br><span class="line"># 配置 coding</span><br><span class="line"> Host coding.net</span><br><span class="line"> HostName coding.net</span><br><span class="line"> IdentityFile C:\\Users\\用户名\\.ssh\\coding_rsa</span><br><span class="line"> PreferredAuthentications publickey</span><br><span class="line"> User np</span><br><span class="line">​</span><br><span class="line"># 配置 公司内网</span><br><span class="line"> Host 10.0.11.44</span><br><span class="line"> HostName 10.0.11.44</span><br><span class="line"> IdentityFile C:\\Users\\n用户名\\.ssh\\com_rsa</span><br><span class="line"> PreferredAuthentications publickey</span><br><span class="line"> User np</span><br></pre></td></tr></table></figure><h4 id="第三步:"><a href="#第三步:" class="headerlink" title="第三步:"></a>第三步:</h4><p>将 对应的 key 粘贴到对应网站 添加 ssh-key 的地方。</p>]]></content>
128
128
 
@@ -145,7 +145,7 @@
145
145
  <link href="https://xuehuayu.cn/article/46ee4850.html"/>
146
146
  <id>https://xuehuayu.cn/article/46ee4850.html</id>
147
147
  <published>2022-02-18T12:57:12.000Z</published>
148
- <updated>2023-07-20T03:42:31.824Z</updated>
148
+ <updated>2023-07-21T09:10:52.811Z</updated>
149
149
 
150
150
  <content type="html"><![CDATA[<p>作为一名前端er,日常工作打交道最多(之一)的莫过于熟悉而又陌生的浏览器了,熟悉是每天都会基于浏览器的应用层面之上码业务,陌生是很多人可能跟我一样不熟悉其内部运行原理,比如js是怎样运行的呢?精美样式页面是怎样渲染到电脑屏幕的呢?在开放的互联网它又是怎样保证我们个人信息安全的呢?带着种种疑云开始肝李兵老师的《浏览器基本原理与实践》,不得不说,大家之作,通俗易懂,层层拨开云雾见青天,下面就(非常非常)简单总结一下。</p><span id="more"></span><h4 id="Chrome-架构:仅仅打开了-1-个页面,为什么有-4-个进程"><a href="#Chrome-架构:仅仅打开了-1-个页面,为什么有-4-个进程" class="headerlink" title="Chrome 架构:仅仅打开了 1 个页面,为什么有 4 个进程"></a><a href="https://link.segmentfault.com/?enc=+jFhh6RcKWtwZ4W3QhITyg==.a98UFK/EBtYdR1CN33GfQoWytde5kxjUu8+FAeKwEPoY6Nh+kQAA6Q1eQH9U4WXgpICY+OVTCzD1dPgmPz3pCzkmJF2QMT7H/eddHQixlVDkfvbCHkU87Zi9yM6kpgOnFQTpRAJWOyYVGdlIugBwG/2mSfhh8ZtQKhI0X2A+TFUqnjOMeYDLMsYsND6Jh7eECb7qlSf6+Em7Cc08Q9UBDQNqXIQ8Xaz/LrhkH4uoneU0vQeh6K3T4VyAx6nuvPRfMIpMS/3lqojVM2FifF5KFOxIOow4s+IfOj/Eeet2hdB3lsR3ezBrRi4XvJxZ2tT6PCMFn6n+qT5I664I/WLn2Q==">Chrome 架构:仅仅打开了 1 个页面,为什么有 4 个进程</a></h4><p><strong>线程和进程区别</strong>:多线程可以并行处理任务,线程不能单独存在,它是由进程来启动和管理的。一个进程是一个程序的运行实例。</p><p><strong>线程和进程的关系</strong>:1、进程中任意一线程执行出错,都会导致整个进程的崩溃。2、线程之间共享进程中的数据。3、当一个进程关闭后,操作系统会回收进程所占用的内存。4、进程之间的内容相互隔离。</p><p><strong>单进程 浏览器</strong>:1、不稳定。单进程中的插件、渲染线程崩溃导致整个浏览器崩溃。2、不流畅。脚本(死循环)或插件会使浏览器卡顿。3、不安全。插件和脚本可以获取到操作系统任意资源。</p><p><strong>多进程浏览器</strong>:1、解决不稳定。进程相互隔离,一个页面或者插件崩溃时,影响仅仅时当前插件或者页面,不会影响到其他页面。2、解决不流畅。脚本阻塞当前页面渲染进程,不会影响到其他页面。3、解决不安全。采用多进程架构使用沙箱。沙箱看成时操作系统给进程上来一把锁,沙箱的程序可以运行,但是不能在硬盘上写入任何数据,也不能在敏感位置读取任何数据。</p><p><strong>多进程架构</strong>:分为 浏览器进程、渲染进程、GPU 进程、网络进程、插件进程。</p><p><strong>缺点</strong>:1、资源占用高。2、体系架构复杂。</p><p><strong>面向服务架构</strong>:把原来的各种模块重构成独立的服务,每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口,通过 IPC 通讯,使得系统更内聚、松耦合、易维护和拓展。</p><h4 id="TCP-协议:如何保证页面文件能被完整送达浏览器"><a href="#TCP-协议:如何保证页面文件能被完整送达浏览器" class="headerlink" title="TCP 协议:如何保证页面文件能被完整送达浏览器"></a><a href="https://link.segmentfault.com/?enc=h/OsUKvgdOtOF27PRZk8OQ==.IRmpaQz7WbcPeNTVHNB8pqRe68jdeyTmUz4mgqQxbd5+il4AtzJTJ69k0FwnEhI7yzf5jmnrphTzQeMTQaPI9lig88SUTtE/Yl7MgInRGOUC83sQ+VHjkfkBQaDwG7no0yfdpefFM3acbwqeWuqHd80TV9fq5nBnUNzFj590r1jZ8qmfIx7RkTIWBhrD+kPd2CVmzbhkSyLCzD4qyUIMG5r/NKwFlqFcOsmnmtDbpqR7GzA++NaA8MBXkDR18SrlFqw9ZLRzE3rnBORynszHOBD3I2zdHDk1gS3PXGhDVi9tz22XXdFN0QWjasmze51jTOoQeKp8r/7wehObKU+4y5ev7LmYsFFw7R/Azof9v/4=">TCP 协议:如何保证页面文件能被完整送达浏览器</a></h4><ul><li>IP 头是 IP 数据包开头的信息,包含 IP 版本、源 IP 地址、目标 IP 地址、生存时间等信息;</li><li>UDP 头中除了目的端口,还有源端口号等信息;</li><li>IP 负责把数据包送达目的主机;</li><li>UDP 负责把数据包送达具体应用;</li><li>对于错误的数据包,UDP 不提供重发机制,只是丢弃当前的包,不能保证数据的可靠性,但是传输速度非常块;</li><li>TCP 头除了包含了目标端口和本机端口号外,还提供了用于排序的序列号,保证了数据完整地传输,它的连接可分为三个阶段:建立连接、传输数据和断开连接;</li></ul><h4 id="HTTP-请求流程:为什么很多站点第二次打开速度会很快"><a href="#HTTP-请求流程:为什么很多站点第二次打开速度会很快" class="headerlink" title="HTTP 请求流程:为什么很多站点第二次打开速度会很快"></a><a href="https://link.segmentfault.com/?enc=ar/GecFA2TKQouSdKzyWzQ==.zZVp08h7V0XSg7bnC3rPe3kT4TcCS7HLochAF5YEdRqoBT/+KJMiUWhD4lGQv33SZEUyPRubHRluZd6iwf5p2dod0BUYZXZsputKWECDINiTAMVFPW7z98rWowoKFi9wKnJCiy0rJId4Dd+Bg5E4Qqkt1zYtoLprlZUddtqdlU8E9sqoc4tXvemw+yabWaglGsLhbSNKc0QKPeq6nWR/Ac62cPccJuq0d8Sj+KzgKJjJleCJez651demyylayy3XKNQOFKlQdbHDeOrkmzZadZhXoZ2V0JyqSCrXRtw0cG8ueJ+UX4/YEAZHviJFWrEQgpM1ZtMHcYG9pFoHO+gsP1P4GOC/leF8n+hkY3QbK9CtrUhYN6YWc+6eEy1c8tQM">HTTP 请求流程:为什么很多站点第二次打开速度会很快</a></h4><ul><li>浏览器中的 HTTP 请求从发起到结束一共经历如下八个阶段:构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求和断开连接;</li><li>构建请求。浏览器构建请求行,构建好后,准备发起网络请求;</li><li>查找缓存。在真正发起请求前浏览器会查询缓存中是否有请求资源副本,有则拦截请求,返回资源副本,否则进入网络请求;</li><li>准备 IP 地址和端口。HTTP 网络请求需要和服务器建立 TCP 连接,而建立 TCP 连接需要准备 IP 地址和端口号,浏览器需要请求 DNS 返回域名对应的 IP,同时会缓存域名解析结果,供下次查询使用;</li><li>等待 TCP 队列。Chrome 机制,同一个域名同时最多只能建立 6 个 TCP 连接;</li><li>建立 TCP 连接。TCP 通过“三次握手”建立连接,传输数据,“四次挥手”断开连接;</li><li>发送 HTTP 请求。建立 TCP 连接后,浏览器就可以和服务器进行 HTTP 数据传输了,首先会向服务器发送请求行,然后以请求头形式发送一些其他信息,如果是 POST 请求还会发送请求体;</li><li>服务器处理请求。首先服务器会返回响应行,随后,服务器向浏览器发送响应头和响应体。通常服务器返回数据,就要关闭 TCP 连接,如果请求头或者响应头有 Connection:keep-alive TCP 保持打开状态;</li></ul><h4 id="导航流程:从输入-URL-到页面展示这中间发生了什么"><a href="#导航流程:从输入-URL-到页面展示这中间发生了什么" class="headerlink" title="导航流程:从输入 URL 到页面展示这中间发生了什么"></a><a href="https://link.segmentfault.com/?enc=0n9MvPJveRquY6eRHlCg5Q==.DL9WvIDOWOXpjcB64QC9A8C3rfjzyKIRF1S/rV7PuNDkvZqJURu2Hck9ziYtSr+lbavukovuoNZLBVyQqcOvjhlwEA3OcpYjcePP8Fle0xRkC61fRH8bCUZW7hU6rTOp0u1acT2Y3P/2LW2PatQakdy3ObEgvRVws5OZvZtPmSMJ2stJF4/wdl0Z+1d5UtJXFiKTqn9oCH0aTmtSuY5XFJxATe713Gh5kWPz/xVMOtM/yMZV5rGzaK4lJtOw4Vr9g0o+VssBTDk1cDkkSQATv1NTkxZzKEHOxzJqfcDK+4cgGwsJ358Pq65tTMdPyOpcjM87K880q2YHWh8cILFBF8h/VkUtdF2NPoeLart4zwI=">导航流程:从输入 URL 到页面展示这中间发生了什么</a></h4><ul><li><p>用户输入 URL 并回车</p></li><li><p>浏览器进程检查 URL,组装协议,构成完整 URL</p></li><li><p>浏览器进程通过进程通信(IPC)把 URL 请求发送给网络进程</p></li><li><p>网络进程接收到 URL 请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程</p></li><li><p>如果没有,网络进程向 web 服务器发起 http 请求(网络请求),请求流程如下:</p></li><li><p>进行 DNS 解析,获取服务器 IP 地址,端口</p></li><li><p>利用 IP 地址和服务器建立 tcp 连接</p></li><li><p>构建请求头信息</p></li><li><p>发送请求头信息</p></li><li><p>服务器响应后,网络进程接收响应头和响应信息,并解析响应内容</p></li><li><p>网络进程解析响应流程:</p></li><li><p>检查状态码,如果是 301&#x2F;302,则需要重定向,从 Location 自动读取地址,重新进行第 4 步,如果是 200,则继续处理请求</p></li><li><p>200 响应处理:检查响应类型 Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续渲染。如果是 html 则通知浏览器进程准备渲染进程进行渲染</p></li><li><p>准备渲染进程</p></li><li><p>浏览器进程检查当前 URL 是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程</p></li><li><p>传输数据、更新状态</p></li><li><p>渲染进程准备好后,浏览器向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”</p></li><li><p>渲染进程接收完数据后,向浏览器发送“确认提交”</p></li><li><p>浏览器进程接收到确认消息后 engine 浏览器界面状态:安全、地址 URL、前进后退的历史状态、更新 web 页面</p></li></ul><h4 id="渲染流程(上):HTML、CSS-和-JavaScript-是如何变成页面的"><a href="#渲染流程(上):HTML、CSS-和-JavaScript-是如何变成页面的" class="headerlink" title="渲染流程(上):HTML、CSS 和 JavaScript 是如何变成页面的"></a><a href="https://link.segmentfault.com/?enc=SgV5RR+kE4Jo5tZ3HmJ+Lw==.JdDfH4tZ/fiRlNAJDlkAB/MMO9X4rwM3jx+SQddpwaOERgeIG4w3tf2psbDDgpUU9in96ycyf7s2mjknZWNs2ifMVpT3AXCo4AkrlqHQq7OSSXPvFrC4PaNThlCB64kupEc4NHq5fQ0axYvDisFdODbzsMQoxv/51RGS9Dc35shkRAp5AGGv00FTGkIwPYabIw8B7yuz2IcJVFc2XhgW40BDs1HFG4jGPXJD02DPc6c0/cWtAu3wdUVyaDHxlgs/UJU6U93v/S6AmajmK0SejwTMy4vo2Heb110qKRSoE+rUJ9GB7A4vMRN/K21yyq3Pp+ycSbstcHeUkfEN8kYy4g==">渲染流程(上):HTML、CSS 和 JavaScript 是如何变成页面的</a></h4><ul><li>浏览器不能直接理解 HTML 数据,需要将其转化为 DOM 树结构;</li><li>生成 DOM 树后,根据 CSS 样式表,计算出 DOM 树所有节点样式;</li><li>创建布局树:遍历 DOM 树所有可见节点,把这些节点加到布局中,不可见节点忽略,如 head 标签下所有内容,display: none 元素;</li></ul><h4 id="渲染流程(下):HTML、CSS-和-JavaScript-是如何变成页面的"><a href="#渲染流程(下):HTML、CSS-和-JavaScript-是如何变成页面的" class="headerlink" title="渲染流程(下):HTML、CSS 和 JavaScript 是如何变成页面的"></a><a href="https://link.segmentfault.com/?enc=078u0RMBFYSDZ+zLn9rE2A==.3uHvjLrzHNzjlHAsq6pbvgswcaxTcalz0jTUDqvuTuYlMI8N18BHEYORck7thGJvp9mdojzL+hPWNiJFOpuwP108CF+zmo1lISi18NGmG4G0SmFJ90p7LdStasdnIkvFTi4Yy5ICPRz7oDZ0YAVTsN73slca3/050rY1DVPBbZv/UaEcn8f6vg7ZXUOosbRz3tvF1MMVy0n4hKFnoWtIMzD2W0wFjJyspz4xGnbtPzV2X6v7FrLXUFR0jRQg7jflHLwyNbaUz4b37mHYjBqRg6vVghKkidoiIrG0R+bPM5XqWxSUS7S3h/OP5j3vKjrzTk4nTsU5ckxBVmoYg8Oa8Q==">渲染流程(下):HTML、CSS 和 JavaScript 是如何变成页面的</a></h4><ul><li>分层:层叠上下文属性的元素(比如定位属性元素、透明属性元素、CSS 滤镜属性元素)提升为单独的一层,需要裁剪的地方(比如出现滚动条)也会被创建为图层;</li><li>图层绘制:完成图层树构建后,渲染引擎会对图层树每一层进行绘制,把一个图层拆分成小的绘制指令,再把指令按照顺序组成一个带绘制列表;</li><li>有些情况图层很大,一次绘制所有图层内容,开销太大,合成线程会将图层划分为图块(256x256 或者 512x512);</li><li>合成线程将图块提交给栅格线程进行栅格化,将图块转换为位图。栅格化过程都会使用 GPU 加速,生成的位图保存周期 GPU 内存中;</li><li>一旦所有图块都被栅格化,合成线程会生成一个绘制图块命令(DrawQuad),然会将命令提交给浏览器进程,viz 组件接收到该指令,将页面内容绘制到内存中,显示在屏幕上;</li><li>重排:通过 JavaScript 或者 CSS 修改元素几何位置属性,会触发重新布局,解析后面一系列子阶段;重绘:不引起布局变换,直接进入绘制及其以后子阶段;合成:跳过布局和绘制阶段,执行的后续操作,发生在合成线程,非主线程;</li></ul><h4 id="变量提升:javascript-代码是按顺序执行的吗"><a href="#变量提升:javascript-代码是按顺序执行的吗" class="headerlink" title="变量提升:javascript 代码是按顺序执行的吗"></a><a href="https://link.segmentfault.com/?enc=HDjBdOsE5RdCVLRlNandOA==.TZHVY6ING/0EjW0SlKo17xo2psnFvStNBX+RrOB0+dLj8ijmx1h16PJTEWwWtgUpGb1goNlc+R9xSvUde+InvMcEnVp6JzS7KCJ+Y1DE4vdWvzmAj0q1gGOoRaVTgxzEY0gisQ3hxIRQP8T+DdyYKXL2pyi1cEqgWkaMD00hHr7+cmpIHzbDU1pII58ydN5nNDdqP07ZxY4EP3r/XxtNdDW6GIS5/BgqY2Lzcfejm4NNFqONhhUVOJJ+/syR05IUh5bvozLzjMlz+RP8vzZnZIUqnhrd9q26CKMZ2/oDoq8=">变量提升:javascript 代码是按顺序执行的吗</a></h4><ul><li>JavaScript 代码在执行之前需要先编译,在编译阶段,变量和函数会被存放到变量环境中,变量默认值会被设置为 undefined;</li><li>在代码执行阶段,JavaScript 引擎会从变量环境中查找自定义的变量和函数;</li><li>如果在编译阶段,窜爱两个相同的函数,那么最终放在变量环境中的是最后定义的那个,后定义的覆盖先定义的;</li></ul><h4 id="调用栈:为什么-JavaScript-代码会出现栈溢出"><a href="#调用栈:为什么-JavaScript-代码会出现栈溢出" class="headerlink" title="调用栈:为什么 JavaScript 代码会出现栈溢出"></a><a href="https://link.segmentfault.com/?enc=KeGSN31ZkRz5TvSbw1loqg==.//1jVfG5kWcIkncsXe9794k1x6B1snMnDd3j9F0at9LeJ0khxAZt19h0m70p3qT1+sDo4ZfMzZezsmcOVgqTwFsqXrtuiBtBPhlcZAKC94x68WymGTi7XMdNCaKrIwwtl1f5pqL21A2J8Zu+Bs2Z2N4lrmB/2Vk6s2wHDOpSP9qXn2P4fBh97KQyokNl2aqhVnTST3IDObu+GiNKc2JD8dgh2M0zZeM5kExKz8yliqwio/Ypmi4km/odz7TSmr4n2n/3UhzlnJlLYdKojquB2+lrl51M5HuZFVCvbRh8Jto=">调用栈:为什么 JavaScript 代码会出现栈溢出</a></h4><ul><li>每调用一个函数,JavaScript 引擎会为其创建执行上下文压入调用栈,然后,JavaScript 引擎开始执行函数代码。</li><li>如果一个函数 A 调用另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶。</li><li>当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。</li><li>当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。</li></ul><h4 id="块级作用域:var-缺陷以及为什么要引入-let-和-const"><a href="#块级作用域:var-缺陷以及为什么要引入-let-和-const" class="headerlink" title="块级作用域:var 缺陷以及为什么要引入 let 和 const"></a><a href="https://link.segmentfault.com/?enc=i1IQmLccAIQ/axwERdbyeA==.uFp+2YSRO09WqJ8VuGrVW6Q0/9bhJEmFmKWfnT+Sd/xpoQR4XocGUt2/fwrhONXaMr6NqGr18lGIhMv+yUfxJMf902wPtE28DSGfdy+w2H5osWxH3Us18nzVa2WKn7kUP1VWB9+3aFN0Yl5peWXTMMrGhPmkjyHnjEJFG7nEqYBPmVLsFYAi4Bno2E0RUKEoSTUem7V04tRG1ran9yd1XOXk3kbKVkzvcvdkID557uRKbsG07XKD49jP3b9l2uT3WtK8UYRmLiUy3TBTycLSQPMmheQNzFal6OXQS0dlLa0rMhExAyLg0MvNzvqAxDzM">块级作用域:var 缺陷以及为什么要引入 let 和 const</a></h4><ul><li>let、const 申明的变量不会被提升。在 javascript 引擎编译后,会保存在词法环境中。</li><li>块级作用域在代码执行时,将 let、const 变量存放在词法环境的一个单独的区域。词法环境内部维护一个小型的栈结构,作用域内部变量压入栈顶。作用域执行完,从栈顶弹出。</li></ul><h4 id="作用域链和闭包:代码中出现相同的变量,JavaScript-引擎如何选择"><a href="#作用域链和闭包:代码中出现相同的变量,JavaScript-引擎如何选择" class="headerlink" title="作用域链和闭包:代码中出现相同的变量,JavaScript 引擎如何选择"></a><a href="https://link.segmentfault.com/?enc=Tg5vs05uHGA08Uh4fU6x4w==.rtb/7lTbrTsoYewcZ4lAzAcyXM8zD6QpHpl+C8tVDCv4rafrihGwEBR8+SMmrfhPfljojlEYINrwCkXWyNGS9rCChW1zy5UcYjl1MS8sAAFlyIKCfIRpdHI8MPhCyfLe3HomJwnn4v5mKe61QBCY02TyXuwYXysvVn/foJEsNl6yjdXp0nQ6BmTh11F30Hbpkdq4NCvfAO7nlbDMdrFmS/fauw/lD+tY61uburaYkVuJVVG4KdL6lhwY21aEvVarVMTdeas8hVVUQA7dwHo2rRat0UL9M5I0j+TIIIB3GBrinR7QfXaHu7xD5Zexxa+y/KKtQo7XLRxHL3l5kqghi/zUBhB1SFBXD68AxIvHriZ2g3ixlkOzsSfte5JlvncJdjr11CGdsqOg+ctl3/1l8CbC1ntV9OESTeCU7w0GPyo=">作用域链和闭包:代码中出现相同的变量,JavaScript 引擎如何选择</a></h4><ul><li>使用一个变量,JavaScript 引擎会在当前的执行上下文中查找变量,如果没有找到,会继续在 outer(执行环境指向外部执行上下文的引用)所指向的执行上下文中查找;</li><li>JavaScript 执行过程,作用域链是由词法作用域决定,而词法作用域是由代码中函数声明的位置决定;</li><li>根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使外部函数已经执行结束了,但是内部函数引用外部函数的变量依旧保存在内存中,把这些变量的集合称为闭包;</li></ul><h4 id="this:从-JavaScript-执行上下文视角讲-this"><a href="#this:从-JavaScript-执行上下文视角讲-this" class="headerlink" title="this:从 JavaScript 执行上下文视角讲 this"></a><a href="https://link.segmentfault.com/?enc=h1YWbj2HGPnmDFeVULFPvQ==.j4pZf1VPrJdNByuOMoMTYOF1HQSy14YAb+gmNFoOxsXnjQmH30SLEyziyD1rao5LzPC+phrSfo/ZPNIR9udKl8RG8LTzUyjGuyrftsPrONxwghleZ1Opq7yHeX3t7OHkPmlnMV0IM4bgekF/Wls4UyiMrSYtMTIF7uUZEsC49yqUa8J+nICj7urgOSy69S2eB2FQKspJ7zu0p7WL0RCb8jEM+jR6y4XsLYY3YNYvWRD65zJIYSauui7QCdqPwN+Q">this:从 JavaScript 执行上下文视角讲 this</a></h4><p>当执行 new CreateObj 的时候,JavaScript 引擎做了四件事:</p><ul><li>首先创建一个控对象 tempObj;</li><li>接着调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数,这样当 createObj 的执行上下文创建时,它的 this 就指向 tempObj 对象;</li><li>然后执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向 tempObj 对象;</li><li>最后返回 tempObj 对象。</li></ul><p>this 的使用分为:</p><ul><li>当函数最为对象的方法调用时,函数中的 this 就是该对象;</li><li>当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;</li><li>嵌套函数中的 this 不会继承外层函数的 this 值;</li><li>箭头函数没有自己的执行上下文,this 是外层函数的 this。</li></ul><h4 id="栈空间和堆空间:数据是如何存储的"><a href="#栈空间和堆空间:数据是如何存储的" class="headerlink" title="栈空间和堆空间:数据是如何存储的"></a><a href="https://link.segmentfault.com/?enc=QY/Tyt3YsjRonyjIwg0jxw==.P9IwZfXXOULejaHCTRSVGkszrxapuxk+h8Ju3SVnQ70bwHXsFeGKyj5ulYZClt6YOyydKbKQ/lHzwi7rmQhQYpzEmDiZnhW15HhaYNieG72ZXTODSur1u05iAWCj5qP24nCKgokCBYynAooO3E4OtjSpA6ntVAgaMf27R9HaF0deHtrLBJAuPqmUk8SKOxYhZ9Uuf09rzp+fjbmI/XCyHQ7ARRALLKW3lr9xttpCeNazB/MvCkEv5JNWJePGwUjNQfx+SjJJntFEAws0KRsUhfBtqC0TlsJ4O1WJjJkMfZo=">栈空间和堆空间:数据是如何存储的</a></h4><p>动态语言:在使用时需要检查数据类型的语言。<br>弱类型语言:支持隐式转换的语言。</p><p>JavaScript 中的 8 种数据类型,它们可以分为两大类——原始类型和引用类型。<br>原始类型数据存放在栈中,引用类型数据存放在堆中。堆中的数据是通过引用与变量关系联系起来的。</p><p>从内存视角了解闭包:词法扫描内部函数,引用了外部函数变量,堆空间创建一个“closure”对象,保存变量。</p><h4 id="垃圾回收:垃圾数据如何自动回收"><a href="#垃圾回收:垃圾数据如何自动回收" class="headerlink" title="垃圾回收:垃圾数据如何自动回收"></a><a href="https://link.segmentfault.com/?enc=zhVpYk/9P1I2SL8iAJI4Zw==.jpBsHt9nL7lLCJRwOyMtKatoxWBt0QTPvBJk0/b0K3q4ArApG5guisEtPcYUgv0oFvWmTI9SPioTb7HCOhPCe8oOSj3HXHzZ6XNzJ3dJYE3s7tVyfLPT4w3BzCJaloWAIp7Lyx55v0/5/vI32OFuYmmdeGVsUeZktLmQBQF7XiS3xIsmP5/pk8M+nvhHAWxKcTXPMso2lqjL6Redrn0DoL037eaLnKcDnebpBk+IQU/cFa6JxzvJjtxY3/70uDQE328E9c0Ro3fqcRZm2/H+3ka3mDn6AK1NMRUe7plQpA0=">垃圾回收:垃圾数据如何自动回收</a></h4><ul><li>栈中数据回收:执行状态指针 ESP 在执行栈中移动,移过某执行上下文,就会被销毁;</li><li>堆中数据回收:V8 引擎采用标记-清除算法;</li><li>V8 把堆分为两个区域——新生代和老生代,分别使用副、主垃圾回收器;</li><li>副垃圾回收器负责新生代垃圾回收,小对象(1 ~ 8M)会被分配到该区域处理;</li><li>新生代采用 scavenge 算法处理:将新生代空间分为两半,一半空闲,一半存对象,对对象区域做标记,存活对象复制排列到空闲区域,没有内存碎片,完成后,清理对象区域,角色反转;</li><li>新生代区域两次垃圾回收还存活的对象晋升至老生代区域;</li><li>主垃圾回收器负责老生区垃圾回收,大对象,存活时间长;</li><li>新生代区域采用标记-清除算法回收垃圾:从根元素开始,递归,可到达的元素活动元素,否则是垃圾数据;</li><li>为了不造成卡顿,标记过程被切分为一个个子标记,交替进行。</li></ul><h4 id="编译器和解析器:V8-如何执行一段-JavaScript-代码的"><a href="#编译器和解析器:V8-如何执行一段-JavaScript-代码的" class="headerlink" title="编译器和解析器:V8 如何执行一段 JavaScript 代码的"></a><a href="https://link.segmentfault.com/?enc=ZSNm+jBqpSd7Y+qiHQmRxA==.cVKn10t8gzekufmHrKjlKHyE3jnNYYYWZzPsGKEWHPc+EFsAv1HQ9LwCKjHLVUVeDHFyX4nOB75aZaRqql0yJtnFiKSGfi1ng9+WhQmq+Jg/jHp9D6GHdver9LmPcHIEkx72e7d/u32UF1R4HnQGiQ/zLTZ9r0xLXQAYgqVOhUBcQaKh8WYBmMU9y1RGqPHwEmiAmeM7ku3XmJG3rZlhLpOMx+lH/PmyI0ukghmQH87PtLDuXwQwCjtM0kT4L4NAaF+CSfZ3FxfY2ccyuLbz6Yk1/9LWU32C6vngFyFguZCQEcyGR9rDlf+sIPDOgARk">编译器和解析器:V8 如何执行一段 JavaScript 代码的</a></h4><ul><li>计算机语言可以分为两种:编译型和解释型语言。编译型语言经过编译器编译后保留机器能读懂的二进制文件,比如 C&#x2F;C++,go 语言。解释型语言是在程序运行时通过解释器对程序进行动态解释和执行,比如 Python,JavaScript 语言。</li><li>编译型语言的编译过程:编译器首先将代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后生成处理器能够理解的机器码;</li><li>解释型语言解释过程:解释器会对代码进行词法分析、语法分析,并生产抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后根据字节码执行程序;</li><li>AST 的生成:第一阶段是分词(词法分析),将一行行源码拆解成一个个 token(语法上不可再分、最小单个字符)。第二阶段是解析(语法分析),将上一步生成的 token 数据,根据语法规则转为 AST,这一阶段会检查语法错误;</li><li>字节码存在的意义:直接将 AST 转化为机器码,执行效率是非常高,但是消耗大量内存,从而先转化为字节码解决内存问题;</li><li>解释器 ignition 在解释执行字节码,同时会手机代码信息,发现某一部分代码是热点代码(HotSpot),编译器把热点的字节码转化为机器码,并保存起来,下次使用;</li><li>字节码配合解释器和编译器的计数实现称为即时编译(JIT)。</li></ul><h4 id="消息队列和事件循环:页面是怎么活起来的"><a href="#消息队列和事件循环:页面是怎么活起来的" class="headerlink" title="消息队列和事件循环:页面是怎么活起来的"></a><a href="https://link.segmentfault.com/?enc=CyJ/k5c5UXnYf2/6Yqeh0w==.+fTguKMEuLlezhfoRw05XJ7lvKWYQ8EVcc37H1U9sagNUi9mgOzDKpw1F53HNXiXv6cqsK4t7FgJOdfSIe+Wt1f10FRfTYrl/J4Q94q+wyukS7Todzokzq2+B7lZ+ciAZ+b1qVWhhbtAcLUD4shpLjgNm08O361QSKiYrKyGuR2XtrJH3Kmx7lW9JQZxmN2HBSPiO2Hsrws7UhcsUNGyXb26MmjdiGBzspYokrDQxkhE36Rw2ZzeSncTcojOao/Ncl8cxNIuXknCf7uZ4zcqV0DpKE0gZ/dPgeo6KQMhgQYflw2pWhVUB43C6bN3qVSPLU6jDro6OFPdr9geaD+m3g==">消息队列和事件循环:页面是怎么活起来的</a></h4><ul><li>每个渲染进程都有一个主线程,主线程会处理 DOM,计算样式,处理布局,JavaScript 任务以及各种输入事件;</li><li>维护一个消息队列,新任务(比如 IO 线程)添加到消息队列尾部,主线程循环地从消息队列头部读取任务,执行任务;</li><li>解决处理优先级高的任务:消息队列的中的任务称为宏任务,每个宏任务中都会包含一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,将该变化添加到微任务队列中;</li><li>解决单个任务执行时长过久:JavaScript 通过回调功能来规避。</li></ul><h4 id="webapi:setTimeout-是怎么实现的"><a href="#webapi:setTimeout-是怎么实现的" class="headerlink" title="webapi:setTimeout 是怎么实现的"></a><a href="https://link.segmentfault.com/?enc=Xd/mNwjDf+DrZblcBg+GLQ==.Do4vnAqL8Syx3bvW/lk+4hJ7y+8HQq4OCpH1a8eTG4h55ucqJ7U6DNY8GkPiQkfuUZQeZ9RWGvev7J6/7aHG4/0m8TbjOXxSjKOTIdUWTgT5Q7M/VdrZ4GyRbWycZHwP72/T754QN+r87Ih3jHOdfk5Aer2pbw2zYMGfoLpqqOnV9bKWNhBdeoq0T2DoyfN/Z286mPxAgkuqSSIREFmShw==">webapi:setTimeout 是怎么实现的</a></h4><ul><li>JavaScript 调用 setTimeout 设置回调函数的时候,渲染进程会创建一个回调任务,延时执行队列存放定时器任务;</li><li>当定时器任务到期,就会从延时队列中取出并执行;</li><li>如果当前任务执行时间过久,会影响延时到期定时器任务的执行;</li><li>如果 setTimeout 存在嵌套调用(5 次以上),判断该函数方法被阻塞,那么系统会设置最短时间间隔为 4 秒;</li><li>未激活的页面,setTimeout 执行最小间隔是 1000 毫秒,目的是为了降低加载损耗;</li><li>延时执行时间最大值是 24.8 天,因为延时值是以 32 个 bit 存储的;</li><li>setTimeout 设置的回调函数中的 this 指向全局 window。</li></ul><h4 id="webpai:XMLHttpRequest-是怎么实现的"><a href="#webpai:XMLHttpRequest-是怎么实现的" class="headerlink" title="webpai:XMLHttpRequest 是怎么实现的"></a><a href="https://link.segmentfault.com/?enc=Twu2PTCsWO7+VysqohRAGg==.XE20MPg7WX+3Z+egXb588oEHDzmVP6MZqX+2ubXyLlQkmK8WCSVyKxz1xEzCXDm4fNpXpQkL1bH3gFoSp94ZlalUjui1VeLFB/wM1JgLsYdFMRbA7sHsyeBQ+ZcoNR31ECWh+5gI74nCuWiv6jg7Q8fZFErSYD+wlaxcGS4lxKtXtOSoRLgs2fHF3lcFH98aTfLsKDiSL47QjzwcIhzPXA==">webpai:XMLHttpRequest 是怎么实现的</a></h4><ul><li>XMLHttpRequest onreadystatechange 处理流程:未初始化 -&gt; OPENED -&gt; HEADERS_RECEIVED -&gt; LOADING -&gt; DONE;</li><li>渲染进程会将请求发送给网络进程,然后网络进程负责资源下载,等网络进程接收到数据后,利用 IPC 通知渲染进程;</li><li>渲染进程接收到消息之后,会将 xhr 回调函数封装成任务并添加到消息队列中,等主线程循环系统执行到该任务的时候,会根据相关状态来调用回调函数。</li></ul><h4 id="宏任务和微任务:不是所有的任务都是一个待遇"><a href="#宏任务和微任务:不是所有的任务都是一个待遇" class="headerlink" title="宏任务和微任务:不是所有的任务都是一个待遇"></a><a href="https://link.segmentfault.com/?enc=9WMnHJ6euVXt1D5O8OzynA==.Xr+c1OVMlb3lbRJ8e3xpG3M6X79HDv1a4ij8kAulZNfqCTcnn65dwxC0A75ModrG19bmimexc6gja8eabNhAB1g/Y2LlEYGFzWB1k1AB22G9FQWRseHz3S4x0REQfHzzxDvi6e6YigV5SNIWMcpZQQhrdQ7ylmYAMI3xc/VivacOHHNmFA55AiywhzeSD1V5S3eOI3mnUT4QrPcIlpaoq18QoHaGI7dLqljE4U5xqGw2wLZTuY7ek+lPhhHyc1Iil8F0YhxsCex1AtGjf30XISf90kRnvRztaiDpEc9TGiRwD4SqwXG7uQOUqTOQ9uhdwtEgxpYgTGGc2GGXgLMMkDSjU4zNGAR3YEPyrxNZB2g=">宏任务和微任务:不是所有的任务都是一个待遇</a></h4><ul><li>消息队列中的任务为宏任务。渲染进程内部会维护多个消息队列,比如延时执行队列和普通消息队列,主线程采用 for 循环,不断地从这些任务队列中取出任务并执行;</li><li>微任务是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前;</li><li>V8 在执行 javascript 脚本时,会为其创建一个全局执行上下文,同时会创建一个微任务队列;</li><li>执行微任务过程中产生的微任务不会推迟到下个宏任务中执行,而是在当前宏任务中继续执行;</li></ul><h4 id="使用-Promise-告别回调函数"><a href="#使用-Promise-告别回调函数" class="headerlink" title="使用 Promise 告别回调函数"></a><a href="https://link.segmentfault.com/?enc=bEeUyBMAJaYQj5xs2taGGQ==.8iXKAqC6nZwe9GjO8wSoj+cu5oxftYCPXycbUjMa5wMmYbUqkuG8as9+9Qv2b8FJ74EUowvQExcT2wVbRZi3Q+kUFqP07wWolN2R8EChj3AC7qtj6zR+4y+EepByo+wPElffvuku9N1XC026ioatsfckJrN+K4i3y6IzyDLXG10uphQ4+eCoisbwWZqodSi7LSe/BFAoK3NWF3xuS7Ecog==">使用 Promise 告别回调函数</a></h4><ul><li><p>使用 Promise 解决了回调地狱问题,消灭嵌套和多次处理;</p></li><li><p>模拟实现 Promise</p><p> functionBromise(executor) {<br>var _onResolve &#x3D; nullthis.then &#x3D; function (onResolve) {<br> _onResolve &#x3D; onResolve<br>}<br>functionresolve(value) {<br> setTimeout(() &#x3D;&gt; {<br> _onResolve(value)<br> }, 0)<br>}<br>executor(resolve, null)<br> }</p></li></ul><h4 id="async-await-使用同步方式写异步代码"><a href="#async-await-使用同步方式写异步代码" class="headerlink" title="async await 使用同步方式写异步代码"></a><a href="https://link.segmentfault.com/?enc=hTtmwAUeKRgyK1osAOH/Ig==./akyGZnIc70uhNu+lsPCYAOtbWTh9rR7qQCq84Xm+DcZQ+y2J3w4Qz+7wVU8SMfBJStty+dNLLgIEn7xItbObXGXqtIbDqT+stiO1EpqEhxTL9mZ3Bob+qhQBYe6nqYwSgHvvJN2Bzw8p3qIwFGo0PjwpXxHYQn0/yUkBDtteYw9qa+0gq0cCMkUGrj53hTIvzj/MV1J7cPk4kQ+a0ESIqavmXwI5WWvQk60dWyReLuCEalrRydw+e3EmYyVgGAt">async await 使用同步方式写异步代码</a></h4><ul><li>生成器函数是一个带星号函数,而且是可以暂停执行和回复执行的;</li><li>生成器函数内部执行一段代码,遇到 yield 关键字,javascript 引擎返回关键字后面的内容给外部,并且暂停该函数的执行;</li><li>外部函数可以同步 next 方法恢复函数的执行;</li><li>协程是一种比线程更加轻量级的存在,协程可以看成是跑在线程上的任务,一个线程可以存在多个协程,但是同时只能执行一个协程,如果 A 协程启动 B 协程,A 为 B 的父协程;</li><li>协程不被操作协同内核所管理,而完全由程序所控制,这样性能提升;</li><li><code>await xxx</code> 会创建一个 Promise 对象,将 <code>xxx</code> 任务提交给微任务队列;</li><li>暂停当前协程的执行,将主线程的控制权力转交给父协程执行,同时将 Promise 对象返回给父协程,继续执行父协程;</li><li>父协程执行结束之前会检查微任务队列,微任务队列中有 <code>resolve(xxx)</code> 等待执行,触发 then 的回调函数;</li><li>回调函数被激活后,会将主线程的控制权交给协程,继续执行后续语句,完成后将控制权还给父协程。</li></ul><h4 id="页面性能分析:利用-chrome-做-web-性能分析"><a href="#页面性能分析:利用-chrome-做-web-性能分析" class="headerlink" title="页面性能分析:利用 chrome 做 web 性能分析"></a><a href="https://link.segmentfault.com/?enc=5Clm1M0Gi5edXEquWZXF6A==.s5Bmwe9MNfTUFvbP2CHxWNPpiU0ZOqAnd/hMP6vjt7/UtgyYNfQObRK76ceueg3FoFUDofYzP0GBgi/qvFbw4orXU09YjrFjJBlGQ38wK7YZZ+eaRkHMil1884yX9aIi9p6578HjSBWI3wuSw31Nq7s6t59qFlarRncxn5k3FvGBdqQfEtemKfDd9yrJMR/5oVRNtdWtt3ViZQDnLf1S9DJQEl79i8gY+3Y1NyNpS7R1fxgnDwSfZ0SEzvqi3r1Kxx/BNmEEUPx3pDVhrQiJ6IwTHzp+//yA9kx9aFPQYqg=">页面性能分析:利用 chrome 做 web 性能分析</a></h4><ul><li>Chrome 开发者工具(简称 DevTools)是一组网页制作和调试的工具,内嵌于 Google Chrome 浏览器中。它一共包含了 10 个功能面板,包括了 Elements、Console、Sources、NetWork、Performance、Memory、Application、Security、Audits 和 Layers。</li></ul><h4 id="DOM-树:JavaScript-是如何影响-DOM-树构建的"><a href="#DOM-树:JavaScript-是如何影响-DOM-树构建的" class="headerlink" title="DOM 树:JavaScript 是如何影响 DOM 树构建的"></a><a href="https://link.segmentfault.com/?enc=0DNA0QfcJKP7hnrHQ7AKxg==.pOyFY3cwRU+QM/o1NszENhKXW0qLLtNnYCsCpqHykp2qDQAceo95146HKBMhqGDb814dPRIfG1cqb6kGNvX2GawZMxaHqRH54PUm7S4mbPU0bUmdk5RBVGHuFDwM4t+i6mhBgYgIGhT/Xh8h3luRHwIKNb+qeB9VuyZ0YEdlLtBLO2oURKJ20p0uxvYjR0RiL1Rt9wprQw3025xQ49LBSfuGBV/N6vZg7uZw7tHeFSrZUzq1q1RLAzsODiJ8xfvL">DOM 树:JavaScript 是如何影响 DOM 树构建的</a></h4><ul><li>HTML 解析器(HTMLParse)负责将 HTML 字节流转换为 DOM 结构;</li><li>HTML 解析器并不是等整个文档加载完成之后再解析,而是网络进程加载流多少数据,便解析多少数据;</li><li>字节流转换成 DOM 三个阶段:1、字节流转换为 Token;2、维护一个 Token 栈,遇到 StartTag Token 入栈,遇到 EndTag Token 出栈;3、为每个 Token 创建一个 DOM 节点;</li><li>JavaScript 文件和 CSS 样式表文件都会阻塞 DOM 解析;</li></ul><h4 id="渲染流水线:CSS-如何影响首次加载时的白屏时间?"><a href="#渲染流水线:CSS-如何影响首次加载时的白屏时间?" class="headerlink" title="渲染流水线:CSS 如何影响首次加载时的白屏时间?"></a><a href="https://link.segmentfault.com/?enc=MnJExBRqSbBfodjQtYuKgA==.wVblMGzPAN0PYS68Qhe9y1FPmNMRbRZc4ygjVS5bW2tGIvsw7u2c5PvPIF5OQcOtiHI7E2QLyhnsyFN1fXAKF16CB152b1adRNZrmlj6nF0PQSA34l75Qd3mczJtJVypE8Nzrmd02QwaH0PL9tBuERYZkGacuekybPX3stPKkoVQV0HsS0xIYE/DYeSioVEeWlpr4KrBB2/F3dVcW/n9AKCOQx8I6PbjZZMIodZ/rQXk/6GvqaeqjRmppMXLTF3nbWNNfaAtuX2R5k+zBqGLmL1iAypeC3nTqFAQ6RRBcIrBnHL1hRWcViHEacVLNSK1L7M+d0n7pkNiEUfXReUtMjsLYYmqCw2CVFlT9hOa35U=">渲染流水线:CSS 如何影响首次加载时的白屏时间?</a></h4><ul><li>DOM 构建结束之后,css 文件还未下载完成,渲染流水线空闲,因为下一步是合成布局树,合成布局树需要 CSSOM 和 DOM,这里需要等待 CSS 加载结束并解析成 CSSOM;</li><li>CSSOM 两个作用:提供给 JavaScript 操作样式表能力,为布局树的合成提供基础样式信息;</li><li>在执行 JavaScript 脚本之前,如果页面中包含了外部 CSS 文件的引用,或者通过 style 标签内置了 CSS 内容,那么渲染引擎还需要将这些内容转化为 CSSOM,因为 JavaScript 有修改 CSSOM 的能力,所以在执行 JavaScript 之前,还需要依赖 CSSOM。也就是说 CSS 在部分情况下也会阻塞 DOM 的生成。</li></ul><h4 id="分层和合成机制:为什么-CSS-动画比-JavaScript-高效"><a href="#分层和合成机制:为什么-CSS-动画比-JavaScript-高效" class="headerlink" title="分层和合成机制:为什么 CSS 动画比 JavaScript 高效"></a><a href="https://link.segmentfault.com/?enc=tJjm7PYlwT8d/oFvmutcRg==.hhMVfCxt4s0PjhGHdB7sf2Aiv/gQXWa5MDNpTsq46FlHN0VFLxO0X3YzPfDq2wwJAJlybB1vdn5FmQe7/Hi1opG5H4/DMacYv036fvvwfKGDviUsnqBinHn6dFj/JsuF5KY2SrrirDUJAx+hVWPu1N52kX3Oj0fhSdL2CiRkGuezFE+bu09sMtahIJxred0SZyG+ODDF67ppbrBI/v5fBTfyfZBqTG+pog3OtBDJYOfr+hyJm+ag2H/KH+Rqsgjp4VI4jb2G06DT6+kKchZkM2lb5rWcG2WH7D9HeLwK3NbJva+oSq9dfDkD5YHyabtK">分层和合成机制:为什么 CSS 动画比 JavaScript 高效</a></h4><ul><li>显示器固定刷新频率是 60HZ,即每秒更新 60 张图片,图片来自显卡的前缓冲区;</li><li>显卡的职责是合成新的图像,保存在后缓冲区,然后后缓冲区和前缓冲区互换,显卡更新频率和显示前刷新频率不一致,就会造成视觉上的卡顿;</li><li>渲染流水线生成的每一副图片称为一帧,生成一帧的方式有重排、重绘和合成三种;</li><li>重排会根据 CSSOM 和 DOM 计算布局树,重绘没有重新布局阶段;</li><li>生成布局树之后,渲染引擎根据布局树特点转化为层树,每一层解析出绘制列表;</li><li>栅格线程根据绘制列表中的指令生成图片,每一层对应一张图片,合成线程将这些图片合成一张图片,发送到后缓存区;</li><li>合成线程会将每个图层分割成大小固定的图块,优先绘制靠近视口的图块;</li></ul><h4 id="页面性能:如何系统优化页面"><a href="#页面性能:如何系统优化页面" class="headerlink" title="页面性能:如何系统优化页面"></a><a href="https://link.segmentfault.com/?enc=oFLuHRzl45UC4plGeofYxQ==.HUtCnO6TEbRwsoc0dkc3AGGFu6NQPiaJfgGVsjKNBTpoMlEtZZMNMGKRbpzYL+fQxVVq0AlqIWexyYJ5IhThYk9TDYZF8xNELYPT4PbuDXhiMJ/YlZ45Jo1pbTcuvd0A91CiLnIyYZWVnC0zlK2vOWy4YFl+2qi9KnLEbHzx0FTFtJn4ctJGEPg6NusaFE+5REVW0cJ0VM2yQW5v7hVwn3znDJqohR0WYu0Hnp70EFQPIv7ClcZs4hgtzCswt6Zf">页面性能:如何系统优化页面</a></h4><ul><li>加载阶段:减少关键资源个数,降低关键资源大小,降低关键资源的 RTT 次数;</li><li>交互阶段:减少 JavaScript 脚本执行时间,避免强制同步布局:操作 DOM 的同时获取布局样式会引发,避免布局抖动:多次执行强制布局和抖动,合理利用 CSS 合成动画:标记 will-change,避免频繁的垃圾回收;</li><li>CSS 实现一些变形、渐变、动画等特效,这是由 CSS 触发的,并且是在合成线程中执行,这个过程称为合成,它不会触发重排或者重绘;</li></ul><h4 id="虚拟-DOM:虚拟-DOM-和真实-DOM-有何不同"><a href="#虚拟-DOM:虚拟-DOM-和真实-DOM-有何不同" class="headerlink" title="虚拟 DOM:虚拟 DOM 和真实 DOM 有何不同"></a><a href="https://link.segmentfault.com/?enc=5f+ScpHLN9bRYcmqWQzmqA==.SqH9iJw0UwXXIiLjEBLftRTKJuI7VDV35+rBxjqdVcFQ+OUHvv3hiOJeRz5wJ8ePjtTpy9vk8lLMqC+7uk7rDYh+Q076mmtAlTbwmX/7A0KWxfZ44IQzWdMIurn24Vyrzfvgx3+ADTPgp9p13qzZHMWRBJwcYvH9ogM3smgcD7q0ZfhqJj7hnrwhneFwTj2sUnBGWEU2Lt05n+uvy26fesv+c8owqiVyPlvZy5iP4HI97b7KRYiqQjZMHSY0IokT">虚拟 DOM:虚拟 DOM 和真实 DOM 有何不同</a></h4><ul><li>当有数据更新时, React 会生产一个新的虚拟 DOM,然会拿新的虚拟 DOM 和之前的虚拟 DOM 进行比较,这个过程找出变化的节点,然后将变化的节点应用到 DOM 上;</li><li>最开始的时候,比较两个 DOM 的过程是在一个递归函数里执行的,其核心算法是 reconciliation。通常情况,这个比较过程执行很快,不过虚拟 DOM 比较复杂时,执行比较函数可能占据主线程比较久的时间,这样会导致其他任务的等待,造成页面卡顿。React 团队重写了 reconciliation 算法,称为 Fiber reconciler,之前老的算法称为 Stack reconciler;</li></ul><h4 id="PWA:解决-web-应用哪些问题"><a href="#PWA:解决-web-应用哪些问题" class="headerlink" title="PWA:解决 web 应用哪些问题"></a><a href="https://link.segmentfault.com/?enc=YV88FA3sQelt8t42WLL+IA==.SNzmCYECw4WtKrRaQnuBq8WmM+q6icdXJFBsgeV/ZOZPatA/HdqepynZykLB5+kaD67dYVzRdK5779KXJBjo0ptBgIaPVp9yuNuu2YB8XBGENrVVuaL5ZrQoKtHPgZVH8EIDKvtLaQw9e9SELpjlZtPgCvhYzapsjKAJZHbnH5pYdDyhMWtVkgGUcyzPqAMZFzs71/2Lyxzp8M18kFURWb7xcZZqf7gYnRi5dL/XD3w=">PWA:解决 web 应用哪些问题</a></h4><ul><li>PWA(Progressive Web App),渐进式 Web 应用。一个渐进式过渡方案,让普通站点过渡到 Web 应用,降低站点改造代价,逐渐支持新技术,而不是一步到位;</li><li>PWA 引入 ServiceWorker 来试着解决离线存储和消息推送问题,引入 mainfest.json 来解决一级入口问题;</li><li>暗转了 ServiceWorker 模块之后,WebApp 请求资源时,会先通过 ServiceWorker,让它判断是返回 Serviceworker 缓存的资源还是重新去网络请求资源,一切的控制权交给 ServiceWorker 来处理;</li><li>在目前的 Chrome 架构中,Service Worker 是运行在浏览器进程中的,因为浏览器进程生命周期是最长的,所以在浏览器的生命周期内,能够为所有的页面提供服务;</li></ul><h4 id="WebComponent:像搭积木一样构建-web-应用"><a href="#WebComponent:像搭积木一样构建-web-应用" class="headerlink" title="WebComponent:像搭积木一样构建 web 应用"></a><a href="https://link.segmentfault.com/?enc=sPxJodGETrVbdPxKLDmTKg==.C/jOBuIVjbQDdQ0SMbFviC6ASLog7aEzLJL2Og6RVnZHOd4hnTeHi0UrDQXmq+vJJDZZZB/ONWnv0/JPTvtSpxKBdPRyjKcevgZuFftHvWL22On2I9cTxTDWpu9yIv6soT/3AmFP0F6keoKvgHO57dKaqEMtnoChF0cVP/mbsdtnPca/u8GDdswlhw0Mpp9CvpxLC4vsU8SLX6hxT52QWJ5ynoPKZXT894LCsCqt+dwnQa4ecZNVTii0jo1tgfAT">WebComponent:像搭积木一样构建 web 应用</a></h4><ul><li>CSS 的全局属性会阻碍组件化,DOM 也是阻碍组件化的一个因素,因为页面中只有一个 DOM,任何地方都可以直接读取和修改 DOM;</li><li>WebComponent 提供了对局部试图封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中;</li><li>template 创建模版,查找模版内容,创建影子 DOM,模版添加到影子 DOM 上;</li><li>影子 DOM 可以隔离全局 CSS 和 DOM,但是 JavaScript 是不会被隔离的;</li></ul><h4 id="HTTP1:HTTP1-性能优化"><a href="#HTTP1:HTTP1-性能优化" class="headerlink" title="HTTP1:HTTP1 性能优化"></a><a href="https://link.segmentfault.com/?enc=UidB9qzPJ7hft4RvM5ppXw==.Lvv4PW3JOTkLaJTw5aVCAYBE4K2mh5NeNW0SmZO9usaf8i1mQSWP6Un6ziuIIJrY3sQV0/40jU3wzgPxW6QyW603HKILYg4pbdP4cyukWA7HfX66P5XtROSlbNNVRd4h4emeOe1bxZmL9NyfoYnE6+G+ru119fxewHbX/R7FJEc=">HTTP1:HTTP1 性能优化</a></h4><ul><li>HTTP&#x2F;0.9 基于 TCP 协议,三次握手建立连接,发送一个 GET 请求行(没有请求头和请求体),服务器接收请求之后,读取对应 HTML 文件,数据以 ASCII 字符流返回,传输完成断开连接;</li><li>HTTP&#x2F;1.0 增加请求头和响应头来进行协商,在发起请求时通过请求头告诉服务器它期待返回什么类型问题、什么形式压缩、什么语言以及文件编码。引入来状态吗,Cache 机制等;</li><li>HTTP&#x2F;1.1 改进持久化连接,解决建立 TCP 连接、传输数据和断开连接带来的大量开销,支持在一个 TCP 连接上可以传输多个 HTTP 请求,目前浏览器对于一个域名同时允许建立 6 个 TCP 持久连接;</li><li>HTTP&#x2F;1.1 引入 Chunk transfer 支持动态生成内容:服务器将数据分割成若干任意大小的数据块,每个数据块发送时附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志。在 HTTP&#x2F;1.1 需要在响应头中设置完整的数据大小,如 Content-Length。</li></ul><h4 id="HTTP2:如何提升网络速度"><a href="#HTTP2:如何提升网络速度" class="headerlink" title="HTTP2:如何提升网络速度"></a><a href="https://link.segmentfault.com/?enc=e8dTRn2orbpfWIH/MEgHjw==.0pu3lCjJFLgAzc98oGe3A/vl5fxahgTjXuwkM35yCX6cxOYZDGfygKWF+erkvW01ohrFlJvY2UuHMiTYj7CFGpwYDCUnoaBUSMhyxIMTEIS7iRKj4WCz6+mIxbdNWX8KubSzPzlnJ0Mc+fcg+ckh9Jqda/xcnwp9mE1e5PfYpdPG19W282JfoDi7vdxJGKAgYT5C2iKh5zAqego7gS7Mcg==">HTTP2:如何提升网络速度</a></h4><ul><li>HTTP&#x2F;1.1 主要问题:TCP 慢启动;同时开启多条 TCP 连接,会竞争固定宽带;对头阻塞问题;</li><li>HTTP&#x2F;2 在一个域名下只使用一个 TCP 长连接和消除对头阻塞问题;</li><li>多路复用的实现:HTTP&#x2F;2 添加了二进制分帧层,将发送或响应数据经过二进制分帧处理,转化为一个个带有请求 ID 编号的帧,服务器或者浏览器接收到响应帧后,根据相同 ID 帧合并为一条完整信息;</li><li>设置请求优先级:发送请求可以设置请求优先级,服务器可以优先处理;</li><li>服务器推送:请求一个 HTML 页面,服务器可以知道引用了哪些 JavaScript 和 CSS 文件,附带一起发送给浏览器;</li><li>头部压缩:对请求头和响应头进行压缩;</li></ul><h4 id="HTTP3:甩掉-TCP、TCL-包袱,构建高效网络"><a href="#HTTP3:甩掉-TCP、TCL-包袱,构建高效网络" class="headerlink" title="HTTP3:甩掉 TCP、TCL 包袱,构建高效网络"></a><a href="https://link.segmentfault.com/?enc=WPP2EPfRQFVjAkZIu0o2Fg==.ONJEDxkNMhZY3nIyYNqmwdkDnf21nX/V1ROMFNjYSEf/+2SXgVk3xZhnNeFUK4WvJ2o24GPcMcnYW6ufly9hAt6PwQfYd3+5Ss/Ry5SJ8t6hwFsjfKsL7o79djh3w8mDf/EyXlgLtRMnGrbxMt9p1L2cRoOQGKufv0WDIFXZJwlqQeo2PWeDkPCLxSYiogmi+GAmJ2f5uTg4/w+YWB1eVwRiFMXSJAdfLhBZ708gSPIuD/aSVEjO9OzlZir4PqTG3TISuZEAy5dBnC7U9N1SnA==">HTTP3:甩掉 TCP、TCL 包袱,构建高效网络</a></h4><ul><li>虽然 HTTP&#x2F;2 解决了应用层面的对头阻塞问题,不过和 HTTP&#x2F;1.1 一样,HTTP&#x2F;2 依然是基于 TCP 协议,而 TCP 最初是为了单连接而设计;</li><li>TCP 可以看成是计算机之间的一个虚拟管道,数据从一端发送到另一端会被拆分为一个个按照顺序排列的数据包,如果在传输过程中,有一个数据因为网络故障或者其他原因丢失,那么整个连接会处于暂停状态,只有等到该数据重新传输;</li><li>由于 TCP 协议僵化,也不可能使用新的协议,HTTP&#x2F;3 选择了一个折衷的方法,基于现有的 UDP 协议,实现类似 TC 片多路复用,传输可靠等功能,称为 QULC 协议;</li><li>QULC 实现类似 TCP 流量控制,传输可靠功能;集成 TLS 加密功能;实现多路复用功能;</li></ul><h4 id="同源策略:为什么-XMLHttpRequst-不能跨域请求"><a href="#同源策略:为什么-XMLHttpRequst-不能跨域请求" class="headerlink" title="同源策略:为什么 XMLHttpRequst 不能跨域请求"></a><a href="https://link.segmentfault.com/?enc=Ee52c13eHqJ7jOB3+6B/2A==.qrwmPTn6Dm8yFkgJ5t0ZtOpuplSh4nzBS8uF2guSgG1yEnZ2NghvEbCo5sE43kJFmzFOOwJwOhjdW/fHBmJIc1wkJ8FHz+qU5gl3O9OeigELBwZKBEBvloRxm83JuRrZg+rPpIP3xMGg2ls62JDmRZleXXwjvX8tYzwLnZIh5PESSEqLzVMKniYtR/b8pRQ9hwrCeoj/qzbimKHc8a8fxzGo9iLlmcLIt8pGZ0LRDq35MLfZ+NxIqgvWXVygGoTtDij0sBFPfasfqgP1mDLVVql3tyUJ0PPUP0LzHR8rHw3SozKHPXsGQvHMSTcqI/aH">同源策略:为什么 XMLHttpRequst 不能跨域请求</a></h4><ul><li>协议、域名和端口号相同的 URL 是同源的;</li><li>同源策略会隔离不同源的 DOM、页面数据和网络通信;</li><li>页面可以引用第三方资源,不过暴露出诸如 XSS 问题,引入内容安全策略 CSP 限制;</li><li>默认 XMLHttpRequest 和 Fetch 不能跨站请求资源,引入跨域资源共享(CORS)进行跨域访问控制;</li></ul><h4 id="跨站脚本攻击-XSS:为什么-cookie-中有-httpOnly-属性"><a href="#跨站脚本攻击-XSS:为什么-cookie-中有-httpOnly-属性" class="headerlink" title="跨站脚本攻击 XSS:为什么 cookie 中有 httpOnly 属性"></a><a href="https://link.segmentfault.com/?enc=nBTLgeAAUFTJT9VGOBNf1g==.VvwHuMAfEIK6ElbFAYCSiOyPzWzfwMFF/mzRhs4X3C9+jiHhbXyEs005P4sl7G38kKJO/2zfFdlF0XziNcSrgXU9Jj45b1okKP+/8H3ra4e7qBLBN5Aqi8ecWcWsAqlFtfvy+afUmRtel+7VmoyMNw34OPXnvc9/d3R/g73CdmMECfOjGB5YUEAkhnlJK7wks6fB+jmiyx5LVQiPbjiu7xxjQrqbj2QtC0bT72vRuOzjnlJbIc3ksEhHL/4W5Eiw2vndQ53NFRf7Vff9rBOHTAr914di6oFzLv+ksAQTC30=">跨站脚本攻击 XSS:为什么 cookie 中有 httpOnly 属性</a></h4><ul><li>XSS 跨站脚本,往 HTML 文件中注入恶意代码,对用户实施攻击;</li><li>XSS 攻击主要有存储型 XSS 攻击、反射型 XSS 攻击和 DOM 的 XSS 攻击;</li><li>阻止 XSS 攻击:服务器对脚本进行过滤或转码,利用 CSP 策略,使用 HttpOnly;</li></ul><h4 id="CSRF-攻击:陌生连接不要随便点"><a href="#CSRF-攻击:陌生连接不要随便点" class="headerlink" title="CSRF 攻击:陌生连接不要随便点"></a><a href="https://link.segmentfault.com/?enc=V7Y1vqnmkG09SEKLISGK1g==.NPoB42luudwYGMpSAxSw+CLE9i4w6z3pzq1HaDwtFuylo8vcs3bRSoIfoxVHSqgeHbfCWztl+ad60Lq6qKMy0lkFCxnwEzvlEJlMGJfEx5qruXXZiOy2BA9KlWN1P+o/+B32BLZ7W3kEpOCaKT95vXALc/RpmiBgrUO39oAXSn/Y6JvulhZK85kNbK0shmCXbsbmmxkVcIJhxTGQ0jfp50oFH2WN2KxdVlAN/5RrLzdRV+qLnXE2f15BmKDHSsxY">CSRF 攻击:陌生连接不要随便点</a></h4><ul><li>CSRF 跨站请求伪造,利用用户的登录状态,通过第三方站点攻击;</li><li>避免 CSRF 攻击:利用 SameSite(三种模式:Strict、Lax、None) 让浏览器禁止第三方站点发起请求携带关键 Cookie;验证请求的来源站点,请求头中的 Referer 和 Origin 属性;利用 CSRF Token;</li></ul><h4 id="沙盒:页面和系统之间的隔离墙"><a href="#沙盒:页面和系统之间的隔离墙" class="headerlink" title="沙盒:页面和系统之间的隔离墙"></a><a href="https://link.segmentfault.com/?enc=TY46V1bTsG/FwY2prj7KxA==.EbunNmiU46Os3BwnYRXAMEjUANEhcg7yN6puPCnUuyW+XMIEMTNV/QbtjD0LHpdwxYrReFZlqTA9RY6HuQ2BN+9FebKHPfvzE0rZY53jzBa+2HyM5ITIDh0g8dt83NyH1Gfvui5JtqwHjCn0L5iKq5Ml9wJTQ7vGE8Vl4arKNkcjvpCoq+a0fiQ4BeQt0NnmORv3DIPRJJM8BWIjEpYxLvf0q8JPS2KLRGsGGFlRDdH1Q79QVnBEyVvjTBWueKmVsWBg/54z9SM4hs35bO9OCQ==">沙盒:页面和系统之间的隔离墙</a></h4><ul><li>浏览器被划分为浏览器内核和渲染内核两个核心模块,其中浏览器内核石油网络进程、浏览器主进程和 GPU 进程组成的,渲染内核就是渲染进程;</li><li>浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程;</li><li>站点隔离(Site Isolation)将同一站点(包含相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行;</li><li>实现站点隔离,就可以将恶意的 iframe 隔离在恶意进程内部,使得它无法继续访问其他 iframe 进程的内容,因此无法攻击其他站点;</li></ul><h4 id="HTTPS:让数据传输更安全"><a href="#HTTPS:让数据传输更安全" class="headerlink" title="HTTPS:让数据传输更安全"></a><a href="https://link.segmentfault.com/?enc=5+izJPuX/JH0xYgwSnAvQw==.xGO225bg9VVXol2egy+TA4CHHpxRqWvNi7Z6Tj4YXKjqg+YyJaQRXi7/V4oBpg5UK+QZmkR+JPb8x9bewEhLg2O74ynXyj1CU+5PD1EhsDVlBhr0SiTRi9qeVq7pJxfnAUM9CsroNK6mYD6JDyDG9xDXXLi8BC5PTbkKbr523e1P8d4Z3clef5FWgSOA1FsGIILJNJzMsXPi07XprzELww==">HTTPS:让数据传输更安全</a></h4><ul><li>在 TCP 和 HTTP 之间插入一个安全层,所有经过安全层的数据都会被加密或者解密;</li><li>对称加密:浏览器发送加密套件列表和一个随机数 client-random,服务器会从加密套件中选取一个加密套件,然后生成一个随机数 service-random,返回给浏览器。这样浏览器和服务器都有相同 client-random 和 service-random,再用相同的方法将两者混合生成一个密钥 master secret,双方就可以进行数据加密传输了;</li><li>对称加密缺点:client-random 和 service-random 的过程都是明文,黑客可以拿到协商的加密套件和双方随机数,生成密钥,数据可以被破解;</li><li>非对称加密:浏览器发送加密套件列表给服务器,服务器选择一个加密套件,返回加密套件和公钥,浏览器用公钥加密数据,服务器用私钥解密;</li><li>非对称加密缺点:加密效率太低,不能保证服务器发送给浏览器的数据安全,黑客可以获取公钥;</li><li>对称加密结合非对称加密:浏览器发送对称加密套件列表、非对称加密列表和随机数 client-random 给服务器,服务器生成随机数 service-random,选择加密套件和公钥返回给浏览器,浏览器利用 client-random 和 service-random 计算出 pre-master,然后利用公钥给 pre-master 加密,向服务器发送加密后的数据,服务器用私钥解密出 pre-master 数据,结合 client-random 和 service-random 生成对称密钥,使用对称密钥传输加密数据;</li><li>引入数字证书是为了证明“我就是我”,防止 DNS 被劫持,伪造服务器;</li><li>证书的作用:一个是向浏览器证明服务器的身份,另一个是包含服务器公钥;</li><li>数字签名过程:CA 使用 Hash 函数技术明文信息,得出信息摘要,然后 CA 使用私钥对信息摘要进行加密,加密后的秘文就是数字签名;</li><li>验证数字签名:读取证书明文信息,使用相同 Hash 函数计算得到信息摘要 A,再利用 CA 的公钥解密得到 B,对比 A 和 B,如果一致,则确认证书合法;</li></ul><blockquote><p>作者 wuwhs<br><a href="https://segmentfault.com/a/1190000040896436">https://segmentfault.com/a/1190000040896436</a></p></blockquote>]]></content>
151
151
 
@@ -170,7 +170,7 @@
170
170
  <link href="https://xuehuayu.cn/article/624d1918.html"/>
171
171
  <id>https://xuehuayu.cn/article/624d1918.html</id>
172
172
  <published>2022-02-17T13:34:06.000Z</published>
173
- <updated>2023-07-20T03:42:31.820Z</updated>
173
+ <updated>2023-07-21T09:10:52.811Z</updated>
174
174
 
175
175
  <content type="html"><![CDATA[<p>在组件库系列文章中介绍了如何从 0 到 1 搭建一个 React 组件库架子,但为了一两个组件去搭建组件库未免显得大材小用。<br>这次以移动端一个常用组件 <code>Popup</code> 为例,以最方便快捷的形式发布一个完整的 npm 包。</p><span id="more"></span><blockquote><p><a href="https://github.com/worldzhao/react-easy-popup">✨ 仓库传送门</a><br><a href="https://worldzhao.github.io/react-easy-popup/">🚀 在线预览</a></p></blockquote><p>本文包含以下内容:</p><ol><li><p><code>Popup</code>组件的开发;</p></li><li><p>一些工具的使用</p><ul><li><a href="https://github.com/jaredpalmer/tsdx">tsdx</a> :项目初始化、开发以及打包大管家;</li><li><a href="https://github.com/sindresorhus/np">np</a>:一键发布 npm 包;</li><li><a href="https://www.npmjs.com/package/gh-pages">gh-pages</a>:部署示例 demo ;</li><li><a href="https://github.com/kefranabg/readme-md-generator">readme-md-generator</a>:生成一份规范的<code>README.md</code>文件。</li></ul></li></ol><p>本文不会和组件库那篇文章一般死扣打包细节,因为单个组件和组件库的打包有本质上的区别:</p><ul><li>组件库需要提供按需引入的能力,所以对组件仅仅是进行了语法上的编译(以及比较绕的样式处理),故选择了 gulp 管理打包流程;</li><li>单组件一般不需要提供按需引入的能力,只需要打包出一个 js bundle 和 css bundle 即可,webpack 与 rollup 就更适用于此类场景。</li></ul><p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-1.gif" alt="react-popup-1"></p><h2 id="项目初始化"><a href="#项目初始化" class="headerlink" title="项目初始化"></a>项目初始化</h2><p><a href="https://github.com/jaredpalmer/tsdx">tsdx</a> 内置三种项目模板:</p><ol><li>basic &#x3D;&gt; 工具包模板</li><li>react &#x3D;&gt; React 组件模板,使用 parcel 用作 example 调试</li><li>react-with-storybook &#x3D;&gt; 同上,使用 storybook 编写文档以及 example 调试</li></ol><p>模板还内置了<code>start</code>、<code>build</code>、<code>test</code>以及<code>lint</code>等 npm scripts,的确是零配置开箱即用(大误)。</p><p>为了方便讲解,此处选择<code>react</code>模板。</p><p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-2.webp" alt="react-popup-2"></p><p>执行<code>npx tsdx create react-easy-popup</code>,选择<code>react</code>完成项目创建后进入项目目录。</p><p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-3.webp" alt="react-popup-3"></p><h3 id="配置-tsdx"><a href="#配置-tsdx" class="headerlink" title="配置 tsdx"></a>配置 tsdx</h3><p>由于<code>tsdx</code>没有提供样式文件打包支持,使用<code>css in js</code>方案会带来额外的依赖以及运行时消耗,所以需要简单配置一下<code>tsdx</code>以支持 less 样式。</p><p>参照<a href="https://github.com/jaredpalmer/tsdx#customization">customization-tsdx</a>这一小节进行配置。</p><p>安装相关依赖:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add rollup-plugin-postcss autoprefixer cssnano less --dev</span><br></pre></td></tr></table></figure><p>新建 <code>tsdx.config.js</code>,写入以下内容:</p><p><strong>tsdx.config.js</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> postcss = <span class="built_in">require</span>(<span class="string">&#x27;rollup-plugin-postcss&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> autoprefixer = <span class="built_in">require</span>(<span class="string">&#x27;autoprefixer&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> cssnano = <span class="built_in">require</span>(<span class="string">&#x27;cssnano&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="title function_">rollup</span>(<span class="params">config, options</span>) &#123;</span><br><span class="line"> config.<span class="property">plugins</span>.<span class="title function_">push</span>(</span><br><span class="line"> <span class="title function_">postcss</span>(&#123;</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="title function_">autoprefixer</span>(),</span><br><span class="line"> <span class="title function_">cssnano</span>(&#123;</span><br><span class="line"> <span class="attr">preset</span>: <span class="string">&#x27;default&#x27;</span>,</span><br><span class="line"> &#125;),</span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">inject</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">extract</span>: <span class="string">&#x27;react-easy-popup.min.css&#x27;</span>,</span><br><span class="line"> &#125;)</span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> config;</span><br><span class="line"> &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>在 <code>package.json</code> 中配置<code>browserslist</code>字段。</p><p><strong>package.json</strong></p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// ...</span><br><span class="line"><span class="addition">+ &quot;browserslist&quot;: [</span></span><br><span class="line"><span class="addition">+ &quot;last 2 versions&quot;,</span></span><br><span class="line"><span class="addition">+ &quot;Android &gt;= 4.4&quot;,</span></span><br><span class="line"><span class="addition">+ &quot;iOS &gt;= 9&quot;</span></span><br><span class="line"><span class="addition">+ ],</span></span><br><span class="line">// ...</span><br></pre></td></tr></table></figure><p>清空<code>src</code>目录,新建<code>index.tsx</code>、<code>index.less</code>。</p><p><strong>src&#x2F;index.tsx</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;./index.less&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Popup</span> = (<span class="params"></span>) =&gt; (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;react-easy-popup&quot;</span>&gt;</span>hello,react-easy-popup<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Popup</span>;</span><br></pre></td></tr></table></figure><p><strong>src&#x2F;index.less</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">.react-easy-popup &#123;</span><br><span class="line"> display: flex;</span><br><span class="line"> color: skyblue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>example&#x2F;index.tsx</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&#x27;react-app-polyfill/ie11&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&#x27;react-dom&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Popup</span> <span class="keyword">from</span> <span class="string">&#x27;../.&#x27;</span>; <span class="comment">// 此处存在parcel alias 见下文</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;../dist/react-easy-popup.min.css&#x27;</span>; <span class="comment">// 此处不存在parcel alias 写好相对路径</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;<span class="name">Popup</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"> );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">ReactDOM</span>.<span class="title function_">render</span>(<span class="language-xml"><span class="tag">&lt;<span class="name">App</span> /&gt;</span></span>, <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;root&#x27;</span>));</span><br></pre></td></tr></table></figure><p>进入项目根目录,执行以下命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn start</span><br></pre></td></tr></table></figure><p>现在 <code>src</code> 目录下的内容的变更会被实时监听,在根目录下生成的<code>dist</code>文件夹包含打包后的内容。</p><p>开发时调试的文件夹为<code>example</code>,另起一个终端。执行以下命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd example</span><br><span class="line">yarn # 安装依赖</span><br><span class="line">yarn start # 启动example</span><br></pre></td></tr></table></figure><p>在<code>localhost:1234</code>可以发现项目启动啦,样式生效且有浏览器前缀。</p><p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-4.webp" alt="react-popup-4"></p><blockquote><p>若 example 启动后网页报错,删除 example 下的.cache 以及 dist 目录重新 start</p></blockquote><p>需要注意的是 <code>example</code> 的入口文件<code>index.tsx</code>引入的是我们打包后的文件,即<code>dist/index.js</code>。</p><p>但是引入路径却为<code>&#39;../.&#39;</code>,这是因为 <code>tsdx</code> 使用了 <code>parcel</code> 的 <a href="https://github.com/palmerhq/tsdx/pull/88/files">aliasing</a>。</p><p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-5.webp" alt="react-popup-5"></p><p>同时,观察根目录下的<code>dist</code>文件夹:</p><p><strong>dist</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">├── index.d.ts # 组件声明文件</span><br><span class="line">├── index.js # 组件入口</span><br><span class="line">├── react-easy-popup.cjs.development.js # 开发时引入的组件代码 Commonjs规范</span><br><span class="line">├── react-easy-popup.cjs.development.js.map # soucemap</span><br><span class="line">├── react-easy-popup.cjs.production.min.js # 压缩后的组件代码</span><br><span class="line">├── react-easy-popup.cjs.production.min.js.map # sourcemap</span><br><span class="line">├── react-easy-popup.esm.js # ES Module规范的组件组件代码</span><br><span class="line">├── react-easy-popup.esm.js.map # sourcemap</span><br><span class="line">└── react-easy-popup.min.css # 样式文件</span><br></pre></td></tr></table></figure><p>也可以很轻易地在<code>package.json</code>中找到<code>main</code>、<code>module</code>以及<code>typings</code>相关配置。</p><blockquote><p>基于 rollup 手动搭一个组件模板并不困难,但是社区已经提供了方便的轮子,就不要重复造轮子啦。既要有造轮子的能力,也要有不造轮子的觉悟。似乎我们正在造轮子?</p></blockquote><h2 id="实现-Portal"><a href="#实现-Portal" class="headerlink" title="实现 Portal"></a>实现 Portal</h2><p><code>Popup</code>在移动端场景下极其常见,其内部基于<code>Portal</code>实现,自身又可以作为<code>Toast</code>和<code>Modal</code>等组件的下层组件。</p><p>要实现<code>Popup</code>,就要先基于<a href="https://zh-hans.reactjs.org/docs/portals.html">ReactDOM.createPortal</a>实现一个<code>Portal</code>。</p><p>此处结合官方文档做一个简单总结。</p><ol><li>什么是传送门?<code>Portal</code> 是一种将子节点渲染到存在于父组件以外的 <code>DOM</code> 节点的优秀的方案。</li><li>为什么需要传送门?父组件有 <code>overflow: hidden</code> 或 <code>z-index</code> 样式,我们又需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。</li></ol><p>同时还有很重要的一点:<code>portal</code>与普通的 <code>React</code> 子节点行为一致,仍存在于<code>React</code>树,所以<code>Context</code>依旧可以触及。有一些弹层组件会提供<code>xxx.show()</code>的 API 形式进行弹出,这种调用形式较为方便,虽然底层也是基于<code>Portal</code>,但是内部重新执行了<code>ReactDOM.render</code>,脱离了当前主应用的<code>React</code>树,自然也无法获取到<code>Context</code>。</p><blockquote><p>推荐阅读:<a href="https://zhuanlan.zhihu.com/p/29880992">传送门:React Portal-程墨 Morgan</a></p></blockquote><p>清空 src 目录,新建以下文件:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">├── index.less # 样式文件</span><br><span class="line">├── index.ts # 入口文件</span><br><span class="line">├── popup.tsx # popup 组件</span><br><span class="line">├── portal.tsx # portal 组件</span><br><span class="line">└── type.ts # 类型定义文件</span><br></pre></td></tr></table></figure><p>在编写代码之前,需要确定好<code>Portal</code>组件的 API。</p><p>与<code>ReactDOM.createPortal</code>方法接受的参数基本一致:指定的挂载节点以及内容。唯一的区别是:<code>Portal</code> 在未传入指定的挂载节点时,会创建一个节点以供使用。</p><p>属性说明类型默认值<br>node可选,自定义容器节点HTMLElement-<br>children需要传送的内容ReactNode-<br>在<code>type.ts</code>中写入<code>Portal</code>的<code>Props</code>类型定义。</p><p><strong>src&#x2F;type.ts</strong></p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">PortalProps</span> = <span class="title class_">React</span>.<span class="property">PropsWithChildren</span>&lt;&#123;</span><br><span class="line"> node?: <span class="title class_">HTMLElement</span>;</span><br><span class="line">&#125;&gt;;</span><br></pre></td></tr></table></figure><p>现在开始编写代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&#x27;react-dom&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PortalProps</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./type&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Portal</span> = (<span class="params">&#123; node, children &#125;: PortalProps</span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">ReactDOM</span>.<span class="title function_">createPortal</span>(children, node);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Portal</span>;</span><br></pre></td></tr></table></figure><blockquote><p>注意:此处没有使用 React.FC 去进行声明 <a href="https://github.com/typescript-cheatsheets/react-typescript-cheatsheet">react-typescript-cheatsheet</a>:Section 2: Getting Started &#x3D;&gt; Function Components &#x3D;&gt; What about <code>React.FC</code>&#x2F;<code>React.FunctionComponent</code>?</p></blockquote><p>代码实现比较简单,就是调用了一下<code>ReactDOM.createPortal</code>,没有考虑到使用者未传入<code>node</code>的情况:需要内部创建,组件销毁时销毁该<code>node</code>。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&quot;react&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&quot;react-dom&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PortalProps</span> &#125; <span class="keyword">from</span> <span class="string">&quot;./type&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 判断是否为浏览器环境</span></span><br><span class="line"><span class="keyword">const</span> canUseDOM = !!(</span><br><span class="line"> <span class="keyword">typeof</span> <span class="variable language_">window</span> !== <span class="string">&quot;undefined&quot;</span> &amp;&amp;</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">document</span> &amp;&amp;</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">document</span>.<span class="property">createElement</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Portal</span> = (<span class="params">&#123; node, children &#125;: PortalProps</span>) =&gt; &#123;</span><br><span class="line"> <span class="comment">// 使用ref记录内部创建的节点 初始值为null</span></span><br><span class="line"> <span class="keyword">const</span> defaultNodeRef = <span class="title class_">React</span>.<span class="property">useRef</span>&lt;<span class="title class_">HTMLElement</span> | <span class="literal">null</span>&gt;(<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 组件卸载时 移除该节点</span></span><br><span class="line"> <span class="title class_">React</span>.<span class="title function_">useEffect</span>(</span><br><span class="line"> <span class="function">() =&gt;</span> <span class="function">() =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (defaultNodeRef.<span class="property">current</span>) &#123;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">removeChild</span>(defaultNodeRef.<span class="property">current</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;,</span><br><span class="line"> []</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果非浏览器环境 直接返回 null 服务端渲染需要</span></span><br><span class="line"> <span class="keyword">if</span> (!canUseDOM) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 若用户未传入节点,Portal也未创建节点,则创建节点并添加至body</span></span><br><span class="line"> <span class="keyword">if</span> (!node &amp;&amp; !defaultNodeRef.<span class="property">current</span>) &#123;</span><br><span class="line"> <span class="keyword">const</span> defaultNode = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;div&quot;</span>);</span><br><span class="line"> defaultNode.<span class="property">className</span> = <span class="string">&quot;react-easy-popup__portal&quot;</span>;</span><br><span class="line"> defaultNodeRef.<span class="property">current</span> = defaultNode;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(defaultNode);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">ReactDOM</span>.<span class="title function_">createPortal</span>(children, (node || defaultNodeRef.<span class="property">current</span>)!); <span class="comment">// 这里需要进行断言</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Portal</span>;</span><br></pre></td></tr></table></figure><p>同时为了让非 ts 用户能够享受到良好的运行时错误提示,需要安装<code>prop-types</code>。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add prop-types</span><br></pre></td></tr></table></figure><p><strong>src&#x2F;portal.tsx</strong></p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// ...</span><br><span class="line"></span><br><span class="line"><span class="addition">+ Portal.propTypes = &#123;</span></span><br><span class="line"><span class="addition">+ node: canUseDOM ? PropTypes.instanceOf(HTMLElement) : PropTypes.any,</span></span><br><span class="line"><span class="addition">+ children: PropTypes.node,</span></span><br><span class="line"><span class="addition">+ &#125;;</span></span><br><span class="line"></span><br><span class="line">export default Portal;</span><br></pre></td></tr></table></figure><p>这样就完成了 <code>Portal</code> 组件的编写,在入口文件进行导出。</p><p><strong>src&#x2F;index.ts</strong></p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> &#123; <span class="keyword">default</span> <span class="keyword">as</span> <span class="title class_">Portal</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./portal&#x27;</span>;</span><br></pre></td></tr></table></figure><p><code>example/index.ts</code>中引入<code>Portal</code>,进行测试。</p><p><strong>example&#x2F;index.tsx</strong></p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">import &quot;react-app-polyfill/ie11&quot;;</span><br><span class="line">import * as React from &quot;react&quot;;</span><br><span class="line">import * as ReactDOM from &quot;react-dom&quot;;</span><br><span class="line"><span class="deletion">- import Popup from &quot;../.&quot;; // 此处存在parcel alias 见下文</span></span><br><span class="line"><span class="deletion">- import &quot;../dist/react-easy-popup.min.css&quot;; // 此处不存在</span></span><br><span class="line"><span class="addition">+ import &#123; Portal &#125; from &#x27;../.&#x27;;</span></span><br><span class="line"></span><br><span class="line">// 创建自定义node节点</span><br><span class="line"><span class="addition">+ const node = document.createElement(&#x27;div&#x27;);</span></span><br><span class="line"><span class="addition">+ node.className = &#x27;react-easy-popup__test-node&#x27;;</span></span><br><span class="line"><span class="addition">+ document.body.appendChild(node);</span></span><br><span class="line"></span><br><span class="line">const App = () =&gt; &#123;</span><br><span class="line"> return (</span><br><span class="line"> &lt;div&gt;</span><br><span class="line"><span class="deletion">- &lt;Popup /&gt;</span></span><br><span class="line"><span class="addition">+ &lt;Portal&gt;123&lt;/Portal&gt;</span></span><br><span class="line"><span class="addition">+ &lt;Portal node=&#123;node&#125;&gt;456&lt;/Portal&gt;</span></span><br><span class="line"> &lt;/div&gt;</span><br><span class="line"> );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(&lt;App /&gt;, document.getElementById(&quot;root&quot;));</span><br></pre></td></tr></table></figure><p>在网页中看到预期的<code>DOM</code>结构。</p><p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-6.webp" alt="react-popup-6"></p><h2 id="实现-Popup"><a href="#实现-Popup" class="headerlink" title="实现 Popup"></a>实现 Popup</h2><h3 id="API-梳理"><a href="#API-梳理" class="headerlink" title="API 梳理"></a>API 梳理</h3><p>老规矩,先规划 API,写好类型定义,再动手写代码。</p><p>我写这个组件的时候参考了<a href="https://didi.github.io/cube-ui/#/zh-CN/docs/popup">Popup-cube-ui</a>。</p><p>最终确定 API 如下:</p><p>属性说明类型默认值<br>visible可选,控制 popup 显隐booleanfalse<br>position可选,内容定位‘center’ &#x2F; ‘top’ &#x2F; ‘bottom’ &#x2F; ‘left’ &#x2F; ‘right’‘center’<br>mask可选,控制蒙层显隐booleantrue<br>maskClosable可选,点击蒙层是否可以关闭booleanfalse<br>onClose可选,关闭函数,若 maskClosable 为 true,点击蒙层调用该函数function()&#x3D;&gt;{}<br>node可选,元素挂载节点HTMLElement-<br>destroyOnClose可选,关闭是否卸载内部元素booleanfalse<br>wrapClassName可选,自定义 Popup 外层容器类名string‘’<br><strong>src&#x2F;type.ts</strong></p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">Position</span> = <span class="string">&#x27;top&#x27;</span> | <span class="string">&#x27;right&#x27;</span> | <span class="string">&#x27;bottom&#x27;</span> | <span class="string">&#x27;left&#x27;</span> | <span class="string">&#x27;center&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">PopupPropsWithoutChildren</span> = &#123;</span><br><span class="line"> node?: <span class="title class_">HTMLElement</span>;</span><br><span class="line">&#125; &amp; <span class="keyword">typeof</span> defaultProps;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">PopupProps</span> = <span class="title class_">React</span>.<span class="property">PropsWithChildren</span>&lt;<span class="title class_">PopupPropsWithoutChildren</span>&gt;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 默认属性写在这儿很难受 实在是typescript 对react组件默认属性的声明就是得这么拧巴</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> defaultProps = &#123;</span><br><span class="line"> <span class="attr">visible</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">position</span>: <span class="string">&#x27;center&#x27;</span> <span class="keyword">as</span> <span class="title class_">Position</span>,</span><br><span class="line"> <span class="attr">mask</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">maskClosable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">onClose</span>: <span class="function">() =&gt;</span> &#123;&#125;,</span><br><span class="line"> <span class="attr">destroyOnClose</span>: <span class="literal">false</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>编写 <code>Popup</code> 的基本结构。</p><p><strong>src&#x2F;popup.tsx</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">PropTypes</span> <span class="keyword">from</span> <span class="string">&#x27;prop-types&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PopupProps</span>, defaultProps &#125; <span class="keyword">from</span> <span class="string">&#x27;./type&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;./index.less&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Popup</span> = (<span class="params">props: PopupProps</span>) =&gt; &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(props);</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;react-easy-popup&quot;</span>&gt;</span>hello,react-easy-popup<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Popup</span>.<span class="property">propTypes</span> = &#123;</span><br><span class="line"> <span class="attr">visible</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">position</span>: <span class="title class_">PropTypes</span>.<span class="title function_">oneOf</span>([<span class="string">&#x27;top&#x27;</span>, <span class="string">&#x27;right&#x27;</span>, <span class="string">&#x27;bottom&#x27;</span>, <span class="string">&#x27;left&#x27;</span>, <span class="string">&#x27;center&#x27;</span>]),</span><br><span class="line"> <span class="attr">mask</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">maskClosable</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">onClose</span>: <span class="title class_">PropTypes</span>.<span class="property">func</span>,</span><br><span class="line"> <span class="attr">stopScrollUnderMask</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">destroyOnClose</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Popup</span>.<span class="property">defaultProps</span> = defaultProps;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Popup</span>;</span><br></pre></td></tr></table></figure><p>在入口文件进行导出。</p><p><strong>src&#x2F;index.ts</strong></p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="addition">+ export &#123; default as Popup &#125; from &#x27;./popup&#x27;;</span></span><br></pre></td></tr></table></figure><h3 id="前置-CSS-知识"><a href="#前置-CSS-知识" class="headerlink" title="前置 CSS 知识"></a>前置 CSS 知识</h3><p>在正式开发逻辑之前,先明确一点:</p><p>蒙层 Mask 以及内容 Content 入场以及出场均有动画效果。具体表现为:蒙层为 Fade 动画,内容则取决于当前 position,比如内容在中间(position &#x3D;&#x3D;&#x3D; ‘center’),则其动画效果为 Fade,如果在左边(position &#x3D;&#x3D;&#x3D; ‘left’),则其动画效果为 SlideRight,其他 position 以此类推。</p><p>再回顾张鑫旭大大的一篇文章:<a href="https://www.zhangxinxu.com/wordpress/2013/05/transition-visibility-show-hide/">小 tip: transition 与 visibility</a></p><p>划重点:</p><ol><li><code>opacity</code>的值在 <code>0</code> 与 <code>1</code> 之间相互过渡(<code>transition</code>)可以实现 Fade 动画。然而元素即使透明度变成 0,肉眼看不见,在页面上却依旧点击,还是可以覆盖其他元素的,我们希望元素淡出动画结束后,元素可以自动隐藏;</li><li>元素隐藏很容易想到<code>display:none</code>。而<code>display:none</code> 无法应用 <code>transition</code> 效果,甚至是破坏作用;</li><li><code>visibility:hidden</code> 可以看成 <code>visibility:0</code>;<code>visibility:visible</code> 可以看成 <code>visibility:1</code>。实际上,只要 <code>visibility</code> 的值大于 <code>0</code> 就是显示的。</li></ol><p>总结一下:我们想用<code>opacity</code>实现淡入淡出的 Fade 动画,但是希望元素淡出后,能够隐藏,而不仅仅是透明度为 <code>0</code>,覆盖在其他元素上。所以需要配置 <code>visibility</code>属性,淡出动画结束时,<code>visibility</code>值也由<code>visible</code>变为了<code>hidden</code>,元素成功隐藏。</p><blockquote><p>如果蒙层淡出动画结束后仅仅是透明度变为 0,却未隐藏,那么蒙层在视觉上虽然消失了,实际还是覆盖在页面上,就无法触发页面上的事件。</p></blockquote><h3 id="预设动画样式"><a href="#预设动画样式" class="headerlink" title="预设动画样式"></a>预设动画样式</h3><p>借助<a href="https://github.com/reactjs/react-transition-group">react-transition-group</a>完成动画效果,需要内置一些动画样式。</p><p>新建<code>animation.less</code>,写入以下动画样式。</p><p>展开查看代码</p><h3 id="完成基本逻辑"><a href="#完成基本逻辑" class="headerlink" title="完成基本逻辑"></a>完成基本逻辑</h3><p>安装相关依赖。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yarn add react-transition-group classnames</span><br><span class="line"></span><br><span class="line">yarn add @types/classnames @types/react-transition-group --dev</span><br></pre></td></tr></table></figure><ul><li>node: 透传给<code>Portal</code>即可;</li><li>visible: 将该属性赋值给蒙层以及内容外层<code>CSSTransition</code>组件的<code>in</code>属性,控制蒙层以及内容的过渡显隐;</li><li>destroyOnClose: 将该属性赋值给内容外层<code>CSSTransition</code>组件的<code>unmountOnExit</code>属性,决定隐藏时是否卸载内容节点;</li><li>wrapClassName: 拼接在外层容器节点的 <code>className</code> ;</li><li>position: 1)用于获取内容节点的对应动画名称;2)决定容器节点以及内容节点类名,配合样式决定内容节点位置;</li><li>mask: 决定蒙层节点的 <code>className</code>,从而控制蒙层有无;</li><li>maskClose: 决定点击蒙层是否触发 onClose 函数。</li></ul><p>用过 <code>antd</code> 的同学都知道,<code>antd</code>的<code>modal</code>在首次<code>visible === true</code>之前,内容节点是不会被挂载的,只有首次 <code>visible === true</code>,内容节点才挂载,而后都是样式上隐藏,而不会去卸载内容节点,除非手动设置 <code>destroyOnClose</code> 属性,我们也顺带实现这个特点。</p><blockquote><p>代码逻辑比较简单,在拼接类名时注意配合样式文件一起阅读,重要的点都有注释标出。</p></blockquote><p>展开查看逻辑代码<br>展开查看样式代码<br>组件编写完毕,接下来在<code>example/index.ts</code>中编写相关示例测试功能即可。</p><p><a href="https://github.com/worldzhao/react-easy-popup/blob/master/example/index.tsx">example&#x2F;index.ts</a></p><h2 id="部署-github-pages"><a href="#部署-github-pages" class="headerlink" title="部署 github pages"></a>部署 github pages</h2><p>相信大多数人使用一个 npm 包会先看示例再看文档。</p><p>接下来将 <code>example</code> 中的示例项目打包,并部署到 github pages 上。</p><p>安装<code>gh-pages</code>。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add gh-pages --dev</span><br></pre></td></tr></table></figure><p>package.json 新增脚本。</p><p><strong>package.json</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"> <span class="attr">&quot;scripts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="attr">&quot;predeploy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;npm run build &amp;&amp; cd example &amp;&amp; npm run build&quot;</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">&quot;deploy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;gh-pages -d ./example/dist&quot;</span></span><br><span class="line"> <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>由于 gh-pages 默认部署在<code>https://username.github.io/repo</code>下,而非根路径。为了能够正确引用到静态资源,还需要修改打包的 <code>public-url</code>。</p><p>修改 example 的 package.json 中的打包命令:</p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;scripts&quot;:&#123;</span><br><span class="line"><span class="deletion">- &quot;build&quot;: &quot;parcel build index.html&quot;</span></span><br><span class="line"><span class="addition">+ &quot;build&quot;: &quot;parcel build index.html --public-url https://username.github.io/repo&quot;</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p><code>https://username.github.io/repo</code>记得换成你自己的哦。</p></blockquote><p>在根目录下执行 <code>yarn deploy</code>,等脚本执行完再去看看吧。</p><h2 id="编写-README-md"><a href="#编写-README-md" class="headerlink" title="编写 README.md"></a>编写 README.md</h2><p>一份规范的 README 会显得作者很专业,此处使用<code>readme-md-generator</code>生成基本框架,向里面填充内容即可。</p><p><a href="https://github.com/kefranabg/readme-md-generator">readme-md-generator</a>:📄 CLI that generates beautiful README.md files</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx readme-md-generator -y</span><br></pre></td></tr></table></figure><p><a href="https://github.com/worldzhao/react-easy-popup/blob/master/README.md">README.md</a></p><h2 id="使用-np-发包"><a href="#使用-np-发包" class="headerlink" title="使用 np 发包"></a>使用 np 发包</h2><p>在上一篇文章中,专门编写了一个脚本来处理以下六点内容:</p><ol><li>版本更新</li><li><del>生成 CHANGELOG</del></li><li>推送至 git 仓库</li><li>组件打包</li><li>发布至 npm</li><li>打 tag 并推送至 git</li></ol><p>这次就不生成 CHANGELOG 文件了,其他五点配合<code>np</code>,操作十分简单。</p><p><a href="https://github.com/sindresorhus/np">np</a>:A better <code>npm publish</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add np --dev</span><br></pre></td></tr></table></figure><p><strong>package.json</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"> <span class="attr">&quot;scripts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="attr">&quot;release&quot;</span><span class="punctuation">:</span> <span class="string">&quot;np --no-yarn --no-tests --no-cleanup&quot;</span></span><br><span class="line"> <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm login</span><br><span class="line">npm run release</span><br></pre></td></tr></table></figure><ul><li><code>--no-yarn</code>: 不使用 <code>yarn</code>。发包时出现 npm 与 yarn 之间的一些问题;</li><li><code>--no-tests</code>:测试用例暂时还未编写,先跳过;</li><li><code>--no-cleanup</code>:发包时不要重新安装 node_modules;</li><li>首次发布新包时可能会<a href="https://github.com/sindresorhus/np/issues/398">报错</a>,因为 np 进行了 npm 双因素认证,但依旧可以发布成功,等后续更新。</li></ul><p>更多配置请查看官方文档。</p><blockquote><p>本文转载自 <code>https://github.com/worldzhao/blog/issues/2</code></p></blockquote>]]></content>
176
176
 
@@ -191,7 +191,7 @@
191
191
  <link href="https://xuehuayu.cn/article/29abbd1c.html"/>
192
192
  <id>https://xuehuayu.cn/article/29abbd1c.html</id>
193
193
  <published>2022-02-17T10:15:08.000Z</published>
194
- <updated>2023-07-20T03:42:31.820Z</updated>
194
+ <updated>2023-07-21T09:10:52.811Z</updated>
195
195
 
196
196
  <content type="html"><![CDATA[<p>如果公司有开发国际化的业务,某些国家书写的方向是从右往左的,不同于我国古代的写法,他们只是布局方向从右往左,但是文字还是从左往右写的。</p><span id="more"></span><p><img src="https://s4.ax1x.com/2022/02/17/H4448P.png" alt="xuehuayu.cn/html dir 属性"><br>要对页面的内容进行反转,但是某些内容又不需要反转,是不是很头疼?</p><p>其实,这些头疼的问题是有成熟的解决方案的。</p><ol><li><p><strong>HTML dir 属性</strong><br> 为body元素加上dir&#x3D;”rtl”,浏览器就跨一自动反转了。</p></li><li><p><strong>rtlcss</strong><br> 使用rtlcss,他的实现思路就是配置rtl属性使用,将页面上的left相关属性都转为right属性,核心思想就是某些属性的全局转换。</p></li></ol><h4 id="HTML-dir-属性"><a href="#HTML-dir-属性" class="headerlink" title="HTML dir 属性"></a>HTML dir 属性</h4><p>文本方向为从右向左的段落:</p><p><bdo dir="rtl">文本方向从右到左!</bdo></p><h4 id="浏览器支持"><a href="#浏览器支持" class="headerlink" title="浏览器支持"></a>浏览器支持</h4><p>所有主流浏览器都支持 dir 属性</p><h4 id="定义和用法"><a href="#定义和用法" class="headerlink" title="定义和用法"></a>定义和用法</h4><p>dir 属性规定元素内容的文本方向。</p><h4 id="HTML-4-01-与-HTML5之间的差异"><a href="#HTML-4-01-与-HTML5之间的差异" class="headerlink" title="HTML 4.01 与 HTML5之间的差异"></a>HTML 4.01 与 HTML5之间的差异</h4><p>在 HTML5 中, dir 属性可用于任何的 HTML 元素 (它会验证任何HTML元素。但不一定是有用)。</p><p>在 HTML 4.01 中, dir 元素不能用于: <code>&lt;base&gt;, &lt;br&gt;, &lt;frame&gt;, &lt;frameset&gt;, &lt;hr&gt;, &lt;iframe&gt;, &lt;param&gt;, 和 &lt;script&gt;</code>。</p><h5 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h5><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">element</span> <span class="attr">dir</span>=<span class="string">&quot;ltr|rtl|auto&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="属性值"><a href="#属性值" class="headerlink" title="属性值"></a>属性值</h5><table><thead><tr><th align="left">值</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left">ltr</td><td align="left">默认。从左向右的文本方向。</td></tr><tr><td align="left">tl</td><td align="left">从右向左的文本方向。</td></tr><tr><td align="left">auto</td><td align="left">让浏览器根据内容来判断文本方向。仅在文本方向未知时推荐使用。</td></tr></tbody></table><blockquote><p>html dir 属性<br> <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/dir">https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes&#x2F;dir</a></p></blockquote><blockquote><p>rtlcss官网<br> <a href="https://rtlcss.com/">https://rtlcss.com/</a></p></blockquote>]]></content>
197
197
 
@@ -212,7 +212,7 @@
212
212
  <link href="https://xuehuayu.cn/article/c1867fbf.html"/>
213
213
  <id>https://xuehuayu.cn/article/c1867fbf.html</id>
214
214
  <published>2022-02-16T11:09:06.000Z</published>
215
- <updated>2023-07-20T03:42:31.820Z</updated>
215
+ <updated>2023-07-21T09:10:52.811Z</updated>
216
216
 
217
217
  <content type="html"><![CDATA[<p>你是否经常遇到github.com的各种404,git push的时候各种403,npm安装总是timeout,那么下面说的这款工具,你一定值得拥有~~~</p><span id="more"></span><p>本文已发布在公众号“入门前端”<br>据不完全统计,99%看到的人都关注了这个公众号,点击下面关注并设置星标,不迷路!<br><img src="https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/mp-mini.jpg" alt="入门前端"></p><p>dev-sidecar<br>开发者边车,命名取自service-mesh的service-sidecar,意为为开发者打辅助的边车工具<br>通过本地代理的方式将https请求代理到一些国内的加速通道上</p><p><img src="https://s4.ax1x.com/2022/02/16/HhmTDf.png" alt="xuehuayu.cn/dev-sidercar"></p><p>一、 特性<br>1、 dns优选(解决***污染问题)<br>根据网络状况智能解析最佳域名ip地址,获取最佳网络速度</p><p>解决一些网站和库无法访问或访问速度慢的问题</p><p>建议遇到打开比较慢的国外网站,可以优先尝试将该域名添加到dns设置中(注意:被***封杀的无效)</p><p>2、 请求拦截<br>拦截打不开的网站,代理到加速镜像站点上去。</p><p>可配置多个镜像站作为备份</p><p>具备测速机制,当访问失败或超时之后,自动切换到备用站点,使得目标服务高可用</p><p>3、 github加速<br>github 直连加速 (通过修改sni实现,感谢 fastGithub 提供的思路)</p><p>release、source、zip下载加速</p><p>clone 加速</p><p>头像加速</p><p>解决readme中图片引用无法加载的问题</p><p>gist.github.com 加速</p><p>解决git push 偶尔失败需要输入账号密码的问题(fatal: TaskCanceledException encountered &#x2F; fatal: HttpRequestException encountered)</p><p>raw&#x2F;blame加速</p><p>以上部分功能通过X.I.U的油猴脚本实现, 以下是仓库和脚本下载链接,大家可以去支持一下。</p><p><a href="https://github.com/XIU2/UserScript">https://github.com/XIU2/UserScript</a></p><p><a href="https://greasyfork.org/scripts/412245">https://greasyfork.org/scripts/412245</a></p><p>由于此脚本在ds中是打包在本地的,更新会不及时,你可以直接通过浏览器安装油猴插件使用此脚本,从而获得最新更新(ds本地的可以通过加速服务-&gt;基本设置-&gt;启用脚本进行关闭)。</p><p>4、 Stack Overflow 加速<br>将ajax.google.com代理到加速CDN上</p><p>recaptcha 图片验证码加速</p><p>5、 npm加速<br>支持开启npm代理</p><p>官方与淘宝npm registry一键切换,</p><p>某些npm install的时候,并且使用cnpm也无法安装时,可以尝试开启npm代理再试</p><p>安全警告:</p><p>请勿使用来源不明的服务地址,有隐私和账号泄露风险</p><p>本应用及服务端承诺不收集任何信息。介意者请使用安全模式。</p><blockquote><p>注意:由于electron无法监听windows的关机事件,开着ds情况下直接重启电脑,会导致无法上网,你可以手动启动ds即可恢复网络,你也可以将ds设置为开机自启。<br>关于此问题的更多讨论请前往:<a href="https://gitee.com/docmirror/dev-sidecar/issues/I49OUL">https://gitee.com/docmirror/dev-sidecar/issues/I49OUL</a> <a href="https://github.com/docmirror/dev-sidecar/issues/109">https://github.com/docmirror/dev-sidecar/issues/109</a></p></blockquote><p><a href="https://www.123pan.com/s/7x5A-d3l8">点击下载</a></p>]]></content>
218
218
 
@@ -233,7 +233,7 @@
233
233
  <link href="https://xuehuayu.cn/article/7a3ef8f.html"/>
234
234
  <id>https://xuehuayu.cn/article/7a3ef8f.html</id>
235
235
  <published>2022-02-15T09:41:29.000Z</published>
236
- <updated>2023-07-20T03:42:31.820Z</updated>
236
+ <updated>2023-07-21T09:10:52.811Z</updated>
237
237
 
238
238
  <content type="html"><![CDATA[<p>ECMAScript 规范每年都会更新一次,正式标准化 JavaScript 语言的 ECMAScript 的下一次年度更新将在 2022 年 6 月左右获得批准。自 2015 年以来,TC39 团队成员每年都会一起讨论可用的提案,并发布已接受的提案。今年是 ECMAScript 的第 13 版,其中包括许多实用的功能。所有在 2022 年 3 月之前达到阶段 4 的提案都将包含在全新的 ECMAScript 2022 标准中。</p><span id="more"></span><p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="ECMAScript 2022 新特性介绍"><img src="https://media.mybj123.com/wp-content/uploads/2022/01/1643433785-4ffce04d92a4d6c.webp" alt="ECMAScript 2022 新特性介绍"></p><p>对于一个提案,从提出到最后被纳入ES新特性,TC39的规范中分为五步:</p><ul><li>stage0(strawman),任何TC39的成员都可以提交。</li><li>stage1(proposal),进入此阶段就意味着这一提案被认为是正式的了,需要对此提案的场景与API进行详尽的描述。</li><li>stage2(draft),演进到这一阶段的提案如果能最终进入到标准,那么在之后的阶段都不会有太大的变化,因为理论上只接受增量修改。</li><li>state3(candidate),这一阶段的提案只有在遇到了重大问题才会修改,规范文档需要被全面的完成。</li><li>state4(finished),这一阶段的提案将会被纳入到ES每年发布的规范之中</li></ul><p>目前,一些提案还处于第三阶段,一些提案已经进入第四阶段。提案的功能将在达到第 4 阶段后被添加到新的ECMAScript标准中,这意味着它们已获得 TC-39 的批准,通过了测试,并且至少有两个实现。下面就来看看 ECMAScript 2022 预计会推出的新功能吧~</p><h2 id="一、Top-level-Await"><a href="#一、Top-level-Await" class="headerlink" title="一、Top-level Await"></a>一、Top-level Await</h2><p>在ES2017中引入了 <code>async</code> 函数和 <code>await</code> 关键字,以简化 <code>Promise</code> 的使用,但是 <code>await</code> 关键字只能在 <code>async</code> 函数内部使用。尝试在异步函数之外使用 <code>await</code> 就会报错:<code>SyntaxError - SyntaxError: await is only valid in async function</code>。</p><p>顶层 <code>await</code> 允许我们在 <code>async</code> 函数外面使用 <code>await</code> 关键字,目前提案正处于第 4 阶段,并且已经在三个主要的浏览器的 JavaScript 引擎中实现,模块系统会协调所有的异步 <code>promise</code>。</p><p>顶层 <code>await</code> 允许模块充当大型异步函数,通过顶层 <code>await</code>,这些ECMAScript模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。下面来看一个简单的例子。</p><p>由于 <code>await</code> 仅在 <code>async</code> 函数中可用,因此模块可以通过将代码包装在 <code>async</code> 函数中来在代码中包含 <code>await</code>:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.js</span></span><br><span class="line"><span class="keyword">import</span> fetch <span class="keyword">from</span> <span class="string">&quot;node-fetch&quot;</span>;</span><br><span class="line"><span class="keyword">let</span> users;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">fetchUsers</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> resp = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://jsonplaceholder.typicode.com/users&#x27;</span>);</span><br><span class="line"> users = resp.<span class="title function_">json</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">fetchUsers</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> &#123; users &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// usingAwait.js</span></span><br><span class="line"><span class="keyword">import</span> &#123;users&#125; <span class="keyword">from</span> <span class="string">&#x27;./a.js&#x27;</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;users: &#x27;</span>, users);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;usingAwait module&#x27;</span>);</span><br></pre></td></tr></table></figure><p>我们还可以立即调用顶层<code>async</code>函数(IIAFE):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> fetch <span class="keyword">from</span> <span class="string">&quot;node-fetch&quot;</span>;</span><br><span class="line">(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> resp = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://jsonplaceholder.typicode.com/users&#x27;</span>);</span><br><span class="line"> users = resp.<span class="title function_">json</span>();</span><br><span class="line">&#125;)();</span><br><span class="line"><span class="keyword">export</span> &#123; users &#125;;</span><br></pre></td></tr></table></figure><p>这样会有一个缺点,直接导入的 users 是 undefined,需要在异步执行完成之后才能访问它:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// usingAwait.js</span></span><br><span class="line"><span class="keyword">import</span> &#123;users&#125; <span class="keyword">from</span> <span class="string">&#x27;./a.js&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;users:&#x27;</span>, users); <span class="comment">// undefined</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;users:&#x27;</span>, users);</span><br><span class="line">&#125;, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;usingAwait module&#x27;</span>);</span><br></pre></td></tr></table></figure><p>当然,这种方法并不安全,因为如果异步函数执行花费的时间超过100毫秒, 它就不会起作用了,users 仍然是 undefined。</p><p>另一个方法是导出一个 promise,让导入模块知道数据已经准备好了:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//a.js</span></span><br><span class="line"><span class="keyword">import</span> fetch <span class="keyword">from</span> <span class="string">&quot;node-fetch&quot;</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> (<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line"> <span class="keyword">const</span> resp = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://jsonplaceholder.typicode.com/users&#x27;</span>);</span><br><span class="line"> users = resp.<span class="title function_">json</span>();</span><br><span class="line">&#125;)();</span><br><span class="line"><span class="keyword">export</span> &#123; users &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//usingAwait.js</span></span><br><span class="line"><span class="keyword">import</span> promise, &#123;users&#125; <span class="keyword">from</span> <span class="string">&#x27;./a.js&#x27;</span>;</span><br><span class="line">promise.<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123; </span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;usingAwait module&#x27;</span>);</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;users:&#x27;</span>, users), <span class="number">100</span>); </span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>虽然这种方法似乎是给出了预期的结果,但是有一定的局限性:导入模块必须了解这种模式才能正确使用它。</p><p>而顶层<code>await</code>就可以消除这些缺点:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.js</span></span><br><span class="line"><span class="keyword">const</span> resp = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://jsonplaceholder.typicode.com/users&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> users = resp.<span class="title function_">json</span>();</span><br><span class="line"><span class="keyword">export</span> &#123; users &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// usingAwait.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; users &#125; <span class="keyword">from</span> <span class="string">&#x27;./a.mjs&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(users);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;usingAwait module&#x27;</span>);</span><br></pre></td></tr></table></figure><p>顶级 await 在以下场景中将非常有用:</p><p><strong>(1)动态加载模块</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> strings = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">`/i18n/<span class="subst">$&#123;navigator.language&#125;</span>`</span>);</span><br></pre></td></tr></table></figure><p><strong>(2)资源初始化</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> connection = <span class="keyword">await</span> <span class="title function_">dbConnector</span>();</span><br></pre></td></tr></table></figure><p><strong>(3)依赖回退</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> translations;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line"> translations = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">&#x27;https://app.fr.json&#x27;</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> &#123;</span><br><span class="line"> translations = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">&#x27;https://fallback.en.json&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>目前,在这些地方已经支持 Top-level await:</p><ul><li>V8 v8.9</li><li>Webpack 5.0.0</li><li>Babel</li><li>Chrome DevTools REPL</li><li>Node REPL</li></ul><h2 id="二、类的实例成员"><a href="#二、类的实例成员" class="headerlink" title="二、类的实例成员"></a>二、类的实例成员</h2><h3 id="1-公共实例字段"><a href="#1-公共实例字段" class="headerlink" title="1. 公共实例字段"></a>1. 公共实例字段</h3><p>公共类字段允许我们使用赋值运算符 (&#x3D;) 将实例属性添加到类定义中。下面来一个计数器的例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; <span class="title class_">Component</span> &#125; <span class="keyword">from</span> <span class="string">&quot;react&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Incrementor</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Component</span> &#123;</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="variable language_">super</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = &#123;</span><br><span class="line"> <span class="attr">count</span>: <span class="number">0</span>,</span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">increment</span> = <span class="variable language_">this</span>.<span class="property">increment</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="title function_">increment</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">setState</span>(&#123; <span class="attr">count</span>: <span class="variable language_">this</span>.<span class="property">state</span>.<span class="property">count</span> + <span class="number">1</span> &#125;);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.increment&#125;</span>&gt;</span>Increment: &#123;this.state.count&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"> );</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这个例子中,在构造函数中定义了实例字段和绑定方法,通过新的类语法,我们可以使代码更加直观。新的公共类字段语法允许我们直接将实例属性作为属性添加到类上,而无需使用构造函数方法。这样就简化了类的定义,使代码更加简洁、可读:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&quot;react&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Incrementor</span> <span class="keyword">extends</span> <span class="title class_ inherited__">React.Component</span> &#123;</span><br><span class="line"> state = &#123; <span class="attr">count</span>: <span class="number">0</span> &#125;;</span><br><span class="line"></span><br><span class="line"> increment = <span class="function">() =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">setState</span>(&#123; <span class="attr">count</span>: <span class="variable language_">this</span>.<span class="property">state</span>.<span class="property">count</span> + <span class="number">1</span> &#125;);</span><br><span class="line"></span><br><span class="line"> render = <span class="function">() =&gt;</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.increment&#125;</span>&gt;</span>Increment: &#123;this.state.count&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"> );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>有些小伙伴可能就疑问了,这个功能很早就可以使用了呀。但是它现在还不是标准的 ECMAScript,默认是不开启的,如果使用 create-react-app 创建 React 项目,那么它默认是启用的,否则我们必须使用正确的babel插件才能正常使用(@babel&#x2F;preset-env)。</p><p>下面来看看关于公共实例字段的注意事项:</p><p>(1)公共实例字段存在于每个创建的类实例上。它们要么是在<code>Object.defineProperty()</code>中添加,要么是在基类中的构造时添加(构造函数主体执行之前执行),要么在子类的<code>super()</code>返回之后添加:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Incrementor</span> &#123;</span><br><span class="line"> count = <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> instance = <span class="keyword">new</span> <span class="title class_">Incrementor</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(instance.<span class="property">count</span>); <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><p>(2)未初始化的字段会自动设置为 undefined:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Incrementor</span> &#123;</span><br><span class="line"> count</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> instance = <span class="keyword">new</span> <span class="title class_">Incrementor</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">assert</span>(instance.<span class="title function_">hasOwnProperty</span>(<span class="string">&#x27;count&#x27;</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(instance.<span class="property">count</span>); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p>(3)可以进行字段的计算:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="variable constant_">PREFIX</span> = <span class="string">&#x27;main&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Incrementor</span> &#123;</span><br><span class="line"> [<span class="string">`<span class="subst">$&#123;PREFIX&#125;</span>Count`</span>] = <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> instance = <span class="keyword">new</span> <span class="title class_">Incrementor</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(instance.<span class="property">mainCount</span>); <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><h3 id="2-私有实例字段、方法和访问器"><a href="#2-私有实例字段、方法和访问器" class="headerlink" title="2. 私有实例字段、方法和访问器"></a>2. 私有实例字段、方法和访问器</h3><p>默认情况下,ES6 中所有属性都是公共的,可以在类外检查或修改。下面来看一个例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">TimeTracker</span> &#123;</span><br><span class="line"> name = <span class="string">&#x27;zhangsan&#x27;</span>;</span><br><span class="line"> project = <span class="string">&#x27;blog&#x27;</span>;</span><br><span class="line"> hours = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">set</span> <span class="title function_">addHours</span>(<span class="params">hour</span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">hours</span> += hour;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">get</span> <span class="title function_">timeSheet</span>() &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;<span class="variable language_">this</span>.name&#125;</span> works <span class="subst">$&#123;<span class="variable language_">this</span>.hours || <span class="string">&#x27;nothing&#x27;</span>&#125;</span> hours on <span class="subst">$&#123;<span class="variable language_">this</span>.project&#125;</span>`</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="title class_">TimeTracker</span>();</span><br><span class="line">person.<span class="property">addHours</span> = <span class="number">2</span>; <span class="comment">// 标准 setter</span></span><br><span class="line">person.<span class="property">hours</span> = <span class="number">4</span>; <span class="comment">// 绕过 setter 进行设置</span></span><br><span class="line">person.<span class="property">timeSheet</span>;</span><br></pre></td></tr></table></figure><p>可以看到,在类中没有任何措施可以防止在不调用 setter 的情况下更改属性。</p><p>而私有类字段将使用哈希#前缀定义,从上面的示例中,我们可以修改它以包含私有类字段,以防止在类方法之外更改属性:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">TimeTracker</span> &#123;</span><br><span class="line"> name = <span class="string">&#x27;zhangsan&#x27;</span>;</span><br><span class="line"> project = <span class="string">&#x27;blog&#x27;</span>;</span><br><span class="line"> #hours = <span class="number">0</span>; <span class="comment">// 私有类字段</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">set</span> <span class="title function_">addHours</span>(<span class="params">hour</span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.#hours += hour;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">get</span> <span class="title function_">timeSheet</span>() &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;<span class="variable language_">this</span>.name&#125;</span> works <span class="subst">$&#123;<span class="variable language_">this</span>.#hours || <span class="string">&#x27;nothing&#x27;</span>&#125;</span> hours on <span class="subst">$&#123;<span class="variable language_">this</span>.project&#125;</span>`</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="title class_">TimeTracker</span>();</span><br><span class="line">person.<span class="property">addHours</span> = <span class="number">4</span>; <span class="comment">// 标准 setter</span></span><br><span class="line">person.<span class="property">timeSheet</span> <span class="comment">// zhangsan works 4 hours on blog</span></span><br></pre></td></tr></table></figure><p>当我们尝试在 setter 方法之外修改私有类字段时,就会报错:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">person.<span class="property">hours</span> = <span class="number">4</span> <span class="comment">// Error Private field &#x27;#hours&#x27; must be declared in an enclosing class</span></span><br></pre></td></tr></table></figure><p>我们还可以将方法或 <code>getter/setter</code> 设为私有,只需要给这些方法名称前面加<code>#</code>即可:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">TimeTracker</span> &#123;</span><br><span class="line"> name = <span class="string">&#x27;zhangsan&#x27;</span>;</span><br><span class="line"> project = <span class="string">&#x27;blog&#x27;</span>;</span><br><span class="line"> #hours = <span class="number">0</span>; <span class="comment">// 私有类字段</span></span><br><span class="line"></span><br><span class="line"> set #<span class="title function_">addHours</span>(<span class="params">hour</span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.#hours += hour;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> get #<span class="title function_">timeSheet</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;<span class="variable language_">this</span>.name&#125;</span> works <span class="subst">$&#123;<span class="variable language_">this</span>.#hours || <span class="string">&#x27;nothing&#x27;</span>&#125;</span> hours on <span class="subst">$&#123;<span class="variable language_">this</span>.project&#125;</span>`</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">hours</span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.#addHours = hours;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.#timeSheet);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="title class_">TimeTracker</span>(<span class="number">4</span>); <span class="comment">// zhangsan works 4 hours on blog</span></span><br></pre></td></tr></table></figure><p>由于尝试访问对象上不存在的私有字段会发生异常,因此需要能够检查对象是否具有给定的私有字段。可以使用 <code>in</code> 运算符来检查对象上是否有私有字段:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Example</span> &#123;</span><br><span class="line"> #field</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> <span class="title function_">isExampleInstance</span>(<span class="params">object</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> #field <span class="keyword">in</span> object;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>查看更多公有和私有字段提案信息:点击<a href="https://www.mybj123.com/gohref.php?url=https://github.com/tc39/proposal-class-fields">这里</a></p><h3 id="3-静态公共字段"><a href="#3-静态公共字段" class="headerlink" title="3. 静态公共字段"></a>3. 静态公共字段</h3><p>在ES6中,不能在类的每个实例中访问静态字段或方法,只能在原型中访问。ES 2022 将提供一种在 JavaScript 中使用 <code>static</code> 关键字声明静态类字段的方法。下面来看一个例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Shape</span> &#123;</span><br><span class="line"> <span class="keyword">static</span> color = <span class="string">&#x27;blue&#x27;</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> <span class="title function_">getColor</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">color</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="title function_">getMessage</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`color:<span class="subst">$&#123;<span class="variable language_">this</span>.color&#125;</span>`</span> ;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们可以从类本身访问静态字段和方法:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Shape</span>.<span class="property">color</span>); <span class="comment">// blue</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Shape</span>.<span class="title function_">getColor</span>()); <span class="comment">// blue</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;color&#x27;</span> <span class="keyword">in</span> <span class="title class_">Shape</span>); <span class="comment">// true</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;getColor&#x27;</span> <span class="keyword">in</span> <span class="title class_">Shape</span>); <span class="comment">// true</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;getMessage&#x27;</span> <span class="keyword">in</span> <span class="title class_">Shape</span>); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p>实例不能访问静态字段和方法:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> shapeInstance = <span class="keyword">new</span> <span class="title class_">Shape</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(shapeInstance.<span class="property">color</span>); <span class="comment">// undefined</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(shapeInstance.<span class="property">getColor</span>); <span class="comment">// undefined</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(shapeInstance.<span class="title function_">getMessage</span>());<span class="comment">// color:undefined</span></span><br></pre></td></tr></table></figure><p>静态字段只能通过静态方法访问:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Shape</span>.<span class="title function_">getColor</span>()); <span class="comment">// blue</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Shape</span>.<span class="title function_">getMessage</span>()); <span class="comment">//TypeError: Shape.getMessage is not a function</span></span><br></pre></td></tr></table></figure><p>这里的 Shape.getMessage() 就报错了,这是因为 getMessage 不是一个静态函数,所以它不能通过类名 Shape 访问。可以通过以下方式来解决这个问题:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">getMessage</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`color:<span class="subst">$&#123;Shape.color&#125;</span>`</span> ;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>静态字段和方法是从父类继承的:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Shape</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Rectangle</span>.<span class="property">color</span>); <span class="comment">// blue</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Rectangle</span>.<span class="title function_">getColor</span>()); <span class="comment">// blue</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;color&#x27;</span> <span class="keyword">in</span> <span class="title class_">Rectangle</span>); <span class="comment">// true</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;getColor&#x27;</span> <span class="keyword">in</span> <span class="title class_">Rectangle</span>); <span class="comment">// true</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;getMessage&#x27;</span> <span class="keyword">in</span> <span class="title class_">Rectangle</span>); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><h3 id="4-静态私有字段和方法"><a href="#4-静态私有字段和方法" class="headerlink" title="4. 静态私有字段和方法"></a>4. 静态私有字段和方法</h3><p>与私有实例字段和方法一样,静态私有字段和方法也使用哈希 (<code>#</code>) 前缀来定义:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">class Shape &#123;</span><br><span class="line"> static #color = &#x27;blue&#x27;;</span><br><span class="line"></span><br><span class="line"> static #getColor() &#123;</span><br><span class="line"> return this.#color;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> getMessage() &#123;</span><br><span class="line"> return `color:$&#123;Shape.#getColor()&#125;` ;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line">const shapeInstance = new Shape();</span><br><span class="line">shapeInstance.getMessage(); // color:blue</span><br></pre></td></tr></table></figure><p>私有静态字段有一个限制:只有定义私有静态字段的类才能访问该字段。这可能在我们使用 this 时导致出乎意料的情况:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Shape</span> &#123;</span><br><span class="line"> <span class="keyword">static</span> #color = <span class="string">&#x27;blue&#x27;</span>;</span><br><span class="line"><span class="keyword">static</span> #<span class="title function_">getColor</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.#color;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">static</span> <span class="title function_">getMessage</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`color:<span class="subst">$&#123;<span class="variable language_">this</span>.#color&#125;</span>`</span> ;</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">getMessageNonStatic</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`color:<span class="subst">$&#123;<span class="variable language_">this</span>.#getColor()&#125;</span>`</span> ;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Shape</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Rectangle</span>.<span class="title function_">getMessage</span>()); <span class="comment">// Uncaught TypeError: Cannot read private member #color from an object whose class did not declare it</span></span><br><span class="line"><span class="keyword">const</span> rectangle = <span class="keyword">new</span> <span class="title class_">Rectangle</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(rectangle.<span class="title function_">getMessageNonStatic</span>()); <span class="comment">// TypeError: Cannot read private member #getColor from an object whose class did not declare it</span></span><br></pre></td></tr></table></figure><p>在这个例子中,this 指向的是 Rectangle 类,它无权访问私有字段 #color。当我们尝试调用 Rectangle.getMessage() 时,它无法读取 #color 并抛出了 TypeError。可以这样来进行修改:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Shape</span> &#123;</span><br><span class="line"> <span class="keyword">static</span> #color = <span class="string">&#x27;blue&#x27;</span>;</span><br><span class="line"> <span class="keyword">static</span> #<span class="title function_">getColor</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.#color;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">static</span> <span class="title function_">getMessage</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;Shape.#color&#125;</span>`</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="title function_">getMessageNonStatic</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`color:<span class="subst">$&#123;Shape.#getColor()&#125;</span> color`</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Shape</span> &#123;&#125;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Rectangle</span>.<span class="title function_">getMessage</span>()); <span class="comment">// color:blue</span></span><br><span class="line"><span class="keyword">const</span> rectangle = <span class="keyword">new</span> <span class="title class_">Rectangle</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(rectangle.<span class="title function_">getMessageNonStatic</span>()); <span class="comment">// color:blue</span></span><br></pre></td></tr></table></figure><p>静态字段目前是比较稳定的,并且提供了各种实现:</p><p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="静态字段各种实现表"><img src="https://media.mybj123.com/wp-content/uploads/2022/01/1643434935-0b70bc0c4393cc8.webp" alt="静态字段各种实现表"></p><h3 id="5-类静态初始化块"><a href="#5-类静态初始化块" class="headerlink" title="5. 类静态初始化块"></a>5. 类静态初始化块</h3><p>静态私有和公共字段只能让我们在类定义期间执行静态成员的每个字段初始化。如果我们需要在初始化期间像 try … catch 一样进行异常处理,就不得不在类之外编写此逻辑。该提案就提供了一种在类声明&#x2F;定义期间评估静态初始化代码块的优雅方法,可以访问类的私有字段。</p><p>先来看一个例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="keyword">static</span> <span class="variable constant_">GENDER</span> = <span class="string">&quot;Male&quot;</span></span><br><span class="line"> <span class="keyword">static</span> <span class="variable constant_">TOTAL_EMPLOYED</span>;</span><br><span class="line"> <span class="keyword">static</span> <span class="variable constant_">TOTAL_UNEMPLOYED</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码就会引发错误,可以使用类静态块来重构它,只需将try…catch包裹在 static 中即可:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="keyword">static</span> <span class="variable constant_">GENDER</span> = <span class="string">&quot;Male&quot;</span></span><br><span class="line"> <span class="keyword">static</span> <span class="variable constant_">TOTAL_EMPLOYED</span>;</span><br><span class="line"> <span class="keyword">static</span> <span class="variable constant_">TOTAL_UNEMPLOYED</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此外,类静态块提供对词法范围的私有字段和方法的特权访问。这里需要在具有实例私有字段的类和同一范围内的函数之间共享信息的情况下很有用。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> getData;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"> #x</span><br><span class="line"> </span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">x</span>) &#123;</span><br><span class="line"> <span class="variable language_">this</span>.#x = &#123; <span class="attr">data</span>: x &#125;;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> getData = <span class="function">(<span class="params">obj</span>) =&gt;</span> obj.#x;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">readPrivateData</span>(<span class="params">obj</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">getData</span>(obj).<span class="property">data</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> john = <span class="keyword">new</span> <span class="title class_">Person</span>([<span class="number">2</span>,<span class="number">4</span>,<span class="number">6</span>,<span class="number">8</span>]);</span><br><span class="line"></span><br><span class="line"><span class="title function_">readPrivateData</span>(john); <span class="comment">// [2,4,6,8]</span></span><br></pre></td></tr></table></figure><p>这里,Person 类与 readPrivateData 函数共享了私有实例属性。</p><h2 id="三、Temporal"><a href="#三、Temporal" class="headerlink" title="三、Temporal"></a>三、Temporal</h2><p>JavaScript 中的日期处理 <code>[Date()](https://www.mybj123.com/tag/date/)</code> 对象一直是饱受诟病,该对象是1995 年受到 Java 的启发而实现的,自此就一直没有改变过。虽然Java已经放弃了这个对象,但是 <code>Date()</code> 仍保留在 JavaScript 中来实现浏览器的兼容。</p><p>Date() API 存在的问题:</p><ul><li>只支持UTC和用户的PC时间;</li><li>不支持公历以外的日历;</li><li>字符串到日期解析容易出错;</li><li>Date 对象是可变的,比如:</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> today = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"><span class="keyword">const</span> tomorrow = <span class="keyword">new</span> <span class="title class_">Date</span>(today.<span class="title function_">setDate</span>(today.<span class="title function_">getDate</span>() + <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(tomorrow); </span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(today);</span><br></pre></td></tr></table></figure><p>此时,两个时间输出是一样的,不符合我们的预期。正因为 Date() 对象存在的种种问题。平时我们经常需要借助<code>moment.js</code>、<code>Day.js</code>等日期库,但是它们的体积较大,有时一个简单的日期处理就需要引入一个库,得不偿失。</p><p>目前,由于Date API 在很多库和浏览器引擎中的广泛使用,没有办法修复API的不好的部分。而改变Date API 的工作方式也很可能会破坏许多网站和库。</p><p>正因如此,TC39提出了一个全新的用于处理日期和时间的标准对象和函数——Temporal。新的Temporal API 提案旨在解决Date API的问题。它为 JavaScript 日期&#x2F;时间操作带来了以下修复:</p><ul><li>仅可以创建和处理不可变Temporal对象;</li><li>提供用于日期和时间计算的简单 API;</li><li>支持所有时区;</li><li>从 ISO-8601 格式进行严格的日期解析;</li><li>支持非公历。</li></ul><p>Temporal 将取代 Moment.js 之类的库,这些库很好地填补了 JavaScript 中的空白,这种空白非常普遍,因此将功能作为语言的一部分更有意义。</p><p>由于该提案还未正式发布,所以,可以借助官方提供的prlyfill来测试。首选进行安装:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install @js-temporal/polyfill</span><br></pre></td></tr></table></figure><p>导入并使用:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Temporal</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@js-temporal/polyfill&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Temporal</span>);</span><br></pre></td></tr></table></figure><p>Temporal 对象如下:</p><p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Temporal 对象"><img src="https://media.mybj123.com/wp-content/uploads/2022/01/1643435164-e4fe75af2011b87.webp" alt="Temporal 对象"></p><p>下面就来看看 Temporal 对象有哪些实用的功能。</p><h3 id="1-当前时间和日期"><a href="#1-当前时间和日期" class="headerlink" title="1. 当前时间和日期"></a>1. 当前时间和日期</h3><p><code>Temporal.Now</code> 会返回一个表示当前日期和时间的对象:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自1970年1月1日以来的时间(秒和毫秒)</span></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">Now</span>.<span class="title function_">instant</span>().<span class="property">epochSeconds</span>;</span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">Now</span>.<span class="title function_">instant</span>().<span class="property">epochMilliseconds</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当前位置的时间</span></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">Now</span>.<span class="title function_">zonedDateTimeISO</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当前时区</span></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">Now</span>.<span class="title function_">timeZone</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 指定时区的当前时间</span></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">Now</span>.<span class="title function_">zonedDateTimeISO</span>(<span class="string">&#x27;Europe/London&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="2-实例时间和日期"><a href="#2-实例时间和日期" class="headerlink" title="2. 实例时间和日期"></a>2. 实例时间和日期</h3><p><code>Temporal.Instant</code> 根据 ISO 8601 格式的字符串返回一个表示日期和时间的对象,结果会精确到纳秒:</p><p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="实例时间和日期"><img src="https://media.mybj123.com/wp-content/uploads/2022/01/1643435243-b87f0c4dcc3e91d.webp" alt="实例时间和日期"></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Temporal</span>.<span class="property">Instant</span>.<span class="title function_">from</span>(<span class="string">&#x27;2022-02-01T05:56:78.999999999+02:00[Europe/Berlin]&#x27;</span>);</span><br><span class="line"><span class="comment">// 输出结果:2022-02-01T03:57:18.999999999Z </span></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">Instant</span>.<span class="title function_">from</span>(<span class="string">&#x27;2022-02-011T05:06+07:00&#x27;</span>);</span><br><span class="line"><span class="comment">// 输出结果:2022-01-31T22:06:00Z</span></span><br></pre></td></tr></table></figure><p>除此之外,我们还可以获取纪元时间的对应的日期(UTC 1970年1月1日0点是纪元时间):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Temporal</span>.<span class="property">Instant</span>.<span class="title function_">fromEpochSeconds</span>(<span class="number">1.0e8</span>);</span><br><span class="line"><span class="comment">// 输出结果:1973-03-03T09:46:40Z</span></span><br></pre></td></tr></table></figure><h3 id="3-时区日期和时间"><a href="#3-时区日期和时间" class="headerlink" title="3. 时区日期和时间"></a>3. 时区日期和时间</h3><p><code>Temporal.ZonedDateTime</code> 返回一个对象,该对象表示在特定时区的日期&#x2F;时间:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">ZonedDateTime</span>(</span><br><span class="line"> <span class="number">1234567890000</span>, <span class="comment">// 纪元时间</span></span><br><span class="line"> <span class="title class_">Temporal</span>.<span class="property">TimeZone</span>.<span class="title function_">from</span>(<span class="string">&#x27;Europe/London&#x27;</span>), <span class="comment">// 时区</span></span><br><span class="line"> <span class="title class_">Temporal</span>.<span class="property">Calendar</span>.<span class="title function_">from</span>(<span class="string">&#x27;iso8601&#x27;</span>) <span class="comment">// 默认日历</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">from</span>(<span class="string">&#x27;2025-09-05T02:55:00+02:00[Africa/Cairo]&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="title class_">Instant</span>(<span class="string">&#x27;2022-08-05T20:06:13+05:45&#x27;</span>).<span class="title function_">toZonedDateTime</span>(<span class="string">&#x27;+05:45&#x27;</span>);</span><br><span class="line"><span class="comment">// 输出结果:</span></span><br><span class="line"></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">from</span>(&#123;</span><br><span class="line"> <span class="attr">timeZone</span>: <span class="string">&#x27;America/New_York&#x27;</span>,</span><br><span class="line"> <span class="attr">year</span>: <span class="number">2025</span>,</span><br><span class="line"> <span class="attr">month</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">day</span>: <span class="number">28</span>,</span><br><span class="line"> <span class="attr">hour</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">minute</span>: <span class="number">15</span>,</span><br><span class="line"> <span class="attr">second</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">millisecond</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">microsecond</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">nanosecond</span>: <span class="number">0</span></span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 输出结果:2025-02-28T10:15:00-05:00[America/New_York]</span></span><br></pre></td></tr></table></figure><h3 id="4-简单的日期和时间"><a href="#4-简单的日期和时间" class="headerlink" title="4. 简单的日期和时间"></a>4. 简单的日期和时间</h3><p>我们并不会总是需要使用精确的时间,因此 Temporal API 提供了独立于时区的对象。这些可以用于更简单的活动。</p><ul><li>Temporal.PlainDateTime:指日历日期和时间;</li><li>Temporal.PlainDate:指特定的日历日期;</li><li>Temporal.PlainTime:指一天中的特定时间;</li><li>Temporal.PlainYearMonth:指没有日期成分的日期,例如“2022 年 2 月”;Temporal.PlainMonthDay:指没有年份的日期,例如“10 月 1 日”。</li></ul><p>它们都有类似的构造函数,以下有两种形式来创建简单的时间和日期:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">PlainDateTime</span>(<span class="number">2021</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>);</span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">PlainDateTime</span>.<span class="title function_">from</span>(<span class="string">&#x27;2021-05-04T13:14:15&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">PlainDate</span>(<span class="number">2021</span>, <span class="number">5</span>, <span class="number">4</span>);</span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">PlainDate</span>.<span class="title function_">from</span>(<span class="string">&#x27;2021-05-04&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">PlainTime</span>(<span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>);</span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">PlainTime</span>.<span class="title function_">from</span>(<span class="string">&#x27;13:14:15&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">PlainYearMonth</span>(<span class="number">2021</span>, <span class="number">4</span>);</span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">PlainYearMonth</span>.<span class="title function_">from</span>(<span class="string">&#x27;2019-04&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">PlainMonthDay</span>(<span class="number">3</span>, <span class="number">14</span>);</span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">PlainMonthDay</span>.<span class="title function_">from</span>(<span class="string">&#x27;03-14&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="5-日期和时间值"><a href="#5-日期和时间值" class="headerlink" title="5. 日期和时间值"></a>5. 日期和时间值</h3><p>所有 Temporal 对象都可以返回特定的日期&#x2F;时间值。例如,使用<code>ZonedDateTime</code>:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> t1 = <span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">from</span>(<span class="string">&#x27;2025-12-07T03:24:30+02:00[Africa/Cairo]&#x27;</span>);</span><br><span class="line"></span><br><span class="line">t1.<span class="property">year</span>; <span class="comment">// 2025</span></span><br><span class="line">t1.<span class="property">month</span>; <span class="comment">// 12</span></span><br><span class="line">t1.<span class="property">day</span>; <span class="comment">// 7</span></span><br><span class="line">t1.<span class="property">hour</span>; <span class="comment">// 3</span></span><br><span class="line">t1.<span class="property">minute</span>; <span class="comment">// 24</span></span><br><span class="line">t1.<span class="property">second</span>; <span class="comment">// 30</span></span><br><span class="line">t1.<span class="property">millisecond</span>; <span class="comment">// 0</span></span><br><span class="line">t1.<span class="property">microsecond</span>; <span class="comment">// 0</span></span><br><span class="line">t1.<span class="property">nanosecond</span>; <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><p>其他有用的属性包括:</p><ul><li><code>dayOfWeek</code>(周一为 1 至周日为 7)</li><li><code>dayOfYear</code>(1 至 365 或 366)</li><li><code>weekOfYear</code>(1 到 52,有时是 53)</li><li><code>daysInMonth</code>(28、29、30、31)</li><li><code>daysInYear</code>(365 或 366)</li><li><code>inLeapYear</code>(true或false)</li></ul><h3 id="6-比较和排序日期"><a href="#6-比较和排序日期" class="headerlink" title="6. 比较和排序日期"></a>6. 比较和排序日期</h3><p>所有 Temporal 对象都可以使用 <code>compare()</code> 返回整数的函数进行比较。例如,比较两个<code>ZonedDateTime</code>对象:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">compare</span>(t1, t2);</span><br></pre></td></tr></table></figure><p>这个比较结果会有三种情况:</p><ul><li>当两个时间值相等时,返回 0;</li><li>当 t1 在 t2 之后时,返回 1;</li><li>当 t1 在 t2 之前时,但会 -1;</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> date1 = <span class="title class_">Temporal</span>.<span class="property">Now</span>,</span><br><span class="line"><span class="keyword">const</span> date2 = <span class="title class_">Temporal</span>.<span class="property">PlainDateTime</span>.<span class="title function_">from</span>(<span class="string">&#x27;2022-05-01&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">compare</span>(date1, date2); <span class="comment">// -1</span></span><br></pre></td></tr></table></figure><p><code>compare()</code> 的结果可以用于数组的 <code>sort()</code> 方法来对时间按照升序进行排列(从早到晚):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> t = [</span><br><span class="line"> <span class="string">&#x27;2022-01-01T00:00:00+00:00[Europe/London]&#x27;</span>,</span><br><span class="line"> <span class="string">&#x27;2022-01-01T00:00:00+00:00[Africa/Cairo]&#x27;</span>,</span><br><span class="line"> <span class="string">&#x27;2022-01-01T00:00:00+00:00[America/New_York]&#x27;</span></span><br><span class="line">].<span class="title function_">map</span>(<span class="function"><span class="params">d</span> =&gt;</span> <span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">from</span>(d))</span><br><span class="line"> .<span class="title function_">sort</span>(<span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="property">compare</span>);</span><br></pre></td></tr></table></figure><h3 id="7-日期计算"><a href="#7-日期计算" class="headerlink" title="7. 日期计算"></a>7. 日期计算</h3><p>提案还提供了几种方法来对任何 Temporal 对象执行日期计算。当传递一个Temporal.Duration对象时,它们都会返回一个相同类型的新的 Temporal,该对象使用<code>years</code>, <code>months</code>, <code>weeks</code>, <code>days</code>, <code>hours</code>, <code>minutes</code>, <code>seconds</code>, <code>milliseconds</code>, <code>microseconds</code> 和 <code>nanoseconds</code> 字段来设置时间。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> t1 = <span class="title class_">Temporal</span>.<span class="property">ZonedDateTime</span>.<span class="title function_">from</span>(<span class="string">&#x27;2022-01-01T00:00:00+00:00[Europe/London]&#x27;</span>);</span><br><span class="line"></span><br><span class="line">t1.<span class="title function_">add</span>(&#123; <span class="attr">hours</span>: <span class="number">8</span>, <span class="attr">minutes</span>: <span class="number">30</span> &#125;); <span class="comment">// 往后8小时30分</span></span><br><span class="line"></span><br><span class="line">t1.<span class="title function_">subtract</span>(&#123; <span class="attr">days</span>: <span class="number">5</span> &#125;); <span class="comment">// 往前5天</span></span><br><span class="line"></span><br><span class="line">t1.<span class="title function_">round</span>(&#123; <span class="attr">smallestUnit</span>: <span class="string">&#x27;month&#x27;</span> &#125;); <span class="comment">// 四舍五入到最近的月份</span></span><br></pre></td></tr></table></figure><p><code>until()</code> 和 <code>since()</code> 方法会返回一个对象,该 <code>Temporal.Duration</code> 对象描述基于当前日期&#x2F;时间的特定日期和时间之前或之后的时间,例如:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">t1.<span class="title function_">until</span>().<span class="property">months</span>; <span class="comment">// 到t1还有几个月</span></span><br><span class="line"></span><br><span class="line">t2.<span class="title function_">until</span>().<span class="property">days</span>; <span class="comment">// 到t2还有几天</span></span><br><span class="line"></span><br><span class="line">t3.<span class="title function_">since</span>().<span class="property">weeks</span>; <span class="comment">// t3已经过去了几周</span></span><br></pre></td></tr></table></figure><p><code>[equals()](https://www.mybj123.com/tag/equals/)</code> 方法用来确定两个日期&#x2F;时间值是否相同:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> d1 = <span class="title class_">Temporal</span>.<span class="property">PlainDate</span>.<span class="title function_">from</span>(<span class="string">&#x27;2022-01-31&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> d2 = <span class="title class_">Temporal</span>.<span class="property">PlainDate</span>.<span class="title function_">from</span>(<span class="string">&#x27;2023-01-31&#x27;</span>);</span><br><span class="line">d1.<span class="title function_">equals</span>(d2); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><h3 id="8-使用国际化-API-格式化日期"><a href="#8-使用国际化-API-格式化日期" class="headerlink" title="8. 使用国际化 API 格式化日期"></a>8. 使用国际化 API 格式化日期</h3><p>虽然这不是 Temporal API 的一部分,但 JavaScript Intl(国际化)API提供了一个 <code>[DateTimeFormat()](https://www.mybj123.com/tag/datetimeformat/)</code> 构造函数,可以用于格式化 Temporal 或 Date 对象:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> d = <span class="keyword">new</span> <span class="title class_">Temporal</span>.<span class="title class_">PlainDate</span>(<span class="number">2022</span>, <span class="number">3</span>, <span class="number">14</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 美国日期格式:3/14/2022</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(<span class="string">&#x27;en-US&#x27;</span>).<span class="title function_">format</span>(d);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 英国日期格式:14/3/2022</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(<span class="string">&#x27;en-GB&#x27;</span>).<span class="title function_">format</span>(d);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 西班牙长日期格式:miércoles, 14 de abril de 2022</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(<span class="string">&#x27;es-ES&#x27;</span>, &#123; <span class="attr">dateStyle</span>: <span class="string">&#x27;full&#x27;</span> &#125;).<span class="title function_">format</span>(d);</span><br></pre></td></tr></table></figure><p>附:</p><ul><li>TC39 关于 Temporal 的提案进度:<a href="https://www.mybj123.com/gohref.php?url=https://tc39.es/proposal-temporal/">传送门</a></li><li>Chrome 关于 Temporal 的实现进度:<a href="https://www.mybj123.com/gohref.php?url=https://chromestatus.com/feature/5668291307634688#details">传送门</a></li></ul><h2 id="四、内置对象"><a href="#四、内置对象" class="headerlink" title="四、内置对象"></a>四、内置对象</h2><h3 id="1-Object-hasOwn"><a href="#1-Object-hasOwn" class="headerlink" title="1. Object.hasOwn()"></a>1. Object.hasOwn()</h3><p>在ES2022之前,可以使用 <code>Object.prototype.hasOwnProperty()</code> 来检查一个属性是否属于对象。</p><p>提案中的 <code>Object.hasOwn</code> 特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> example = &#123;</span><br><span class="line"> <span class="attr">property</span>: <span class="string">&#x27;123&#x27;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">hasOwnProperty</span>.<span class="title function_">call</span>(example, <span class="string">&#x27;property&#x27;</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Object</span>.<span class="title function_">hasOwn</span>(example, <span class="string">&#x27;property&#x27;</span>));</span><br></pre></td></tr></table></figure><h3 id="2-at"><a href="#2-at" class="headerlink" title="2. at()"></a>2. <a href="https://www.mybj123.com/tag/at/">at()</a></h3><p><code>at()</code> 是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> array = [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>];</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(array[array.<span class="property">length</span>-<span class="number">1</span>]); <span class="comment">// 5</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(array.<span class="title function_">at</span>(-<span class="number">1</span>)); <span class="comment">// 5</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(array[array.<span class="property">lenght</span>-<span class="number">2</span>]); <span class="comment">// 4</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(array.<span class="title function_">at</span>(-<span class="number">2</span>)); <span class="comment">// 4</span></span><br></pre></td></tr></table></figure><p>除了数组,字符串也可以使用<code>at()</code>方法进行索引:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> str = <span class="string">&quot;hello world&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(str[str.<span class="property">length</span> - <span class="number">1</span>]); <span class="comment">// d</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(str.<span class="title function_">at</span>(-<span class="number">1</span>)); <span class="comment">// d</span></span><br></pre></td></tr></table></figure><h3 id="3-cause"><a href="#3-cause" class="headerlink" title="3. cause"></a>3. cause</h3><p>在 ECMAScript 2022 提案中,new Error() 中可以指定导致它的原因:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">readFiles</span>(<span class="params">filePaths</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> filePaths.<span class="title function_">map</span>(</span><br><span class="line"> <span class="function">(<span class="params">filePath</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// ···</span></span><br><span class="line"> &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(</span><br><span class="line"> <span class="string">`While processing <span class="subst">$&#123;filePath&#125;</span>`</span>,</span><br><span class="line"> &#123;<span class="attr">cause</span>: error&#125;</span><br><span class="line"> );</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-正则表达式匹配索引"><a href="#4-正则表达式匹配索引" class="headerlink" title="4. 正则表达式匹配索引"></a>4. 正则表达式匹配索引</h3><p>这个新提案已经进入第 4 阶段,它将允许我们利用 d 字符来表示我们想要匹配字符串的开始和结束索引。以前,我们只能在字符串匹配操作期间获得一个包含提取的字符串和索引信息的数组。在某些情况下,这是不够的。因此,在这个新提案中,如果设置标志 <code>/d</code>,将额外获得一个带有开始和结束索引的数组。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> matchObj = <span class="regexp">/(a+)(b+)/</span>d.<span class="title function_">exec</span>(<span class="string">&#x27;aaaabb&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj[<span class="number">1</span>]) <span class="comment">// &#x27;aaaa&#x27;</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj[<span class="number">2</span>]) <span class="comment">// &#x27;bb&#x27;</span></span><br></pre></td></tr></table></figure><p>由于 <code>/d</code> 标识的存在,matchObj还有一个属性<code>.indices</code>,它用来记录捕获的每个编号组:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj.<span class="property">indices</span>[<span class="number">1</span>]) <span class="comment">// [0, 4]</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj.<span class="property">indices</span>[<span class="number">2</span>]) <span class="comment">// [4, 6]</span></span><br></pre></td></tr></table></figure><p>我们还可以使用命名组:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> matchObj = <span class="regexp">/(?&lt;as&gt;a+)(?&lt;bs&gt;b+)/</span>d.<span class="title function_">exec</span>(<span class="string">&#x27;aaaabb&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj.<span class="property">groups</span>.<span class="property">as</span>); <span class="comment">// &#x27;aaaa&#x27;</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj.<span class="property">groups</span>.<span class="property">bs</span>); <span class="comment">// &#x27;bb&#x27;</span></span><br></pre></td></tr></table></figure><p>这里给两个字符匹配分别命名为<code>as</code>和<code>bs</code>,然后就可以通过groups来获取到这两个命名分别匹配到的字符串。</p><p>它们的索引存储在 <code>matchObj.indices.groups</code> 中:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj.<span class="property">indices</span>.<span class="property">groups</span>.<span class="property">as</span>); <span class="comment">// [0, 4]</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(matchObj.<span class="property">indices</span>.<span class="property">groups</span>.<span class="property">bs</span>); <span class="comment">// [4, 6]</span></span><br></pre></td></tr></table></figure><p>匹配索引的一个重要用途就是指向语法错误所在位置的解析器。下面的代码解决了一个相关问题:它指向引用内容的开始和结束位置。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> reQuoted = <span class="regexp">/“([^”]+)”/</span>dgu;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">pointToQuotedText</span>(<span class="params">str</span>) &#123;</span><br><span class="line"> <span class="keyword">const</span> startIndices = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"> <span class="keyword">const</span> endIndices = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> match <span class="keyword">of</span> str.<span class="title function_">matchAll</span>(reQuoted)) &#123;</span><br><span class="line"> <span class="keyword">const</span> [start, end] = match.<span class="property">indices</span>[<span class="number">1</span>];</span><br><span class="line"> startIndices.<span class="title function_">add</span>(start);</span><br><span class="line"> endIndices.<span class="title function_">add</span>(end);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">let</span> result = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> index=<span class="number">0</span>; index &lt; str.<span class="property">length</span>; index++) &#123;</span><br><span class="line"> <span class="keyword">if</span> (startIndices.<span class="title function_">has</span>(index)) &#123;</span><br><span class="line"> result += <span class="string">&#x27;[&#x27;</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (endIndices.<span class="title function_">has</span>(index+<span class="number">1</span>)) &#123;</span><br><span class="line"> result += <span class="string">&#x27;]&#x27;</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> result += <span class="string">&#x27; &#x27;</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">pointToQuotedText</span>(<span class="string">&#x27;They said “hello” and “goodbye”.&#x27;</span>));</span><br><span class="line"><span class="comment">// &#x27; [ ] [ ] &#x27;</span></span><br></pre></td></tr></table></figure>]]></content>
239
239
 
@@ -254,7 +254,7 @@
254
254
  <link href="https://xuehuayu.cn/article/5dbb6f41.html"/>
255
255
  <id>https://xuehuayu.cn/article/5dbb6f41.html</id>
256
256
  <published>2022-02-11T17:54:00.000Z</published>
257
- <updated>2023-07-20T03:42:31.820Z</updated>
257
+ <updated>2023-07-21T09:10:52.811Z</updated>
258
258
 
259
259
  <content type="html"><![CDATA[<p>这款浏览器支持chrome扩展商店和edge扩展商店的所有扩展,<br>抓包发现: 有umeng.com的链接,应该是统计,也有可能后面会加广告。</p><span id="more"></span><p><img src="https://s4.ax1x.com/2022/02/11/HaIgkq.jpg" alt="支持扩展的安卓手机浏览器"></p><p>首次使用只需要点击右下角的 扩展 按钮,<br><img src="https://s4.ax1x.com/2022/02/11/HaI67n.jpg" alt="支持扩展的安卓手机浏览器"></p><p>即可弹出 扩展商店 选择弹框,可选择 chrome扩展商店 和 edge扩展商店<br><img src="https://s4.ax1x.com/2022/02/11/HaIRhV.jpg" alt="支持扩展的安卓手机浏览器"></p><p>进入选择的扩展商店,搜索自己需要的扩展,然后点击“下载”,下载完成后会自己提示是否安装扩展。点击”确定”<br><img src="https://s4.ax1x.com/2022/02/11/HaIsmj.jpg" alt="支持扩展的安卓手机浏览器"></p><p>安装成功后,点击浏览器下方的“扩展”按钮,会显示已安装的扩展列表,点击扩展,进入扩展详情页<br><img src="https://s4.ax1x.com/2022/02/11/HaI2t0.jpg" alt="支持扩展的安卓手机浏览器"></p><p>详情页对应PC上点击扩展显示的页面,手机上使用一个底部弹窗的方式实现的,显示的内容与PC一致,扩展的设置页面和PC是也是相同的。</p><p><img src="https://s4.ax1x.com/2022/02/11/HaIX9K.jpg" alt="支持扩展的安卓手机浏览器"></p><p>这款浏览器同样采用的是谷歌的Chromium内核,其他使用方面和chrome、edge 都大同小异。<br><img src="https://s4.ax1x.com/2022/02/11/HaIy0s.jpg" alt="支持扩展的安卓手机浏览器"></p><p>官方的介绍是:</p><p>全新Chromium高速内核引擎,精心为您适配更好用的网络帮手,体验飞一般的感觉</p><p>Infinity New Tab、Tampermonkey、Adblock Plus等成千上万个扩展在狐猴浏览器上也同样可以使用</p><p>多层次保护,利用沙箱技术阻挡恶意软件。使您免遭网上诱骗网站和危险网站的侵害,保障您的隐私安全</p><blockquote><p>下载地址</p></blockquote><p>关注公众号“入门前端”回复“20220211”获取</p>]]></content>
260
260
 
@@ -275,7 +275,7 @@
275
275
  <link href="https://xuehuayu.cn/article/fc3b727a.html"/>
276
276
  <id>https://xuehuayu.cn/article/fc3b727a.html</id>
277
277
  <published>2022-02-10T17:46:43.000Z</published>
278
- <updated>2023-07-20T03:42:31.820Z</updated>
278
+ <updated>2023-07-21T09:10:52.811Z</updated>
279
279
 
280
280
  <content type="html"><![CDATA[<p>本站评论系统更换为Waline</p><span id="more"></span><p>选择<a href="https://waline.js.org/">Waline</a>:</p><ol><li>由于支持的部署平台较多;</li><li>因为作者的更新维护目前还很活跃(在此要感谢主要作者<a href="https://imnerd.org/">怡红公子</a>大佬的倾情奉献和无私帮助)</li><li>官方提供了很方便的数据迁移工具。</li></ol><p>部署使用的是官方推荐的<code>Vercel</code>,数据库使用的 <code>LeanCloud国际版</code>。</p><p>以前使用的是<code>LeanCloud国内版</code>,由于数据导出为json时,时间格式的字段导出后为String类型,导致导入一直失败,LeanCloud官方文档说的是几个时间字段是系统自动生成的,所以导入的时候旧把时间相关的字段删除了,导致了评论中的留言顺序回复顺序发生了错乱。</p><p>所以准备手动改数据。如果不好弄就放弃旧数据了。😭</p>]]></content>
281
281
 
@@ -296,7 +296,7 @@
296
296
  <link href="https://xuehuayu.cn/article/dcb0db28.html"/>
297
297
  <id>https://xuehuayu.cn/article/dcb0db28.html</id>
298
298
  <published>2022-01-10T14:51:41.000Z</published>
299
- <updated>2023-07-20T03:42:31.820Z</updated>
299
+ <updated>2023-07-21T09:10:52.811Z</updated>
300
300
 
301
301
  <content type="html"><![CDATA[<p>从昨天也就是2022-01-09开始,github的actions一直失败</p><span id="more"></span><p>在执行 hexo clean 时会报错,如下<br><img src="https://s4.ax1x.com/2022/01/10/7E0SB9.png" alt="github actions"></p><blockquote><p>原因</p></blockquote><p>This is caused by the NPM package colors in a version &gt; 1.4.0. The developer deliberately introduced an infinite loop that prints this stuff to the console. The dependency on colors should be pinned to version 1.4.0</p><p><a href="https://github.com/Marak/colors.js/issues/285">Marak&#x2F;colors.js#285</a></p>]]></content>
302
302
 
@@ -317,7 +317,7 @@
317
317
  <link href="https://xuehuayu.cn/article/64189b69.html"/>
318
318
  <id>https://xuehuayu.cn/article/64189b69.html</id>
319
319
  <published>2021-12-31T14:47:39.000Z</published>
320
- <updated>2023-07-20T03:42:31.820Z</updated>
320
+ <updated>2023-07-21T09:10:52.811Z</updated>
321
321
 
322
322
  <content type="html"><![CDATA[<p>Hbuilder格式化插件js-beautify配置,<br>hbuilder X — 工具 — 设置 — 插件配置 — 打开 jsbeautifyrc.js 文件<br>工具 — 插件安装 — 如果有Prettier 则卸载 Prettier</p><span id="more"></span><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="attr">parsers</span>: &#123;</span><br><span class="line"> <span class="string">&quot;.js&quot;</span>: <span class="string">&quot;js&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.json&quot;</span>: <span class="string">&quot;js&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.njs&quot;</span>: <span class="string">&quot;js&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.sjs&quot;</span>: <span class="string">&quot;js&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.wxs&quot;</span>: <span class="string">&quot;js&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.css&quot;</span>: <span class="string">&quot;css&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.nss&quot;</span>: <span class="string">&quot;css&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.wxss&quot;</span>: <span class="string">&quot;css&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.acss&quot;</span>: <span class="string">&quot;css&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.ttss&quot;</span>: <span class="string">&quot;css&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.qss&quot;</span>: <span class="string">&quot;css&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.html&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.ux&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.wxml&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.nml&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.vue&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.nvue&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.axml&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.swan&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.ttml&quot;</span>: <span class="string">&quot;html&quot;</span>,</span><br><span class="line"> <span class="string">&quot;.qml&quot;</span>: <span class="string">&quot;html&quot;</span></span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">options</span>: &#123;</span><br><span class="line"> <span class="string">&quot;indent_size&quot;</span>: <span class="string">&quot;2&quot;</span>,</span><br><span class="line"> <span class="string">&quot;indent_char&quot;</span>: <span class="string">&quot;\t&quot;</span>,</span><br><span class="line"> <span class="string">&quot;indent_with_tabs&quot;</span>: <span class="literal">false</span>, <span class="comment">//使用tab缩进</span></span><br><span class="line"> <span class="string">&quot;eol&quot;</span>: <span class="string">&quot;\r\n&quot;</span>, <span class="comment">//行结束符</span></span><br><span class="line"> <span class="string">&quot;end_with_newline&quot;</span>: <span class="literal">false</span>, <span class="comment">//使用换行结束输出</span></span><br><span class="line"> <span class="string">&quot;indent_level&quot;</span>: <span class="number">0</span>, <span class="comment">//起始代码缩进数</span></span><br><span class="line"> <span class="string">&quot;preserve_newlines&quot;</span>: <span class="literal">true</span>, <span class="comment">//保留空行</span></span><br><span class="line"> <span class="string">&quot;max_preserve_newlines&quot;</span>: <span class="number">2</span>, <span class="comment">//最大连续保留换行符个数。比如设为2,则会将2行以上的空行删除为只保留1行</span></span><br><span class="line"> <span class="string">&quot;space_in_paren&quot;</span>: <span class="literal">false</span>, <span class="comment">//括弧添加空格 示例 f( a, b )</span></span><br><span class="line"> <span class="string">&quot;space_in_empty_paren&quot;</span>: <span class="literal">false</span>, <span class="comment">//函数的括弧内没有参数时插入空格 示例 f( )</span></span><br><span class="line"> <span class="string">&quot;jslint_happy&quot;</span>: <span class="literal">false</span>, <span class="comment">//启用jslint-strict模式</span></span><br><span class="line"> <span class="string">&quot;space_after_named_function&quot;</span>: <span class="literal">true</span>, <span class="comment">//在命名函数的括号之前添加一个空格,即。function example ()。 </span></span><br><span class="line"> <span class="string">&quot;space_after_anon_function&quot;</span>: <span class="literal">true</span>, <span class="comment">//匿名函数的括号前加空格</span></span><br><span class="line"> <span class="string">&quot;brace_style&quot;</span>: <span class="string">&quot;preserve-inline&quot;</span>, <span class="comment">//代码样式,可选值 [collapse|expand|end-expand|none][,preserve-inline] [collapse,preserve-inline</span></span><br><span class="line"> <span class="string">&quot;unindent_chained_methods&quot;</span>: <span class="literal">false</span>, <span class="comment">//不缩进链式方法调用</span></span><br><span class="line"> <span class="string">&quot;break_chained_methods&quot;</span>: <span class="literal">false</span>, <span class="comment">//在随后的行中断开链式方法调用</span></span><br><span class="line"> <span class="string">&quot;keep_array_indentation&quot;</span>: <span class="literal">false</span>, <span class="comment">//保持数组缩进</span></span><br><span class="line"> <span class="string">&quot;unescape_strings&quot;</span>: <span class="literal">false</span>, <span class="comment">//使用xNN符号编码解码可显示的字符</span></span><br><span class="line"> <span class="string">&quot;wrap_line_length&quot;</span>: <span class="number">9999</span>,</span><br><span class="line"> <span class="string">&quot;e4x&quot;</span>: <span class="literal">false</span>, <span class="comment">//支持jsx</span></span><br><span class="line"> <span class="string">&quot;comma_first&quot;</span>: <span class="literal">false</span>, <span class="comment">//把逗号放在新行开头,而不是结尾</span></span><br><span class="line"> <span class="string">&quot;operator_position&quot;</span>: <span class="string">&quot;before-newline&quot;</span>,</span><br><span class="line"> <span class="string">&quot;unformatted&quot;</span>: [<span class="string">&quot;wbr&quot;</span>],</span><br><span class="line"> <span class="string">&quot;html&quot;</span>: &#123;</span><br><span class="line"> <span class="string">&quot;wrap_attributes&quot;</span>: <span class="string">&quot;force-expand-multiline&quot;</span>, <span class="comment">//【own新添加】html属性是否换新行【auto自动,force强制换行,preserve保护现有格式 </span></span><br><span class="line"> <span class="string">&quot;indent_handlebars&quot;</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="string">&quot;indent_inner_html&quot;</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="string">&quot;indent-scripts&quot;</span>: <span class="string">&quot;normal&quot;</span>, <span class="comment">//[keep|separate|normal]</span></span><br><span class="line"> <span class="string">&quot;extra_liners&quot;</span>: [] <span class="comment">//配置标签列表,需要在这些标签前面额外加一空白行</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
323
323
 
@@ -338,7 +338,7 @@
338
338
  <link href="https://xuehuayu.cn/article/4292.html"/>
339
339
  <id>https://xuehuayu.cn/article/4292.html</id>
340
340
  <published>2021-12-20T16:36:23.000Z</published>
341
- <updated>2023-07-20T03:42:31.820Z</updated>
341
+ <updated>2023-07-21T09:10:52.811Z</updated>
342
342
 
343
343
  <content type="html"><![CDATA[<p>今天是2021.12.20,<code>jsdelivr</code> <code>cdn</code>大陆挂了不能访问了</p><span id="more"></span><p><code>jsdelivr</code>挂了,截止18点前,已经恢复了。</p><h4 id="项目中静态文件替换"><a href="#项目中静态文件替换" class="headerlink" title="项目中静态文件替换"></a>项目中静态文件替换</h4><p><a href="https://unicloud.dcloud.net.cn/">uniCloud云存储</a></p><p>使用阿里的cdn链接,还是免费的,步骤如下</p><ol><li>创建免费的[阿里云]服务空间</li><li>进入云空间,左侧点击“云存储”</li><li>上传文件后,点击“详情”,复制链接,</li></ol><h4 id="npm库替换"><a href="#npm库替换" class="headerlink" title="npm库替换"></a>npm库替换</h4><p>如果你使用的是<code>npm</code>库,使用<code>unpkg.com</code>替换!<br>什么?<code>unpkg</code>太慢了?好吧,使用知乎的国内镜像<code>unpkg.zhimg.com</code><br><strong>有人反应因为jsdelivr问题导致知乎镜像访问量剧增导致偶尔也会挂掉</strong></p><p>全局搜索<code>cdn.jsdelivr.net/npm</code>替换为<code>unpkg.zhimg.com</code></p><h4 id="github库替换"><a href="#github库替换" class="headerlink" title="github库替换"></a>github库替换</h4><ol><li><p>如果你使用的是<code>github</code>库,可以使用<code>staticaly</code>,请看我这篇文章:<a href="/article/54918.html"><strong>推荐几款免费且不限流量的全球CDN</strong></a></p></li><li><p>如果没有敏感信息,可以使用国内开源中国的<code>gitee</code>和CSDN的<code>gitcode</code>等,将github仓库克隆至<code>gitee</code> <code>gitcode</code>,然后使用<code>raw</code>原始文件</p></li></ol>]]></content>
344
344
 
@@ -359,7 +359,7 @@
359
359
  <link href="https://xuehuayu.cn/article/37789.html"/>
360
360
  <id>https://xuehuayu.cn/article/37789.html</id>
361
361
  <published>2021-12-20T13:31:59.000Z</published>
362
- <updated>2023-07-20T03:42:31.820Z</updated>
362
+ <updated>2023-07-21T09:10:52.811Z</updated>
363
363
 
364
364
  <content type="html"><![CDATA[<p>Promise.any无效?Promise.any在部分环境下并不支持,通过Promise.all实现Promise.any</p><span id="more"></span><blockquote><p>先看一下 Prmose.any 介绍</p></blockquote><p>只要有一个<code>Promise</code>状态变为<code>fullfilled</code>,则状态变为<code>fullfilled</code>,<br>只有所有的<code>Promise</code>状态变为<code>rejected</code>,则状态变为<code>rejected</code></p><blockquote><p>再看一下 Prmose.all 介绍</p></blockquote><p>只要有一个<code>Promise</code>状态变为<code>rejected</code>,则状态变为<code>rejected</code>,<br>只有所有的<code>Promise</code>状态变为<code>fullfilled</code>,则状态变为<code>fullfilled</code></p><blockquote><p>所以可以通过 Prmose.all 实现 Prmose.any, 取反就行了</p></blockquote><p><code>Talk is cheap, show you the code.</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> axios <span class="keyword">from</span> <span class="string">&#x27;axios&#x27;</span></span><br><span class="line"><span class="keyword">const</span> urls = [url1, url2, url3]</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ajaxUrls = urls.<span class="title function_">map</span>(<span class="function"><span class="params">url</span> =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line"> axios</span><br><span class="line"> .<span class="title function_">get</span>(url)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> <span class="title function_">reject</span>(res))</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> <span class="title function_">resolve</span>(err))</span><br><span class="line"> &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span></span><br><span class="line"> .<span class="title function_">all</span>(ajaxUrls)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// 都 ajax 都 resolve 时,即都 catch 的时候</span></span><br><span class="line"> <span class="comment">// 错误处理的代码</span></span><br><span class="line"> &#125;)</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// 只要有一个 ajax reject 时,即 then 的时候执</span></span><br><span class="line"> <span class="comment">// 处理 正常数据</span></span><br><span class="line"> &#125;)</span><br></pre></td></tr></table></figure>]]></content>
365
365
 
@@ -380,7 +380,7 @@
380
380
  <link href="https://xuehuayu.cn/article/50001.html"/>
381
381
  <id>https://xuehuayu.cn/article/50001.html</id>
382
382
  <published>2021-12-19T16:20:00.000Z</published>
383
- <updated>2023-07-20T03:42:31.820Z</updated>
383
+ <updated>2023-07-21T09:10:52.811Z</updated>
384
384
 
385
385
  <content type="html"><![CDATA[<p>vue3+vuex4,如获取模块state中的值,如何获取模块getters中的值,如何提交mutation和触发action</p><span id="more"></span><blockquote><p>store 写法</p></blockquote><p>模块a.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line"> <span class="attr">namespaced</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">state</span>: <span class="function">() =&gt;</span> (&#123;</span><br><span class="line"> <span class="attr">configs</span>: &#123;</span><br><span class="line"> <span class="attr">list</span>: []</span><br><span class="line"> &#125;,</span><br><span class="line"> &#125;),</span><br><span class="line"> <span class="attr">getters</span>: &#123;</span><br><span class="line"> list (state) &#123;</span><br><span class="line"> <span class="keyword">return</span> state.<span class="property">configs</span>.<span class="property">list</span> || []</span><br><span class="line"> &#125;,</span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">actions</span>: &#123;</span><br><span class="line"> <span class="keyword">async</span> myAction (&#123; commit &#125;) &#123;</span><br><span class="line"> <span class="keyword">const</span> url = <span class="string">&#x27;http://.....&#x27;</span></span><br><span class="line"> <span class="keyword">const</span> res = <span class="keyword">await</span> ajax.<span class="title function_">get</span>(url)</span><br><span class="line"> <span class="keyword">const</span> &#123; status, data &#125; = res</span><br><span class="line"> <span class="keyword">if</span> (status === <span class="number">200</span> &amp;&amp; data) &#123;</span><br><span class="line"> <span class="title function_">commit</span>(<span class="string">&#x27;myMutation&#x27;</span>, data)</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;,</span><br><span class="line"></span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">mutations</span>: &#123;</span><br><span class="line"> myMutation (state, payload) &#123;</span><br><span class="line"> state.<span class="property">configs</span> = payload</span><br><span class="line"> &#125;,</span><br><span class="line"> &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>modules.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 引入多个 模块</span></span><br><span class="line"><span class="keyword">import</span> a <span class="keyword">from</span> <span class="string">&#x27;./a.js&#x27;</span></span><br><span class="line">...其他模块...</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line"> a,</span><br><span class="line"> ...其他模块...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>store.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">&#x27;vuex&#x27;</span></span><br><span class="line"><span class="keyword">import</span> modules <span class="keyword">from</span> <span class="string">&#x27;./modules&#x27;</span> <span class="comment">// 引入 模块</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = <span class="title function_">createStore</span>(&#123;</span><br><span class="line"> modules, <span class="comment">// 模块</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">state</span>: &#123;&#125;, <span class="comment">// 全局</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">mutations</span>: &#123; &#125;, <span class="comment">// 全局</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">actions</span>: &#123; &#125;, <span class="comment">// 全局</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">getters</span>: &#123; &#125; <span class="comment">// 全局</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> store</span><br></pre></td></tr></table></figure><blockquote><p>在.vue文件中 使用 store</p></blockquote><p>.vue文件</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"> <span class="comment">// setup vue3 组合式 api 语法糖,所有变量方法可直接 使用</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 引入 响应式 如果不使用,则store中的值改变后,页面不会自动刷新</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; computed &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span> </span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; useStore &#125; <span class="keyword">from</span> <span class="string">&#x27;vuex&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> store = <span class="title function_">useStore</span>()</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> list = <span class="title function_">computed</span>(<span class="function">() =&gt;</span> store.<span class="property">getters</span>[<span class="string">&#x27;a/list&#x27;</span>]) <span class="comment">// 获取 getter中的值</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> config = <span class="title function_">computed</span>(<span class="function">() =&gt;</span> store.<span class="property">state</span>.<span class="property">a</span>.<span class="property">config</span>) <span class="comment">// 获取 state中的值</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">changeSite</span> = (<span class="params">index</span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript"> store.<span class="title function_">commit</span>(<span class="string">&#x27;a/myMutation&#x27;</span>, [index]) <span class="comment">// 提交 mutation</span></span></span><br><span class="line"><span class="language-javascript"> store.<span class="title function_">dispatch</span>(<span class="string">&#x27;a/myAction&#x27;</span>) <span class="comment">// 触发 action</span></span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">template</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-for</span>=<span class="string">&quot;(item, index) in list&quot;</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:key</span>=<span class="string">&quot;index&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123; &#123;item&#125; &#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure>]]></content>
386
386
 
@@ -401,7 +401,7 @@
401
401
  <link href="https://xuehuayu.cn/article/63074.html"/>
402
402
  <id>https://xuehuayu.cn/article/63074.html</id>
403
403
  <published>2021-12-17T16:37:04.000Z</published>
404
- <updated>2023-07-20T03:42:31.820Z</updated>
404
+ <updated>2023-07-21T09:10:52.811Z</updated>
405
405
 
406
406
  <content type="html"><![CDATA[<p>有时候,前端网页需要知道,用户使用的是手机浏览器还是桌面浏览器。<br>本文根据 StackOverflow,整理了 JavaScript 侦测手机浏览器的五种方法。</p><span id="more"></span><p>一、navigator.userAgent<br>最简单的方法就是分析浏览器的 user agent 字符串,它包含了设备信息。</p><p>JS 通过navigator.userAgent属性拿到这个字符串,只要里面包含mobi、android、iphone等关键字,就可以认定是移动设备。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="regexp">/Mobi|Android|iPhone/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>)) &#123;</span><br><span class="line"> <span class="comment">// 当前设备是移动设备</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 另一种写法</span></span><br><span class="line"><span class="keyword">if</span> (</span><br><span class="line"> navigator.<span class="property">userAgent</span>.<span class="title function_">match</span>(<span class="regexp">/Mobi/i</span>) ||</span><br><span class="line"> navigator.<span class="property">userAgent</span>.<span class="title function_">match</span>(<span class="regexp">/Android/i</span>) ||</span><br><span class="line"> navigator.<span class="property">userAgent</span>.<span class="title function_">match</span>(<span class="regexp">/iPhone/i</span>)</span><br><span class="line">) &#123;</span><br><span class="line"> <span class="comment">// 当前设备是移动设备</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种方法的优点是简单方便,缺点是不可靠,因为用户可以修改这个字符串,让手机浏览器伪装成桌面浏览器。</p><p>Chromium 系的浏览器,还有一个navigator.userAgentData属性,也是类似的作用。不同之处是它将 user agent 字符串解析为一个对象,该对象的mobile属性,返回一个布尔值,表示用户是否使用移动设备。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isMobile = navigator.<span class="property">userAgentData</span>.<span class="property">mobile</span>; </span><br></pre></td></tr></table></figure><p>注意,苹果的 Safari 浏览器和 Firefox 浏览器都不支持这个属性,具体情况可以查看 Caniuse 网站。</p><p>此外,还有一个已经废除的navigator.platform属性,所有浏览器都支持,所以也可以用。它返回一个字符串,表示用户的操作系统。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="regexp">/Android|iPhone|iPad|iPod/i</span>.<span class="title function_">test</span>(navigator.<span class="property">platform</span>)) &#123;</span><br><span class="line"> <span class="comment">// 当前设备是移动设备</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>二、window.screen,window.innerWidth<br>另一种方法是通过屏幕宽度,判断是否为手机。</p><p>window.screen对象返回用户设备的屏幕信息,该对象的width属性是屏幕宽度(单位为像素)。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">screen</span>.<span class="property">width</span> &lt; <span class="number">500</span>) &#123;</span><br><span class="line"> <span class="comment">// 当前设备是移动设备 </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面示例中,如果屏幕宽度window.screen.width小于500像素,就认为是手机。</p><p>这个方法的缺点在于,如果手机横屏使用,就识别不了。</p><p>另一个属性window.innerWidth返回浏览器窗口里面的网页可见部分的宽度,比较适合指定网页在不同宽度下的样式。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getBrowserWidth = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">innerWidth</span> &lt; <span class="number">768</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;xs&quot;</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">innerWidth</span> &lt; <span class="number">991</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;sm&quot;</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">innerWidth</span> &lt; <span class="number">1199</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;md&quot;</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;lg&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>三、window.orientation<br>第三种方法是侦测屏幕方向,手机屏幕可以随时改变方向(横屏或竖屏),桌面设备做不到。</p><p>window.orientation属性用于获取屏幕的当前方向,只有移动设备才有这个属性,桌面设备会返回undefined。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable language_">window</span>.<span class="property">orientation</span> !== <span class="string">&#x27;undefined&#x27;</span>) &#123;</span><br><span class="line"> <span class="comment">// 当前设备是移动设备 </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意,iPhone 的 Safari 浏览器不支持该属性。</p><p>四、touch 事件<br>第四种方法是,手机浏览器的 DOM 元素可以通过ontouchstart属性,为touch事件指定监听函数。桌面设备没有这个属性。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">isMobile</span>(<span class="params"></span>) &#123; </span><br><span class="line"> <span class="keyword">return</span> (<span class="string">&#x27;ontouchstart&#x27;</span> <span class="keyword">in</span> <span class="variable language_">document</span>.<span class="property">documentElement</span>); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 另一种写法</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">isMobile</span>(<span class="params"></span>) &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">createEvent</span>(<span class="string">&quot;TouchEvent&quot;</span>); <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>五、window.matchMedia()<br>最后一种方法是结合 CSS 来判断。</p><p>CSS 通过 media query(媒介查询)为网页指定响应式样式。如果某个针对手机的 media query 语句生效了,就可以认为当前设备是移动设备。</p><p>window.matchMedia()方法接受一个 CSS 的 media query 语句作为参数,判断这个语句是否生效。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> isMobile = <span class="variable language_">window</span>.<span class="title function_">matchMedia</span>(<span class="string">&quot;only screen and (max-width: 760px)&quot;</span>).<span class="property">matches</span>;</span><br></pre></td></tr></table></figure><p>上面示例中,window.matchMedia()的参数是一个 CSS 查询语句,表示只对屏幕宽度不超过 700 像素的设备生效。它返回一个对象,该对象的matches属性是一个布尔值。如果是true,就表示查询生效,当前设备是手机。</p><p>除了通过屏幕宽度判断,还可以通过指针的精确性判断。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> isMobile = <span class="variable language_">window</span>.<span class="title function_">matchMedia</span>(<span class="string">&quot;(pointer:coarse)&quot;</span>).<span class="property">matches</span>;</span><br></pre></td></tr></table></figure><p>上面示例中,CSS 语句pointer:coarse表示当前设备的指针是不精确的。由于手机不支持鼠标,只支持触摸,所以符合这个条件。</p><p>有些设备支持多种指针,比如同时支持鼠标和触摸。pointer:coarse只用来判断主指针,此外还有一个any-pointer命令判断所有指针。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> isMobile = <span class="variable language_">window</span>.<span class="title function_">matchMedia</span>(<span class="string">&quot;(any-pointer:coarse)&quot;</span>).<span class="property">matches</span>;</span><br></pre></td></tr></table></figure><p>上面示例中,any-pointer:coarse表示所有指针里面,只要有一个指针是不精确的,就符合查询条件。</p><p>六、工具包<br>除了上面这些方法,也可以使用别人写好的工具包。这里推荐 react-device-detect,它支持多种粒度的设备侦测。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;isMobile&#125; <span class="keyword">from</span> <span class="string">&#x27;react-device-detect&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (isMobile) &#123;</span><br><span class="line"> <span class="comment">// 当前设备是移动设备</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>(完)</p><blockquote><p>作者: 阮一峰 日期: 2021年9月29日<br><code>https://www.ruanyifeng.com/blog/2021/09/detecting-mobile-browser.html</code></p></blockquote>]]></content>
407
407
 
@@ -422,7 +422,7 @@
422
422
  <link href="https://xuehuayu.cn/article/45267.html"/>
423
423
  <id>https://xuehuayu.cn/article/45267.html</id>
424
424
  <published>2021-12-14T14:09:06.000Z</published>
425
- <updated>2023-07-20T03:42:31.820Z</updated>
425
+ <updated>2023-07-21T09:10:52.811Z</updated>
426
426
 
427
427
  <content type="html"><![CDATA[<p>uniapp横竖屏切换横屏根据手机重力感应方向自动转换</p><span id="more"></span><blockquote><p>通过配置锁定方向<br>pages.json文件中配置</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;globalStyle&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"> <span class="attr">&quot;pageOrientation&quot;</span><span class="punctuation">:</span> <span class="string">&quot;landscape&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p>通过js动态切换</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// #ifdef APP-PLUS</span></span><br><span class="line"><span class="comment">// 竖屏方向 根据手机中立感应 自动 切换 竖屏 正反方向</span></span><br><span class="line">plus.<span class="property">screen</span>.<span class="title function_">lockOrientation</span>(<span class="string">&#x27;portrait&#x27;</span>);</span><br><span class="line"><span class="comment">// 锁死 竖屏正方向</span></span><br><span class="line">plus.<span class="property">screen</span>.<span class="title function_">lockOrientation</span>(<span class="string">&#x27;portrait-primary&#x27;</span>);</span><br><span class="line"><span class="comment">// 锁死 竖屏反方向</span></span><br><span class="line">plus.<span class="property">screen</span>.<span class="title function_">lockOrientation</span>(<span class="string">&#x27;portrait-secondary&#x27;</span>);</span><br><span class="line"><span class="comment">// 横屏方向 根据手机中立感应 自动 切换 横屏 正反方向</span></span><br><span class="line">plus.<span class="property">screen</span>.<span class="title function_">lockOrientation</span>(<span class="string">&#x27;landscape&#x27;</span>);</span><br><span class="line"><span class="comment">// 锁死 横屏正方向</span></span><br><span class="line">plus.<span class="property">screen</span>.<span class="title function_">lockOrientation</span>(<span class="string">&#x27;landscape-primary&#x27;</span>);</span><br><span class="line"><span class="comment">// 锁死 横屏反方向</span></span><br><span class="line">plus.<span class="property">screen</span>.<span class="title function_">lockOrientation</span>(<span class="string">&#x27;landscape-secondary);</span></span><br><span class="line"><span class="string">// #endif</span></span><br></pre></td></tr></table></figure><blockquote><p>推荐</p></blockquote><p><a href="/article/54351.html">uni-app横竖屏切换之后样式错乱字体变大解决方法</a></p><blockquote><p>参考文档:<code>https://www.html5plus.org/doc/zh_cn/device.html#plus.screen.lockOrientation</code></p></blockquote>]]></content>
428
428