<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>lzh</title>
  
  <subtitle>简单生活</subtitle>
  <link href="/newBlog/atom.xml" rel="self"/>
  
  <link href="http://lizehongss.github.io/newBlog/"/>
  <updated>2022-01-31T16:00:00.000Z</updated>
  <id>http://lizehongss.github.io/newBlog/</id>
  
  <author>
    <name>lizehong</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>vue-router源码简要分析</title>
    <link href="http://lizehongss.github.io/newBlog/2022/02/01/vue-router%E6%BA%90%E7%A0%81%E7%AE%80%E8%A6%81%E5%88%86%E6%9E%90/"/>
    <id>http://lizehongss.github.io/newBlog/2022/02/01/vue-router%E6%BA%90%E7%A0%81%E7%AE%80%E8%A6%81%E5%88%86%E6%9E%90/</id>
    <published>2022-01-31T16:00:00.000Z</published>
    <updated>2022-01-31T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>主要分析了vue-router的部分源码，从而帮助理解vue-router的相关原理。主要从三个方面分析:</p><ul><li>vue-router插件初始化时所做的工作;</li><li>当路由发生改变时如何渲染<strong>router-view</strong>组件;</li><li>使用<strong>router-link</strong>是如何进行路由跳转的;</li></ul><p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c8dc8a5d6176408dba295b97ba14b339~tplv-k3u1fbpfcp-watermark.image?" alt="vue-router.png"></p><h2 id="vue-router插件初始化"><a href="#vue-router插件初始化" class="headerlink" title="vue-router插件初始化"></a>vue-router插件初始化</h2><p>一般在项目中引入vue-router插件时，所需代码如下</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="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Vue</span> <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">VueRouter</span> <span class="keyword">from</span> <span class="string">&#x27;vue-rouer&#x27;</span></span><br><span class="line"><span class="comment">// 引入vue-router插件</span></span><br><span class="line"><span class="title class_">Vue</span>.<span class="title function_">use</span>(<span class="title class_">VueRouter</span>)</span><br><span class="line"><span class="comment">// 实例化vue-router对象,传入options对象</span></span><br><span class="line"><span class="keyword">const</span> routes = [</span><br><span class="line">  &#123; <span class="attr">path</span>: <span class="string">&#x27;/foo&#x27;</span>, <span class="attr">component</span>: <span class="title class_">Foo</span> &#125;,</span><br><span class="line">  &#123; <span class="attr">path</span>: <span class="string">&#x27;/bar&#x27;</span>, <span class="attr">component</span>: <span class="title class_">Bar</span> &#125;</span><br><span class="line">]</span><br><span class="line"><span class="keyword">const</span> router  = <span class="keyword">new</span> <span class="title class_">VueRouter</span>(&#123;</span><br><span class="line">  routes</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 通过router配置参数注入路由实例化对象，使应用具有路由对象并且能够访问到路由实例化对象的属性和方法</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Vue</span>(&#123;</span><br><span class="line">  <span class="attr">components</span>: &#123; <span class="title class_">App</span> &#125;,</span><br><span class="line">  router,</span><br><span class="line">  store,</span><br><span class="line">  <span class="attr">template</span>: <span class="string">&#x27;&lt;App/&gt;&#x27;</span></span><br><span class="line">&#125;).$mount(<span class="string">&#x27;#app&#x27;</span>)</span><br></pre></td></tr></table></figure><p><strong>vue-router</strong>插件初始化调用的核心文件是<a href="https://github.com/vuejs/vue-router/blob/dev/src/index.js">index.js</a>,<a href="https://github.com/vuejs/vue-router/blob/dev/src/install.js">install.js</a>,<a href="https://github.com/vuejs/vue-router/blob/dev/src/history/base.js">base.js</a>文件。具体分析如下:</p><h3 id="调用install文件"><a href="#调用install文件" class="headerlink" title="调用install文件"></a>调用install文件</h3><p>当调用 <strong>Vue.use(VueRouter)</strong> 时会调用vue-router入口文件中的install方法</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="comment">// index.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; install &#125; <span class="keyword">from</span> <span class="string">&#x27;./install&#x27;</span></span><br><span class="line"><span class="comment">// ---</span></span><br><span class="line"><span class="title class_">VueRouter</span>.<span class="property">install</span> = install</span><br></pre></td></tr></table></figure><p>install方法定义在<a href="https://github.com/vuejs/vue-router/blob/dev/src/install.js">install.js</a>文件中，从源码分析它主要完成以下内容：</p><ol><li><p>在<strong>Vue</strong>中<strong>beforeCreate</strong>和<strong>destroyed</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></pre></td><td class="code"><pre><span class="line"><span class="title class_">Vue</span>.<span class="title function_">mixin</span>(&#123;</span><br><span class="line">  <span class="title function_">beforeCreate</span> () &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_">isDef</span>(<span class="variable language_">this</span>.<span class="property">$options</span>.<span class="property">router</span>)) &#123;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">_routerRoot</span> = <span class="variable language_">this</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">_router</span> = <span class="variable language_">this</span>.<span class="property">$options</span>.<span class="property">router</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">_router</span>.<span class="title function_">init</span>(<span class="variable language_">this</span>)</span><br><span class="line">      <span class="title class_">Vue</span>.<span class="property">util</span>.<span class="title function_">defineReactive</span>(<span class="variable language_">this</span>, <span class="string">&#x27;_route&#x27;</span>, <span class="variable language_">this</span>.<span class="property">_router</span>.<span class="property">history</span>.<span class="property">current</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">_routerRoot</span> = (<span class="variable language_">this</span>.<span class="property">$parent</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">$parent</span>.<span class="property">_routerRoot</span>) || <span class="variable language_">this</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">registerInstance</span>(<span class="variable language_">this</span>, <span class="variable language_">this</span>)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="title function_">destroyed</span> () &#123;</span><br><span class="line">    <span class="title function_">registerInstance</span>(<span class="variable language_">this</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>主要作用是当vue组件渲染时，执行以下逻辑：</p><ul><li>将Vue根实例或者子组件离它最近的父实例赋值给<strong>this._routerRoot</strong></li><li>将<strong>this.$options.router</strong>  (访问 vue的options,在main.js已将其指向vue-router的实例化对象)  赋值给this._router</li><li>调用vue-router实例化对象的init方法（后文分析）</li><li>将 <strong>this._route</strong> 赋值为 <strong>this._router.history.curren</strong>，并使其为响应式对象</li><li>执行registerInstance方法(后文分析)</li></ul></li><li><p>定义<strong>this.$router</strong>和<strong>this.$route</strong>属性，方便Vue组件使用</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></pre></td><td class="code"><pre><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="title class_">Vue</span>.<span class="property"><span class="keyword">prototype</span></span>, <span class="string">&#x27;$router&#x27;</span>, &#123;</span><br><span class="line">  <span class="comment">// 返回vue-router实例对象</span></span><br><span class="line">  <span class="title function_">get</span> () &#123; <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">_routerRoot</span>.<span class="property">_router</span> &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="title class_">Vue</span>.<span class="property"><span class="keyword">prototype</span></span>, <span class="string">&#x27;$route&#x27;</span>, &#123;</span><br><span class="line">  <span class="comment">// 返回history实例化对象的current属性</span></span><br><span class="line">  <span class="title function_">get</span> () &#123; <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">_routerRoot</span>.<span class="property">_route</span> &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li><li><p>注册<strong>router-view</strong>和<strong>router-link</strong>组件</p></li></ol><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_">Vue</span>.<span class="title function_">component</span>(<span class="string">&#x27;RouterView&#x27;</span>, <span class="title class_">View</span>)</span><br><span class="line"><span class="title class_">Vue</span>.<span class="title function_">component</span>(<span class="string">&#x27;RouterLink&#x27;</span>, <span class="title class_">Link</span>)</span><br></pre></td></tr></table></figure><h3 id="实例化vue-router对象"><a href="#实例化vue-router对象" class="headerlink" title="实例化vue-router对象"></a>实例化vue-router对象</h3><p>VueRouter类定义在<a href="https://github.com/vuejs/vue-router/blob/dev/src/components/view.js">index.js</a>文件中，当在项目的<strong>main.js</strong>实例化一个vue-router对象时，在<strong>constructor</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><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></pre></td><td class="code"><pre><span class="line"><span class="title function_">constructor</span> (<span class="params">options: RouterOptions = &#123;&#125;</span>) &#123;</span><br><span class="line">  <span class="comment">// ....</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">app</span> = <span class="literal">null</span> <span class="comment">// 保存根Vue实例</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">apps</span> = [] <span class="comment">// 保存有this.$options.router属性的Vue实例</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">options</span> = options <span class="comment">// 保存传入的路由配置</span></span><br><span class="line">  <span class="comment">// 保存用户定义的钩子回调函数</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">beforeHooks</span> = []</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">resolveHooks</span> = []</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">afterHooks</span> = []</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">matcher</span> = <span class="title function_">createMatcher</span>(options.<span class="property">routes</span> || [], <span class="variable language_">this</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> mode = options.<span class="property">mode</span> || <span class="string">&#x27;hash&#x27;</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">fallback</span> =</span><br><span class="line">    mode === <span class="string">&#x27;history&#x27;</span> &amp;&amp; !supportsPushState &amp;&amp; options.<span class="property">fallback</span> !== <span class="literal">false</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">fallback</span>) &#123;</span><br><span class="line">    mode = <span class="string">&#x27;hash&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (!inBrowser) &#123;</span><br><span class="line">    mode = <span class="string">&#x27;abstract&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">mode</span> = mode</span><br><span class="line"></span><br><span class="line">  <span class="keyword">switch</span> (mode) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;history&#x27;</span>:</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">history</span> = <span class="keyword">new</span> <span class="title class_">HTML5History</span>(<span class="variable language_">this</span>, options.<span class="property">base</span>)</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;hash&#x27;</span>:</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">history</span> = <span class="keyword">new</span> <span class="title class_">HashHistory</span>(<span class="variable language_">this</span>, options.<span class="property">base</span>, <span class="variable language_">this</span>.<span class="property">fallback</span>)</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;abstract&#x27;</span>:</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">history</span> = <span class="keyword">new</span> <span class="title class_">AbstractHistory</span>(<span class="variable language_">this</span>, options.<span class="property">base</span>)</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">    <span class="attr">default</span>:</span><br><span class="line">      <span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> !== <span class="string">&#x27;production&#x27;</span>) &#123;</span><br><span class="line">        <span class="title function_">assert</span>(<span class="literal">false</span>, <span class="string">`invalid mode: <span class="subst">$&#123;mode&#125;</span>`</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><ul><li>调用<a href="https://github.com/vuejs/vue-router/blob/dev/src/history/base.js">createMatcher</a>方法将传入的路由配置进行处理生成<strong>路由匹配器</strong>并赋值给this.matcher(后方分析)</li><li>根据路由创建的模式实例化history对象</li></ul><h3 id="Vue组件挂载渲染时VueRouter初始化"><a href="#Vue组件挂载渲染时VueRouter初始化" class="headerlink" title="Vue组件挂载渲染时VueRouter初始化"></a>Vue组件挂载渲染时VueRouter初始化</h3><p>当Vue组件渲染时会触发<strong>beforeCreate</strong>钩子，从上文可以得知如果是根实例时会触发VueRouter实例的init方法，传入根实例的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><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></pre></td><td class="code"><pre><span class="line"><span class="title function_">init</span> (<span class="attr">app</span>: any <span class="comment">/* Vue component instance */</span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">apps</span>.<span class="title function_">push</span>(app)</span><br><span class="line">  <span class="comment">// ---</span></span><br><span class="line">  <span class="comment">// vueRouter实例已经初始化时返回</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">app</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">app</span> = app</span><br><span class="line">  <span class="keyword">const</span> history = <span class="variable language_">this</span>.<span class="property">history</span></span><br><span class="line">  <span class="keyword">if</span> (history <span class="keyword">instanceof</span> <span class="title class_">HTML5History</span> || history <span class="keyword">instanceof</span> <span class="title class_">HashHistory</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="title function_">handleInitialScroll</span> = routeOrError =&gt; &#123;</span><br><span class="line">      <span class="keyword">const</span> <span class="keyword">from</span> = history.<span class="property">current</span></span><br><span class="line">      <span class="keyword">const</span> expectScroll = <span class="variable language_">this</span>.<span class="property">options</span>.<span class="property">scrollBehavior</span></span><br><span class="line">      <span class="keyword">const</span> supportsScroll = supportsPushState &amp;&amp; expectScroll</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (supportsScroll &amp;&amp; <span class="string">&#x27;fullPath&#x27;</span> <span class="keyword">in</span> routeOrError) &#123;</span><br><span class="line">        <span class="title function_">handleScroll</span>(<span class="variable language_">this</span>, routeOrError, <span class="keyword">from</span>, <span class="literal">false</span>)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> <span class="title function_">setupListeners</span> = routeOrError =&gt; &#123;</span><br><span class="line">      history.<span class="title function_">setupListeners</span>()</span><br><span class="line">      <span class="title function_">handleInitialScroll</span>(routeOrError)</span><br><span class="line">    &#125;</span><br><span class="line">    history.<span class="title function_">transitionTo</span>(</span><br><span class="line">      history.<span class="title function_">getCurrentLocation</span>(),</span><br><span class="line">      setupListeners,</span><br><span class="line">      setupListeners</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  history.<span class="title function_">listen</span>(<span class="function"><span class="params">route</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">apps</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">      app.<span class="property">_route</span> = route</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>主要逻辑如下:</p><ul><li>调用<strong>history.transitionTo</strong>方法去更新修改浏览器url路径</li><li>添加<strong>history</strong>事件监听，当浏览器url路径更新时，更新<strong>app._route</strong>(app._route是在上文install文件中的响应式对象_route)</li></ul><p>需要注意的是在<a href="https://github.com/vuejs/vue-router/blob/dev/src/history/base.js">transitionTo</a>中会调用<a href="https://github.com/vuejs/vue-router/blob/dev/src/history/base.js">confirmTransition</a>方法去执行路由导航守卫钩子。</p><h2 id="router-view渲染机制"><a href="#router-view渲染机制" class="headerlink" title="router-view渲染机制"></a>router-view渲染机制</h2><p>在上文中，已经知道浏览器url改变时会触发<strong>app._route</strong>更新，而它在初始化时被设为响应式对象。</p><p>在<a href="https://github.com/vuejs/vue-router/blob/dev/src/components/view.js">router-view</a>源码中可以看到在执行render函数时会调用<strong>parnet.$route</strong>, 由于 <strong>route</strong>是响应式对象，当访问 <strong>route</strong> 时会使 <strong>router-view</strong>组件对 <strong>route</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">render</span> (_, &#123; props, children, parent, data &#125;) &#123;</span><br><span class="line">  <span class="keyword">const</span> route = parent.<span class="property">$route</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> matched = route.<span class="property">matched</span>[depth]</span><br><span class="line">  <span class="keyword">const</span> component = matched &amp;&amp; matched.<span class="property">components</span>[name]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">h</span>(component, data, children)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<a href="https://github.com/vuejs/vue-router/blob/dev/src/install.js">install</a>文件中，可以看到获取 <strong>$route</strong> 的值时，返回的是 <strong>_router</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></pre></td><td class="code"><pre><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="title class_">Vue</span>.<span class="property"><span class="keyword">prototype</span></span>, <span class="string">&#x27;$route&#x27;</span>, &#123;</span><br><span class="line">  <span class="title function_">get</span> () &#123; <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">_routerRoot</span>.<span class="property">_route</span> &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>结合上面的分析，当浏览器url改变时，会修改 <strong>_route</strong>的值，而 <strong>_route</strong>是一个响应式对象，它更新时会触发setter,从而通知route-view的渲染<strong>watcher</strong>更新，重新渲染组件。</p><h2 id="router-link跳转机制"><a href="#router-link跳转机制" class="headerlink" title="router-link跳转机制"></a>router-link跳转机制</h2><p><a href="https://github.com/vuejs/vue-router/blob/dev/src/components/link.js">router-link</a>定义在文件src/components/link中，主要的点击跳转代码如下:</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="keyword">const</span> router = <span class="variable language_">this</span>.<span class="property">$router</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">handler</span> = e =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="title function_">guardEvent</span>(e)) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">replace</span>) &#123;</span><br><span class="line">      router.<span class="title function_">replace</span>(location, noop)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      router.<span class="title function_">push</span>(location, noop)</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><span class="line"><span class="keyword">const</span> on = &#123; <span class="attr">click</span>: guardEvent &#125;</span><br><span class="line"><span class="keyword">if</span> (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(<span class="variable language_">this</span>.<span class="property">event</span>)) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">event</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">e</span> =&gt;</span> &#123;</span><br><span class="line">    on[e] = handler</span><br><span class="line">  &#125;)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  on[<span class="variable language_">this</span>.<span class="property">event</span>] = handler</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当<strong>router-link</strong>触发点击事件时，会执行router.replace或者push方法，从上文可以得知<strong>this.$router</strong>是<strong>vue-router</strong>的实例对象,replace和push方法定义在<a href="https://github.com/vuejs/vue-router/blob/dev/src/index.js">index.js</a>文件中。</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="title function_">push</span> (<span class="attr">location</span>: <span class="title class_">RawLocation</span>, onComplete?: <span class="title class_">Function</span>, onAbort?: <span class="title class_">Function</span>) &#123;</span><br><span class="line">  <span class="comment">// $flow-disable-line</span></span><br><span class="line">  <span class="keyword">if</span> (!onComplete &amp;&amp; !onAbort &amp;&amp; <span class="keyword">typeof</span> <span class="title class_">Promise</span> !== <span class="string">&#x27;undefined&#x27;</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">      <span class="variable language_">this</span>.<span class="property">history</span>.<span class="title function_">push</span>(location, resolve, reject)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">history</span>.<span class="title function_">push</span>(location, onComplete, onAbort)</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 function_">replace</span> (<span class="attr">location</span>: <span class="title class_">RawLocation</span>, onComplete?: <span class="title class_">Function</span>, onAbort?: <span class="title class_">Function</span>) &#123;</span><br><span class="line">  <span class="comment">// $flow-disable-line</span></span><br><span class="line">  <span class="keyword">if</span> (!onComplete &amp;&amp; !onAbort &amp;&amp; <span class="keyword">typeof</span> <span class="title class_">Promise</span> !== <span class="string">&#x27;undefined&#x27;</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">      <span class="variable language_">this</span>.<span class="property">history</span>.<span class="title function_">replace</span>(location, resolve, reject)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">history</span>.<span class="title function_">replace</span>(location, onComplete, onAbort)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到会访问history实例化对象中的replace和push方法，不同的<strong>mode</strong>push和replace定义不同，当<strong>mode</strong>为hashj时，会访问到<a href="https://github.com/vuejs/vue-router/blob/dev/src/history/hash.js">hash.js</a>中，主要代码如下:</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">push</span> (<span class="attr">location</span>: <span class="title class_">RawLocation</span>, onComplete?: <span class="title class_">Function</span>, onAbort?: <span class="title class_">Function</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; <span class="attr">current</span>: fromRoute &#125; = <span class="variable language_">this</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="title function_">transitionTo</span>(</span><br><span class="line">    location,</span><br><span class="line">    <span class="function"><span class="params">route</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="title function_">pushHash</span>(route.<span class="property">fullPath</span>)</span><br><span class="line">      <span class="title function_">handleScroll</span>(<span class="variable language_">this</span>.<span class="property">router</span>, route, fromRoute, <span class="literal">false</span>)</span><br><span class="line">      onComplete &amp;&amp; <span class="title function_">onComplete</span>(route)</span><br><span class="line">    &#125;,</span><br><span class="line">    onAbort</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">replace</span> (<span class="attr">location</span>: <span class="title class_">RawLocation</span>, onComplete?: <span class="title class_">Function</span>, onAbort?: <span class="title class_">Function</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; <span class="attr">current</span>: fromRoute &#125; = <span class="variable language_">this</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="title function_">transitionTo</span>(</span><br><span class="line">    location,</span><br><span class="line">    <span class="function"><span class="params">route</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="title function_">replaceHash</span>(route.<span class="property">fullPath</span>)</span><br><span class="line">      <span class="title function_">handleScroll</span>(<span class="variable language_">this</span>.<span class="property">router</span>, route, fromRoute, <span class="literal">false</span>)</span><br><span class="line">      onComplete &amp;&amp; <span class="title function_">onComplete</span>(route)</span><br><span class="line">    &#125;,</span><br><span class="line">    onAbort</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>push和reaplace方法都会调用transitionTo方法去修改浏览器url（主要不同是修改浏览器url的方式不同），从而触发 <strong>router-view</strong> 组件重新渲染，进且更新页面。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>从上文可以看出，vue-router的主要原理是通过监听浏览器url的改变，来触发router-view组件根据路由定义的组件重新渲染页面。而调用路由的push，replace等方法时，最终都会触发改变浏览器的url。从而保证了组件的及时刷新。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://ustbhuangyi.github.io/vue-analysis/v2/vue-router/">Vue.js 技术揭秘</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;主要分析了vue-router的部分源码，从而帮助理解vue-router的相关原理。主要从三个方面分析:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vue
      
    
    </summary>
    
    
      <category term="源码学习" scheme="http://lizehongss.github.io/newBlog/categories/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>PDF展示与合成Vue组件(lz-pdf-merage)</title>
    <link href="http://lizehongss.github.io/newBlog/2022/01/21/PDF%E5%B1%95%E7%A4%BA%E4%B8%8E%E5%90%88%E6%88%90Vue%E7%BB%84%E4%BB%B6(lz-pdf-merage)/"/>
    <id>http://lizehongss.github.io/newBlog/2022/01/21/PDF%E5%B1%95%E7%A4%BA%E4%B8%8E%E5%90%88%E6%88%90Vue%E7%BB%84%E4%BB%B6(lz-pdf-merage)/</id>
    <published>2022-01-20T16:00:00.000Z</published>
    <updated>2022-01-20T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>主要是介绍使用pdfjs和jsPdf在vue组件中对pdf文件进行展示和对原有pdf进行合成(包括文字，图片合成)。<br><br></p><h2 id="体验"><a href="#体验" class="headerlink" title="体验"></a>体验</h2><p><a href="https://lizehongss.github.io/lz-pdf-merage/dist/index.html">demo地址</a></p><p>npm包引入</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">npm install lz-pdf-merage</span><br></pre></td></tr></table></figure><h2 id="pdf展示"><a href="#pdf展示" class="headerlink" title="pdf展示"></a>pdf展示</h2><p>主要使用pdfjs(<strong>使用的版本是2.1.266</strong>)对pdf文件通过对canvas对其进行预览展示，主要原理如下:</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="title class_">Pdfjs</span> <span class="keyword">from</span> <span class="string">&#x27;pdfjs-dist&#x27;</span></span><br><span class="line"><span class="comment">// 获取pdf对象</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">pdf</span> = <span class="keyword">await</span> <span class="title class_">Pdfjs</span>.<span class="title function_">getDocument</span>(&#123;</span><br><span class="line">  <span class="attr">url</span>: pdfPath</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 获取一页的pdf数据</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">pdf</span>.<span class="title function_">getPage</span>(<span class="number">1</span>)</span><br><span class="line"><span class="comment">// 获取htmlK中的canvas元素</span></span><br><span class="line"><span class="keyword">let</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;canvas&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> context = canvas.<span class="title function_">getContext</span>(<span class="string">&#x27;2d&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> scale = <span class="number">3.2</span></span><br><span class="line"><span class="comment">// 放大pdf</span></span><br><span class="line"><span class="keyword">let</span> viewport = page.<span class="title function_">getViewport</span>(scale, <span class="variable language_">this</span>.<span class="property">rotate</span>)</span><br><span class="line"><span class="comment">// 设置canvas的宽度和高度，以解决显示模糊的问题</span></span><br><span class="line">canvas.<span class="property">height</span> = viewport.<span class="property">height</span>;</span><br><span class="line">canvas.<span class="property">width</span> = viewport.<span class="property">width</span>;</span><br><span class="line">canvas.<span class="property">style</span>.<span class="property">width</span> = <span class="string">`<span class="subst">$&#123;<span class="variable language_">this</span>.width * widthScale&#125;</span>px`</span></span><br><span class="line">canvas.<span class="property">style</span>.<span class="property">height</span> = <span class="string">`<span class="subst">$&#123;<span class="variable language_">this</span>.width/viewport.width * viewport.height * widthScale&#125;</span>px`</span>;</span><br><span class="line"><span class="comment">//将pdf数据渲染到指定的canvas上</span></span><br><span class="line"><span class="keyword">let</span> renderContext = &#123;</span><br><span class="line">  <span class="attr">canvasContext</span>: context,</span><br><span class="line">  <span class="attr">viewport</span>: viewport</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">await</span> page.<span class="title function_">render</span>(renderContext);</span><br></pre></td></tr></table></figure><p>需要注意的点：</p><ul><li>为了解决pdf在canvas上渲染后模糊的问题，需要将pdf通过getViewport(scale，0)方法进行放大。同时设置canvas的style的width和height使其在页面上正常展示。</li><li>对于对pdf进行放大缩小的功能，pdf本身已经放大了3.2倍了，所以只需要控制canvas的style样式展示（即通过widthScale控制），就可以在页面上呈现放大缩小的效果。</li><li>旋转功能的实现，一样是通过getViewport(scale, this.rotate)控制。</li></ul><h2 id="pdf合成"><a href="#pdf合成" class="headerlink" title="pdf合成"></a>pdf合成</h2><p>pdf合成主要是创建一个新的canvas元素，其大小与pdf预览的canvas一致，然后将需要添加的图片和文字记录坐标并添加到新的canvas元素上。最后通过drawImage分别获取到两个canvas对应的图片，通过jsPdf将两张图片合成新的pdf页，从而实现pdf合成。</p><h3 id="canvas元素合成"><a href="#canvas元素合成" class="headerlink" title="canvas元素合成"></a>canvas元素合成</h3><p>主要代码及解释如下：</p><figure class="highlight html"><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="tag">&lt;<span class="name">canvas</span> <span class="attr">class</span>=<span class="string">&quot;draw-canvas&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;drawCanvas&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">Page</span> <span class="attr">ref</span>=<span class="string">&quot;page&quot;</span> <span class="attr">:defaultRotate</span>=<span class="string">&quot;pageRotate&quot;</span> <span class="attr">:page</span>=<span class="string">&quot;currentPage&quot;</span> <span class="attr">:width</span>=<span class="string">&quot;pdfWidth - 25&quot;</span> @<span class="attr">saveRotate</span>=<span class="string">&quot;saveRotate&quot;</span> @<span class="attr">drawFinish</span>=<span class="string">&quot;handleFinishDraw&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">Page</span>&gt;</span></span><br></pre></td></tr></table></figure><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="comment">// 在pdfjs渲染完成canvas后触发</span></span><br><span class="line"><span class="title function_">handleFinishDraw</span>(<span class="params">canvas</span>)&#123;</span><br><span class="line">  <span class="comment">// 获取当前pdf所在页</span></span><br><span class="line">  <span class="keyword">let</span> current = <span class="variable language_">this</span>.<span class="property">current</span> - <span class="number">1</span></span><br><span class="line">  <span class="comment">// 将渲染的pdf存放到baseCanvasImages中，方便后续合成</span></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">baseCanvasImages</span>[current] = &#123;</span><br><span class="line">    <span class="attr">url</span>: canvas.<span class="title function_">toDataURL</span>(<span class="string">&#x27;image/jpeg&#x27;</span>),</span><br><span class="line">    <span class="attr">width</span>: canvas.<span class="property">width</span>,</span><br><span class="line">    <span class="attr">height</span>: canvas.<span class="property">height</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="property">canvasArray</span>[current]) &#123;</span><br><span class="line">    <span class="keyword">let</span> &#123; width, height &#125; = canvas.<span class="title function_">getBoundingClientRect</span>()</span><br><span class="line">    <span class="comment">// 设置canvas的宽高</span></span><br><span class="line">    <span class="keyword">let</span> drawCanvas = <span class="variable language_">this</span>.<span class="title function_">setDrawCanvas</span>(canvas)</span><br><span class="line">    <span class="comment">// 生成一个DrawObj实例，并存入canvasArray中，方便后续切换页时添加,</span></span><br><span class="line">    <span class="comment">// DrawObj是对canvas画图进行操作的类，会在后续中介绍</span></span><br><span class="line">    <span class="comment">// 当添加图片，文字时，都是对该实例进行操作</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">canvasArray</span>[current] = <span class="keyword">new</span> <span class="title class_">DrawObj</span>(drawCanvas, current, canvas.<span class="property">width</span>/width, canvas.<span class="property">height</span>/height)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 对drawCanvas进行重绘，因为有可能进行了切换页</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">canvasArray</span>[current].<span class="title function_">drawItemList</span>()</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 设置drawCanvas的宽高</span></span><br><span class="line">  <span class="title function_">setDrawCanvas</span>(<span class="params">canvas</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> drawCanvas = <span class="variable language_">this</span>.<span class="property">$refs</span>.<span class="property">drawCanvas</span></span><br><span class="line">    drawCanvas.<span class="property">width</span> = canvas.<span class="property">width</span></span><br><span class="line">    drawCanvas.<span class="property">height</span> = canvas.<span class="property">height</span></span><br><span class="line">    drawCanvas.<span class="property">style</span>.<span class="property">width</span> = canvas.<span class="property">style</span>.<span class="property">width</span></span><br><span class="line">    drawCanvas.<span class="property">style</span>.<span class="property">height</span> = canvas.<span class="property">style</span>.<span class="property">height</span></span><br><span class="line">    <span class="keyword">return</span> drawCanvas</span><br><span class="line">  &#125;,</span><br></pre></td></tr></table></figure><h3 id="pdf合成叠加"><a href="#pdf合成叠加" class="headerlink" title="pdf合成叠加"></a>pdf合成叠加</h3><p>主要介绍对pdfjs渲染而成的canvas和drawCanvas通过jsPdf进行合成</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="title function_">getTheMeragePdf</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> pdf = <span class="keyword">new</span> <span class="title function_">jsPDF</span>(<span class="string">&#x27;p&#x27;</span>, <span class="string">&#x27;pt&#x27;</span>, <span class="string">&#x27;a4&#x27;</span>)</span><br><span class="line">  <span class="comment">// pdfjs渲染而成的canvas生成base64</span></span><br><span class="line">  <span class="keyword">let</span> pdfImages = <span class="keyword">await</span> <span class="variable language_">this</span>.<span class="title function_">getThePdfImages</span>()</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i&lt; pdfImages.<span class="property">length</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> imageObj = pdfImages[i]</span><br><span class="line">    pdf.<span class="title function_">addImage</span>(imageObj.<span class="property">url</span>, <span class="string">&#x27;JPEG&#x27;</span>, <span class="number">0</span>,<span class="number">0</span>, <span class="number">592.28</span>, <span class="number">592.28</span>/imageObj.<span class="property">width</span> * imageObj.<span class="property">height</span>)</span><br><span class="line">    <span class="comment">//如果当前页对应的drawCanvas存在，叠加图片</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">canvasArray</span>[i]) &#123;</span><br><span class="line">      <span class="keyword">let</span> drawCanvas = <span class="variable language_">this</span>.<span class="property">canvasArray</span>[i].<span class="title function_">getDrawCanvas</span>()</span><br><span class="line">      <span class="keyword">const</span> drawData = drawCanvas.<span class="title function_">toDataURL</span>(<span class="string">&#x27;image/png&#x27;</span>)</span><br><span class="line">      pdf.<span class="title function_">addImage</span>(drawData, <span class="string">&#x27;PNG&#x27;</span>, <span class="number">0</span>,<span class="number">0</span>, <span class="number">592.28</span>, <span class="number">592.28</span>/drawCanvas.<span class="property">width</span> * drawCanvas.<span class="property">height</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 大于一页时，生成新的一页</span></span><br><span class="line">    <span class="keyword">if</span> (i &lt; pdfImages.<span class="property">length</span> - <span class="number">1</span>) pdf.<span class="title function_">addPage</span>() </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 下载合成后的pdf</span></span><br><span class="line">    pdf.<span class="title function_">save</span>(<span class="string">&#x27;合成的pdf&#x27;</span>)</span><br><span class="line">  &#125;\</span><br><span class="line">  <span class="comment">// 对pdf渲染而成的canvas生成base64</span></span><br><span class="line">  <span class="keyword">async</span> <span class="title function_">getThePdfImages</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> pdfImages = <span class="title class_">Array</span>(<span class="variable language_">this</span>.<span class="property">total</span>).<span class="title function_">fill</span>(<span class="title class_">Infinity</span>)</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i&lt;pdfImages.<span class="property">length</span>; i++) &#123;</span><br><span class="line">      <span class="comment">// use the canvas after the user operation</span></span><br><span class="line">      <span class="comment">// 如果已存在，直接返回</span></span><br><span class="line">      <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">baseCanvasImages</span>[i])&#123;</span><br><span class="line">        pdfImages[i] = <span class="variable language_">this</span>.<span class="property">baseCanvasImages</span>[i]</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 不存在时，调用page接口渲染生成</span></span><br><span class="line">        pdfImages[i] = <span class="keyword">await</span> <span class="variable language_">this</span>.<span class="property">$refs</span>.<span class="property">page</span>.<span class="title function_">drawPage</span>(<span class="variable language_">this</span>.<span class="property">pdf</span>.<span class="title function_">getPage</span>(i+<span class="number">1</span>))</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> pdfImages</span><br><span class="line">    &#125;,</span><br></pre></td></tr></table></figure><h3 id="DrawObj"><a href="#DrawObj" class="headerlink" title="DrawObj"></a>DrawObj</h3><p><a href="https://github.com/lizehongss/lz-pdf-merage/blob/master/src/components/pdf-view/canvasDraw.js">DrawObj</a>是一个自定义的类，它主要实现在canvas中进行添加图片，添加文字，对添加的图片，文字进行移动和删除功能。</p><p><strong>对元素的操作都会反映到_drawItemList数组中,每一次操作都会清空画布，然后再重新对_drawItemList里的元素进行重绘，元素操作对_drawItemList数组的修改如下:</strong></p><ul><li>添加元素时，会向_drawItemList数组中添加一个元素对象(主要包括元素内容，元素类型，元素位置等信息)。</li><li>删除元素时， 会删除_drawItemList数组中该元素。</li><li>移动元素时， 会修改_drawItemList数组中对应元素的X,Y。</li></ul><p>需要注意的是由于在pdf展示时是对原有canvas进行缩小展示的，所以在调用canvas绘制元素前要调用<strong>ctx.scale(this._scaleX, this._scaleY)</strong>，this._scaleX和this._scaleY在初始化DrawObj时传入：</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="comment">// canvas.width/width, canvas.height/height, 是pdf展示的canvas缩放比例</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">canvasArray</span>[current] = <span class="keyword">new</span> <span class="title class_">DrawObj</span>(drawCanvas, current, canvas.<span class="property">width</span>/  width, canvas.<span class="property">height</span>/height)</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h2 id="pdf打印"><a href="#pdf打印" class="headerlink" title="pdf打印"></a>pdf打印</h2><p>pdf打印的原理主要还是pdfjs对canvas进行渲染生成，然后将每一页对应的canvas通过drawImage方法生成图片，再将图片添加到body上，并通过 <strong>@media print</strong>媒体查询控制打印样式最后调用window.print()打印。具体代码可以看这里<br><a href="https://github.com/lizehongss/lz-pdf-merage/blob/master/src/components/pdf-view/printPdf.js">printPdf</a>。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;主要是介绍使用pdfjs和jsPdf在vue组件中对pdf文件进行展示和对原有pdf进行合成(包括文字，图片合成)。&lt;br&gt;&lt;br&gt;&lt;/p&gt;
      
    
    </summary>
    
    
      <category term="vue组件" scheme="http://lizehongss.github.io/newBlog/categories/vue%E7%BB%84%E4%BB%B6/"/>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>vue3.2是怎么发布vue-release的</title>
    <link href="http://lizehongss.github.io/newBlog/2021/12/02/%E7%AC%AC%E4%B8%89%E6%9C%9F%20_%20vue%203.2%20%E6%98%AF%E6%80%8E%E4%B9%88%E5%8F%91%E5%B8%83%E7%9A%84%20vue-release/"/>
    <id>http://lizehongss.github.io/newBlog/2021/12/02/%E7%AC%AC%E4%B8%89%E6%9C%9F%20_%20vue%203.2%20%E6%98%AF%E6%80%8E%E4%B9%88%E5%8F%91%E5%B8%83%E7%9A%84%20vue-release/</id>
    <published>2021-12-01T16:00:00.000Z</published>
    <updated>2021-12-01T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="相关源码文件及依赖"><a href="#相关源码文件及依赖" class="headerlink" title="相关源码文件及依赖"></a>相关源码文件及依赖</h1><p>vue3 跟发布相关的文件主要目录如下:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vue-next/scripts/release.<span class="property">js</span></span><br></pre></td></tr></table></figure><p>所使用到的引入依赖如下：</p><figure class="highlight javascript"><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> args = <span class="built_in">require</span>(<span class="string">&#x27;minimist&#x27;</span>)(process.<span class="property">argv</span>.<span class="title function_">slice</span>(<span class="number">2</span>))</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> chalk = <span class="built_in">require</span>(<span class="string">&#x27;chalk&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> semver = <span class="built_in">require</span>(<span class="string">&#x27;semver&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> currentVersion = <span class="built_in">require</span>(<span class="string">&#x27;../package.json&#x27;</span>).<span class="property">version</span></span><br><span class="line"><span class="keyword">const</span> &#123; prompt &#125; = <span class="built_in">require</span>(<span class="string">&#x27;enquirer&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> execa = <span class="built_in">require</span>(<span class="string">&#x27;execa&#x27;</span>)</span><br></pre></td></tr></table></figure><p>各依赖作用如下</p><ul><li><p><a href="https://github.com/substack/minimist">minimist</a> 主要是获取命令行参数, 这里会将所有的命令行参数添加进args对象中</p></li><li><p>fs, path 都是node常用的自带模块，主要是操作文件和路径</p></li><li><p><a href="https://github.com/chalk/chalk">chalk</a> 主要用于终端美化</p></li><li><p><a href="https://www.npmjs.cn/misc/semver/">semver</a> 主要作用是对版本校验比较</p></li><li><p>currentVersion 获取当前package.jsonr的version信息</p></li><li><p><a href="https://github.com/enquirer/enquirer">enquirer</a>: 主要用来创建命令行交互式询问用户输入，这里引用的prompt给用户提供输入，选择等功能</p></li><li><p><a href="https://github.com/sindresorhus/execa">execa</a>: 主要是用来执行命令行命令<br><a name="VR10z"></a></p><h1 id="主要流程"><a href="#主要流程" class="headerlink" title="主要流程"></a>主要流程</h1><p><a name="U5zw5"></a></p><h2 id="流程图"><a href="#流程图" class="headerlink" title="流程图"></a>流程图</h2><p><img src="https://s3.bmp.ovh/imgs/2021/12/ee6e02c0e482ff2c.png" alt="vue3发布流程图.drawio (1).png"><br><a name="ddal6"></a></p><h2 id="重要函数分析"><a href="#重要函数分析" class="headerlink" title="重要函数分析"></a>重要函数分析</h2><figure class="highlight javascript"><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 function_">inc</span> = i =&gt; semver.<span class="title function_">inc</span>(currentVersion, i, preId)</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">bin</span> = name =&gt; path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;../node_modules/.bin/&#x27;</span> + name)</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">run</span> = (<span class="params">bin, args, opts = &#123;&#125;</span>) =&gt;</span><br><span class="line">  <span class="title function_">execa</span>(bin, args, &#123; <span class="attr">stdio</span>: <span class="string">&#x27;inherit&#x27;</span>, ...opts &#125;)</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">dryRun</span> = (<span class="params">bin, args, opts = &#123;&#125;</span>) =&gt;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(chalk.<span class="title function_">blue</span>(<span class="string">`[dryrun] <span class="subst">$&#123;bin&#125;</span> <span class="subst">$&#123;args.join(<span class="string">&#x27; &#x27;</span>)&#125;</span>`</span>), opts)</span><br><span class="line"><span class="keyword">const</span> runIfNotDry = isDryRun ? dryRun : run</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getPkgRoot</span> = pkg =&gt; path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;../packages/&#x27;</span> + pkg)</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">step</span> = msg =&gt; <span class="variable language_">console</span>.<span class="title function_">log</span>(chalk.<span class="title function_">cyan</span>(msg))</span><br></pre></td></tr></table></figure></li><li><p><strong>inc</strong>函数主要是通过semver库的inc方法生成一个版本号,如：semver.inc(‘3.2.4’, ‘prerelease’, ‘beta’) =&gt; 3.2.5-beta.0</p></li><li><p><strong>bin</strong>函数主要使用是传入name，返回node_modules/.bin/${name} ,方便获取 node_modules/.bin/ 目录下的命令</p></li><li><p><strong>run</strong>函数主要通过execa执行命令行命令, 如: run(‘yarn’, [‘build’, ‘–release’]) =&gt; yarn build –release</p></li><li><p><strong>dryRun</strong>函数是空跑命令， 通过console.log打印传入的命令，方便调试</p></li><li><p><strong>runIfNotDry</strong>通过一开始用户传入的命令行参数<strong>isDryRun</strong>来判断是空跑还是真的执行命令</p></li><li><p><strong>getPKgRoot</strong>获取包路径</p></li><li><p><strong>step函数</strong>通过chalk打印传入的信息<br><a name="o8UBA"></a></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>大致明白了Vue3是如何发布包的，整个流程其实也可以应用在公司前端项目的发布中，例如通过输入的版本号修改pack.json信息，自动执行git命令，校验版本号等。<br><a name="Le0Dk"></a></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1></li><li><p><a href="https://juejin.cn/post/6997943192851054606#heading-24">Vue 3.2 发布了，那尤雨溪是怎么发布 Vue.js 的？</a></p></li><li><p><a href="https://www.yuque.com/ruochuan12/notice/p2">第二期 | vue3 工具函数 | 源码共读公告【必看】</a>​</p></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;相关源码文件及依赖&quot;&gt;&lt;a href=&quot;#相关源码文件及依赖&quot; class=&quot;headerlink&quot; title=&quot;相关源码文件及依赖&quot;&gt;&lt;/a&gt;相关源码文件及依赖&lt;/h1&gt;&lt;p&gt;vue3 跟发布相关的文件主要目录如下:&lt;/p&gt;
&lt;figure class=&quot;hi
      
    
    </summary>
    
    
      <category term="源码学习" scheme="http://lizehongss.github.io/newBlog/categories/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="vue3" scheme="http://lizehongss.github.io/newBlog/tags/vue3/"/>
    
  </entry>
  
  <entry>
    <title>vue3 工具函数源码学习</title>
    <link href="http://lizehongss.github.io/newBlog/2021/11/07/vue3%20%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    <id>http://lizehongss.github.io/newBlog/2021/11/07/vue3%20%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/</id>
    <published>2021-11-06T16:00:00.000Z</published>
    <updated>2021-11-06T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="vue3工具函数目录结构"><a href="#vue3工具函数目录结构" class="headerlink" title="vue3工具函数目录结构"></a>vue3工具函数目录结构</h2><p>vue3工具函数所在目录如下所示</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">vue-nex/packages/shared/src</span><br></pre></td></tr></table></figure><p>src目录下有以下文件:<strong>​</strong><br><img src="https://cdn.nlark.com/yuque/0/2021/png/1022616/1635670732893-e86882a3-292d-4b84-b787-9bc1c4ac4a77.png#clientId=uf59860a1-0cb7-4&from=paste&height=301&id=u8ebe0df7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=301&originWidth=447&originalType=binary&ratio=1&size=19079&status=done&style=none&taskId=ue3e0b117-2885-4ec7-a0c7-8e2c3370b55&width=447" alt="image.png"><br>其中index.ts是入口文件，其它文件在index.ts中引入， index中定义了常用的工具函数</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></pre></td><td class="code"><pre><span class="line">import &#123; makeMap &#125; from &#x27;./makeMap&#x27;</span><br><span class="line"></span><br><span class="line">export &#123; makeMap &#125;</span><br><span class="line">export * from &#x27;./patchFlags&#x27;</span><br><span class="line">export * from &#x27;./shapeFlags&#x27;</span><br><span class="line">export * from &#x27;./slotFlags&#x27;</span><br><span class="line">export * from &#x27;./globalsWhitelist&#x27;</span><br><span class="line">export * from &#x27;./codeframe&#x27;</span><br><span class="line">export * from &#x27;./normalizeProp&#x27;</span><br><span class="line">export * from &#x27;./domTagConfig&#x27;</span><br><span class="line">export * from &#x27;./domAttrConfig&#x27;</span><br><span class="line">export * from &#x27;./escapeHtml&#x27;</span><br><span class="line">export * from &#x27;./looseEqual&#x27;</span><br><span class="line">export * from &#x27;./toDisplayString&#x27;</span><br></pre></td></tr></table></figure><h2 id="主要工具函数解析"><a href="#主要工具函数解析" class="headerlink" title="主要工具函数解析"></a>主要工具函数解析</h2><p>这里主要学习理解index里的工具函数，部分源码和解析如下:</p><figure class="highlight javascript"><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">export</span> <span class="keyword">const</span> <span class="attr">EMPTY_OBJ</span>: &#123; readonly [<span class="attr">key</span>: string]: any &#125; = __DEV__</span><br><span class="line">  ? <span class="title class_">Object</span>.<span class="title function_">freeze</span>(&#123;&#125;)</span><br><span class="line">  : &#123;&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="variable constant_">EMPTY_ARR</span> = __DEV__ ? <span class="title class_">Object</span>.<span class="title function_">freeze</span>([]) : []</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">NOOP</span> = (<span class="params"></span>) =&gt; &#123;&#125;</span><br></pre></td></tr></table></figure><ul><li><p>EMPTY_OBJ和EMPTY_ARR都使用<strong>Object.freeze()</strong>, 它的作用是冻结一个对象，使其不能被修改。</p></li><li><p>EMPTY_OBJ和EMPTY_ARR返回一个空对象和空数组，并且在开发环境是被冻结， 主要作用应该是在开发环境修改空对象时使其报错明显。</p></li><li><p>NOOP返回一个空函数，方便判断和压缩代码，每个地方都写 ()=&gt; {} 明显代码量会增多</p><figure class="highlight javascript"><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">export</span> <span class="keyword">const</span> <span class="title function_">NO</span> = (<span class="params"></span>) =&gt; <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> onRE = <span class="regexp">/^on[^a-z]/</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">isOn</span> = (<span class="params">key: string</span>) =&gt; onRE.<span class="title function_">test</span>(key)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">isModelListener</span> = (<span class="params">key: string</span>) =&gt; key.<span class="title function_">startsWith</span>(<span class="string">&#x27;onUpdate:&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> extend = <span class="title class_">Object</span>.<span class="property">assign</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> remove = &lt;T&gt;<span class="function">(<span class="params">arr: T[], el: T</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> i = arr.<span class="title function_">indexOf</span>(el)</span><br><span class="line">  <span class="keyword">if</span> (i &gt; -<span class="number">1</span>) &#123;</span><br><span class="line">    arr.<span class="title function_">splice</span>(i, <span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>NO 返回false，主要也是压缩代码量</p></li><li><p>isOn 判断通过正则判断字符串是否是on开头，并且on 后首字母不是小写字母，如 <strong>onReg</strong></p></li><li><p>isModelListener 通过判断字符串是否是<strong>onUpdate:</strong> 开头</p></li><li><p>extend 提供<strong>Object.assign</strong>方法的缩写， 应该主要也是为了压缩代码</p></li><li><p>remove 通过<strong>splice方法</strong>删除数组中的一项，传入数组和索引</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> hasOwnProperty = <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">hasOwnProperty</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> hasOwn = (</span><br><span class="line">  <span class="attr">val</span>: object,</span><br><span class="line">  <span class="attr">key</span>: string | symbol</span><br><span class="line">): key is keyof <span class="keyword">typeof</span> val =&gt; hasOwnProperty.<span class="title function_">call</span>(val, key)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isArray = <span class="title class_">Array</span>.<span class="property">isArray</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isMap = (<span class="attr">val</span>: unknown): val is <span class="title class_">Map</span>&lt;any, any&gt; =&gt;</span><br><span class="line">  <span class="title function_">toTypeString</span>(val) === <span class="string">&#x27;[object Map]&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isSet = (<span class="attr">val</span>: unknown): val is <span class="title class_">Set</span>&lt;any&gt; =&gt;</span><br><span class="line">  <span class="title function_">toTypeString</span>(val) === <span class="string">&#x27;[object Set]&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isDate = (<span class="attr">val</span>: unknown): val is <span class="title class_">Date</span> =&gt; val <span class="keyword">instanceof</span> <span class="title class_">Date</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isFunction = (<span class="attr">val</span>: unknown): val is <span class="title class_">Function</span> =&gt;</span><br><span class="line">  <span class="keyword">typeof</span> val === <span class="string">&#x27;function&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isString = (<span class="attr">val</span>: unknown): val is string =&gt; <span class="keyword">typeof</span> val === <span class="string">&#x27;string&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isSymbol = (<span class="attr">val</span>: unknown): val is symbol =&gt; <span class="keyword">typeof</span> val === <span class="string">&#x27;symbol&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isObject = (<span class="attr">val</span>: unknown): val is <span class="title class_">Record</span>&lt;any, any&gt; =&gt;</span><br><span class="line">  val !== <span class="literal">null</span> &amp;&amp; <span class="keyword">typeof</span> val === <span class="string">&#x27;object&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isPromise = &lt;T = any&gt;(<span class="attr">val</span>: unknown): val is <span class="title class_">Promise</span>&lt;T&gt; =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">isObject</span>(val) &amp;&amp; <span class="title function_">isFunction</span>(val.<span class="property">then</span>) &amp;&amp; <span class="title function_">isFunction</span>(val.<span class="property">catch</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> objectToString = <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> toTypeString = (<span class="attr">value</span>: unknown): <span class="function"><span class="params">string</span> =&gt;</span></span><br><span class="line">  objectToString.<span class="title function_">call</span>(value)</span><br></pre></td></tr></table></figure><p>上述的主要函数的主要作用都是判断是否为ES的某一些内置类型</p></li><li><p>hasOwn 通过Object.prototype.hasOwnProerty判断对象本身是否有key对应的属性</p></li><li><p>isArray 通过Array.isArray方法判断,使用instanceof判断并不准确</p></li><li><p>isMap isSet通过Object.prototype.toString判断是否为<strong>Map</strong>,<strong>Set</strong></p></li><li><p>isDate 通过instanceof判断</p></li><li><p>isFunction, isString,isSymbol,isObject通过<strong>typeof判断</strong></p></li><li><p>isPromise通过val是否为对象，then和catch是否为函数判断</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> toRawType = (<span class="attr">value</span>: unknown): <span class="function"><span class="params">string</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// extract &quot;RawType&quot; from strings like &quot;[object RawType]&quot;</span></span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">toTypeString</span>(value).<span class="title function_">slice</span>(<span class="number">8</span>, -<span class="number">1</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">const</span> isPlainObject = (<span class="attr">val</span>: unknown): val is object =&gt;</span><br><span class="line">  <span class="title function_">toTypeString</span>(val) === <span class="string">&#x27;[object Object]&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">isIntegerKey</span> = (<span class="params">key: unknown</span>) =&gt;</span><br><span class="line">  <span class="title function_">isString</span>(key) &amp;&amp;</span><br><span class="line">  key !== <span class="string">&#x27;NaN&#x27;</span> &amp;&amp;</span><br><span class="line">  key[<span class="number">0</span>] !== <span class="string">&#x27;-&#x27;</span> &amp;&amp;</span><br><span class="line">  <span class="string">&#x27;&#x27;</span> + <span class="built_in">parseInt</span>(key, <span class="number">10</span>) === key</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isReservedProp = <span class="comment">/*#__PURE__*/</span> <span class="title function_">makeMap</span>(</span><br><span class="line">  <span class="comment">// the leading comma is intentional so empty string &quot;&quot; is also included</span></span><br><span class="line">  <span class="string">&#x27;,key,ref,&#x27;</span> +</span><br><span class="line">    <span class="string">&#x27;onVnodeBeforeMount,onVnodeMounted,&#x27;</span> +</span><br><span class="line">    <span class="string">&#x27;onVnodeBeforeUpdate,onVnodeUpdated,&#x27;</span> +</span><br><span class="line">    <span class="string">&#x27;onVnodeBeforeUnmount,onVnodeUnmounted&#x27;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cacheStringFunction = &lt;T <span class="title function_">extends</span> (<span class="attr">str</span>: string) =&gt; string&gt;(<span class="attr">fn</span>: T): <span class="function"><span class="params">T</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> <span class="attr">cache</span>: <span class="title class_">Record</span>&lt;string, string&gt; = <span class="title class_">Object</span>.<span class="title function_">create</span>(<span class="literal">null</span>)</span><br><span class="line">  <span class="keyword">return</span> (<span class="function">(<span class="params">str: string</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> hit = cache[str]</span><br><span class="line">    <span class="keyword">return</span> hit || (cache[str] = <span class="title function_">fn</span>(str))</span><br><span class="line">  &#125;) <span class="keyword">as</span> any</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>toRawType截取toTypeString中的一部分，主要是_string,object_这些，如[object Set]返回Set</p></li><li><p>isPlainObject 判断是不是纯粹的对象， 如isPlainObject([]) // false</p></li><li><p>isIntegerKey 判断是不是数字型的字符串key值</p></li><li><p>cacheStringFunction 缓存函数， 实现一个单例</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> camelizeRE = <span class="regexp">/-(\w)/g</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@private</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> camelize = <span class="title function_">cacheStringFunction</span>((<span class="attr">str</span>: string): <span class="function"><span class="params">string</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> str.<span class="title function_">replace</span>(camelizeRE, <span class="function">(<span class="params">_, c</span>) =&gt;</span> (c ? c.<span class="title function_">toUpperCase</span>() : <span class="string">&#x27;&#x27;</span>))</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> hyphenateRE = <span class="regexp">/\B([A-Z])/g</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@private</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> hyphenate = <span class="title function_">cacheStringFunction</span>(<span class="function">(<span class="params">str: string</span>) =&gt;</span></span><br><span class="line">  str.<span class="title function_">replace</span>(hyphenateRE, <span class="string">&#x27;-$1&#x27;</span>).<span class="title function_">toLowerCase</span>()</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@private</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> capitalize = <span class="title function_">cacheStringFunction</span>(</span><br><span class="line">  <span class="function">(<span class="params">str: string</span>) =&gt;</span> str.<span class="title function_">charAt</span>(<span class="number">0</span>).<span class="title function_">toUpperCase</span>() + str.<span class="title function_">slice</span>(<span class="number">1</span>)</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@private</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> toHandlerKey = <span class="title function_">cacheStringFunction</span>(<span class="function">(<span class="params">str: string</span>) =&gt;</span></span><br><span class="line">  str ? <span class="string">`on<span class="subst">$&#123;capitalize(str)&#125;</span>`</span> : <span class="string">``</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>上述函数都使用<strong>cacheStringFunction函数</strong>包裹，确保返回第一次所创建的那唯一的一个实例。</p></li><li><p>camelize 连字符 - 转驼峰， 将on-click中的-c匹配并替换为C</p></li><li><p>hyphenate 将驼峰转连字符， 将onClick中的C匹配到并替换为-C再通过toLowerCase转为小写</p></li><li><p>capitalize 首字母大写</p></li><li><p>toHandlerKey将click转化onClick这种</p><figure class="highlight javascript"><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="comment">// compare whether a value has changed, accounting for NaN.</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> hasChanged = (<span class="attr">value</span>: any, <span class="attr">oldValue</span>: any): <span class="function"><span class="params">boolean</span> =&gt;</span></span><br><span class="line">  !<span class="title class_">Object</span>.<span class="title function_">is</span>(value, oldValue)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">invokeArrayFns</span> = (<span class="params">fns: <span class="built_in">Function</span>[], arg?: any</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; fns.<span class="property">length</span>; i++) &#123;</span><br><span class="line">    fns[i](arg)</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">export</span> <span class="keyword">const</span> <span class="title function_">def</span> = (<span class="params">obj: object, key: string | symbol, value: any</span>) =&gt; &#123;</span><br><span class="line">  <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(obj, key, &#123;</span><br><span class="line">    <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">enumerable</span>: <span class="literal">false</span>,</span><br><span class="line">    value</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">export</span> <span class="keyword">const</span> toNumber = (<span class="attr">val</span>: any): <span class="function"><span class="params">any</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> n = <span class="built_in">parseFloat</span>(val)</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">isNaN</span>(n) ? val : n</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>hasChanged: 通过<strong>Object.is()</strong>判断两个值是否严格相等</p></li><li><p>invokeArrayFns：执行数组里的函数</p></li><li><p>def: 定义对象属性，使其可删除和不可枚举</p></li><li><p>toNumber : 将值转换为数字</p><figure class="highlight javascript"><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">let</span> <span class="attr">_globalThis</span>: any</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> getGlobalThis = (): <span class="function"><span class="params">any</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    _globalThis ||</span><br><span class="line">    (_globalThis =</span><br><span class="line">      <span class="keyword">typeof</span> globalThis !== <span class="string">&#x27;undefined&#x27;</span></span><br><span class="line">        ? globalThis</span><br><span class="line">        : <span class="keyword">typeof</span> self !== <span class="string">&#x27;undefined&#x27;</span></span><br><span class="line">        ? self</span><br><span class="line">        : <span class="keyword">typeof</span> <span class="variable language_">window</span> !== <span class="string">&#x27;undefined&#x27;</span></span><br><span class="line">        ? <span class="variable language_">window</span></span><br><span class="line">        : <span class="keyword">typeof</span> <span class="variable language_">global</span> !== <span class="string">&#x27;undefined&#x27;</span></span><br><span class="line">        ? <span class="variable language_">global</span></span><br><span class="line">        : &#123;&#125;)</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>getGlobalThis: 获取全局 this 指向, 依次查找__globalThis, self ,window, global</p></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;vue3工具函数目录结构&quot;&gt;&lt;a href=&quot;#vue3工具函数目录结构&quot; class=&quot;headerlink&quot; title=&quot;vue3工具函数目录结构&quot;&gt;&lt;/a&gt;vue3工具函数目录结构&lt;/h2&gt;&lt;p&gt;vue3工具函数所在目录如下所示&lt;/p&gt;
&lt;figure c
      
    
    </summary>
    
    
      <category term="源码学习" scheme="http://lizehongss.github.io/newBlog/categories/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="vue3" scheme="http://lizehongss.github.io/newBlog/tags/vue3/"/>
    
  </entry>
  
  <entry>
    <title>vue-响应式原理</title>
    <link href="http://lizehongss.github.io/newBlog/2021/10/24/vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86/"/>
    <id>http://lizehongss.github.io/newBlog/2021/10/24/vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86/</id>
    <published>2021-10-23T16:00:00.000Z</published>
    <updated>2021-10-23T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="通过object-defineProperty设置getter和setter"><a href="#通过object-defineProperty设置getter和setter" class="headerlink" title="通过object.defineProperty设置getter和setter"></a>通过object.defineProperty设置getter和setter</h1><p>vue初始化调用_init方法, 在init中会执行initState方法，对props,methods,data等属性进行初始化, 主要代码如下：</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></pre></td><td class="code"><pre><span class="line">// 省略部分代码</span><br><span class="line">export function initState (vm: Component) &#123;</span><br><span class="line">  vm._watchers = []</span><br><span class="line">  const opts = vm.$options</span><br><span class="line">  if (opts.props) initProps(vm, opts.props)</span><br><span class="line">  if (opts.methods) initMethods(vm, opts.methods)</span><br><span class="line">  if (opts.data) &#123;</span><br><span class="line">    initData(vm)</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    observe(vm._data = &#123;&#125;, true /* asRootData */)</span><br><span class="line">  &#125;</span><br><span class="line">  if (opts.computed) initComputed(vm, opts.computed)</span><br><span class="line">  if (opts.watch &amp;&amp; opts.watch !== nativeWatch) &#123;</span><br><span class="line">    initWatch(vm, opts.watch)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中主要方法作用如下：</p><ul><li>initProps()调用deineReactive方法使其变成响应式。</li><li>initData()-&gt;调用observe方法使其变为响应式</li><li>observe()给对象添加getter和setter,用于依赖收集和派发更新。代码如下:<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></pre></td><td class="code"><pre><span class="line">export function observe (value: any, asRootData: ?boolean): Observer | void &#123;</span><br><span class="line">  if (!isObject(value) || value instanceof VNode) &#123;</span><br><span class="line">    return</span><br><span class="line">  &#125;</span><br><span class="line">  let ob: Observer | void</span><br><span class="line">  if (hasOwn(value, &#x27;__ob__&#x27;) &amp;&amp; value.__ob__ instanceof Observer) &#123;</span><br><span class="line">    ob = value.__ob__</span><br><span class="line">  &#125; else if (</span><br><span class="line">    shouldObserve &amp;&amp;</span><br><span class="line">    !isServerRendering() &amp;&amp;</span><br><span class="line">    (Array.isArray(value) || isPlainObject(value)) &amp;&amp;</span><br><span class="line">    Object.isExtensible(value) &amp;&amp;</span><br><span class="line">    !value._isVue</span><br><span class="line">  ) &#123;</span><br><span class="line">    ob = new Observer(value)</span><br><span class="line">  &#125;</span><br><span class="line">  if (asRootData &amp;&amp; ob) &#123;</span><br><span class="line">    ob.vmCount++</span><br><span class="line">  &#125;</span><br><span class="line">  return ob</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>主要是实例化Observer实现响应式, Observer的实现如下: 通过定义getter和setter实现<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></pre></td><td class="code"><pre><span class="line">export class Observer &#123;</span><br><span class="line">  value: any;</span><br><span class="line">  dep: Dep;</span><br><span class="line">  vmCount: number; </span><br><span class="line">  constructor (value: any) &#123;</span><br><span class="line">    this.value = value</span><br><span class="line">  // 实例化Dep对象，Dep是一个订阅者，用来存放Watcher观察者对象</span><br><span class="line">    this.dep = new Dep()</span><br><span class="line">    this.vmCount = 0</span><br><span class="line">    // 添加 this.__ob__ = value</span><br><span class="line">    def(value, &#x27;__ob__&#x27;, this)</span><br><span class="line">    if (Array.isArray(value)) &#123;</span><br><span class="line">      const augment = hasProto</span><br><span class="line">        ? protoAugment</span><br><span class="line">        : copyAugment</span><br><span class="line">      augment(value, arrayMethods, arrayKeys)</span><br><span class="line">      this.observeArray(value)</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      this.walk(value)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  // 遍历对象的 key 调用 defineReactive</span><br><span class="line">  walk (obj: Object) &#123;</span><br><span class="line">    const keys = Object.keys(obj)</span><br><span class="line">    for (let i = 0; i &lt; keys.length; i++) &#123;</span><br><span class="line">      defineReactive(obj, keys[i])</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  // 遍历数组调用observe</span><br><span class="line">  observeArray (items: Array&lt;any&gt;) &#123;</span><br><span class="line">    for (let i = 0, l = items.length; i &lt; l; i++) &#123;</span><br><span class="line">      observe(items[i])</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>definReactive定义一个响应式对象<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><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><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">export function defineReactive (</span><br><span class="line">  obj: Object,</span><br><span class="line">  key: string,</span><br><span class="line">  val: any,</span><br><span class="line">  customSetter?: ?Function,</span><br><span class="line">  shallow?: boolean</span><br><span class="line">) &#123;</span><br><span class="line">  // 实例化Dep对象</span><br><span class="line">  const dep = new Dep()</span><br><span class="line">  const property = Object.getOwnPropertyDescriptor(obj, key)</span><br><span class="line">  if (property &amp;&amp; property.configurable === false) &#123;</span><br><span class="line">    return</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // cater for pre-defined getter/setters</span><br><span class="line">  const getter = property &amp;&amp; property.get</span><br><span class="line">  const setter = property &amp;&amp; property.set</span><br><span class="line">  if ((!getter || setter) &amp;&amp; arguments.length === 2) &#123;</span><br><span class="line">    val = obj[key]</span><br><span class="line">  &#125;</span><br><span class="line">  // 对子对象递归调用</span><br><span class="line">  let childOb = !shallow &amp;&amp; observe(val)</span><br><span class="line">  //给对象添加getter和setter</span><br><span class="line">  Object.defineProperty(obj, key, &#123;</span><br><span class="line">    enumerable: true,</span><br><span class="line">    configurable: true,</span><br><span class="line">    get: function reactiveGetter () &#123;</span><br><span class="line">      const value = getter ? getter.call(obj) : val</span><br><span class="line">      if (Dep.target) &#123;</span><br><span class="line">        dep.depend()</span><br><span class="line">        if (childOb) &#123;</span><br><span class="line">          childOb.dep.depend()</span><br><span class="line">          if (Array.isArray(value)) &#123;</span><br><span class="line">            dependArray(value)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      return value</span><br><span class="line">    &#125;,</span><br><span class="line">    set: function reactiveSetter (newVal) &#123;</span><br><span class="line">      const value = getter ? getter.call(obj) : val</span><br><span class="line">      /* eslint-disable no-self-compare */</span><br><span class="line">      if (newVal === value || (newVal !== newVal &amp;&amp; value !== value)) &#123;</span><br><span class="line">        return</span><br><span class="line">      &#125;</span><br><span class="line">      /* eslint-enable no-self-compare */</span><br><span class="line">      if (process.env.NODE_ENV !== &#x27;production&#x27; &amp;&amp; customSetter) &#123;</span><br><span class="line">        customSetter()</span><br><span class="line">      &#125;</span><br><span class="line">      if (setter) &#123;</span><br><span class="line">        setter.call(obj, newVal)</span><br><span class="line">      &#125; else &#123;</span><br><span class="line">        val = newVal</span><br><span class="line">      &#125;</span><br><span class="line">      childOb = !shallow &amp;&amp; observe(newVal)</span><br><span class="line">      dep.notify()</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><h1 id="依赖收集和派发更新"><a href="#依赖收集和派发更新" class="headerlink" title="依赖收集和派发更新"></a>依赖收集和派发更新</h1><h2 id="依赖收集"><a href="#依赖收集" class="headerlink" title="依赖收集"></a>依赖收集</h2></li><li>defineReactive的get方法会调用dep.depen()进行依赖收集，Dep类的主要作用是管理Watch对象<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></pre></td><td class="code"><pre><span class="line">export default class Dep &#123;</span><br><span class="line">  // target全局唯一 Watcher</span><br><span class="line">  static target: ?Watcher;</span><br><span class="line">  id: number;</span><br><span class="line">  subs: Array&lt;Watcher&gt;;</span><br><span class="line"></span><br><span class="line">  constructor () &#123;</span><br><span class="line">    this.id = uid++</span><br><span class="line">    this.subs = []</span><br><span class="line">  &#125;</span><br><span class="line">  // 在 subs中添加Watcher，也就是添加属性发生改变时，要通知的watcher</span><br><span class="line">  addSub (sub: Watcher) &#123;</span><br><span class="line">    this.subs.push(sub)</span><br><span class="line">  &#125;</span><br><span class="line">  // 移除Wathcer</span><br><span class="line">  removeSub (sub: Watcher) &#123;</span><br><span class="line">    remove(this.subs, sub)</span><br><span class="line">  &#125;</span><br><span class="line">  // 依赖收集</span><br><span class="line">  depend () &#123;</span><br><span class="line">    if (Dep.target) &#123;</span><br><span class="line">      Dep.target.addDep(this)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  // 通知Watcher更新，也就是派发更新</span><br><span class="line">  notify () &#123;</span><br><span class="line">    // stabilize the subscriber list first</span><br><span class="line">    const subs = this.subs.slice()</span><br><span class="line">    for (let i = 0, l = subs.length; i &lt; l; i++) &#123;</span><br><span class="line">      subs[i].update()</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></li><li>Watcher类的定义如下:<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><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><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line">let uid = 0</span><br><span class="line">export default class Watcher &#123;</span><br><span class="line">  constructor (</span><br><span class="line">    vm: Component,</span><br><span class="line">    expOrFn: string | Function,</span><br><span class="line">    cb: Function,</span><br><span class="line">    options?: ?Object,</span><br><span class="line">    isRenderWatcher?: boolean</span><br><span class="line">  ) &#123;</span><br><span class="line">    this.vm = vm</span><br><span class="line">    if (isRenderWatcher) &#123;</span><br><span class="line">      vm._watcher = this</span><br><span class="line">    &#125;</span><br><span class="line">    vm._watchers.push(this)</span><br><span class="line">    // options</span><br><span class="line">    if (options) &#123;</span><br><span class="line">      this.deep = !!options.deep</span><br><span class="line">      this.user = !!options.user</span><br><span class="line">      this.computed = !!options.computed</span><br><span class="line">      this.sync = !!options.sync</span><br><span class="line">      this.before = options.before</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      this.deep = this.user = this.computed = this.sync = false</span><br><span class="line">    &#125;</span><br><span class="line">    this.cb = cb</span><br><span class="line">    this.id = ++uid // uid for batching</span><br><span class="line">    this.active = true</span><br><span class="line">    this.dirty = this.computed // for computed watchers</span><br><span class="line">    this.deps = []</span><br><span class="line">    this.newDeps = []</span><br><span class="line">    this.depIds = new Set()</span><br><span class="line">    this.newDepIds = new Set()</span><br><span class="line">    this.expression = process.env.NODE_ENV !== &#x27;production&#x27;</span><br><span class="line">      ? expOrFn.toString()</span><br><span class="line">      : &#x27;&#x27;</span><br><span class="line">    // parse expression for getter</span><br><span class="line">    if (typeof expOrFn === &#x27;function&#x27;) &#123;</span><br><span class="line">      this.getter = expOrFn</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      this.getter = parsePath(expOrFn)</span><br><span class="line">      if (!this.getter) &#123;</span><br><span class="line">        this.getter = function () &#123;&#125;</span><br><span class="line">        process.env.NODE_ENV !== &#x27;production&#x27; &amp;&amp; warn(</span><br><span class="line">          `Failed watching path: &quot;$&#123;expOrFn&#125;&quot; ` +</span><br><span class="line">          &#x27;Watcher only accepts simple dot-delimited paths. &#x27; +</span><br><span class="line">          &#x27;For full control, use a function instead.&#x27;,</span><br><span class="line">          vm</span><br><span class="line">        )</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    if (this.computed) &#123;</span><br><span class="line">      this.value = undefined</span><br><span class="line">      this.dep = new Dep()</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      this.value = this.get()</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">   * Evaluate the getter, and re-collect dependencies.</span><br><span class="line">   */</span><br><span class="line">  get () &#123;</span><br><span class="line">    pushTarget(this)</span><br><span class="line">    let value</span><br><span class="line">    const vm = this.vm</span><br><span class="line">    try &#123;</span><br><span class="line">      value = this.getter.call(vm, vm)</span><br><span class="line">    &#125; catch (e) &#123;</span><br><span class="line">      if (this.user) &#123;</span><br><span class="line">        handleError(e, vm, `getter for watcher &quot;$&#123;this.expression&#125;&quot;`)</span><br><span class="line">      &#125; else &#123;</span><br><span class="line">        throw e</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; finally &#123;</span><br><span class="line">      // &quot;touch&quot; every property so they are all tracked as</span><br><span class="line">      // dependencies for deep watching</span><br><span class="line">      if (this.deep) &#123;</span><br><span class="line">        traverse(value)</span><br><span class="line">      &#125;</span><br><span class="line">      popTarget()</span><br><span class="line">      this.cleanupDeps()</span><br><span class="line">    &#125;</span><br><span class="line">    return value</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  /**</span><br><span class="line">   * Add a dependency to this directive.</span><br><span class="line">   */</span><br><span class="line">  addDep (dep: Dep) &#123;</span><br><span class="line">    const id = dep.id</span><br><span class="line">    if (!this.newDepIds.has(id)) &#123;</span><br><span class="line">      this.newDepIds.add(id)</span><br><span class="line">      this.newDeps.push(dep)</span><br><span class="line">      if (!this.depIds.has(id)) &#123;</span><br><span class="line">        dep.addSub(this)</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><span class="line">  /**</span><br><span class="line">   * Clean up for dependency collection.</span><br><span class="line">   */</span><br><span class="line">  cleanupDeps () &#123;</span><br><span class="line">    let i = this.deps.length</span><br><span class="line">    while (i--) &#123;</span><br><span class="line">      const dep = this.deps[i]</span><br><span class="line">      if (!this.newDepIds.has(dep.id)) &#123;</span><br><span class="line">        dep.removeSub(this)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    let tmp = this.depIds</span><br><span class="line">    this.depIds = this.newDepIds</span><br><span class="line">    this.newDepIds = tmp</span><br><span class="line">    this.newDepIds.clear()</span><br><span class="line">    tmp = this.deps</span><br><span class="line">    this.deps = this.newDeps</span><br><span class="line">    this.newDeps = tmp</span><br><span class="line">    this.newDeps.length = 0</span><br><span class="line">  &#125;</span><br><span class="line">  // ...</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></pre></td><td class="code"><pre><span class="line">this.deps = [] </span><br><span class="line">this.newDeps = [] </span><br><span class="line">//表示Wather实例持有的Dep实例，也就是订阅了多少dep</span><br></pre></td></tr></table></figure></li></ul><ol><li>组件挂载时会调用mountComponent函数，在mountComponent中实例化一个渲染Wathcer，而在实例化Wathcer时调用this.get() <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">get () &#123;</span><br><span class="line">  pushTarget(this)</span><br><span class="line">  let value</span><br><span class="line">  const vm = this.vm</span><br><span class="line">  try &#123;</span><br><span class="line">    value = this.getter.call(vm, vm)</span><br><span class="line">  &#125; catch (e) &#123;</span><br><span class="line">    // ....</span><br><span class="line">  &#125; finally &#123;</span><br><span class="line">    if (this.deep) &#123;</span><br><span class="line">      traverse(value)</span><br><span class="line">    &#125;</span><br><span class="line">    popTarget()</span><br><span class="line">    this.cleanupDeps()</span><br><span class="line">  &#125;</span><br><span class="line">  return value</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>调用pushTarget(this)将要挂载的组件渲染Wather对象赋值给Dep.target -&gt; 执行this.getter.call(vm,vm)，实际上是执行new Wathcer时传入的回调函数-&gt;执行updateComponent() <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">new Watcher(vm, updateComponent, //...)</span><br></pre></td></tr></table></figure></li><li>updateComponent有对组件data数据的访问-&gt; 触发defineReactive的get方法 -&gt; 依赖收集dep.depen()-&gt;调用Dep.target.addDep(this)-&gt;实际是调用渲染Watcher实例中的addDep <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">addDep (dep: Dep) &#123;</span><br><span class="line">  const id = dep.id</span><br><span class="line">  // 保证同一数据不会被添加多次</span><br><span class="line">  if (!this.newDepIds.has(id)) &#123;</span><br><span class="line">    this.newDepIds.add(id)</span><br><span class="line">    this.newDeps.push(dep)</span><br><span class="line">    if (!this.depIds.has(id)) &#123;</span><br><span class="line">      dep.addSub(this)</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></li><li>将这个渲染Wather添加到在defineReactive中实例化的dep中的subs中,这就是整个依赖收集过程。<h2 id="派发更新"><a href="#派发更新" class="headerlink" title="派发更新"></a>派发更新</h2></li></ol><ul><li>set时，派发更新的主要流程如下:<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">set: function reactiveSetter (newVal) &#123;</span><br><span class="line">    const value = getter ? getter.call(obj) : val</span><br><span class="line">    // ....</span><br><span class="line">    if (setter) &#123;</span><br><span class="line">      setter.call(obj, newVal)</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      val = newVal</span><br><span class="line">    &#125;</span><br><span class="line">    childOb = !shallow &amp;&amp; observe(newVal)</span><br><span class="line">    dep.notify()</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></li><li>observe(newVal)使新设置的值变成响应式的</li><li>dep.notify()通知所有订阅了这个dep的Wathcher对象-&gt; dep.notify()调用Watcher的update()方法<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">update () &#123;</span><br><span class="line">  if (this.computed) &#123;</span><br><span class="line">    if (this.dep.subs.length === 0) &#123;</span><br><span class="line">      this.dirty = true</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      this.getAndInvoke(() =&gt; &#123;</span><br><span class="line">        this.dep.notify()</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; else if (this.sync) &#123;</span><br><span class="line">    this.run()</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    queueWatcher(this)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>一般组件数据更新会调用queueWatcher(this)</li><li>queueWathcer使用了队列，先把Wather添加到队列queue，在下个nextTick执行flushSchedulerQueue() -&gt; 执行watcher.run()方法<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">run () &#123;</span><br><span class="line">  if (this.active) &#123;</span><br><span class="line">    this.getAndInvoke(this.cb)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>run()方法执行-&gt;getAndInvoke(this.cb)传入回调函数-&gt;getAndInvoke判断满足新旧值不等-&gt;执行this.cb回调函数</li><li>在渲染watcher中，this.cb为<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></pre></td><td class="code"><pre><span class="line">updateComponent = () =&gt; &#123;</span><br><span class="line">  vm._update(vm._render(), hydrating)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>所以当修改组件相关的响应式数据时会触发组件重新渲染</li></ul><h1 id="computed实现"><a href="#computed实现" class="headerlink" title="computed实现"></a>computed实现</h1><ul><li>Vue实例初始化时调用initComputed()<br>在initComputed()中为每一个计算属性getter创建<strong>computed Wather</strong>,<strong>computed Wathcer</strong>初始化时会持有一个dep实例,用来管理computed Wathcer. 当update()时，要更新订阅了这个dep的watcher。<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">if (this.computed) &#123;</span><br><span class="line">  this.value = undefined</span><br><span class="line">  this.dep = new Dep()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>最后调用defineComputed()<br>defineComputed()–&gt;调用Object.defineProperty为计算属性设置对应的key的getter和setter,一般setter为空，getter对应createComputedGetter(key)<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">function createComputedGetter (key) &#123;</span><br><span class="line">  return function computedGetter () &#123;</span><br><span class="line">    const watcher = this._computedWatchers &amp;&amp; this._computedWatchers[key]</span><br><span class="line">    if (watcher) &#123;</span><br><span class="line">      watcher.depend()</span><br><span class="line">      return watcher.evaluate()</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>当render 函数访问到计算属性时-&gt; 触发getter-&gt; 执行watcher.depend-&gt;将渲染Wather订阅上文实例化的dep-&gt;执行watcher.evaluate()<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">evaluate () &#123;</span><br><span class="line">  if (this.dirty) &#123;</span><br><span class="line">    this.value = this.get()</span><br><span class="line">    this.dirty = false</span><br><span class="line">  &#125;</span><br><span class="line">  return this.value</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>watcher.evaluate()执行this.get()-&gt; this.get()为计算属性定义的getter函数获取到this.value值<br>此时也会访问到计算属性所依赖的数据的getter,所以Dep.target为这个computed watcher,此时这个computed watcher订阅了所依赖数据持有的dep</li></ul><p>当对计算属性依赖的数据做修改时-&gt;触发数据setter-&gt;通知订阅它变化的computed watcher更新-&gt; 执行watcher.update()方法</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">if (this.computed) &#123;</span><br><span class="line">  if (this.dep.subs.length === 0) &#123;</span><br><span class="line">    this.dirty = true</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    this.getAndInvoke(() =&gt; &#123;</span><br><span class="line">      this.dep.notify()</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>在上文中渲染Wather订阅了this.dep, 所以渲染Wathcer通过this.getAndInvoke()的回调执行update()-&gt;重新渲染组件<br>this.getAndInvoke()主要是对比新旧值</p><p>在computed Watcher中this.dirty的作用时在重新渲染组件后，通过evalute()获取计算属性的值时，因为在getAndInvoke()中将this.value设为新值并置this.dirty为false,所以直接返回this.value，不触发所依赖数据的getter</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">// 在 getAndInvoke()中</span><br><span class="line">const oldValue = this.value</span><br><span class="line">this.value = value</span><br><span class="line">this.dirty = false</span><br></pre></td></tr></table></figure><h1 id="watch的实现"><a href="#watch的实现" class="headerlink" title="watch的实现"></a>watch的实现</h1><p>Vue实例初始化时-&gt;initWatch()-&gt;调用createWatcher(vm, key, handler[i])-&gt;返回vm.$watch(expOrFn, handler, options)<br>$watch在执行stateMixin时定义<br>实例化了Wathcer </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">const watcher = new Watcher(vm, expOrFn, cb, options)</span><br></pre></td></tr></table></figure><p>是一个 user Wathcer, options.user = true,<br>在实例化 user Wathcer时，调用了watcher的this.get()方法，之后的依赖收集和更新与一般组件数据相同,不同是对options属性值不同时，进入不同的分支.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;通过object-defineProperty设置getter和setter&quot;&gt;&lt;a href=&quot;#通过object-defineProperty设置getter和setter&quot; class=&quot;headerlink&quot; title=&quot;通过object.defineP
      
    
    </summary>
    
    
      <category term="源码学习" scheme="http://lizehongss.github.io/newBlog/categories/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>构造函数笔记</title>
    <link href="http://lizehongss.github.io/newBlog/2021/10/17/%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E7%AC%94%E8%AE%B0/"/>
    <id>http://lizehongss.github.io/newBlog/2021/10/17/%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E7%AC%94%E8%AE%B0/</id>
    <published>2021-10-16T16:00:00.000Z</published>
    <updated>2021-10-16T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h1><ul><li>构造函数主要作用是用于实例化类，类实例是由构造函数构造的，构造函数的主要任务是初始化实例需要的信息。(<em>通常构造函数的首字母一般大写</em>)。</li><li>构造函数使用<code>new</code>关键字调用  <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">function Animal () &#123;</span><br><span class="line">    this.type = &#x27;animal&#x27;</span><br><span class="line">&#125;</span><br><span class="line">let dog = new Animal()</span><br><span class="line">let cat = new Animal()</span><br><span class="line">dog.type // animal</span><br><span class="line">cat.type // animal</span><br></pre></td></tr></table></figure>  实现实例化类主要是<code>new</code>的作用，<code>new</code>的主要作用如下:<ol><li>自动创建一个空对象</li><li>为新创建的对象添加<strong>proto</strong>,将该属性链接至构造函数的原型对象</li><li>把空对象和函数里的this 衔接起来(this指向实例化对象)</li><li>隐式返还<code>this</code>(即该函数没有返回对象，则返回<code>this</code>)；<br>在例子中 ，一个继承自Animal.prototyper的新对象被创建,<code>this</code>会指向dog。所以在调用时Animal里的<code>this</code>会指向dog,因此调用dog.type，会打印’animal’<ul><li>基于<code>new</code>的作用，可以仿写一个new运算符<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">function mynew(constructor, ...arg) &#123;</span><br><span class="line">    //创建 一个空对象</span><br><span class="line">    let obj = &#123;&#125;</span><br><span class="line">    // this指向obj对象</span><br><span class="line">    constructor.call(obj, ...arg)</span><br><span class="line">    // 添加属性__proto__,指向构造函数的原型</span><br><span class="line">    obj.__proto__ = constructor.prototype;</span><br><span class="line">    // 返回 对象</span><br><span class="line">    return obj</span><br><span class="line">&#125;</span><br><span class="line">// 使用</span><br><span class="line">    let dog = mynew(Animal)</span><br><span class="line">    dog.type // animal</span><br></pre></td></tr></table></figure><h1 id="构造函数的原型链"><a href="#构造函数的原型链" class="headerlink" title="构造函数的原型链"></a>构造函数的原型链</h1>因为 <code>new</code>会为新的对象添加<strong>proto</strong>,并将该属性链接至构造函数的原型对象,所以原型链如下所示:</li></ul></li></ol></li></ul><p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bcc2f69c4bb34367b647a4ee33cf2eb5~tplv-k3u1fbpfcp-watermark.image?" alt="构造函数原型.png"></p><p>其中Animal.prototype会有一个默认的constructor属性，引用对象关联的函数(在本例中是Animal)。</p><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">Animal.prototype.constructor === Animal // true</span><br><span class="line">dog.constructor === Animal // true</span><br></pre></td></tr></table></figure><p>需要注意的是dog.constructor之所以会“指向创建dog的函数Animal”是因为dog本身并没有constructor属性，访问它时会委托到Animal.prototype，而Animal.prototype.constructor默认指向Foo。</p><p>也正因为构造函数有这样的原型链，所以可以用来实现工厂模式。 一个简单的实现如下:</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></pre></td><td class="code"><pre><span class="line">function Animal (name) &#123;</span><br><span class="line">    this.name = name</span><br><span class="line">    this.type = &#x27;animal&#x27;</span><br><span class="line">&#125;</span><br><span class="line">Animal.prototype.myName = function () &#123;</span><br><span class="line">        console.log(`my name is $&#123;this.name&#125;`)</span><br><span class="line">&#125;</span><br><span class="line">let dog = new Animal(&#x27;dog&#x27;)</span><br><span class="line">let cat = new Animal(&#x27;cat&#x27;)</span><br><span class="line">dog.myName() // dog</span><br><span class="line">cat.myName() // cat</span><br></pre></td></tr></table></figure><p>访问dog的myName方法，因为本身没有这个方法，会通过原型链访问到Animal.prototype上。因此我们可以在Animal.prototype上定义共有的方法来封装。</p><h1 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h1><p>构造函数其实也是一种继承，是类和实例之间的继承，dog可以”继承”Animal.prototype并访问到Animal.prototype的MyName函数。而我们更多使用的是类和类之间的继承。</p><p>类和类之间的继承主要使用的是<code>Object.create()函数</code>，它会创建一个新对象并把新对象的<strong>proto</strong>关联到你指定的对象。在下面的例子中就是创建一个新的Son.prototype对象并关联到Dad.prototype。(会抛弃默认的Son.prototype)</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></pre></td><td class="code"><pre><span class="line">function Dad (name) &#123;</span><br><span class="line">    this.fatherName = name</span><br><span class="line">&#125;</span><br><span class="line">Dad.prototype.fatherName = function () &#123;</span><br><span class="line">        console.log(`I&#x27;m father ,my name is $&#123;this.fatherName&#125;`)</span><br><span class="line">&#125;</span><br><span class="line">function Son (name, father) &#123;</span><br><span class="line">    Dad.call(this, father) //在调用时将Dad的this指向Son,从而在Son中能访问到fatherName</span><br><span class="line">    this.sonName = name</span><br><span class="line">&#125;</span><br><span class="line">// 创建一个新的Son.prototype对象并关联到Dad.prototype</span><br><span class="line">Son.prototype = Object.create(Dad.prototype)</span><br><span class="line">// 注意此时Son.prototype.constructor属性会指向Dad,因为它关联到了Dad.prototype，需要手动修复</span><br><span class="line">Son.prototype.constructor = Son</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>ES6 可以使用<code>Object.setPrototypeOf</code>直接修改现有的Bar.prototype, 此时Son.prototype.constructor不需要修复</p><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">Objcet.setPrototypeOf(Son.prototype, Dad.prototype)</span><br><span class="line">Son.prototype.constructor === Son // true</span><br></pre></td></tr></table></figure><h1 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h1><p>这是最近在重新学习JS相关知识时，所整理的有关构造函数的部分，觉得有所帮助就点个赞和收藏^_^。                                                                               </p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;构造函数&quot;&gt;&lt;a href=&quot;#构造函数&quot; class=&quot;headerlink&quot; title=&quot;构造函数&quot;&gt;&lt;/a&gt;构造函数&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;构造函数主要作用是用于实例化类，类实例是由构造函数构造的，构造函数的主要任务是初始化实例需要的信息。(&lt;em&gt;通
      
    
    </summary>
    
    
      <category term="javaScript" scheme="http://lizehongss.github.io/newBlog/categories/javaScript/"/>
    
    
      <category term="javaScript" scheme="http://lizehongss.github.io/newBlog/tags/javaScript/"/>
    
  </entry>
  
  <entry>
    <title>electron调用dll笔记</title>
    <link href="http://lizehongss.github.io/newBlog/2021/09/29/electron%E8%B0%83%E7%94%A8dll%E7%AC%94%E8%AE%B0/"/>
    <id>http://lizehongss.github.io/newBlog/2021/09/29/electron%E8%B0%83%E7%94%A8dll%E7%AC%94%E8%AE%B0/</id>
    <published>2021-09-28T16:00:00.000Z</published>
    <updated>2021-09-28T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="electron安装相关依赖"><a href="#electron安装相关依赖" class="headerlink" title="electron安装相关依赖"></a>electron安装相关依赖</h2><ol><li>electron调用dll需要安装node-gyp,用于编译node-ffi-napi,ref-arrray-di, ref-struct-di, ref-napi等库。<br>node-gyp在window下运行需要python2.7环境和 Visual Studio Build Tools.(<a href="https://github.com/nodejs/node-gyp">详情在这里</a>)<br>在这里可以使用 windows-build-tools进行安装，命令如下(<strong>注意需要管理员模式</strong>)：<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">npm install --global --production windows-build-tools</span><br></pre></td></tr></table></figure></li><li>为方便开发调用32位和64位dll,建议使用nvm管理node版本,<a href="https://github.com/coreybutler/nvm-windows/releases">可以在这里下载安装</a></li></ol><h2 id="关键库调用说明"><a href="#关键库调用说明" class="headerlink" title="关键库调用说明"></a>关键库调用说明</h2><p>ref-napi, ref-struct,ref-array,在下面引用的是TooTallNate原作者的仓库链接，因为说明文档更加具体和详细,但使用时建议使用node-ffi-napi仓库中的版本，因为它们能够在node 10.0 以上版本使用</p><h3 id="node-ffi-napi"><a href="#node-ffi-napi" class="headerlink" title="node-ffi-napi"></a>node-ffi-napi</h3><p><a href="https://github.com/node-ffi-napi/node-ffi-napi">node-ffi-napi</a>:用于使用js调用c++动态链接库(dll)，主要用法如下:</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">var ffi = require(&#x27;ffi-napi&#x27;);</span><br><span class="line"></span><br><span class="line">// 声明要调用的函数</span><br><span class="line">var libm = ffi.Library(&#x27;libm&#x27;, &#123;</span><br><span class="line">  //第一个参数为该函数的输出类型, 第二个参数为参数数组，定义该函数的传参类型</span><br><span class="line">  &#x27;ceil&#x27;: [ &#x27;double&#x27;, [ &#x27;double&#x27; ] ]</span><br><span class="line">&#125;);</span><br><span class="line">libm.ceil(1.5); // 2</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><a href="https://github.com/node-ffi/node-ffi/wiki/Node-FFI-Tutorial">详细用法可以查看这里</a><br>其中需要注意的是，如果是调用相互动态链接的dll，需要调用window的keilDay32设置目录<br>如下所示:</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">const kernel32 = new ffi.Library(&quot;kernel32&quot;, &#123;</span><br><span class="line">            SetDllDirectoryA: [&quot;bool&quot;, [&quot;string&quot;]]</span><br><span class="line">&#125;)</span><br><span class="line">let result = kernel32.SetDllDirectoryA(path)</span><br></pre></td></tr></table></figure><h3 id="ref-napi"><a href="#ref-napi" class="headerlink" title="ref-napi"></a>ref-napi</h3><p><a href="https://github.com/node-ffi-napi/ref-napi">ref-napi</a>: 用于将node缓冲区的实例转换为指针，方便传参入dll函数进行调用,<a href="https://tootallnate.github.com/ref">具体调用实例在这</a><br>重要类型调用如下:</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">// 在Buffer区创建了一个4个字节的int类型内存空间并将指向该空间的指针赋值给outNumber,这样就可以直接将outNumber作为传参给C++函数，C++函数可以通过该指针赋值到内存区，js使用deref()函数可以拿到实际的值</span><br><span class="line">var outNumber = ref.alloc(&#x27;int&#x27;); </span><br><span class="line">libmylibrary.manipulate_number(outNumber);</span><br><span class="line">var actualNumber = outNumber.deref();</span><br></pre></td></tr></table></figure><h3 id="ref-struct"><a href="#ref-struct" class="headerlink" title="ref-struct"></a>ref-struct</h3><p><a href="https://github.com/TooTallNate/ref-struct">ref-struct</a>提供一个C++的struct接口在node缓冲区的实现，具体实现方法如下:</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></pre></td><td class="code"><pre><span class="line">// 定义一个Struct类型</span><br><span class="line">// 引用node-ffi-napi中的版本</span><br><span class="line">var StructType = require(&#x27;ref-struct-di&#x27;)(ref)</span><br><span class="line">let structText =StructType(&#123;</span><br><span class="line">  x: ref.types.int,</span><br><span class="line">  y: ref.types.int,</span><br><span class="line">  width: ref.types.int,</span><br><span class="line">  height: ref.types.height</span><br><span class="line">&#125;)</span><br><span class="line">// 在C++函数中声明, </span><br><span class="line">someDllMethod: [&#x27;int&#x27;, [ref.refType(structText)]],</span><br><span class="line">// 如果是struct有传参默认值, 调用时如下:</span><br><span class="line">let hasValue = new structText(&#123;</span><br><span class="line">  x: 0,</span><br><span class="line">  y: 0,</span><br><span class="line">  width: 0,</span><br><span class="line">  height: 0</span><br><span class="line">&#125;)</span><br><span class="line">someDllMethod(hasValue.ref())</span><br><span class="line">// 如果要拿到回参的值，直接调用hasValue即可</span><br><span class="line">console.log(hasValue.x)</span><br></pre></td></tr></table></figure><h3 id="ref-array"><a href="#ref-array" class="headerlink" title="ref-array"></a>ref-array</h3><p><a href="https://github.com/TooTallNate/ref-array">ref-array</a>用于在node缓冲区实现一个C++的数组定义，用法如下:</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">// 引用node-ffi-napi中的版本</span><br><span class="line">var ArrayType = require(&#x27;ref-array-di&#x27;)(ref)</span><br><span class="line">// 定义一个int类型，长度为5的数组类型</span><br><span class="line">let textArray = ArrayType(ref.types.int, 5)</span><br></pre></td></tr></table></figure><h3 id="其它特殊使用说明"><a href="#其它特殊使用说明" class="headerlink" title="其它特殊使用说明"></a>其它特殊使用说明</h3><ol><li>如果C++需要传入一个类型为void的二级指针句柄，建议如下使用:<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">someDllMethods: [&#x27;long&#x27;, [ref.refType(ref.refType(ref.types.void))]]</span><br><span class="line"></span><br><span class="line">// 在使用时, 定义一个二级int型指针</span><br><span class="line">// 这里如果使用type为void时，无法正确取值</span><br><span class="line">// void indirection 为2</span><br><span class="line">let void = ref.alloc(ref.refType(ref.types.int))</span><br><span class="line">// 解析一次</span><br><span class="line">void.deref()</span><br><span class="line">// 解析二次</span><br><span class="line">void.deref().deref()</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>c++ 内部使用GBK编码, 如果回参是一个有指定字节数的char数组,需要拿到这个数组的值，并使用<a href="https://www.npmjs.com/package/iconv-lite">iconv-lite</a>将其解析。</li></ol><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">// char数组在buffer区中的数据</span><br><span class="line">let textBuffer = new Buffer.form([12,52,25,2,52])</span><br><span class="line">var iconv = require(&quot;iconv-lite&quot;);</span><br><span class="line">str = iconv.decode(textBuffer, &quot;GBK&quot;);</span><br></pre></td></tr></table></figure><h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><ol><li><a href="https://www.v2ex.com/t/474611">node-ffi 食用指南（难吃</a></li><li><a href="http://tootallnate.github.io/ref/">ref文档</a></li><li><a href="https://blog.csdn.net/zxl761303248/article/details/108051680">electron对接Dll</a></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;electron安装相关依赖&quot;&gt;&lt;a href=&quot;#electron安装相关依赖&quot; class=&quot;headerlink&quot; title=&quot;electron安装相关依赖&quot;&gt;&lt;/a&gt;electron安装相关依赖&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;electron调用dll需要安
      
    
    </summary>
    
    
      <category term="electron" scheme="http://lizehongss.github.io/newBlog/categories/electron/"/>
    
    
      <category term="electron dll" scheme="http://lizehongss.github.io/newBlog/tags/electron-dll/"/>
    
  </entry>
  
  <entry>
    <title>Electron打印方法笔记</title>
    <link href="http://lizehongss.github.io/newBlog/2021/08/22/electron%E6%89%93%E5%8D%B0%E6%96%B9%E6%B3%95%E7%AC%94%E8%AE%B0/"/>
    <id>http://lizehongss.github.io/newBlog/2021/08/22/electron%E6%89%93%E5%8D%B0%E6%96%B9%E6%B3%95%E7%AC%94%E8%AE%B0/</id>
    <published>2021-08-21T16:00:00.000Z</published>
    <updated>2021-08-21T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>主要是总结最近在工作所使用到的electron方法总结，总结的主要是静默打印的方法.</p><h1 id="webview打印"><a href="#webview打印" class="headerlink" title="webview打印"></a>webview打印</h1><ul><li>webview主要是electron提供的在一个独立的 frame 和进程里显示外部 web 内容的标签，详细介绍可以<a href="https://www.electronjs.org/docs/api/webview-tag">看这里</a>.需要注意的是在electron&gt;=5的版本里webview标签是禁用的，需要在主进程<code>BrowserWindow</code>里设置<code>webviewTag</code>为<code>true</code>启用:<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></pre></td><td class="code"><pre><span class="line">mainWindow = new BrowserWindow(&#123;</span><br><span class="line">    title: &#x27;桌面应用程序&#x27;,</span><br><span class="line">    webPreferences: </span><br><span class="line">        webviewTag: true</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li><li>打印主要使用到是webview的isLoading()和print()方法，webview打印主要适用于打印远程的网页内容.在webview标签里设置好要打印的远程网页url，直接获取到webview元素并调用print方法即可打印.<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;webview id=&quot;webview&quot; src=&quot;https://github.com&quot;&gt;&lt;/webview&gt;</span><br></pre></td></tr></table></figure></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">let webview = document.getElementById(&#x27;webview&#x27;)</span><br><span class="line">webview.print(&#123;</span><br><span class="line">    silent: true, // 是否静默打印</span><br><span class="line">    deviceName: &#x27;print&#x27; // 打印机名称</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><ul><li>关于打印机名称的获取可以在主进程中获取并通过IPC传输到渲染进程<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></pre></td><td class="code"><pre><span class="line">ipcMain.on(&#x27;getPrintDeviceList&#x27;, (event) =&gt; &#123;</span><br><span class="line">    event.return.value = mainwindow.webContents.getPrinters()</span><br><span class="line">)</span><br></pre></td></tr></table></figure></li><li>需要注意的是远程网页资源加载需要时间，这里最好使用webview.isLoading()确认页面资源加载完成后再调用打印方法， 具体调用如下:<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></pre></td><td class="code"><pre><span class="line">webview.addEventListener(&#x27;dom-ready&#x27;, checkCanPrint)</span><br><span class="line"></span><br><span class="line">function checkCanPrint() &#123;</span><br><span class="line">    // 还在加载中，则setTimeout秒后再检查</span><br><span class="line">    if (webview.isLoading) &#123;</span><br><span class="line">    setTimeOut(() =&gt; &#123;checkCanPrint&#125;, 1 * 1000)</span><br><span class="line">   &#125; else &#123;</span><br><span class="line">       webview.print() // 打印</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="在主进程中调用webContents-print-打印"><a href="#在主进程中调用webContents-print-打印" class="headerlink" title="在主进程中调用webContents.print()打印"></a>在主进程中调用webContents.print()打印</h1></li><li>如果需要打印渲染进程里的本地网页内容,可以在主进程中调用webContents.print()，在要打印的渲染进程页面中触发主进程调用打印.<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">// main.js</span><br><span class="line">ipcMain.on(&#x27;print&#x27;, (event, deviceName) =&gt; &#123;</span><br><span class="line">    mainWindow.webContent.print( &#123;</span><br><span class="line">        silent: true,</span><br><span class="line">        deviceName: deviceName</span><br><span class="line">    &#125; )</span><br><span class="line">&#125;)</span><br><span class="line">// print.js</span><br><span class="line">const &#123;ipcRenderer&#125; = require(&#x27;electron&#x27;)</span><br><span class="line">ipcRender.send(&#x27;print&#x27;, deviceName)</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>使用webContents.print()打印时，与在浏览器使用window.onprint()一样，在css中可以使用媒体查询控制要打印的内容和样式<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></pre></td><td class="code"><pre><span class="line">//print.css</span><br><span class="line"></span><br><span class="line">@media print &#123;</span><br><span class="line"> .print-dom &#123;</span><br><span class="line">     display: block;</span><br><span class="line"> &#125;</span><br><span class="line"> .not-print-dom &#123;</span><br><span class="line">     display: none;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1>使用webview和webContents.print()方法打印是目前在业务使用的两种方法，网上文章其实好多，但在打印远程网页时如何确保网页内容加载完成再打印是这篇文章形成的主要原因。如果有其它更好的方法也希望各位大佬留言.</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;主要是总结最近在工作所使用到的electron方法总结，总结的主要是静默打印的方法.&lt;/p&gt;
&lt;h1 id=&quot;webview打印&quot;&gt;&lt;a h
      
    
    </summary>
    
    
      <category term="electron" scheme="http://lizehongss.github.io/newBlog/categories/electron/"/>
    
    
      <category term="electron" scheme="http://lizehongss.github.io/newBlog/tags/electron/"/>
    
  </entry>
  
  <entry>
    <title>使用preload远程页面调用electron接口</title>
    <link href="http://lizehongss.github.io/newBlog/2020/10/15/%E9%80%9A%E8%BF%87preload%E4%BD%BF%E8%BF%9C%E7%A8%8B%E9%A1%B5%E9%9D%A2%E8%AE%BF%E9%97%AEelectron%E6%8E%A5%E5%8F%A3/"/>
    <id>http://lizehongss.github.io/newBlog/2020/10/15/%E9%80%9A%E8%BF%87preload%E4%BD%BF%E8%BF%9C%E7%A8%8B%E9%A1%B5%E9%9D%A2%E8%AE%BF%E9%97%AEelectron%E6%8E%A5%E5%8F%A3/</id>
    <published>2020-10-14T16:00:00.000Z</published>
    <updated>2020-10-14T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="主要原理"><a href="#主要原理" class="headerlink" title="主要原理"></a>主要原理</h2><p>主要是通过electron中的preloa在本地中预先加载一个指定脚本js文件，这个文件可以使用node APIs调用electron中的相关接口如ipcRenderer等，所以在electron中使用远程网页的窗口中定义加载js文件，并开放webviewTag,就可以在预加载文件中通过定义window中的方法调用electron接口，然后在远程网页中直接调用预加载文件中定义的方法就可以实现与electron的相关通信。主要实现过程如下:</p><ol><li>在BrowserWindow中定义如下：(<strong>注意preload的文件路径需要绝对路径</strong>)<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></pre></td><td class="code"><pre><span class="line">win = new BrowserWindow(&#123;</span><br><span class="line">    webPreferences: &#123;</span><br><span class="line">        webviewTag: true,</span><br><span class="line">        perload: path.join(__dirname, &#x27;./preload.js&#x27;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li><li>在preload.js中定义相关事件，这里定义一个与electron主进程进行通信的方法<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">// preload.js</span><br><span class="line">const &#123; ipcRenderer &#125; = require(&quot;electron&quot;)</span><br><span class="line">window.senSomeToMain = function () &#123;</span><br><span class="line">    ipcRenderer.send(&#x27;senSomeTomain&#x27;)</span><br><span class="line">&#125;</span><br><span class="line">// 监听electron主进程返回</span><br><span class="line">ipcRenderer.on(&#x27;returnSometh&#x27;, (event, data) =&gt; &#123;</span><br><span class="line">    //该方法在远程网页中定义</span><br><span class="line">    window.getReturn(data)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>在远程页面中的使用如下:<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></pre></td><td class="code"><pre><span class="line">// index.html</span><br><span class="line"></span><br><span class="line">// 与electron主进程通信</span><br><span class="line">window.sendSomeToMain()</span><br><span class="line">// 定义返回信息的回调</span><br><span class="line">window.getReturn = function (data) &#123;</span><br><span class="line">    console.log(data)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="主要用途"><a href="#主要用途" class="headerlink" title="主要用途"></a>主要用途</h2>方便网页资源部署在服务器，使远程网页可以通过本地的electron访问相关的硬件资源，如打印机等。也方便使网页开发功能业务和electron资源调用业务分开,使部署和开发相对简单。</li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;主要原理&quot;&gt;&lt;a href=&quot;#主要原理&quot; class=&quot;headerlink&quot; title=&quot;主要原理&quot;&gt;&lt;/a&gt;主要原理&lt;/h2&gt;&lt;p&gt;主要是通过electron中的preloa在本地中预先加载一个指定脚本js文件，这个文件可以使用node APIs调用ele
      
    
    </summary>
    
    
      <category term="electron" scheme="http://lizehongss.github.io/newBlog/categories/electron/"/>
    
    
      <category term="electron" scheme="http://lizehongss.github.io/newBlog/tags/electron/"/>
    
  </entry>
  
  <entry>
    <title>element源码样式学习</title>
    <link href="http://lizehongss.github.io/newBlog/2020/04/20/element%E6%BA%90%E7%A0%81%E6%A0%B7%E5%BC%8F%E5%AD%A6%E4%B9%A0/"/>
    <id>http://lizehongss.github.io/newBlog/2020/04/20/element%E6%BA%90%E7%A0%81%E6%A0%B7%E5%BC%8F%E5%AD%A6%E4%B9%A0/</id>
    <published>2020-04-19T16:00:00.000Z</published>
    <updated>2020-04-19T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="element源码样式学习"><a href="#element源码样式学习" class="headerlink" title="element源码样式学习"></a>element源码样式学习</h1><h1 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h1><p>element的样式存放在element的<strong>packages/theme-chalk</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><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></pre></td><td class="code"><pre><span class="line">│  alert.scss //组件样式</span><br><span class="line">│  aside.scss</span><br><span class="line">│  autocomplete.scss</span><br><span class="line">│  avatar.scss</span><br><span class="line">│  backtop.scss</span><br><span class="line">│  badge.scss</span><br><span class="line">│  base.scss</span><br><span class="line">│  breadcrumb-item.scss</span><br><span class="line">│  breadcrumb.scss</span><br><span class="line">│  button-group.scss</span><br><span class="line">│  button.scss</span><br><span class="line">|  // 省略部分组件样式</span><br><span class="line">├─common // 组件共用样式</span><br><span class="line">│      popup.scss //弹层类组件共用样式</span><br><span class="line">│      transition.scss //主要定义了element组件中使用vue的 transition 组件时用到的动态效果样式</span><br><span class="line">│      var.scss // 定义了element各组件UI的基本样式的变量,包括颜色,文本大小,边框大小,组件不同尺寸对应的不同样式</span><br><span class="line">│      </span><br><span class="line">├─date-picker // date组件使用样式</span><br><span class="line">│      date-picker.scss</span><br><span class="line">│      date-range-picker.scss</span><br><span class="line">│      date-table.scss</span><br><span class="line">│      month-table.scss</span><br><span class="line">│      picker-panel.scss</span><br><span class="line">│      picker.scss</span><br><span class="line">│      time-picker.scss</span><br><span class="line">│      time-range-picker.scss</span><br><span class="line">│      time-spinner.scss</span><br><span class="line">│      year-table.scss</span><br><span class="line">│      </span><br><span class="line">├─fonts // 字体文件</span><br><span class="line">│      element-icons.ttf</span><br><span class="line">│      element-icons.woff</span><br><span class="line">│      </span><br><span class="line">└─mixins // scss复用函数</span><br><span class="line">       config.scss // 样式名的全局配置</span><br><span class="line">       function.scss // 组件样式使用到的sccss函数</span><br><span class="line">       mixins.scss // 共用的mixins函数</span><br><span class="line">       utils.scss // 工具类函数样式, 如禁用用户选择</span><br><span class="line">       _button.scss // 按钮基本样式</span><br></pre></td></tr></table></figure><h1 id="主要文件分析"><a href="#主要文件分析" class="headerlink" title="主要文件分析"></a>主要文件分析</h1><h2 id="config-scss"><a href="#config-scss" class="headerlink" title="config.scss"></a>config.scss</h2><p><strong>config.scss</strong> 文件定义了element样式的全局配置,如样式名前缀,,样式名分割符等</p><figure class="highlight css"><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">// 这四个样式名配置是element所有样式名定义的基础</span><br><span class="line">// 如: el-button, el-select, is-disabled等样式名</span><br><span class="line">$namespace: <span class="string">&#x27;el&#x27;</span>;</span><br><span class="line">$element-separator: <span class="string">&#x27;__&#x27;</span>;</span><br><span class="line">$modifier-separator: <span class="string">&#x27;--&#x27;</span>;</span><br><span class="line">$state-prefix: <span class="string">&#x27;is-&#x27;</span>;</span><br></pre></td></tr></table></figure><h2 id="mixins-scss"><a href="#mixins-scss" class="headerlink" title="mixins.scss"></a>mixins.scss</h2><p><strong>mixins.scss</strong> 文件定义了element各组件样式使用的基本mixins,这里对最主要的mixinx做分析</p><ol><li><p><a href="#">@mixin</a> b() 混入el</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">@mixin b($block) &#123;</span><br><span class="line">// 假如 $block为button</span><br><span class="line">  $B: $namespace+&#x27;-&#x27;+$block !global;  $namespace在config.scss中定义为el,故$B为el-blutton</span><br><span class="line">  .#&#123;$B&#125; &#123;    // .#&#123;&#125; 为scss变量插值,编译后为.el-button</span><br><span class="line">    @content;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">// @content`用在mixin里面的，当定义一个mixin后，并且设置了@content</span><br><span class="line">// @include的时候可以传入相应的内容到mixin里面</span><br></pre></td></tr></table></figure></li><li><p><a href="#">@mixin</a> e() 混入__</p></li></ol><figure class="highlight css"><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"><span class="keyword">@mixin</span> e($element) &#123;</span><br><span class="line">  <span class="comment">/**假设$element为disabled **/</span></span><br><span class="line">  $E: $element !global;</span><br><span class="line">  $selector: &amp;;  // 父选择器</span><br><span class="line">  $currentSelector: <span class="string">&quot;&quot;</span>; // 要生成的选择器</span><br><span class="line">  <span class="comment">/** 遍历$element //可能有多个</span></span><br><span class="line"><span class="comment">  /* $B 为 mixin b()混入中的变量名</span></span><br><span class="line"><span class="comment">  /* 这里使用$B是因为在element组件样式中e的混入必定是在b混入下的</span></span><br><span class="line"><span class="comment">  /* 如果是在el-button下使用e混入,则生成 .el-button__disabked</span></span><br><span class="line"><span class="comment">  **/</span></span><br><span class="line">  <span class="keyword">@each</span> $unit in $element &#123;</span><br><span class="line">    $currentSelector: #&#123;$currentSelector + &quot;.&quot; + $<span class="selector-tag">B</span> + $element-separator + $unit + &quot;,&quot;&#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/** hitAllSpecialNestRule 判断$elector是否包含--, is-, 在function.scc 中定义</span></span><br><span class="line"><span class="comment">  /* 这里判断是否包含is--,--是因为使用了 @at-root</span></span><br><span class="line"><span class="comment">  /* @at-root指令可以使一个或多个规则被限定输出在文档的根层级上，而不是被嵌套在其父选择器下</span></span><br><span class="line"><span class="comment">  /* 包含有is--等前缀的样式名,在组件中一般是可移除的,所以在输出在文档的根层级上时要加上父选择器</span></span><br><span class="line"><span class="comment">  **/</span></span><br><span class="line">  <span class="keyword">@if</span> hitAllSpecialNestRule($selector) &#123;</span><br><span class="line">    <span class="keyword">@at-root</span> &#123;</span><br><span class="line">      #&#123;$selector&#125; &#123;</span><br><span class="line">        #&#123;$currentSelector&#125; &#123;</span><br><span class="line">          <span class="keyword">@content</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 class="keyword">@else</span> &#123;</span><br><span class="line">    <span class="keyword">@at-root</span> &#123;</span><br><span class="line">      #&#123;$currentSelector&#125; &#123;</span><br><span class="line">        <span class="keyword">@content</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></pre></td></tr></table></figure><ol start="3"><li><a href="#">@mixin</a> m() 混入–</li></ol><figure class="highlight css"><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="comment">/** </span></span><br><span class="line"><span class="comment">/* 该方法与混入e基本相同,不同的是没有判断hitAllSpecialNestRule</span></span><br><span class="line"><span class="comment">/* 因为混入 -- 一般的是组件的尺寸样式,如 el-radio--medium,el-radio--small等,父选择器一般为el-radio等,</span></span><br><span class="line"><span class="comment">/* 故不需要判断</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">@mixin</span> m($modifier) &#123;</span><br><span class="line">  $selector: &amp;;</span><br><span class="line">  $currentSelector: <span class="string">&quot;&quot;</span>;</span><br><span class="line">  <span class="keyword">@each</span> $unit in $modifier &#123;</span><br><span class="line">    $currentSelector: #&#123;$currentSelector + &amp; + $modifier-separator + $unit + &quot;,&quot;&#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">@at-root</span> &#123;</span><br><span class="line">    #&#123;$currentSelector&#125; &#123;</span><br><span class="line">      <span class="keyword">@content</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><ol start="4"><li><a href="#">@mixin</a> w() 混入 is</li></ol><figure class="highlight css"><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">/** 假设$state为check</span></span><br><span class="line"><span class="comment">/* 则编译后为在文档的根层级(在组件中使用时,为组件根样式)为.el-radio.is-checked</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">@mixin</span> when($state) &#123;</span><br><span class="line">  <span class="keyword">@at-root</span> &#123;</span><br><span class="line">    &amp;.#&#123;$state-prefix + $state&#125; &#123;</span><br><span class="line">      <span class="keyword">@content</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><h2 id="functon-scss"><a href="#functon-scss" class="headerlink" title="functon.scss"></a>functon.scss</h2><p><strong>function.scss</strong> 主要是定义了 <strong>hitAllSpecialNestRule</strong> 函数方法，主要用来对选择器是否含有’is-‘, ‘–’, ‘:’主要代码如下:</p><figure class="highlight css"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">@import</span> <span class="string">&quot;config&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* BEM support Func</span></span><br><span class="line"><span class="comment"> -------------------------- */</span></span><br><span class="line"><span class="keyword">@function</span> selectorToString($selector) &#123;</span><br><span class="line">  //字符化</span><br><span class="line">  $selector: <span class="built_in">inspect</span>($selector);</span><br><span class="line">  <span class="comment">/** str-slice </span></span><br><span class="line"><span class="comment">  /* 从 $string 中截取子字符串，通过 $start-at 和 $end-at </span></span><br><span class="line"><span class="comment">  /* 设置始末位置，未指定结束索引值则默认截取到字符串末尾。</span></span><br><span class="line"><span class="comment">  /* 这里主要是去除第一个字符和最后一个字符,避免如 --el, el：等的干扰</span></span><br><span class="line"><span class="comment">  **/</span></span><br><span class="line">  $selector: <span class="built_in">str-slice</span>($selector, <span class="number">2</span>, -<span class="number">2</span>);</span><br><span class="line">  <span class="keyword">@return</span> $selector;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@function</span> containsModifier($selector) &#123;</span><br><span class="line">  $selector: <span class="built_in">selectorToString</span>($selector);</span><br><span class="line">    <span class="comment">/** str-index($string, $substring)</span></span><br><span class="line"><span class="comment">    /* 返回一个下标，标示 $substring 在 $string 中的起始位置。没有找到的话，则返回 null 值。</span></span><br><span class="line"><span class="comment">    /* $modifier-specarator --</span></span><br><span class="line"><span class="comment">  **/</span></span><br><span class="line">  <span class="keyword">@if</span> str-index($selector, $modifier-separator) &#123;</span><br><span class="line">    <span class="keyword">@return</span> true;</span><br><span class="line">  &#125; <span class="keyword">@else</span> &#123;</span><br><span class="line">    <span class="keyword">@return</span> false;</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> containWhenFlag($selector) &#123;</span><br><span class="line">  $selector: <span class="built_in">selectorToString</span>($selector);</span><br><span class="line"><span class="comment">/** $state-prefix: &#x27;is-&#x27;; **/</span></span><br><span class="line">  <span class="keyword">@if</span> str-index($selector, <span class="string">&#x27;.&#x27;</span> + $state-prefix) &#123;</span><br><span class="line">    <span class="keyword">@return</span> true</span><br><span class="line">  &#125; @else &#123;</span><br><span class="line">    <span class="keyword">@return</span> false</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@function containPseudoClass($selector) &#123;</span><br><span class="line">  $selector: <span class="built_in">selectorToString</span>($selector);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">@if</span> str-index($selector, <span class="string">&#x27;:&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">@return</span> true</span><br><span class="line">  &#125; @else &#123;</span><br><span class="line">    <span class="keyword">@return</span> false</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">//** 对上述四种情况进行判断，有一种存在就返回true **/</span><br><span class="line">@function hitAllSpecialNestRule($selector) &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">@return</span> containsModifier($selector) <span class="keyword">or</span> containWhenFlag($selector) <span class="keyword">or</span> containPseudoClass($selector);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;element源码样式学习&quot;&gt;&lt;a href=&quot;#element源码样式学习&quot; class=&quot;headerlink&quot; title=&quot;element源码样式学习&quot;&gt;&lt;/a&gt;element源码样式学习&lt;/h1&gt;&lt;h1 id=&quot;目录结构&quot;&gt;&lt;a href=&quot;#目录结构&quot;
      
    
    </summary>
    
    
      <category term="源码学习" scheme="http://lizehongss.github.io/newBlog/categories/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="element" scheme="http://lizehongss.github.io/newBlog/tags/element/"/>
    
  </entry>
  
  <entry>
    <title>vue相关原理面试题整理</title>
    <link href="http://lizehongss.github.io/newBlog/2019/08/04/vue-interview/"/>
    <id>http://lizehongss.github.io/newBlog/2019/08/04/vue-interview/</id>
    <published>2019-08-03T16:00:00.000Z</published>
    <updated>2019-08-03T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="vue响应式原理实现"><a href="#vue响应式原理实现" class="headerlink" title="vue响应式原理实现"></a>vue响应式原理实现</h2><p>主要通过Object.defineProperty实现，通过它对对象的属性进行getter和setter,从而实现访问和修改这个属性时，通过getter和setter实现依赖注入和派发更新。依赖注入和派发更新通过订阅-观察者模式实现，具体如下, 在访问属性时，通过getter给dep添加watcher类，setter时触发dep.notify()方法，使订阅了这个属性所实例化的dep的所有Watcher触发update方法</p><h2 id="vue监测数组变化"><a href="#vue监测数组变化" class="headerlink" title="vue监测数组变化"></a>vue监测数组变化</h2><p>vue为了监测数组的变化，重新实现了Array的原型方法，包括push,prop等方法,重写的方法会执行本身原有的逻辑，对能增加数组本身长度的方法获取到插入的值，成一个响应式对象，并且再调用 ob.dep.notify() 手动触发依赖通知</p><h2 id="nextTick实现原理"><a href="#nextTick实现原理" class="headerlink" title="nextTick实现原理"></a>nextTick实现原理</h2><p>在下次 DOM 更新循环结束之后执行延迟回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用<br>PromiseMutationObserversetImmediate如果以上都不行则采用setTimeout<br>定义了一个异步方法，多次调用nextTick会将方法存入队列中，通过这个异步方法清空当前队列。</p><h1 id="Vue的生命周期"><a href="#Vue的生命周期" class="headerlink" title="Vue的生命周期"></a>Vue的生命周期</h1><p>主要有created, mounted, updated, destory等， created时Vue实例并没有挂载，无法访问到组件的data,method，mountd是组件开始挂载能访问到data,method等数据,当组件DOM更新时触发update，组件销毁时触发destory</p><h2 id="computed和watcher"><a href="#computed和watcher" class="headerlink" title="computed和watcher"></a>computed和watcher</h2><p>computed是计算属性，计算组件中data的值返回一个结果,当data的值改变时，计算属性的结果值也会改变<br>wather用来监听data数据的变化</p><h2 id="组件中的data为什么是一个函数"><a href="#组件中的data为什么是一个函数" class="headerlink" title="组件中的data为什么是一个函数"></a>组件中的data为什么是一个函数</h2><p>一个组件被复用多次的话，也就会创建多个实例。本质上，这些实例用的都是同一个构造函数。如果data是对象的话，对象属于引用类型，会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突，data必须是一个函数。</p><h2 id="v-model的原理"><a href="#v-model的原理" class="headerlink" title="v-model的原理"></a>v-model的原理</h2><p>v-model本质上是一个语法糖,当表单元素上使用时，实质上是分别定义了data数据和input事件</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;input v-bind=&quot;message&quot; v-on:input=&quot;message = $event.targe.value&quot; &gt;</span><br></pre></td></tr></table></figure><p>在组件上使用时同上,不过事件input通过子组件$emit触发</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;child :value=&quot;message&quot; @input=&quot;message=arguments[0]&quot;&gt;&lt;/child&gt;</span><br></pre></td></tr></table></figure><h2 id="Vue事件绑定原理"><a href="#Vue事件绑定原理" class="headerlink" title="Vue事件绑定原理"></a>Vue事件绑定原理</h2><p>Vue事件绑定分为原生事件和自定义事件，原生事件本质上还是通过addEvent和removeEvent实现, 自定义事件，Vue会维护一个事件总线，当组件绑定自定义事件时，通过$on push到事件总线，当通过$emit调用时，在事件总线中查找并触发事件</p><p>##　Vue模版编译原理<br>将Vue模板通过编译生成AST(一种用JavaScript对象的形式来描述整个模板),再对AST进行优化，主要是对数据不会改变的节点标记为静态节点，最后生成render函数</p><p>##　Vue2.x的diff算法<br>diff算法主要判断vnode和oldVnode生成新的Vnode节点，主要过程如下:<br>同级比较，再比较子节点<br>先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点，将旧的子节点移除)<br>比较都有子节点的情况(核心diff)<br>递归比较子节点<br>Vue2的核心Diff算法采用了双端比较的算法，同时从新旧children的两端开始进行比较，借助key值找到可复用的节点，再进行相关操作。相比React的Diff算法，同样情况下可以减少移动节点次数，减少不必要的性能损耗，更加的优雅。</p><p>##　虚拟Dom以及key属性<br>在浏览器中对DOM操作是很昂贵的。频繁的操作DOM，会产生一定的性能问题，Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象。<br>「key的作用是尽可能的复用 DOM 元素。」<br>新旧 children 中的节点只有顺序是不同的时候，最佳的操作应该是通过移动元素的位置来达到更新的目的。<br>需要在新旧 children 的节点中保存映射关系，以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识。</p><h2 id="组件通信"><a href="#组件通信" class="headerlink" title="组件通信"></a>组件通信</h2><p>父子组件通过$parnet和$refs来访问数据，兄弟组件主要通过Vuex和实现一个 Event Bus</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></pre></td><td class="code"><pre><span class="line">var Event=new Vue();</span><br><span class="line">Event.$emit(事件名,数据);</span><br><span class="line">Event.$on(事件名,data =&gt; &#123;&#125;);</span><br></pre></td></tr></table></figure><p>##　hash路由和history路由实现原理<br>hash模式的原理是 onhashchange 事件，可以在 window 对象上监听这个事件<br>history实际采用了HTML5中提供的API来实现，主要有history.pushState()和history.replaceState()。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;vue响应式原理实现&quot;&gt;&lt;a href=&quot;#vue响应式原理实现&quot; class=&quot;headerlink&quot; title=&quot;vue响应式原理实现&quot;&gt;&lt;/a&gt;vue响应式原理实现&lt;/h2&gt;&lt;p&gt;主要通过Object.defineProperty实现，通过它对对象的属性进
      
    
    </summary>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>vue数据驱动原理</title>
    <link href="http://lizehongss.github.io/newBlog/2019/05/15/vue%E6%95%B0%E6%8D%AE%E9%A9%B1%E5%8A%A8/"/>
    <id>http://lizehongss.github.io/newBlog/2019/05/15/vue%E6%95%B0%E6%8D%AE%E9%A9%B1%E5%8A%A8/</id>
    <published>2019-05-14T16:00:00.000Z</published>
    <updated>2019-05-14T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Vue的定义</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></pre></td><td class="code"><pre><span class="line">new Vue(&#123;</span><br><span class="line">  el: &#x27;#app&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line">    message: &#x27;Hello Vue!&#x27;</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">function Vue (options) &#123;</span><br><span class="line">  //...</span><br><span class="line">  this._init(options)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>new Vue()-&gt; 调用this._init(options)<br>this._init初始化各种配置，包括初始化生命周期，初始化data,props, computed, watcher等等-&gt; 最后调用vm.$mout(vm.$options.el)挂载vm.<br>带 compiler 版本的 $mount 实现</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">Vue.prototype.$mount = function(//...)&#123;</span><br><span class="line">  // 如this.$options.render不存在，调用compileToFunctions方法转换成render方法</span><br><span class="line">        const &#123; render, staticRenderFns &#125; = compileToFunctions(template, &#123;</span><br><span class="line">        shouldDecodeNewlines,</span><br><span class="line">        shouldDecodeNewlinesForHref,</span><br><span class="line">        delimiters: options.delimiters,</span><br><span class="line">        comments: options.comments</span><br><span class="line">      &#125;, this)</span><br><span class="line">      options.render = render</span><br><span class="line">      options.staticRenderFns = staticRenderFns</span><br><span class="line">  // 调用原先原型上的 $mount 方法挂载。</span><br><span class="line">  return mount.call(this, el, hydrating)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用原先原型上的 $mount 方法定义如下:</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></pre></td><td class="code"><pre><span class="line">// public mount method</span><br><span class="line">Vue.prototype.$mount = function (</span><br><span class="line">  el?: string | Element,</span><br><span class="line">  hydrating?: boolean</span><br><span class="line">): Component &#123;</span><br><span class="line">  el = el &amp;&amp; inBrowser ? query(el) : undefined</span><br><span class="line">  return mountComponent(this, el, hydrating)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用mountComponent(this, el, hydrating)</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></pre></td><td class="code"><pre><span class="line">// ....</span><br><span class="line">// 实例化渲染Wathcher</span><br><span class="line">  new Watcher(vm, updateComponent, noop, &#123;</span><br><span class="line">    before () &#123;</span><br><span class="line">      if (vm._isMounted) &#123;</span><br><span class="line">        callHook(vm, &#x27;beforeUpdate&#x27;)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, true /* isRenderWatcher */)</span><br><span class="line">  if (vm.$vnode == null) &#123;</span><br><span class="line">    vm._isMounted = true</span><br><span class="line">    callHook(vm, &#x27;mounted&#x27;)</span><br><span class="line">  &#125;</span><br><span class="line">  // 完成整个渲染工作</span><br><span class="line">  return vm</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>new Wathcer 在初始化时会执行回调函数updateComponent且 vm 实例中的监测的数据发生变化的时候执行回调函数</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></pre></td><td class="code"><pre><span class="line">// 在watcher定义中, this.getter为传入的updateComponent</span><br><span class="line">get () &#123;</span><br><span class="line">  // ...</span><br><span class="line">  value = this.getter.call(vm, vm)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在整个过程中核心是vm._render和vm._update</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></pre></td><td class="code"><pre><span class="line">updateComponent = () =&gt; &#123;</span><br><span class="line">  vm._update(vm._render(), hydrating)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>vm._render()用来把实例渲染成一个虚拟 Node，也就是VNode-&gt;调用vnode = render.call(vm._renderProxy, vm.$createElement)<br>vm.$createElement定义如下：</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">vm.$createElement = (a, b, c, d) =&gt; createElement(vm, a, b, c, d, true)</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Vue的定义&lt;/p&gt;
&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/sp
      
    
    </summary>
    
    
      <category term="源码学习" scheme="http://lizehongss.github.io/newBlog/categories/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>css布局</title>
    <link href="http://lizehongss.github.io/newBlog/2018/06/23/CSS%E5%B8%83%E5%B1%80/"/>
    <id>http://lizehongss.github.io/newBlog/2018/06/23/CSS%E5%B8%83%E5%B1%80/</id>
    <published>2018-06-22T16:00:00.000Z</published>
    <updated>2018-06-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="css布局（持续更新）"><a href="#css布局（持续更新）" class="headerlink" title="css布局（持续更新）"></a>css布局（持续更新）</h1><h2 id="将浮动元素围住"><a href="#将浮动元素围住" class="headerlink" title="将浮动元素围住"></a>将浮动元素围住</h2><ol><li><p>为父元素添加  <strong>overflow:hidden</strong> 属性演示如下：</p></li><li><p>同时浮动父元素 <strong>为父元素添加float:left</strong>演示如下：</p></li><li><p>添加非浮动元素的清除元素 代码如下</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></pre></td><td class="code"><pre><span class="line">.clear:after&#123;</span><br><span class="line">    content:&quot;.&quot;;</span><br><span class="line">    display:block;</span><br><span class="line">    height:0;</span><br><span class="line">    visibility:hidden;</span><br><span class="line">    clear:both;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h2 id="三栏一固定宽度布局"><a href="#三栏一固定宽度布局" class="headerlink" title="三栏一固定宽度布局"></a>三栏一固定宽度布局</h2><ol><li><p>父元素wrapper 设定一固定宽度，水平外边距为auto</p></li><li><p>子元素nav和artice设置浮动，宽度相加为wrapper的宽度（再加以此类推）</p></li><li><p>header和footer 默认与布局同宽 <strong>footer要清除浮动</strong><br>html代码如下：</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></pre></td><td class="code"><pre><span class="line">&lt;div id=&quot;wrapper&quot;&gt;</span><br><span class="line">&lt;header&gt;</span><br><span class="line">&lt;!-- 标题 --&gt;header&lt;/header&gt;</span><br><span class="line">&lt;nav&gt;&lt;!-- 无序列表 -- nav&lt;/nav&gt;</span><br><span class="line">&lt;article&gt;&lt;! -- 文本 --&gt; article&lt;/article&gt;</span><br><span class="line">&lt;aside&gt;&lt;! -- 文本 --&gt;aside&lt;/aside&gt;</span><br><span class="line">&lt;footer&gt;&lt;!-- 文本 --&gt; footer&lt;/footer&gt;</span><br></pre></td></tr></table></figure><p>css代码如下：</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></pre></td><td class="code"><pre><span class="line">#wrapper&#123;</span><br><span class="line">  margin: 0 auto;</span><br><span class="line">  width:960px;</span><br><span class="line">  height:700px;&#125;</span><br><span class="line">header&#123;</span><br><span class="line">  background:#dcd9c0;</span><br><span class="line">  height:30px;&#125;</span><br><span class="line">nav&#123;</span><br><span class="line">  width:150px;</span><br><span class="line">  float:left;</span><br><span class="line">  background:#dcd9c0;</span><br><span class="line">  height:400px;&#125;</span><br><span class="line">  article&#123;</span><br><span class="line">  width:600px;</span><br><span class="line">  float:left;</span><br><span class="line">  background:#ffed53;</span><br><span class="line">  height:400px;&#125;</span><br><span class="line">aside&#123;</span><br><span class="line">  width:210px;</span><br><span class="line">  float:left;</span><br><span class="line">  background:#3f7ccf;  </span><br><span class="line">  height:400px;&#125;</span><br><span class="line">footer&#123;</span><br><span class="line">  clear:both;</span><br><span class="line">  background:#000;</span><br><span class="line">  height:40px;&#125;</span><br></pre></td></tr></table></figure></li></ol><ul><li>各栏边界分开的解决方法：<ol><li>为子元素里的内容加一个div,为div设置一个内边距</li><li>为浮动栏设置 <strong>box-sizing:border-box</strong> 以及内边距和边框即可–  <strong>IE7和IE8不支持</strong> </li></ol></li></ul><h2 id="三栏–中栏流动布局"><a href="#三栏–中栏流动布局" class="headerlink" title="三栏–中栏流动布局"></a>三栏–中栏流动布局</h2><ul><li>方法一</li></ul><ol><li>用一个div class为threecolarap 包围全部三栏并为其设置浮动</li><li>用一个div class为twocolarap包围左栏和中栏并为其设置浮动以及加上 <strong>margin-right:210px</strong> 把右栏拉到区块外边距腾出的位置上</li><li>中栏加上 <strong>margin-right:210px</strong> 在流动居中的栏右测腾出空间</li></ol><ul><li>方法二</li></ul><ol><li>为每一栏display属性设定为table-cell (<strong>IE7不支持</strong>)<h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><h3 id="元素居中"><a href="#元素居中" class="headerlink" title="元素居中"></a>元素居中</h3></li><li>为父元素应用 <strong>text-algin:center</strong> </li><li>为要居中的元素设定 <strong>display:inline-block</strong></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;css布局（持续更新）&quot;&gt;&lt;a href=&quot;#css布局（持续更新）&quot; class=&quot;headerlink&quot; title=&quot;css布局（持续更新）&quot;&gt;&lt;/a&gt;css布局（持续更新）&lt;/h1&gt;&lt;h2 id=&quot;将浮动元素围住&quot;&gt;&lt;a href=&quot;#将浮动元素围住&quot; c
      
    
    </summary>
    
    
      <category term="css" scheme="http://lizehongss.github.io/newBlog/categories/css/"/>
    
    
      <category term="css" scheme="http://lizehongss.github.io/newBlog/tags/css/"/>
    
  </entry>
  
  <entry>
    <title>js算法3</title>
    <link href="http://lizehongss.github.io/newBlog/2018/03/31/%E7%AE%97%E6%B3%953/"/>
    <id>http://lizehongss.github.io/newBlog/2018/03/31/%E7%AE%97%E6%B3%953/</id>
    <published>2018-03-30T16:00:00.000Z</published>
    <updated>2018-03-30T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<ol><li><a href="https://www.codewars.com/kata/digital-cypher/train/javascript">要求介绍</a></li></ol><p>个人代码：</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></pre></td><td class="code"><pre><span class="line">function encode(str,  n)</span><br><span class="line">&#123;  var arr =[];</span><br><span class="line">  var j=0;</span><br><span class="line">  var n = n.toString().split(&#x27;&#x27;).map(Number);</span><br><span class="line">  for(var i =0;i&lt;str.length;i++)&#123;</span><br><span class="line">      arr.push((str[i].charCodeAt()-96));</span><br><span class="line">      arr[i]=arr[i]+n[j];</span><br><span class="line">      j++;</span><br><span class="line">      if(j==n.length) j=0;    </span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">    return arr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>别人代码</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">function encode(str, n) &#123;</span><br><span class="line">  const key = String(n)</span><br><span class="line">  return Array.from(str, (c, i) =&gt; c.charCodeAt(0) - 96 + Number(key[i % key.length]))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.codewars.com/kata/digital-cypher/train/javascript&quot;&gt;要求介绍&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;个人代码：&lt;/p&gt;
&lt;figure class=&quot;highlight p
      
    
    </summary>
    
    
      <category term="算法" scheme="http://lizehongss.github.io/newBlog/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="http://lizehongss.github.io/newBlog/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>js算法1</title>
    <link href="http://lizehongss.github.io/newBlog/2018/03/23/%E7%AE%97%E6%B3%951/"/>
    <id>http://lizehongss.github.io/newBlog/2018/03/23/%E7%AE%97%E6%B3%951/</id>
    <published>2018-03-22T16:00:00.000Z</published>
    <updated>2018-03-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="js相关算法"><a href="#js相关算法" class="headerlink" title="js相关算法"></a>js相关算法</h1><ol><li>传入四个正整数参数，返回最小的两个，代码如下：<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></pre></td><td class="code"><pre><span class="line">function sumTwoSmallestNumbers(numbers) &#123;  </span><br><span class="line">  var min=Math.min.apply(Math,numbers);</span><br><span class="line"> var filterResult= numbers.filter((item)=&gt;&#123;</span><br><span class="line">    return (item!=min)</span><br><span class="line">  &#125;</span><br><span class="line">  )</span><br><span class="line">  var smin=Math.min.apply(Math,filterResult);</span><br><span class="line">  return min+smin;</span><br><span class="line">  //Code here</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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function sumTwoSmallestNumbers(numbers)&#123;  </span><br><span class="line">  numbers = numbers.sort(function(a, b)&#123;return a - b; &#125;);</span><br><span class="line">  return numbers[0] + numbers[1];</span><br><span class="line">&#125;;</span><br><span class="line">//使用排序解决。。。。</span><br></pre></td></tr></table></figure></li><li>给定一个正整数n写成abcd …（a，b，c，d<br>…是数字）和一个正整数p，我们想要找到一个正整数k，如果存在的话，比如数字的总和对于p的连续幂的n等于k * n。换一种说法：<br>例如：（a ^ p + b ^（p + 1）+ c ^（p + 2）+ d ^（p + 3）+ …）= n * k<br>如果是这种情况，我们将返回K，如果不返回-1。<br>注：N，P将始终作为严格正整数给出。<br>代码如下：<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">function digPow(n, p)&#123;</span><br><span class="line">  // ...</span><br><span class="line">  var s=n.toString();</span><br><span class="line">  var sum =0;</span><br><span class="line">  for(var i=0;i&lt;s.length;i++)&#123;</span><br><span class="line">  sum+=Math.pow(s[i],p);</span><br><span class="line">  p++;</span><br><span class="line">  &#125; </span><br><span class="line">  if(sum%n==0) return sum/n;</span><br><span class="line">  else return -1;</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><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">function digPow(n, p) &#123;</span><br><span class="line">  var x = String(n).split(&quot;&quot;).reduce((s, d, i) =&gt; s + Math.pow(d, p + i), 0)</span><br><span class="line">  return x % n ? -1 : x / n</span><br><span class="line">&#125;</span><br><span class="line">//reduce中s为上一次调用回调函数时的返回值，或者初始值</span><br><span class="line">//d为当前正在处理的数组元素</span><br><span class="line">//i为当前数组元素下标</span><br><span class="line">//初始值为0</span><br></pre></td></tr></table></figure></li><li>找到n下面所有倍数的总和m，例子：</li></ol><ul><li>sumMul(2, 9)   ==&gt; 2 + 4 + 6 + 8 = 20</li><li>sumMul(3, 13)  ==&gt; 3 + 6 + 9 + 12 = 30</li><li>sumMul(4, 123) ==&gt; 4 + 8 + 12 + … = 1860</li><li>sumMul(4, -7)  ==&gt; “INVALID”<br>代码如下： <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">function sumMul(n,m)&#123;</span><br><span class="line">//your idea here</span><br><span class="line">var sum=0;</span><br><span class="line">var double=1;</span><br><span class="line">var val=n;</span><br><span class="line">while(val&lt;m)&#123;</span><br><span class="line">sum+=val;</span><br><span class="line">double++;</span><br><span class="line">val=n*double;</span><br><span class="line">&#125;</span><br><span class="line">if(sum==0) return  &quot;INVALID&quot;;</span><br><span class="line">return sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>别人代码</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></pre></td><td class="code"><pre><span class="line">function sumMul(n,m)&#123;</span><br><span class="line">  if (n &gt;= m) return &quot;INVALID&quot;;</span><br><span class="line"></span><br><span class="line">var sum = 0;</span><br><span class="line">  for (var i = n; i &lt; m; i+=n) &#123;</span><br><span class="line">    sum += i;</span><br><span class="line">  &#125;</span><br><span class="line">  return sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>公差公式，示例如下：</li></ol><ul><li>arithmetic_sequence_elements(1, 2, 5) == “1, 3, 5, 7, 9”<br>代码如下：<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">function arithmeticSequenceElements(a,r,n) &#123;</span><br><span class="line">  //your code here</span><br><span class="line">  var arr =[];</span><br><span class="line">  var i=0;</span><br><span class="line">  while(arr.length&lt;n)&#123;</span><br><span class="line">  if(i==0)&#123; arr[i]=a&#125;</span><br><span class="line">  else arr[i]=&#x27; &#x27;+a;</span><br><span class="line">    a=a+r;</span><br><span class="line">    i++;</span><br><span class="line">      </span><br><span class="line">  &#125;</span><br><span class="line"> arr=arr.toString();</span><br><span class="line">  return arr;</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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function arithmeticSequenceElements(a,r,n) &#123;</span><br><span class="line">  var ret = [a]</span><br><span class="line">  while (--n) ret.push(a+=r);</span><br><span class="line">  return ret.join(&#x27;, &#x27;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><ol start="5"><li>去除！示例如下：</li></ol><ul><li>remove(“Hi!”) == “Hi!”</li><li>remove(“Hi!!!”) == “Hi!!!”</li><li>remove(“!Hi”) == “Hi”</li><li>remove(“!Hi!”) == “Hi!”</li><li>remove(“Hi! Hi!”) == “Hi Hi!”</li><li>remove(“Hi”) == “Hi”</li></ul><p>代码如下：</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></pre></td><td class="code"><pre><span class="line">function remove(s)&#123;</span><br><span class="line">  //coding and coding....</span><br><span class="line"> var a=/!+[^!$]/gm;</span><br><span class="line"> return s.replace(a,&quot; &quot;);</span><br><span class="line">&#125;</span><br><span class="line">//自己的想法，然后有两个没过。</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>别人代码系列：</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></pre></td><td class="code"><pre><span class="line">function remove(s)&#123;</span><br><span class="line">  var a=/!+(?!!*$)/g;</span><br><span class="line">  return s.replace(a, &#x27;&#x27;);</span><br><span class="line">&#125;</span><br><span class="line">总体思路是对，不匹配最后的！用零宽负向先行断言，要求接下来的字符不与!匹配</span><br></pre></td></tr></table></figure><ol start="6"><li>返回最大和最小，示例如下：</li></ol><ul><li>highAndLow(“1 2 3 4 5”); // return “5 1”</li><li>highAndLow(“1 2 -3 4 5”); // return “5 -3”</li><li>highAndLow(“1 9 3 4 -5”); // return “9 -5”</li></ul><p>代码如下：</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">function highAndLow(numbers)&#123;</span><br><span class="line">  // ...</span><br><span class="line">  var arr =numbers.split(/[\ ]+/);</span><br><span class="line">    var arr=arr.map(function(item)&#123;</span><br><span class="line">      return parseInt(item);</span><br><span class="line">    &#125;)</span><br><span class="line">    var max=Math.max.apply(Math,arr);</span><br><span class="line">    var min=Math.min.apply(Math,arr);</span><br><span class="line">  console.log(max);</span><br><span class="line">  return max.toString()+&quot; &quot;+min.toString();</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>别人代码 ：</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">function highAndLow(numbers)&#123;</span><br><span class="line">  numbers = numbers.split(&#x27; &#x27;).map(Number);</span><br><span class="line">  return Math.max.apply(0, numbers) + &#x27; &#x27; + Math.min.apply(0, numbers);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;js相关算法&quot;&gt;&lt;a href=&quot;#js相关算法&quot; class=&quot;headerlink&quot; title=&quot;js相关算法&quot;&gt;&lt;/a&gt;js相关算法&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;传入四个正整数参数，返回最小的两个，代码如下：&lt;figure class=&quot;highlight 
      
    
    </summary>
    
    
      <category term="算法" scheme="http://lizehongss.github.io/newBlog/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="http://lizehongss.github.io/newBlog/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>js算法2</title>
    <link href="http://lizehongss.github.io/newBlog/2018/03/23/%E7%AE%97%E6%B3%952/"/>
    <id>http://lizehongss.github.io/newBlog/2018/03/23/%E7%AE%97%E6%B3%952/</id>
    <published>2018-03-22T16:00:00.000Z</published>
    <updated>2018-03-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<ol><li>首字母大写</li></ol><p>代码如下 ：</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></pre></td><td class="code"><pre><span class="line">function capitalizeWord(word) &#123;</span><br><span class="line">  var r =word.charAt(0);</span><br><span class="line">  var s=r.toUpperCase();</span><br><span class="line">var arr=word.split(&#x27;&#x27;);</span><br><span class="line"></span><br><span class="line">  arr[0]=s;</span><br><span class="line">  var arrs=arr.toString().replace(/,/g , &quot;&quot;);</span><br><span class="line">  return arrs;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>别人代码：</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">function capitalizeWord(word) &#123;</span><br><span class="line">  return word[0].toUpperCase() + word.slice(1);</span><br><span class="line">  //删除第一个字母</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>计算体积并得出差值<br>代码如下：<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></pre></td><td class="code"><pre><span class="line">function findDifference(a, b) &#123;</span><br><span class="line">  //loading...</span><br><span class="line">  var s1=1,s2=1;</span><br><span class="line">  for(var i=0;i&lt;a.length;i++)&#123;</span><br><span class="line">  s1=s1*a[i];</span><br><span class="line">  &#125;</span><br><span class="line">  for(var i=0;i&lt;b.length;i++)&#123;</span><br><span class="line">    s2=s2*b[i];</span><br><span class="line">  &#125;</span><br><span class="line">  if(s1&gt;s2)&#123;</span><br><span class="line">    return s1-s2;</span><br><span class="line">  &#125;</span><br><span class="line">  if(s1&lt;s2)&#123;</span><br><span class="line">    return s2-s1;</span><br><span class="line">  &#125;</span><br><span class="line">  if(s1==s2)&#123;</span><br><span class="line">    return 0;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>别人代码如下：</li></ol><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></pre></td><td class="code"><pre><span class="line">function find_difference(a, b) &#123;</span><br><span class="line">  return Math.abs(a.reduce((previous, current) =&gt; previous * current) - b.reduce((previous, current) =&gt; previous * current));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>返回数组中不重复的值<br>代码如下:<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></pre></td><td class="code"><pre><span class="line">function stray(numbers)&#123;</span><br><span class="line">  for (var i in numbers)&#123;</span><br><span class="line">     if (numbers.indexOf(numbers[i]) === numbers.lastIndexOf(numbers[i]))&#123;return numbers[i]&#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></li><li>转换Number类型<br>示例如下：<br>348597 =&gt; [7,9,5,8,4,3]<br>代码如下：<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></pre></td><td class="code"><pre><span class="line">function digitize(n) &#123;</span><br><span class="line">  var s=n.toString();</span><br><span class="line">  var arr=[];</span><br><span class="line">  for(var i=0;i&lt;s.length;i++)&#123;</span><br><span class="line">    arr.push(s[i]);</span><br><span class="line">  &#125;</span><br><span class="line">  return arr.map(Number).reverse();</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></pre></td><td class="code"><pre><span class="line">function digitize(n)&#123;</span><br><span class="line">  return (n + &#x27;&#x27;).split(&#x27;&#x27;).map(Number).reverse();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>solution(‘world’); // returns ‘dlrow’</li></ol><p>代码如下：</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></pre></td><td class="code"><pre><span class="line">var arr =str.split(&#x27;&#x27;);</span><br><span class="line"> arr=arr.sort().toString().replace(/,/g,&#x27;&#x27;);</span><br><span class="line"> return arr;</span><br></pre></td></tr></table></figure><p>别人代码：</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></pre></td><td class="code"><pre><span class="line">function solution(str)&#123;</span><br><span class="line">  return str.split(&#x27;&#x27;).reverse().join(&#x27;&#x27;);  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="6"><li>简单的运算</li></ol><p>代码如下： </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></pre></td><td class="code"><pre><span class="line">function basicOp(operation, value1, value2) &#123;</span><br><span class="line">    switch (operation) &#123;</span><br><span class="line">        case &#x27;+&#x27;:</span><br><span class="line">            return value1 + value2;</span><br><span class="line">        case &#x27;-&#x27;:</span><br><span class="line">            return value1 - value2;</span><br><span class="line">        case &#x27;*&#x27;:</span><br><span class="line">            return value1 * value2;</span><br><span class="line">        case &#x27;/&#x27;:</span><br><span class="line">            return value1 / value2;</span><br><span class="line">        default:</span><br><span class="line">            return 0;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>别人代码：</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">function basicOp(o, a, b) &#123;</span><br><span class="line">  return eval(a+o+b);</span><br><span class="line">&#125;</span><br><span class="line">//eval() 函数可计算某个字符串，并执行其中的的 JavaScript 代码。</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;ol&gt;
&lt;li&gt;首字母大写&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;代码如下 ：&lt;/p&gt;
&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;b
      
    
    </summary>
    
    
      <category term="算法" scheme="http://lizehongss.github.io/newBlog/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="http://lizehongss.github.io/newBlog/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>面试2</title>
    <link href="http://lizehongss.github.io/newBlog/2018/03/23/%E9%9D%A2%E8%AF%952/"/>
    <id>http://lizehongss.github.io/newBlog/2018/03/23/%E9%9D%A2%E8%AF%952/</id>
    <published>2018-03-22T16:00:00.000Z</published>
    <updated>2018-03-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<ol><li>浏览器如何解析css</li></ol><p>CSS选择器的解析是从右向左解析的。若从左向右的匹配，发现不符合规则，需要进行回溯，会损失很多性能。若从右向左匹配，先找到所有的最右节点，对于每一个节点，向上寻找其父节点直到找到根元素或满足条件的匹配规则，则结束这个分支的遍历。两种匹配规则的性能差别很大，是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点（叶子节点），而从左向右的匹配规则的性能都浪费在了失败的查找上面。<br>而在 CSS 解析完毕后，需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree，最终用来进行绘图。在建立 Render Tree 时（WebKit 中的「Attachment」过程），浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果（Style Rules）来确定生成怎样的 Render Tree。</p><ol start="2"><li>数组和对象的原生方法<br>数组方法有</li></ol><ul><li>转换方法：toLocaleString(),toString(),valueOf()</li><li>栈方法：push(),pop()</li><li>队列方法 shift(),unshif()</li><li>重排序方法 reverse() ,sort()</li><li>操作方法 concat() slice() splice()</li><li>位置方法 indexOf() lastIndexOf()</li><li>迭代方法 ecery() filter() fotEach() map() some()</li><li>归并方法 reduce() reduceRight()</li></ul><ol start="3"><li>如果需要手动写动画，你认为最小时间间隔是多久，为什么？</li></ol><p>多数显示器默认频率是60Hz，即1秒刷新60次，所以理论上最小间隔为1/60＊1000ms ＝ 16.7ms。</p><ol start="4"><li>作用域问题(专开坑)</li><li>js数据类型并写出它们的内存图</li></ol><ul><li>js数据类型：Undefined Null Boolean Number String Object</li><li>JS中的基础数据类型，这些值都有固定的大小，往往都保存在栈内存中，由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值</li><li>jS的引用数据类型，比如数组Array，它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。</li></ul><ol start="6"><li>null和undefine的区别</li></ol><p><a href="http://www.ruanyifeng.com/blog/2014/03/undefined-vs-null.html">答案</a></p><ol start="7"><li>为什么会出现浮动和什么时候需要清除浮动？清除浮动的方式</li></ol><ul><li>父元素的高度无法被撑开，影响与父元素同级的元素</li><li>与浮动元素同级的非浮动元素（内联元素）会跟随其后</li><li>若非第一个元素浮动，则该元素之前的元素也需要浮动，否则会影响页面显示的结构。<br>方法：</li><li>父级div定义height</li><li>最后一个浮动元素后加空div标签 并添加样式clear:both。</li><li>包含浮动元素的父标签添加样式overflow为hidden或auto。</li><li>父级div定义zoom</li></ul><ol start="8"><li>:after和::after有什么区别，有什么作用</li></ol><ul><li>作用 使得html更加语义化。有些时候，为了某些特定的展现，不得不添加用于辅助布局的无意义html元素，这两个伪元素能实际起到这种辅助布局的作用，而又不增加无意义纯布局html元素，所以html就更简洁更纯粹了。</li><li>区别before/:after这种写法css2中就有，叫伪类；::befroe/::after这种写法是css3中的，叫伪元素，但两种写法的作用是一样的。后者无法兼容IE低版本，如果要求兼容IE低版本请使用前者。注：对于img和input元素两者都不起作用</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;ol&gt;
&lt;li&gt;浏览器如何解析css&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CSS选择器的解析是从右向左解析的。若从左向右的匹配，发现不符合规则，需要进行回溯，会损失很多性能。若从右向左匹配，先找到所有的最右节点，对于每一个节点，向上寻找其父节点直到找到根元素或满足条件的匹配规则，则结束
      
    
    </summary>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>面试3</title>
    <link href="http://lizehongss.github.io/newBlog/2018/03/23/%E9%9D%A2%E8%AF%953/"/>
    <id>http://lizehongss.github.io/newBlog/2018/03/23/%E9%9D%A2%E8%AF%953/</id>
    <published>2018-03-22T16:00:00.000Z</published>
    <updated>2018-03-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<ol><li>vue 生命周期</li></ol><ul><li><p>beforecreated：el 和 data 并未初始化 </p></li><li><p>created:完成了 data 数据的初始化，el没有</p></li><li><p>beforeMount：完成了 el 和 data 初始化 </p></li><li><p>mounted ：完成挂载</p></li><li><p>beforecreate : 举个栗子：可以在这加个loading事件 </p></li><li><p>created ：在这结束loading，还做一些初始化，实现函数自执行 </p></li><li><p>mounted ： 在这发起后端请求，拿回数据，配合路由钩子做一些事情</p></li><li><p>beforeDestroy： 你确认删除XX吗？ </p></li><li><p>destroyed ：当前组件已被删除，清空相关内容</p></li></ul><ol start="2"><li>组件通信</li></ol><p>父组件向子组件通信</p><ul><li>使用props即可</li></ul><p>子组件向父组件通信</p><ul><li>使用自定义事件，子组件用$emit()来触发向父组件通信的事件</li></ul><p>非父子组件通信</p><ul><li>在vue.js2.x中使用一个空的vu实例来作中介，组件把自定义事件名称和数据发送到这个空实例，其它实例或组件通过监听空实例的自定义事件来刷新数据 </li></ul><ol start="3"><li>网络状态</li></ol><ul><li>1XX 成功  </li><li>2XX 重定向  </li><li>3XX 客户机中出现的错误</li><li>400 请求无效 </li></ul><ol start="4"><li>闭包应用场景</li></ol><p>闭包通常用来创建内部变量，使得<br>这些变量不能被外部随意修改，同时又可以通过指定的函数接口来操作。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;ol&gt;
&lt;li&gt;vue 生命周期&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;beforecreated：el 和 data 并未初始化 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;created:完成了 data 数据的初始化，el没有&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;befor
      
    
    </summary>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>面试1</title>
    <link href="http://lizehongss.github.io/newBlog/2018/03/22/%E9%9D%A2%E8%AF%951/"/>
    <id>http://lizehongss.github.io/newBlog/2018/03/22/%E9%9D%A2%E8%AF%951/</id>
    <published>2018-03-21T16:00:00.000Z</published>
    <updated>2018-03-21T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<ol><li>清除浮动<br>方法1：:after方法<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></pre></td><td class="code"><pre><span class="line">:after&#123;</span><br><span class="line"> content: &quot;.&quot;;</span><br><span class="line"> clean：both;</span><br><span class="line">display:block;</span><br><span class="line">height: 0px;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>方法2：父元素设置overflow:hidden<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">.f&#123;</span><br><span class="line">    overflow:hidden;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>方法3：添加空div标签clean:both<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></pre></td><td class="code"><pre><span class="line">.clean-both&#123;</span><br><span class="line">    clean:both;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>div垂直和水平居中<br>方法1 使用absolute+transform<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">.parent&#123;</span><br><span class="line">    position: relative;</span><br><span class="line">&#125;</span><br><span class="line">.child&#123;</span><br><span class="line">    position:absolute;</span><br><span class="line">    left:50%;</span><br><span class="line">    top:50%;</span><br><span class="line">    transform:tranplate(-50%,-50%);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>方法2 inline-block+text-align<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">.parent&#123;</span><br><span class="line">    text-align:center;</span><br><span class="line">    display:table-cell;</span><br><span class="line">    vertical-align:middle;</span><br><span class="line">&#125;</span><br><span class="line">.child&#123;</span><br><span class="line">    display:inline-block;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>方法3 flex<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">.parent&#123;</span><br><span class="line">    display:flex;</span><br><span class="line">    justify-content:center;</span><br><span class="line">    align-items:center;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>DOM操作<br>获取子元素并插入<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">&lt;ul&gt;</span><br><span class="line">&lt;li&gt;1&lt;/li&gt;</span><br><span class="line">&lt;li&gt;3&lt;/li&gt;</span><br><span class="line">&lt;/ul&gt;</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><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">var li=document.createElement(&quot;li&quot;);</span><br><span class="line">var textNode=document.createTextNode(&quot;2&quot;);</span><br><span class="line">li.appendChild(textNode);</span><br><span class="line">var ul=document.getElementsByTagName(&quot;ul&quot;);</span><br><span class="line">ul.insertBefore(li,ul.childNodes[1]);</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>数组去重<br>遍历，将数组的值添加到一个对象的属性名里，并给属性赋值，对象不能添加相同属性名，以这个为依据可以实现数组去重，然后用Object.keys(对象)返回这个对象可枚举属性组成的数组，这个数组就是去重后的数组。<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"> let a = [&#x27;1&#x27;, &#x27;2&#x27;, &#x27;3&#x27;, 1,NaN,NaN,undefined,undefined,null,null, &#x27;a&#x27;, &#x27;b&#x27;, &#x27;b&#x27;];</span><br><span class="line">    const unique = arr =&gt; &#123;</span><br><span class="line">        var obj = &#123;&#125;</span><br><span class="line">        arr.forEach(value =&gt; &#123;</span><br><span class="line">            obj[value] = 0;//这步新添加一个属性，并赋值，如果不赋值的话，属性会添加不上去</span><br><span class="line">        &#125;)</span><br><span class="line">        return Object.keys(obj);//`Object.keys(对象)`返回这个对象可枚举属性组成的数组，这个数组就是去重后的数组</span><br><span class="line">    &#125;</span><br><span class="line">    console.log(unique(a));//[&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;NaN&quot;, &quot;undefined&quot;, &quot;null&quot;, &quot;a&quot;, &quot;b&quot;]</span><br><span class="line"></span><br><span class="line">作者：OBKoro1</span><br><span class="line">链接：https://juejin.im/post/5aad40e4f265da237f1e12ed</span><br><span class="line">来源：掘金</span><br><span class="line">著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</span><br></pre></td></tr></table></figure>es6<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></pre></td><td class="code"><pre><span class="line">let unique= [...new Set(array)];</span><br><span class="line">//es6 Set数据结构类似于数组，成员值是唯一的，有重复的值会自动去重。</span><br><span class="line">//Set内部使用===来判断是否相等，类似&#x27;1&#x27;和1会两个都保存，NaN和NaN只会保存一个</span><br></pre></td></tr></table></figure></li><li>提高页面加载速度</li></ol><ul><li>减少dom操作</li><li>部署前，图片压缩，代码压缩</li><li>优化js代码结构，减少冗余代码</li><li>减少http请求，合理设置 HTTP缓存</li><li>使用内容分发cdn加速</li><li>静态资源缓存</li><li>图片延迟加载</li></ul><ol start="6"><li>作用域问题<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></pre></td><td class="code"><pre><span class="line">function F()&#123;</span><br><span class="line">    var arr=[],i;</span><br><span class="line">    for(i=0;i&lt;3;i++)&#123;</span><br><span class="line">        arr[i]=function()&#123;</span><br><span class="line">            return i;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    return arr ;</span><br><span class="line">&#125;</span><br><span class="line">结果都为3;闭包问题</span><br><span class="line">方法为</span><br><span class="line">function F()&#123;</span><br><span class="line">    function binder(x)&#123;</span><br><span class="line">        return function()&#123;</span><br><span class="line">            return x;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    var arr =[],i;</span><br><span class="line">    for(i=0;i&lt;3;i++)&#123;</span><br><span class="line">        arr[i]=binder(i);</span><br><span class="line">    &#125;</span><br><span class="line">    return arr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>vue数据监控</li></ol><ul><li>使用watch监控</li><li>使用计算属性</li><li>过渡器</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;ol&gt;
&lt;li&gt;清除浮动&lt;br&gt;方法1：:after方法&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span cla
      
    
    </summary>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
      <category term="面试" scheme="http://lizehongss.github.io/newBlog/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>vue学习笔记(二)————组件</title>
    <link href="http://lizehongss.github.io/newBlog/2018/02/10/vue%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%BB%84%E4%BB%B6%20/"/>
    <id>http://lizehongss.github.io/newBlog/2018/02/10/vue%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%BB%84%E4%BB%B6%20/</id>
    <published>2018-02-09T16:00:00.000Z</published>
    <updated>2018-02-09T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>主要是有关vue组件的笔记，然后也不想为了定blog而写blog,所以应该会写一些真的自己想记下来的东西，也希望可以尽量精简的写</p><h1 id="组件用法"><a href="#组件用法" class="headerlink" title="组件用法"></a>组件用法</h1><p>需要注册才能使用组件，注册分为局部注册和全局注册，代码示例如下：</p><p><strong>必须在实例注册，组件才可以使用</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><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><br><span class="line">Vue.component(&quot;组件名&quot;,&#123;</span><br><span class="line"></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">var 组件名 =&#123;</span><br><span class="line">    //选项</span><br><span class="line">&#125;</span><br><span class="line">html模板如下：</span><br><span class="line">&lt;组件名&gt;&lt;/组件名</span><br></pre></td></tr></table></figure><h1 id="组件选项"><a href="#组件选项" class="headerlink" title="组件选项"></a>组件选项</h1><h2 id="template"><a href="#template" class="headerlink" title="template"></a>template</h2><p>temlplate后面是要渲染的内容，必须有一个元素包围它，代码如下：</p><p><strong>注意v-show不能用在template中，可以使用v-if</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">Vue.component(&quot;组件名&quot;,&#123;</span><br><span class="line">template: &#x27;&lt;div&gt;&lt;/div&gt;&#x27;;</span><br><span class="line">    </span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="data"><a href="#data" class="headerlink" title="data"></a>data</h2><p>data的用法与实方法相同，不同的是要将数据return出去，代码如下：</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></pre></td><td class="code"><pre><span class="line">Vue.component(&quot;组件名&quot;,&#123;</span><br><span class="line">.....</span><br><span class="line">data : function&#123;</span><br><span class="line">    return  &#123;</span><br><span class="line">        mws: &quot;text&quot;</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">&#125;);</span><br></pre></td></tr></table></figure><h2 id="props"><a href="#props" class="headerlink" title="props"></a>props</h2><p>props主要是用来接收来自父组件的数据，可以是字符串数组和对象<br><strong>注意数据是单向的，只能父组件传到子组件</strong><br>代码如下：</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">html:</span><br><span class="line"></span><br><span class="line">&lt;mytext message=&quot;text&quot;&gt;&lt;/mytext&gt;</span><br><span class="line"></span><br><span class="line">js：</span><br><span class="line">Vue.component(&quot;mytext&quot;,&#123;</span><br><span class="line">    props:[&#x27;message&#x27;]</span><br><span class="line">    </span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>props的数据可以进行验证，代码如下：</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></pre></td><td class="code"><pre><span class="line">props: &#123;</span><br><span class="line">    message : &#123;</span><br><span class="line">        type:Number， //类型</span><br><span class="line">        default:&quot;&quot;text //如果没有定义的默认值</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><h2 id="wath"><a href="#wath" class="headerlink" title="wath"></a>wath</h2><p> 主要是用来监听props的值的改变，从而通知父组件或者更新props值<br> 代码如下<br> <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></pre></td><td class="code"><pre><span class="line">watch:&#123;</span><br><span class="line">    message: function(val)&#123;</span><br><span class="line">        //当message发生改变时触发的函数 </span><br><span class="line">        //val 为更新后的message值</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="组件通信"><a href="#组件通信" class="headerlink" title="组件通信"></a>组件通信</h1><h2 id="父组件向子组件通信"><a href="#父组件向子组件通信" class="headerlink" title="父组件向子组件通信"></a>父组件向子组件通信</h2><p>使用上面的props即可</p><h2 id="子组件向父组件通信"><a href="#子组件向父组件通信" class="headerlink" title="子组件向父组件通信"></a>子组件向父组件通信</h2><p>使用自定义事件，子组件用$emit()来触发向父组件通信的事件代码如下：</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">html:</span><br><span class="line">&lt;mytext @send=&quot;handle&quot;&gt;&lt;/mytext&gt;</span><br><span class="line">//其中send为子组件发送过来的参数，为自定义事件的名称</span><br><span class="line">Vue.component(&quot;mytext&quot;,&#123;</span><br><span class="line">template:&#x27;&lt;button @click=&quot;hadles&quot;&gt;&#x27;,</span><br><span class="line">methods:&#123;</span><br><span class="line">    hadles: function()&#123;</span><br><span class="line">        this.$emit(&#x27;handle&#x27;,数据);</span><br><span class="line">        //第一个参数为事件名称，第二个为发送的数据</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></pre></td></tr></table></figure><p><em>可以使用v-model来简化，不过个人感觉没有必要，所以不想写在这里做总结。。。。。。</em></p><h2 id="非父子组件通信"><a href="#非父子组件通信" class="headerlink" title="非父子组件通信"></a>非父子组件通信</h2><p>在vue.js2.x中使用一个空的vu实例来作中介，组件把自定义事件名称和数据发送到这个空实例，其它实例或组件通过监听空实例的自定义事件来刷新数据<br>代码如下：</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></pre></td><td class="code"><pre><span class="line">var bus =new Vue();</span><br><span class="line">bus.$emit(&#x27;clicks&#x27;,mes);</span><br><span class="line">bus.$on(&#x27;clicks&#x27;,function()&#123;&#125;);</span><br></pre></td></tr></table></figure><p>补充：<br>当子组件较多时，可以用ref属性为组件指定一个索引名称：代码如下:</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">html代码如下：</span><br><span class="line">&lt;mytext ref=&quot;A&gt;&lt;/mytext&gt;</span><br><span class="line">//调用指定的子组件的message</span><br><span class="line">var msg=this.$refs.comA.message</span><br></pre></td></tr></table></figure><h1 id="slot"><a href="#slot" class="headerlink" title="slot"></a>slot</h1><p>slot是内容分发，用在将父组件的dom结构挂载，它与props,events触发事件构成组件的3个APi来源.<br><strong>slot的作用域是在父组件上</strong><br>代码如下：</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">&lt;mytext&gt;</span><br><span class="line">&lt;p&gt;&lt;/p&gt;//挂载内容 </span><br><span class="line">&lt;/mytext&gt;</span><br><span class="line">Vue.component(&quot;mytext&quot;,&#123;</span><br><span class="line">template:&#x27;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">    &lt;slot&gt;</span><br><span class="line">    &lt;p&gt;&lt;/p&gt;//没有内容时的默认内容</span><br><span class="line">    &lt;/slot&gt;//父组件的内容将挂载在slot里面</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">&#x27;</span><br><span class="line">    </span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>可以给slot指定一个name，使用分发指定内容代码如下：</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">&lt;mytext&gt;</span><br><span class="line">&lt;p slot=&quot;one&quot;&gt;&lt;/p&gt;//挂载内容,slot为具名slot</span><br><span class="line">&lt;/mytext&gt;</span><br><span class="line">Vue.component(&quot;mytext&quot;,&#123;</span><br><span class="line">template:&#x27;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">    &lt;slot&gt;</span><br><span class="line">    &lt;p&gt;&lt;/p&gt;//没有内容时的默认内容</span><br><span class="line">    &lt;/slot&gt;//父组件的内容将挂载在slot里面</span><br><span class="line">    &lt;slot name=&quot;one&quot;&gt;//具名slot将分发在这里</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">&#x27;</span><br></pre></td></tr></table></figure><p>也可以指定数据传递到父组件的挂载里，使用scope,代码如下：</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></pre></td><td class="code"><pre><span class="line">&lt;mytext&gt;</span><br><span class="line">&lt;p slot=&quot;one&quot; scope=&quot;props&quot;&gt;</span><br><span class="line">    &#123;&#123;props.msg&#125;&#125; //显示子组件的msg</span><br><span class="line">&lt;/p&gt;//挂载内容,slot为具名slot,props为临时变量</span><br><span class="line">&lt;/mytext&gt;</span><br><span class="line">Vue.component(&quot;mytext&quot;,&#123;</span><br><span class="line">template:&#x27;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">    &lt;slot&gt;</span><br><span class="line">    &lt;p&gt;&lt;/p&gt;//没有内容时的默认内容</span><br><span class="line">    &lt;/slot&gt;//父组件的内容将挂载在slot里面</span><br><span class="line">    &lt;slot name=&quot;one&quot; msg=&quot;数据&quot;&gt;//具名slot将分发在这里</span><br><span class="line">    //msg为向父组件传递的数据</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">&#x27;</span><br></pre></td></tr></table></figure><p>可以使用$slots访问分发的内容 </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">var header =this.$slots.one;</span><br></pre></td></tr></table></figure><h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><pre><code>还有许多关于组件的高级用法，不过我不是大佬，等以后用到再写吧。写完收工睡觉</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;主要是有关vue组件的笔记，然后也不想为了定blog而写blog,所以应该会写一些真的自己想记下来的东西，也希望可以尽量精简的写&lt;/p&gt;
&lt;
      
    
    </summary>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/categories/vue/"/>
    
    
      <category term="vue" scheme="http://lizehongss.github.io/newBlog/tags/vue/"/>
    
  </entry>
  
</feed>
