mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-16 01:01:38 +00:00
56 lines
19 KiB
HTML
56 lines
19 KiB
HTML
<!DOCTYPE html><html lang="zh-CN" data-default-color-scheme=""auto""><head><meta charset="UTF-8"><link rel="apple-touch-icon" sizes="76x76" href="/images/img/apple-touch-icon.webp"><link rel="icon" type="image/png" href="/images/img/favicon.webp"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,shrink-to-fit=no"><meta http-equiv="x-ua-compatible" content="ie=edge"><meta name="theme-color" content="#9DC8C8"><meta name="description" content=""><meta name="author" content="Defectink"><meta name="keywords" content=""><title>Vue3中的响应数据 - 🍭Defectink</title><link rel="stylesheet" href="https://cdn.defectink.com/static/twitter-bootstrap/4.5.3/css/bootstrap.min.css"><link rel="stylesheet" href="https://cdn.defectink.com/static/github-markdown-css/4.0.0/github-markdown.min.css"><link rel="stylesheet" href="/lib/hint/hint.min.css"><link rel="stylesheet" href="https://cdn.defectink.com/static/highlight.js/10.0.0/styles/github-gist.min.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css"><link rel="stylesheet" href="https://cdn.defectink.com/static/t/font_1736178_kmeydafke9r.css"><link rel="stylesheet" href="/css/main.css"><link rel="stylesheet" href="/css/xfy.css"><script src="/js/utils.js"></script><script src="/js/color-schema.js"></script><meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/xml/atom.xml" title="🍭Defectink" type="application/atom+xml"><link rel="alternate" href="/xml/rss.xml" title="🍭Defectink" type="application/rss+xml"></head><body><header style="height:75vh"><nav id="navbar" class="navbar fixed-top navbar-expand-lg navbar-dark scrolling-navbar"><div class="container"><a class="navbar-brand" href="/"> <strong>🍭Defectink</strong> </a> <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><div class="animated-icon"><span></span><span></span><span></span></div></button><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav ml-auto text-center"><li class="nav-item"><a class="nav-link" href="/">🏠 首页</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">📕 索引</a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item" href="/categories/">🎁 分类</a> <a class="dropdown-item" href="/tags/">🎐 标签</a></div></li><li class="nav-item"><a class="nav-link" href="/archives/">📂 归档</a></li><li class="nav-item"><a class="nav-link" href="/about/">🎃 关于</a></li><li class="nav-item"><a class="nav-link" href="/links/">🙆♀️ 小伙伴</a></li><li class="nav-item"><a class="nav-link" href="/pgp/">🔐 PGP</a></li><li class="nav-item" id="search-btn"><a class="nav-link" data-toggle="modal" data-target="#modalSearch"> <i class="iconfont icon-search"></i> </a></li><li class="nav-item" id="color-toggle-btn"><a class="nav-link" href="javascript:"> <i class="iconfont icon-dark" id="color-toggle-icon"></i> </a></li></ul></div></div></nav><div class="banner intro-2" id="background" parallax="true" style="background:url(/images/img/post.webp) no-repeat center center;background-size:cover"><div class="full-bg-img"><div class="mask flex-center" style="background-color:rgba(0,0,0,.3)"><div class="container page-header text-center fade-in-up"><span class="h2" id="subtitle"></span><div class="mt-3"><span class="post-meta mr-2"><i class="iconfont icon-author" aria-hidden="true"></i> Defectink</span><span class="post-meta"><i class="iconfont icon-date-fill" aria-hidden="true"></i> <time datetime="2020-11-02 10:01" pubdate>2020年11月2日 上午</time></span></div><div class="mt-1"><span class="post-meta mr-2"><i class="iconfont icon-chart"></i> 1.2k 字</span><span class="post-meta mr-2"><i class="iconfont icon-clock-fill"></i> 15 分钟</span></div></div></div></div></div></header><main><div class="container-fluid"><div class="row"><div class="d-none d-lg-block col-lg-2"></div><div class="col-lg-8 nopadding-md"><div class="container nopadding-md" id="board-ctn"><div class="py-5" id="board"><article class="post-content mx-auto" id="post"><h1 style="display:none">Vue3中的响应数据</h1><p class="note note-info">本文最后水于:2020年11月2日 凌晨</p><div class="markdown-body" id="post-body"><h2 id="实时渲染"><a href="#实时渲染" class="headerlink" title="实时渲染"></a>实时渲染</h2><p>在学习Vue2.x的过程中,做过一个更改数据从而触发实时渲染DOM的小实例。期间很顺利,而后在同样方法测试Vue3的时候发现遇到了一些不同的行为。根据查阅了一些文档以及源码,做出了一些推测。</p><h2 id="数据与方法"><a href="#数据与方法" class="headerlink" title="数据与方法"></a>数据与方法</h2><p>当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。</p><p>在Vue2.x中,可以创建一个数据对象,为实例提供数据。虽然这样的写法和直接在实例中为<code>data</code>添加属性没有多少差别:</p><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
|
||
<span class="hljs-tag"><<span class="hljs-name">p</span>></span>
|
||
{{ message }}
|
||
<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
|
||
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
<span class="hljs-tag"><<span class="hljs-name">script</span>></span>
|
||
<span class="javascript"> <span class="hljs-keyword">let</span> data = {</span>
|
||
<span class="javascript"> message: <span class="hljs-string">'Hello World!'</span></span>
|
||
}
|
||
<span class="javascript"> <span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({</span>
|
||
<span class="javascript"> el: <span class="hljs-string">'#app'</span>,</span>
|
||
data: data
|
||
});
|
||
<span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre><p>这时我们单独创建的<code>data</code>对象与实例中的<code>data</code>成立了引用关系:</p><pre><code class="hljs js">app.$data.message === data.message
|
||
<span class="hljs-comment">// true</span></code></pre><p>并且他们三者是互等的:</p><pre><code class="hljs js">app.message === app.$data.message
|
||
app.$data.message === data.message</code></pre><p>并且我们单独创建的<code>data</code>对象也被转换成了检测数据变化的Observer对象</p><p><img src="../images/Vue3%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE/2020-10-20-14-23-58.webp" srcset="/images/img/loading.gif"></p><p>因此,我们在修改<code>data</code>对象的内容时,app实例的属性也会被改变,从而实时渲染到DOM上。</p><p><img src="../images/Vue3%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE/2020-10-20-14-25-25.webp" srcset="/images/img/loading.gif"></p><p>但在Vue3上发生了一些小小的改变。在Vue3上,我们将实例的<code>data</code>函数直接return为我们在父作用域中创建的对象,这个对象不会被修改为检测属性数据变化的对象。</p><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
|
||
<span class="hljs-tag"><<span class="hljs-name">p</span>></span>
|
||
{{ message }}
|
||
<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
|
||
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
<span class="hljs-tag"><<span class="hljs-name">script</span>></span>
|
||
<span class="javascript"> <span class="hljs-keyword">let</span> data = {</span>
|
||
<span class="javascript"> message: <span class="hljs-string">'Hello World!'</span></span>
|
||
};
|
||
<span class="javascript"> <span class="hljs-keyword">let</span> app = Vue.createApp({</span>
|
||
<span class="javascript"> <span class="hljs-function"><span class="hljs-title">data</span>(<span class="hljs-params"></span>)</span> {</span>
|
||
<span class="javascript"> <span class="hljs-keyword">return</span> data;</span>
|
||
}
|
||
});
|
||
<span class="javascript"> <span class="hljs-keyword">let</span> vm = app.mount(<span class="hljs-string">'#app'</span>);</span>
|
||
<span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre><p>这里的app是我们创建的实例,但最终挂载DOM后返回的实例为vm。不同于2.x的地方是,这里我们在父作用域中创建的对象并没用任何的变化,它还是一个普通的对象。</p><p><img src="../images/Vue3%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE/2020-10-20-14-31-01.webp" srcset="/images/img/loading.gif"></p><p>并且,他们也互相建立了引用的关系;</p><pre><code class="hljs js">vm.message === data.message
|
||
<span class="hljs-comment">// true</span></code></pre><p>虽然他们已经是互相引用,但是<code>data</code>还是一个普通的对象。这里就会发现一个有意思的现象,只更新<code>data.message</code>的值,<code>vm.message</code>或者说<code>vm.$data.message</code>的值会同样更新,保持和<code>data</code>对象一样。但是DOM却没用被实时渲染。</p><p><img src="../images/Vue3%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE/2020-10-20-15-42-32.webp" srcset="/images/img/loading.gif"></p><p>这一点2和3有着很大的差距,在vue2中,我们是可以通过<code>data</code>对象来实时更新DOM的。而在3中就不行了。</p><p>据我的猜测,主要是Vue3没有对父作用域的<code>data</code>对象设置Proxy代理的原因。虽然二者已经是互相引用,修改一个对象值,另一个对象也会被修改。<strong>但是通过修改<code>data</code>的属性,并不会触发<code>vm.$data</code>对象的<code>set()</code>方法。</strong></p><h2 id="模仿行为"><a href="#模仿行为" class="headerlink" title="模仿行为"></a>模仿行为</h2><p>我使用了一个小例子,模仿了一下Vue3的行为:</p><pre><code class="hljs js"><span class="hljs-comment">// 这是在父作用域中的data对象,它是一个普通对象</span>
|
||
<span class="hljs-keyword">let</span> data = {
|
||
message: <span class="hljs-string">'xfy'</span>
|
||
}
|
||
|
||
<span class="hljs-comment">// 这是模拟set方法,成功set时会打印一条信息</span>
|
||
<span class="hljs-keyword">let</span> handler = {
|
||
set: <span class="hljs-function">(<span class="hljs-params">obj, prop, value</span>) =></span> {
|
||
obj[prop] = value;
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'set success: '</span> + value);
|
||
}
|
||
}
|
||
|
||
<span class="hljs-comment">// 通过proxy创建一个继承自data属性的实例</span>
|
||
<span class="hljs-keyword">let</span> vm = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Proxy</span>(data, handler);</code></pre><p>这是一个很简单的例子,我们为<code>vm</code>对象设置了一个来自<code>data</code>对象的代理。现在二者就是互相引用的关系了,就和Vue3一样。</p><pre><code class="hljs js">data.message === vm.message
|
||
<span class="hljs-comment">// true</span></code></pre><p>我在代理的拦截中配置了一个setter,当<code>vm</code>对象成功设置了值后,就会触发这个setter,并在控制台打印一则信息。用来模拟更新DOM。也就是说,现在的<code>vm</code>实例就相当于Vue实例,当我更新其属性时,会在控制台动态的打印信息,就相当于实时更新了DOM。就和Vue实例一样。</p><p>现在我们直接对<code>vm.message</code>赋值,则会成功触发预先设置的setter函数,成功的更新了值并且在控制打印了消息。</p><pre><code class="hljs js">vm.message
|
||
<span class="hljs-comment">// "xfy"</span>
|
||
vm.message = <span class="hljs-string">'hello xfy'</span>;
|
||
<span class="hljs-comment">// set success: hello xfy</span>
|
||
<span class="hljs-comment">// "hello xfy"</span></code></pre><p>并且<code>data</code>对象也同样的被修改了。</p><pre><code class="hljs js">data.message
|
||
<span class="hljs-comment">// "hello xfy"</span></code></pre><p>直接设置<code>data.message</code>可以成功修改<code>vm.message</code>的值,但是却不会触发<code>vm</code>对象的setter方法。</p><pre><code class="hljs js">data.message = <span class="hljs-string">'嘤嘤嘤'</span>;
|
||
<span class="hljs-comment">// "嘤嘤嘤"</span>
|
||
vm.message
|
||
<span class="hljs-comment">// 属性被修改,但是没有触发setter</span>
|
||
<span class="hljs-comment">// "嘤嘤嘤"</span></code></pre><p>这里的小例子最简化的模拟了Vue3的实例行为,在真正的Vue3的实例上,我们也可以很清晰的看到其Proxy属性</p><p><img src="../images/Vue3%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE/2020-10-20-16-28-03.webp" srcset="/images/img/loading.gif"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总的来说就是因为data对象修改时不会触发实例的set方法,但数据依然会改变,只是DOM不会实时更新。</p></div><hr><div><div class="post-metas mb-3"><div class="post-meta mr-3"><i class="iconfont icon-category"></i> <a class="hover-with-bg" href="/categories/%E7%AC%94%E8%AE%B0/">笔记</a></div><div class="post-meta"><i class="iconfont icon-tags"></i> <a class="hover-with-bg" href="/tags/JavaScript/">JavaScript</a> <a class="hover-with-bg" href="/tags/Vue/">Vue</a></div></div><p class="note note-warning"><a target="_blank" href="https://zh.wikipedia.org/wiki/Wikipedia:CC_BY-SA_3.0%E5%8D%8F%E8%AE%AE%E6%96%87%E6%9C%AC" rel="nofollow noopener noopener">CC BY-SA 3.0❤</a></p><div class="post-prevnext row"><article class="post-prev col-6"></article><article class="post-next col-6"><a href="/defect/javascript-iterable-object-and-for-of.html"><span class="hidden-mobile">JavaScript-可迭代对象与for-of</span> <span class="visible-mobile">下一篇</span><i class="iconfont icon-arrowright"></i></a></article></div></div><article class="comments" id="comments"><div id="vcomments"></div><script type="text/javascript">function loadValine(){addScript("https://cdn.defectink.com/static/valine/1.4.14/Valine.min.js",function(){new Valine({el:"#vcomments",app_id:"dD9t7mcIBVzJWag5ez6GPy2v-MdYXbMMI",app_key:"bWG6pmKsEscrH4JjrpNNAAy6",placeholder:"嘤嘤嘤???",path:window.location.pathname,avatar:"retro",meta:["nick","mail","link"],pageSize:"10",lang:"zh-CN",highlight:!0,recordIP:!1,serverURLs:""})})}waitElementVisible("vcomments",loadValine)</script><noscript>Please enable JavaScript to view the <a target="_blank" href="https://valine.js.org" rel="nofollow noopener noopener">comments powered by Valine.</a></noscript></article></article></div></div></div><div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn"><div id="toc"><p class="toc-header"><i class="iconfont icon-list"></i> 目录</p><div id="tocbot"></div></div></div></div></div></main><a id="scroll-top-button" href="#" role="button"><i class="iconfont icon-arrowup" aria-hidden="true"></i></a><div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel" aria-hidden="true"><div class="modal-dialog modal-dialog-scrollable modal-lg" role="document"><div class="modal-content"><div class="modal-header text-center"><h4 class="modal-title w-100 font-weight-bold">搜索</h4><button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="modal-body mx-3"><div class="md-form mb-5"><input type="text" id="local-search-input" class="form-control validate"> <label data-error="x" data-success="v" for="local-search-input">关键词</label></div><div class="list-group" id="local-search-result"></div></div></div></div></div><footer class="text-center mt-5 py-3"><div class="footer-content"><a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a><i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a></div><div class="beian"><a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">皖ICP备17017808号</a></div></footer><script src="https://cdn.defectink.com/static/jquery/3.4.1/jquery.min.js"></script><script src="https://cdn.defectink.com/static/twitter-bootstrap/4.5.3/js/bootstrap.min.js"></script><script src="/js/debouncer.js"></script><script src="/js/main.js"></script><script src="/js/lazyload.js"></script><script defer="defer" src="https://cdn.defectink.com/static/clipboard.js/2.0.6/clipboard.min.js"></script><script src="/js/clipboard-use.js"></script><script src="/js/xfy.js"></script><script src="https://cdn.defectink.com/static/tocbot/4.11.1/tocbot.min.js"></script><script>$(document).ready(function(){var t=$("#board-ctn").offset().top;tocbot.init({tocSelector:"#tocbot",contentSelector:"#post-body",headingSelector:"h1,h2,h3,h4,h5,h6",linkClass:"tocbot-link",activeLinkClass:"tocbot-active-link",listClass:"tocbot-list",isCollapsedClass:"tocbot-is-collapsed",collapsibleClass:"tocbot-is-collapsible",collapseDepth:3,scrollSmooth:!0,headingsOffset:-t}),0<$(".toc-list-item").length&&$("#toc").css("visibility","visible")})</script><script src="https://cdn.defectink.com/static/typed.js/2.0.11/typed.min.js"></script><script>var typed=new Typed("#subtitle",{strings:[" ","Vue3中的响应数据 "],cursorChar:"❤",typeSpeed:70,loop:!1});typed.stop(),$(document).ready(function(){$(".typed-cursor").addClass("h2"),typed.start()})</script><script src="/js/local-search.js"></script><script>var path="/xml/local-search.xml",inputArea=document.querySelector("#local-search-input");inputArea.onclick=function(){searchFunc(path,"local-search-input","local-search-result"),this.onclick=null}</script><script src="https://cdn.defectink.com/static/fancybox/3.5.7/jquery.fancybox.min.js"></script><link rel="stylesheet" href="https://cdn.defectink.com/static/fancybox/3.5.7/jquery.fancybox.min.css"><script>$("#post img:not(.no-zoom img, img[no-zoom]), img[zoom]").each(function(){var t=document.createElement("a");$(t).attr("data-fancybox","images"),$(t).attr("href",$(this).attr("src")),$(this).wrap(t)})</script><script src="https://cdn.defectink.com/static/mermaid/8.5.0/mermaid.min.js"></script><script>window.mermaid&&mermaid.initialize({theme:"default"})</script></body></html> |