Vue 基本知識

<!DOCTYPE html>

<html lang="CN">

<head>

? <meta charset="utf-8" />

? <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

? <title>vue3快速上手</title>

? <meta name="viewport" content="width=device-width, initial-scale=1.0" />

? <style id="mkstylesheet">

body{-webkit-font-smoothing:antialiased;font-family:"Helvetica Neue",Helvetica,Arial,Verdana,sans-serif;margin:30px 0 0;padding:0;background:#fff;color:#303030;font-size:14px;line-height:1.45}#wrapper{padding:20px}li{font-size:110%}li li{font-size:100%}li p{font-size:100%;margin:.5em 0}h1{color:#000}h2{color:#111}h3{color:#111}h4{color:#111}h5{color:#111}h6{color:#111;font-style:italic}p,td,div{color:#111;font-family:"Helvetica Neue",Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word}a{color:#0d6ea1;text-decoration:none;-webkit-transition:color .2s ease-in-out;-moz-transition:color .2s ease-in-out;-o-transition:color .2s ease-in-out;-ms-transition:color .2s ease-in-out;transition:color .2s ease-in-out}a:hover{color:#3593d9}h1,h2,h3,h4,h5{margin:2.75rem 0 1rem;font-weight:600;line-height:1.15}h1{margin-top:0;font-size:3.052em}h2{font-size:2.441em}h3{font-size:1.953em}h4{font-size:1.563em}h5{font-size:1.25em}ul,ol,pre,table,blockquote{margin-top:.625em;margin-bottom:.625em}hr{border:1px solid;margin:-1px 0}ul ul,ol ol,ul ol,ol ul{margin-top:0;margin-bottom:0}b,strong,em,small,code{line-height:1}.footnote{color:#0d6ea1;font-size:.8em;vertical-align:super}abbr,acronym{border-bottom:1px dotted #aaa}#wrapper img{max-width:100%;height:auto}dd{font-size:1em;margin-bottom:1em}li>p:first-of-type{margin:0}li p+p{margin-top:16px}ul,ol{padding-left:2.5rem}#wrapper ol{list-style-position:outside}ul ul,ul ol{margin-bottom:.4em}caption,col,colgroup,table,tbody,td,tfoot,th,thead,tr{border-spacing:0}table{border:1px solid rgba(0,0,0,0.25);border-collapse:collapse;display:table;empty-cells:hide;margin:-1px 0 1.3125em;padding:0;table-layout:fixed}caption{display:table-caption;font-weight:700}col{display:table-column}colgroup{display:table-column-group}tbody{display:table-row-group}tfoot{display:table-footer-group}thead{display:table-header-group}td,th{display:table-cell}tr{display:table-row}table th,table td{font-size:1.1em;line-height:1.3;padding:.5em 1em 0}table thead{background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.15);border-bottom:1px solid rgba(0,0,0,0.2)}table tbody{background:rgba(0,0,0,0.05)}table tfoot{background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.15);border-top:1px solid rgba(0,0,0,0.2)}figure{display:inline-block;overflow:hidden;position:relative;margin:1em 0 2em}figcaption{font-style:italic;text-align:center;background:white;color:#666}.poetry pre{display:block;font-family:Georgia,Garamond,serif !important;font-size:110% !important;font-style:italic;line-height:1.6em;margin-left:1em}.poetry pre code{font-family:Georgia,Garamond,serif !important;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;hyphens:auto;white-space:pre-wrap}blockquote p{font-size:110%;font-style:italic;line-height:1.6em}sup,sub,a.footnote{font-size:1.4ex;height:0;line-height:1;position:relative;vertical-align:super}sub{vertical-align:sub;top:-1px}p{font-size:1.1429em;line-height:1.3125em;margin:1.3125em 0}dt,th{font-weight:700}table tr:nth-child(odd),table th:nth-child(odd),table td:nth-child(odd){background:rgba(255,255,255,0.06)}table tr:nth-child(even),table td:nth-child(even){background:rgba(200,200,200,0.25)}@media print{img,table,figure{page-break-inside:avoid}#wrapper{background:#fff;color:#303030;padding:10px;position:relative;text-indent:0}}@media screen{.inverted{background:#252a2a}.inverted #wrapper{background:#252a2a;color:#eee}.inverted hr{border-color:#333f40 !important}.inverted p,.inverted td,.inverted li,.inverted h1,.inverted h2,.inverted h3,.inverted h4,.inverted h5,.inverted h6,.inverted th,.inverted .math,.inverted caption,.inverted dt,.inverted dd{color:#eee}.inverted pre{background:#ccc;color:#111}.inverted table{background:none}.inverted table tr:nth-child(odd),.inverted table td:nth-child(odd){background:none}.inverted a{color:#acd1d5}::selection{background:rgba(157,193,200,0.5)}h1::selection{background-color:rgba(45,156,208,0.3)}h2::selection{background-color:rgba(90,182,224,0.3)}h3::selection,h4::selection,h5::selection,h6::selection,li::selection,ol::selection{background-color:rgba(133,201,232,0.3)}code::selection{background-color:rgba(0,0,0,0.7);color:#eee}code span::selection{background-color:rgba(0,0,0,0.7) !important;color:#eee !important}a::selection{background-color:rgba(255,230,102,0.2)}.inverted a::selection{background-color:rgba(255,230,102,0.6)}td::selection,th::selection,caption::selection{background-color:rgba(180,237,95,0.5)}}.mkstyle--swiss #wrapper aside.blurb:before{margin-top:.7em}

#mkreplaced-toc{list-style-position:inside;padding:0;margin:0 0 0 1rem;list-style-type:none}#mkreplaced-toc li::before{content:''}#mkreplaced-toc li{font-size:1rem;line-height:1.25;font-weight:normal}#mkreplaced-toc li ul{font-size:1.3rem;font-weight:300;padding:.5rem 0;margin:0 0 0 1rem}#mkreplaced-toc li.missing{list-style-type:none !important}#mkreplaced-toc.max-1 ul,#mkreplaced-toc.max1 ul{display:none}#mkreplaced-toc.max-2 ul ul,#mkreplaced-toc.max2 ul ul{display:none}#mkreplaced-toc.max-3 ul ul ul,#mkreplaced-toc.max3 ul ul ul{display:none}#mkreplaced-toc.max-4 ul ul ul ul,#mkreplaced-toc.max4 ul ul ul ul{display:none}#mkreplaced-toc.max-5 ul ul ul ul ul,#mkreplaced-toc.max5 ul ul ul ul ul{display:none}.mk-rtl{direction:rtl;text-align:right}body.mkkatex-number-equations{counter-reset:eqnum}body.mkkatex-number-equations .katex-display{position:relative}body.mkkatex-number-equations .katex-display::after{counter-increment:eqnum;content:"(" counter(eqnum) ")";position:absolute;left:0;top:25%}body.mkkatex-number-equations.mkkatex-number-equations-right .katex-display::after{right:0;left:auto}.mkprinting,.mkprinting #wrapper{height:auto;margin-bottom:0;padding-bottom:0}.hideProgress #generated-toc,.hideProgress #firstdiff,.hideProgress #toc-title,.hideProgress #mkdocumentprogress,.hideProgress #mkincludechart,.hideProgress #mkprogressbar1,.hideProgress #mkprogressbar2,.hideProgress b.bookmark,.hideProgress .mkscrollmeter,.hideProgress #alllinks,.hideProgress #criticnav,.hideProgress .popup,.hideProgress #progressindicator,.hideProgress #mkautoscroll,.mkprinting #generated-toc,.mkprinting #firstdiff,.mkprinting #toc-title,.mkprinting #mkdocumentprogress,.mkprinting #mkincludechart,.mkprinting #mkprogressbar1,.mkprinting #mkprogressbar2,.mkprinting b.bookmark,.mkprinting .mkscrollmeter,.mkprinting #alllinks,.mkprinting #criticnav,.mkprinting .popup,.mkprinting #progressindicator,.mkprinting #mkautoscroll{display:none !important}.hideProgress .mkstyledtag,.mkprinting .mkstyledtag{display:none}.mkcolor-grammar-error,.mkcolor-spell-error{background:none;border-bottom:none}.mkprinting.mkshowcomments .mkstyledtag{display:inline;background:#ccc;padding:3px 9px;border-radius:20px;font-size:1}@media print{body{background:white;line-height:1.4}html,body,#wrapper{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;-webkit-perspective:none !important;-webkit-text-size-adjust:none;border:0;box-sizing:border-box;float:none;margin:0;max-width:100%;padding:0;margin-top:0;width:auto}.critic #wrapper mark.crit{background-color:#fffd38 !important;text-decoration:none;color:#000}h1,h2,h3,h4,h5,h6{page-break-after:avoid}p,h2,h3{orphans:3;widows:3}section{page-break-before:avoid}pre>code{white-space:pre;word-break:break-word}#generated-toc,#firstdiff,#toc-title,#mkdocumentprogress,#mkincludechart,#mkprogressbar1,#mkprogressbar2,.mkscrollmeter,#alllinks,.popup{display:none !important}.suppressprintlinks a{border-bottom:none !important;color:inherit !important;cursor:default !important;text-decoration:none !important}.hrefafterlinktext #wrapper a:link:after,.hrefafterlinktext #wrapper a:visited:after{content:" (" attr(href) ") ";font-size:90%;opacity:.9}.nocodebreak pre{page-break-inside:avoid}img,table,figure{page-break-inside:avoid}.breakfootnotes .footnotes{page-break-before:always}.breakfootnotes .footnotes hr{display:none}#mktoctitle{display:block}#print-title{border-bottom:solid 1px #666;display:block}#wrapper pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}#wrapper #generated-toc-clone,#wrapper #mkreplaced-toc{display:block}.task-list{padding-left:3.3rem}.mkstyle--ink .task-list,.mkstyle--swiss .task-list{padding-left:3.3rem !important}.mkstyle--upstandingcitizen .task-list,.mkstyle--github .task-list{padding-left:3.6rem !important}.mkstyle--manuscript .task-list{padding-left:2.4rem !important}.mkstyle--amblin .task-list{padding-left:2.1rem !important}.mkstyle--grump .task-list{padding-left:1rem !important}.mkstyle--grump .task-list .task-list-item-checkbox{left:0 !important}.task-list .task-list-item{list-style-type:none !important;left:auto}.task-list .task-list-item .task-list-item-checkbox{-webkit-appearance:none;position:relative;left:auto}.task-list .task-list-item .task-list-item-checkbox:before{border:solid 1px #aaa;border-radius:2px;color:white;content:' ';display:block;font-weight:bold;height:1em;left:-1rem;line-height:1;position:absolute;text-align:center;top:-.75em;width:1em}.task-list .gh-complete.task-list-item .task-list-item-checkbox:before{background:#838387;content:'\2713'}}

#wrapper #generated-toc-clone,#wrapper #mkreplaced-toc,#wrapper #generated-toc-clone ul,#wrapper #mkreplaced-toc ul{list-style-position:inside}#wrapper #generated-toc-clone li.missing,#wrapper #mkreplaced-toc li.missing{list-style-type: none!important}#wrapper #generated-toc-clone ul,#wrapper #mkreplaced-toc ul{list-style-type: upper-roman}#wrapper #generated-toc-clone>ul>li>ul,#wrapper #mkreplaced-toc>li>ul {list-style-type: decimal}#wrapper #generated-toc-clone>ul>li>ul>li>ul,#wrapper #mkreplaced-toc>li>ul>li>ul{list-style-type: decimal-leading-zero}#wrapper #generated-toc-clone>ul>li>ul>li>ul>li>ul,#wrapper #mkreplaced-toc>li>ul>li>ul>li>ul{list-style-type: lower-greek}#wrapper #generated-toc-clone>ul>li>ul>li>ul>li>ul>li>ul,#wrapper #mkreplaced-toc>li>ul>li>ul>li>ul>li>ul{list-style-type: disc}#wrapper #generated-toc-clone>ul>li>ul>li>ul>li>ul>li>ul>li>ul,#wrapper #mkreplaced-toc>li>ul>li>ul>li>ul>li>ul>li>ul{list-style-type: square}#wrapper #generated-toc-clone,#wrapper #mkreplaced-toc{}

</style>

<style id="mkprintstyles">@media print{#wrapper #generated-toc-clone,#generated-toc{display:none!important}

html,body,#wrapper{font-size:10pt!important}

}

</style>

? </head>

<body class="normal firstload">

? <div id="wrapper">

<h1 id="vue3%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B">Vue3快速上手</h1>

<p><img src="https://user-images.githubusercontent.com/499550/93624428-53932780-f9ae-11ea-8d16-af949e16a09f.png" style="width:200px" /></p>

<h2 id="1.vue3%E7%AE%80%E4%BB%8B">1.Vue3簡介</h2>

<ul>

<li>2020年9月18日,Vue.js發布3.0版本,代號:One Piece(海賊王)</li>

<li>耗時2年多、<a >2600+次提交</a>、<a >30+個RFC</a>、<a >600+次PR</a>、<a >99位貢獻者</a></li>

<li>github上的tags地址:https://github.com/vuejs/vue-next/releases/tag/v3.0.0</li>

</ul>

<h2 id="2.vue3%E5%B8%A6%E6%9D%A5%E4%BA%86%E4%BB%80%E4%B9%88">2.Vue3帶來了什么</h2>

<h3 id="1.%E6%80%A7%E8%83%BD%E7%9A%84%E6%8F%90%E5%8D%87">1.性能的提升</h3>

<ul>

<li><p>打包大小減少41%</p></li>

<li><p>初次渲染快55%, 更新渲染快133%</p></li>

<li><p>內存減少54%</p></li>

</ul>

<p>&#8230;&#8230;</p>

<h3 id="2.%E6%BA%90%E7%A0%81%E7%9A%84%E5%8D%87%E7%BA%A7">2.源碼的升級</h3>

<ul>

<li><p>使用Proxy代替defineProperty實現響應式</p></li>

<li><p>重寫虛擬DOM的實現和Tree-Shaking</p></li>

</ul>

<p>&#8230;&#8230;</p>

<h3 id="3.%E6%8B%A5%E6%8A%B1typescript">3.擁抱TypeScript</h3>

<ul>

<li>Vue3可以更好的支持TypeScript</li>

</ul>

<h3 id="4.%E6%96%B0%E7%9A%84%E7%89%B9%E6%80%A7">4.新的特性</h3>

<ol>

<li><p>Composition API(組合API)</p></li>

<li><p>setup配置 </p></li>

<li><p>ref與reactive </p></li>

<li><p>watch與watchEffect </p></li>

<li><p>provide與inject </p></li>

<li><p>&#8230;&#8230; </p></li>

<li><p>新的內置組件 </p></li>

<li><p>Fragment </p></li>

<li><p>Teleport </p></li>

<li><p>Suspense </p></li>

<li><p>其他改變</p></li>

<li><p>新的生命周期鉤子 </p></li>

<li><p>data 選項應始終被聲明為一個函數 </p></li>

<li><p>移除keyCode支持作為 v-on 的修飾符 </p></li>

<li><p>&#8230;&#8230;</p></li>

</ol>

<h1 id="%E4%B8%80%E3%80%81%E5%88%9B%E5%BB%BAvue3.0%E5%B7%A5%E7%A8%8B">一、創建Vue3.0工程</h1>

<h2 id="1.%E4%BD%BF%E7%94%A8vue-cli%E5%88%9B%E5%BB%BA">1.使用 vue-cli 創建</h2>

<p>官方文檔:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create</p>

<pre><code class="bash">## 查看@vue/cli版本,確保@vue/cli版本在4.5.0以上

vue --version

## 安裝或者升級你的@vue/cli

npm install -g @vue/cli

## 創建

vue create vue_test

## 啟動

cd vue_test

npm run serve</code></pre>

<h2 id="2.%E4%BD%BF%E7%94%A8vite%E5%88%9B%E5%BB%BA">2.使用 vite 創建</h2>

<p>官方文檔:https://v3.cn.vuejs.org/guide/installation.html#vite</p>

<p>vite官網:https://vitejs.cn</p>

<ul>

<li>什么是vite?—— 新一代前端構建工具。</li>

<li>優勢如下:</li>

<li>開發環境中,無需打包操作,可快速的冷啟動。</li>

<li>輕量快速的熱重載(HMR)。</li>

<li>真正的按需編譯,不再等待整個應用編譯完成。</li>

<li>傳統構建 與 vite構建對比圖</li>

</ul>

<p><img src="https://cn.vitejs.dev/assets/bundler.37740380.png" style="width:500px;height:280px;float:left" /><img src="https://cn.vitejs.dev/assets/esm.3070012d.png" style="width:480px;height:280px" /></p>

<pre><code class="bash">## 創建工程

npm init vite-app &lt;project-name&gt;

## 進入工程目錄

cd &lt;project-name&gt;

## 安裝依賴

npm install

## 運行

npm run dev</code></pre>

<h1 id="%E4%BA%8C%E3%80%81%E5%B8%B8%E7%94%A8compositionapi">二、常用 Composition API</h1>

<p>官方文檔: https://v3.cn.vuejs.org/guide/composition-api-introduction.html</p>

<h2 id="1.%E6%8B%89%E5%BC%80%E5%BA%8F%E5%B9%95%E7%9A%84setup">1.拉開序幕的setup</h2>

<ol>

<li>理解:Vue3.0中一個新的配置項,值為一個函數。</li>

<li>setup是所有<strong style="color:#DD5145">Composition API(組合API)</strong><i style="color:gray;font-weight:bold">“ 表演的舞臺 ”</i>。</li>

<li>組件中所用到的:數據、方法等等,均要配置在setup中。</li>

<li>setup函數的兩種返回值:</li>

<li>若返回一個對象,則對象中的屬性、方法, 在模板中均可以直接使用。(重點關注!)</li>

<li><span style="color:#aad">若返回一個渲染函數:則可以自定義渲染內容。(了解)</span></li>

<li>注意點:</li>

<li>盡量不要與Vue2.x配置混用

<ul>

<li>Vue2.x配置(data、methos、computed&#8230;)中<strong style="color:#DD5145">可以訪問到</strong>setup中的屬性、方法。</li>

<li>但在setup中<strong style="color:#DD5145">不能訪問到</strong>Vue2.x配置(data、methos、computed&#8230;)。</li>

<li>如果有重名, setup優先。</li>

</ul></li>

<li>setup不能是一個async函數,因為返回值不再是return的對象, 而是promise, 模板看不到return對象中的屬性。(后期也可以返回一個Promise實例,但需要Suspense和異步組件的配合)</li>

</ol>

<h2 id="2.ref%E5%87%BD%E6%95%B0">2.ref函數</h2>

<ul>

<li>作用: 定義一個響應式的數據</li>

<li>語法: <code>const xxx = ref(initValue)</code></li>

<li>創建一個包含響應式數據的<strong style="color:#DD5145">引用對象(reference對象,簡稱ref對象)</strong>。</li>

<li>JS中操作數據: <code>xxx.value</code></li>

<li>模板中讀取數據: 不需要.value,直接:<code>&lt;div&gt;{{xxx}}&lt;/div&gt;</code></li>

<li>備注:</li>

<li>接收的數據可以是:基本類型、也可以是對象類型。</li>

<li>基本類型的數據:響應式依然是靠<code>Object.defineProperty()</code>的<code>get</code>與<code>set</code>完成的。</li>

<li>對象類型的數據:內部 <i style="color:gray;font-weight:bold">“ 求助 ”</i> 了Vue3.0中的一個新函數—— <code>reactive</code>函數。</li>

</ul>

<h2 id="3.reactive%E5%87%BD%E6%95%B0">3.reactive函數</h2>

<ul>

<li>作用: 定義一個<strong style="color:#DD5145">對象類型</strong>的響應式數據(基本類型不要用它,要用<code>ref</code>函數)</li>

<li>語法:<code>const 代理對象= reactive(源對象)</code>接收一個對象(或數組),返回一個<strong style="color:#DD5145">代理對象(Proxy的實例對象,簡稱proxy對象)</strong></li>

<li>reactive定義的響應式數據是“深層次的”。</li>

<li>內部基于 ES6 的 Proxy 實現,通過代理對象操作源對象內部數據進行操作。</li>

</ul>

<h2 id="4.vue3.0%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86">4.Vue3.0中的響應式原理</h2>

<h3 id="vue2.x%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F">vue2.x的響應式</h3>

<ul>

<li><p>實現原理: </p></li>

<li><p>對象類型:通過<code>Object.defineProperty()</code>對屬性的讀取、修改進行攔截(數據劫持)。</p></li>

<li><p>數組類型:通過重寫更新數組的一系列方法來實現攔截。(對數組的變更方法進行了包裹)。</p>

<pre><code class="js">Object.defineProperty(data, &apos;count&apos;, {

? ? get () {},

? ? set () {}

})</code></pre></li>

<li><p>存在問題: </p></li>

<li><p>新增屬性、刪除屬性, 界面不會更新。 </p></li>

<li><p>直接通過下標修改數組, 界面不會自動更新。</p></li>

</ul>

<h3 id="vue3.0%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F">Vue3.0的響應式</h3>

<ul>

<li><p>實現原理: </p></li>

<li><p>通過Proxy(代理): 攔截對象中任意屬性的變化, 包括:屬性值的讀寫、屬性的添加、屬性的刪除等。 </p></li>

<li><p>通過Reflect(反射): 對源對象的屬性進行操作。 </p></li>

<li>MDN文檔中描述的Proxy與Reflect:

<ul>

<li><p>Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy</p></li>

<li><p>Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect</p></li>

</ul>

<p><pre><code class="js">new Proxy(data, {

? ? // 攔截讀取屬性值

? ? get (target, prop) {

? ? ? ? return Reflect.get(target, prop)

? ? },

? ? // 攔截設置屬性值或添加新屬性

? ? set (target, prop, value) {

? ? ? ? return Reflect.set(target, prop, value)

? ? },

? ? // 攔截刪除屬性

? ? deleteProperty (target, prop) {

? ? ? ? return Reflect.deleteProperty(target, prop)

? ? }

})

proxy.name = &apos;tom&apos;? </code></pre></p></li>

</ul>

<h2 id="5.reactive%E5%AF%B9%E6%AF%94ref">5.reactive對比ref</h2>

<ul>

<li>從定義數據角度對比:</li>

<li>ref用來定義:<strong style="color:#DD5145">基本類型數據</strong>。</li>

<li>reactive用來定義:<strong style="color:#DD5145">對象(或數組)類型數據</strong>。</li>

<li>備注:ref也可以用來定義<strong style="color:#DD5145">對象(或數組)類型數據</strong>, 它內部會自動通過<code>reactive</code>轉為<strong style="color:#DD5145">代理對象</strong>。</li>

<li>從原理角度對比:</li>

<li>ref通過<code>Object.defineProperty()</code>的<code>get</code>與<code>set</code>來實現響應式(數據劫持)。</li>

<li>reactive通過使用<strong style="color:#DD5145">Proxy</strong>來實現響應式(數據劫持), 并通過<strong style="color:#DD5145">Reflect</strong>操作<strong style="color:orange">源對象</strong>內部的數據。</li>

<li>從使用角度對比:</li>

<li>ref定義的數據:操作數據<strong style="color:#DD5145">需要</strong><code>.value</code>,讀取數據時模板中直接讀取<strong style="color:#DD5145">不需要</strong><code>.value</code>。</li>

<li>reactive定義的數據:操作數據與讀取數據:<strong style="color:#DD5145">均不需要</strong><code>.value</code>。</li>

</ul>

<h2 id="6.setup%E7%9A%84%E4%B8%A4%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9">6.setup的兩個注意點</h2>

<ul>

<li><p>setup執行的時機 </p></li>

<li><p>在beforeCreate之前執行一次,this是undefined。</p></li>

<li><p>setup的參數 </p></li>

<li><p>props:值為對象,包含:組件外部傳遞過來,且組件內部聲明接收了的屬性。 </p></li>

<li>context:上下文對象

<ul>

<li>attrs: 值為對象,包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性, 相當于 <code>this.$attrs</code>。</li>

<li>slots: 收到的插槽內容, 相當于 <code>this.$slots</code>。</li>

<li>emit: 分發自定義事件的函數, 相當于 <code>this.$emit</code>。</li>

</ul></li>

</ul>

<h2 id="7.%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E4%B8%8E%E7%9B%91%E8%A7%86">7.計算屬性與監視</h2>

<h3 id="1.computed%E5%87%BD%E6%95%B0">1.computed函數</h3>

<ul>

<li><p>與Vue2.x中computed配置功能一致</p></li>

<li><p>寫法</p></li>

</ul>

<p><pre><code class="js">import {computed} from &apos;vue&apos;

setup(){

? ? ...

? ? //計算屬性&mdash;&mdash;簡寫

? ? let fullName = computed(()=&gt;{

? ? ? ? return person.firstName + &apos;-&apos; + person.lastName

? ? })

? ? //計算屬性&mdash;&mdash;完整

? ? let fullName = computed({

? ? ? ? get(){

? ? ? ? ? ? return person.firstName + &apos;-&apos; + person.lastName

? ? ? ? },

? ? ? ? set(value){

? ? ? ? ? ? const nameArr = value.split(&apos;-&apos;)

? ? ? ? ? ? person.firstName = nameArr[0]

? ? ? ? ? ? person.lastName = nameArr[1]

? ? ? ? }

? ? })

}</code></pre></p>

<h3 id="2.watch%E5%87%BD%E6%95%B0">2.watch函數</h3>

<ul>

<li><p>與Vue2.x中watch配置功能一致</p></li>

<li><p>兩個小“坑”:</p></li>

<li><p>監視reactive定義的響應式數據時:oldValue無法正確獲取、強制開啟了深度監視(deep配置失效)。 </p></li>

<li><p>監視reactive定義的響應式數據中某個屬性時:deep配置有效。</p></li>

</ul>

<p><pre><code class="js">//情況一:監視ref定義的響應式數據

watch(sum,(newValue,oldValue)=&gt;{

? ? console.log(&apos;sum變化了&apos;,newValue,oldValue)

},{immediate:true})

//情況二:監視多個ref定義的響應式數據

watch([sum,msg],(newValue,oldValue)=&gt;{

? ? console.log(&apos;sum或msg變化了&apos;,newValue,oldValue)

})

/* 情況三:監視reactive定義的響應式數據

? ? ? ? ? ? 若watch監視的是reactive定義的響應式數據,則無法正確獲得oldValue!!

? ? ? ? ? ? 若watch監視的是reactive定義的響應式數據,則強制開啟了深度監視

*/

watch(person,(newValue,oldValue)=&gt;{

? ? console.log(&apos;person變化了&apos;,newValue,oldValue)

},{immediate:true,deep:false}) //此處的deep配置不再奏效

//情況四:監視reactive定義的響應式數據中的某個屬性

watch(()=&gt;person.job,(newValue,oldValue)=&gt;{

? ? console.log(&apos;person的job變化了&apos;,newValue,oldValue)

},{immediate:true,deep:true})

//情況五:監視reactive定義的響應式數據中的某些屬性

watch([()=&gt;person.job,()=&gt;person.name],(newValue,oldValue)=&gt;{

? ? console.log(&apos;person的job變化了&apos;,newValue,oldValue)

},{immediate:true,deep:true})

//特殊情況

watch(()=&gt;person.job,(newValue,oldValue)=&gt;{

? ? console.log(&apos;person的job變化了&apos;,newValue,oldValue)

},{deep:true}) //此處由于監視的是reactive素定義的對象中的某個屬性,所以deep配置有效</code></pre></p>

<h3 id="3.watcheffect%E5%87%BD%E6%95%B0">3.watchEffect函數</h3>

<ul>

<li><p>watch的套路是:既要指明監視的屬性,也要指明監視的回調。</p></li>

<li><p>watchEffect的套路是:不用指明監視哪個屬性,監視的回調中用到哪個屬性,那就監視哪個屬性。</p></li>

<li><p>watchEffect有點像computed:</p></li>

<li><p>但computed注重的計算出來的值(回調函數的返回值),所以必須要寫返回值。 </p></li>

<li><p>而watchEffect更注重的是過程(回調函數的函數體),所以不用寫返回值。</p></li>

</ul>

<p><pre><code class="js">//watchEffect所指定的回調中用到的數據只要發生變化,則直接重新執行回調。

watchEffect(()=&gt;{

? ? const x1 = sum.value

? ? const x2 = person.age

? ? console.log(&apos;watchEffect配置的回調執行了&apos;)

})</code></pre></p>

<h2 id="8.%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">8.生命周期</h2>

<div style="border:1px solid black;width:380px;float:left;margin-right:20px;"><strong>vue2.x的生命周期</strong><img src="https://cn.vuejs.org/images/lifecycle.png" alt="lifecycle_2" style="zoom:33%;width:1200px" /></div><div style="border:1px solid black;width:510px;height:985px;float:left"><strong>vue3.0的生命周期</strong><img src="https://v3.cn.vuejs.org/images/lifecycle.svg" alt="lifecycle_2" style="zoom:33%;width:2500px" /></div>

<p>1</p>

<ul>

<li>Vue3.0中可以繼續使用Vue2.x中的生命周期鉤子,但有有兩個被更名:</li>

<li><code>beforeDestroy</code>改名為 <code>beforeUnmount</code></li>

<li><code>destroyed</code>改名為 <code>unmounted</code></li>

<li>Vue3.0也提供了 Composition API 形式的生命周期鉤子,與Vue2.x中鉤子對應關系如下:</li>

<li><code>beforeCreate</code>===&gt;<code>setup()</code></li>

<li><code>created</code>=======&gt;<code>setup()</code></li>

<li><code>beforeMount</code> ===&gt;<code>onBeforeMount</code></li>

<li><code>mounted</code>=======&gt;<code>onMounted</code></li>

<li><code>beforeUpdate</code>===&gt;<code>onBeforeUpdate</code></li>

<li><code>updated</code> =======&gt;<code>onUpdated</code></li>

<li><code>beforeUnmount</code>?==&gt;<code>onBeforeUnmount</code></li>

<li><code>unmounted</code> =====&gt;<code>onUnmounted</code></li>

</ul>

<h2 id="9.%E8%87%AA%E5%AE%9A%E4%B9%89hook%E5%87%BD%E6%95%B0">9.自定義hook函數</h2>

<ul>

<li><p>什么是hook?—— 本質是一個函數,把setup函數中使用的Composition API進行了封裝。</p></li>

<li><p>類似于vue2.x中的mixin。</p></li>

<li><p>自定義hook的優勢: 復用代碼, 讓setup中的邏輯更清楚易懂。</p></li>

</ul>

<h2 id="10.toref">10.toRef</h2>

<ul>

<li><p>作用:創建一個 ref 對象,其value值指向另一個對象中的某個屬性。 </p></li>

<li><p>語法:<code>const name = toRef(person,'name')</code> </p></li>

<li><p>應用: 要將響應式對象中的某個屬性單獨提供給外部使用時。</p></li>

<li><p>擴展:<code>toRefs</code> 與<code>toRef</code>功能一致,但可以批量創建多個 ref 對象,語法:<code>toRefs(person)</code></p></li>

</ul>

<h1 id="%E4%B8%89%E3%80%81%E5%85%B6%E5%AE%83compositionapi">三、其它 Composition API</h1>

<h2 id="1.shallowreactive%E4%B8%8Eshallowref">1.shallowReactive 與 shallowRef</h2>

<ul>

<li><p>shallowReactive:只處理對象最外層屬性的響應式(淺響應式)。 </p></li>

<li><p>shallowRef:只處理基本數據類型的響應式, 不進行對象的響應式處理。</p></li>

<li><p>什么時候使用? </p></li>

<li><p>如果有一個對象數據,結構比較深, 但變化時只是外層屬性變化 ===&gt; shallowReactive。 </p></li>

<li><p>如果有一個對象數據,后續功能不會修改該對象中的屬性,而是生新的對象來替換 ===&gt; shallowRef。</p></li>

</ul>

<h2 id="2.readonly%E4%B8%8Eshallowreadonly">2.readonly 與 shallowReadonly</h2>

<ul>

<li>readonly: 讓一個響應式數據變為只讀的(深只讀)。</li>

<li>shallowReadonly:讓一個響應式數據變為只讀的(淺只讀)。</li>

<li>應用場景: 不希望數據被修改時。</li>

</ul>

<h2 id="3.toraw%E4%B8%8Emarkraw">3.toRaw 與 markRaw</h2>

<ul>

<li>toRaw:</li>

<li>作用:將一個由<code>reactive</code>生成的<strong style="color:orange">響應式對象</strong>轉為<strong style="color:orange">普通對象</strong>。</li>

<li>使用場景:用于讀取響應式對象對應的普通對象,對這個普通對象的所有操作,不會引起頁面更新。</li>

<li>markRaw:</li>

<li>作用:標記一個對象,使其永遠不會再成為響應式對象。</li>

<li>應用場景:

<ol>

<li>有些值不應被設置為響應式的,例如復雜的第三方類庫等。</li>

<li>當渲染具有不可變數據源的大列表時,跳過響應式轉換可以提高性能。</li>

</ol></li>

</ul>

<h2 id="4.customref">4.customRef</h2>

<ul>

<li><p>作用:創建一個自定義的 ref,并對其依賴項跟蹤和更新觸發進行顯式控制。</p></li>

<li><p>實現防抖效果:</p></li>

</ul>

<p><pre><code class="vue">&lt;template&gt;

? ? &lt;input type=&quot;text&quot; v-model=&quot;keyword&quot;&gt;

? ? &lt;h3&gt;{{keyword}}&lt;/h3&gt;

&lt;/template&gt;

&lt;script&gt;

? ? import {ref,customRef} from &apos;vue&apos;

? ? export default {

? ? ? ? name:&apos;Demo&apos;,

? ? ? ? setup(){

? ? ? ? ? ? // let keyword = ref(&apos;hello&apos;) //使用Vue準備好的內置ref

? ? ? ? ? ? //自定義一個myRef

? ? ? ? ? ? function myRef(value,delay){

? ? ? ? ? ? ? ? let timer

? ? ? ? ? ? ? ? //通過customRef去實現自定義

? ? ? ? ? ? ? ? return customRef((track,trigger)=&gt;{

? ? ? ? ? ? ? ? ? ? return{

? ? ? ? ? ? ? ? ? ? ? ? get(){

? ? ? ? ? ? ? ? ? ? ? ? ? ? track() //告訴Vue這個value值是需要被&ldquo;追蹤&rdquo;的

? ? ? ? ? ? ? ? ? ? ? ? ? ? return value

? ? ? ? ? ? ? ? ? ? ? ? },

? ? ? ? ? ? ? ? ? ? ? ? set(newValue){

? ? ? ? ? ? ? ? ? ? ? ? ? ? clearTimeout(timer)

? ? ? ? ? ? ? ? ? ? ? ? ? ? timer = setTimeout(()=&gt;{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? value = newValue

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? trigger() //告訴Vue去更新界面

? ? ? ? ? ? ? ? ? ? ? ? ? ? },delay)

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? })

? ? ? ? ? ? }

? ? ? ? ? ? let keyword = myRef(&apos;hello&apos;,500) //使用程序員自定義的ref

? ? ? ? ? ? return {

? ? ? ? ? ? ? ? keyword

? ? ? ? ? ? }

? ? ? ? }

? ? }

&lt;/script&gt;</code></pre></p>

<h2 id="5.provide%E4%B8%8Einject">5.provide 與 inject</h2>

<p><img src="https://v3.cn.vuejs.org/images/components_provide.png" style="width:300px" /></p>

<ul>

<li><p>作用:實現<strong style="color:#DD5145">祖與后代組件間</strong>通信</p></li>

<li><p>套路:父組件有一個 <code>provide</code> 選項來提供數據,后代組件有一個 <code>inject</code> 選項來開始使用這些數據</p></li>

<li><p>具體寫法:</p></li>

<li><p>祖組件中:</p>

<p><pre><code class="js">setup(){

? ? ......

? ? let car = reactive({name:&apos;奔馳&apos;,price:&apos;40萬&apos;})

? ? provide(&apos;car&apos;,car)

? ? ......

}</code></pre></p></li>

<li><p>后代組件中:</p>

<p><pre><code class="js">setup(props,context){

? ? ......

? ? const car = inject(&apos;car&apos;)

? ? return {car}

? ? ......

}</code></pre></p></li>

</ul>

<h2 id="6.%E5%93%8D%E5%BA%94%E5%BC%8F%E6%95%B0%E6%8D%AE%E7%9A%84%E5%88%A4%E6%96%AD">6.響應式數據的判斷</h2>

<ul>

<li>isRef: 檢查一個值是否為一個 ref 對象</li>

<li>isReactive: 檢查一個對象是否是由 <code>reactive</code> 創建的響應式代理</li>

<li>isReadonly: 檢查一個對象是否是由 <code>readonly</code> 創建的只讀代理</li>

<li>isProxy: 檢查一個對象是否是由 <code>reactive</code> 或者 <code>readonly</code> 方法創建的代理</li>

</ul>

<h1 id="%E5%9B%9B%E3%80%81compositionapi%E7%9A%84%E4%BC%98%E5%8A%BF">四、Composition API 的優勢</h1>

<h2 id="1.optionsapi%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98">1.Options API 存在的問題</h2>

<p>使用傳統OptionsAPI中,新增或者修改一個需求,就需要分別在data,methods,computed里修改 。</p>

<div style="width:600px;height:370px;overflow:hidden;float:left">

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f84e4e2c02424d9a99862ade0a2e4114~tplv-k3u1fbpfcp-watermark.image" style="width:600px;float:left" />

</div>

<div style="width:300px;height:370px;overflow:hidden;float:left">

<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e5ac7e20d1784887a826f6360768a368~tplv-k3u1fbpfcp-watermark.image" style="zoom:50%;width:560px;left" />

</div>

<h2 id="2.compositionapi%E7%9A%84%E4%BC%98%E5%8A%BF">2.Composition API 的優勢</h2>

<p>我們可以更加優雅的組織我們的代碼,函數。讓相關功能的代碼更加有序的組織在一起。</p>

<div style="width:500px;height:340px;overflow:hidden;float:left">

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0be8211fc54b6c941c036791ba4efe~tplv-k3u1fbpfcp-watermark.image"style="height:360px"/>

</div>

<div style="width:430px;height:340px;overflow:hidden;float:left">

<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cc55165c0e34069a75fe36f8712eb80~tplv-k3u1fbpfcp-watermark.image"style="height:360px"/>

</div>

<h1 id="%E4%BA%94%E3%80%81%E6%96%B0%E7%9A%84%E7%BB%84%E4%BB%B6">五、新的組件</h1>

<h2 id="1.fragment">1.Fragment</h2>

<ul>

<li>在Vue2中: 組件必須有一個根標簽</li>

<li>在Vue3中: 組件可以沒有根標簽, 內部會將多個標簽包含在一個Fragment虛擬元素中</li>

<li>好處: 減少標簽層級, 減小內存占用</li>

</ul>

<h2 id="2.teleport">2.Teleport</h2>

<ul>

<li>什么是Teleport?—— <code>Teleport</code> 是一種能夠將我們的<strong style="color:#DD5145">組件html結構</strong>移動到指定位置的技術。</li>

</ul>

<p><pre><code class="vue">&lt;teleport to=&quot;移動位置&quot;&gt;

? ? &lt;div v-if=&quot;isShow&quot; class=&quot;mask&quot;&gt;

? ? ? ? &lt;div class=&quot;dialog&quot;&gt;

? ? ? ? ? ? &lt;h3&gt;我是一個彈窗&lt;/h3&gt;

? ? ? ? ? ? &lt;button @click=&quot;isShow = false&quot;&gt;關閉彈窗&lt;/button&gt;

? ? ? ? &lt;/div&gt;

? ? &lt;/div&gt;

&lt;/teleport&gt;</code></pre></p>

<h2 id="3.suspense">3.Suspense</h2>

<ul>

<li><p>等待異步組件時渲染一些額外內容,讓應用有更好的用戶體驗</p></li>

<li><p>使用步驟:</p></li>

<li><p>異步引入組件</p>

<pre><code class="js">import {defineAsyncComponent} from &apos;vue&apos;

const Child = defineAsyncComponent(()=&gt;import(&apos;./components/Child.vue&apos;))</code></pre></li>

<li><p>使用<code>Suspense</code>包裹組件,并配置好<code>default</code> 與 <code>fallback</code></p>

<pre><code class="vue">&lt;template&gt;

? ? &lt;div class=&quot;app&quot;&gt;

? ? ? ? &lt;h3&gt;我是App組件&lt;/h3&gt;

? ? ? ? &lt;Suspense&gt;

? ? ? ? ? ? &lt;template v-slot:default&gt;

? ? ? ? ? ? ? ? &lt;Child/&gt;

? ? ? ? ? ? &lt;/template&gt;

? ? ? ? ? ? &lt;template v-slot:fallback&gt;

? ? ? ? ? ? ? ? &lt;h3&gt;加載中.....&lt;/h3&gt;

? ? ? ? ? ? &lt;/template&gt;

? ? ? ? &lt;/Suspense&gt;

? ? &lt;/div&gt;

&lt;/template&gt;</code></pre></li>

</ul>

<h1 id="%E5%85%AD%E3%80%81%E5%85%B6%E4%BB%96">六、其他</h1>

<h2 id="1.%E5%85%A8%E5%B1%80api%E7%9A%84%E8%BD%AC%E7%A7%BB">1.全局API的轉移</h2>

<ul>

<li><p>Vue 2.x 有許多全局 API 和配置。 </p></li>

<li><p>例如:注冊全局組件、注冊全局指令等。</p>

<pre><code class="js">//注冊全局組件

Vue.component(&apos;MyButton&apos;, {

? data: () =&gt; ({

? ? count: 0

? }),

? template: &apos;&lt;button @click=&quot;count++&quot;&gt;Clicked {{ count }} times.&lt;/button&gt;&apos;

})

//注冊全局指令

Vue.directive(&apos;focus&apos;, {

? inserted: el =&gt; el.focus()

}</code></pre></li>

<li><p>Vue3.0中對這些API做出了調整:</p></li>

<li><p>將全局的API,即:<code>Vue.xxx</code>調整到應用實例(<code>app</code>)上</p>

<table>

<colgroup>

<col style="text-align:left;"/>

<col style="text-align:left;"/>

</colgroup>

<thead>

<tr>

? ? <th style="text-align:left;">2.x 全局 API(<code>Vue</code>)</th>

? ? <th style="text-align:left;">3.x 實例 API (<code>app</code>)</th>

</tr>

</thead>

<tbody>

<tr>

? ? <td style="text-align:left;">Vue.config.xxxx</td>

? ? <td style="text-align:left;">app.config.xxxx</td>

</tr>

<tr>

? ? <td style="text-align:left;">Vue.config.productionTip</td>

? ? <td style="text-align:left;"><strong style="color:#DD5145">移除</strong></td>

</tr>

<tr>

? ? <td style="text-align:left;">Vue.component</td>

? ? <td style="text-align:left;">app.component</td>

</tr>

<tr>

? ? <td style="text-align:left;">Vue.directive</td>

? ? <td style="text-align:left;">app.directive</td>

</tr>

<tr>

? ? <td style="text-align:left;">Vue.mixin</td>

? ? <td style="text-align:left;">app.mixin</td>

</tr>

<tr>

? ? <td style="text-align:left;">Vue.use</td>

? ? <td style="text-align:left;">app.use</td>

</tr>

<tr>

? ? <td style="text-align:left;">Vue.prototype</td>

? ? <td style="text-align:left;">app.config.globalProperties</td>

</tr>

</tbody>

</table>

</li>

</ul>

<h2 id="2.%E5%85%B6%E4%BB%96%E6%94%B9%E5%8F%98">2.其他改變</h2>

<ul>

<li><p>data選項應始終被聲明為一個函數。</p></li>

<li><p>過度類名的更改:</p></li>

<li><p>Vue2.x寫法</p>

<pre><code class="css">.v-enter,

.v-leave-to {

? opacity: 0;

}

.v-leave,

.v-enter-to {

? opacity: 1;

}</code></pre></li>

<li><p>Vue3.x寫法</p>

<pre><code class="css">.v-enter-from,

.v-leave-to {

? opacity: 0;

}

.v-leave-from,

.v-enter-to {

? opacity: 1;

}</code></pre></li>

<li><p><strong style="color:#DD5145">移除</strong>keyCode作為 v-on 的修飾符,同時也不再支持<code>config.keyCodes</code></p></li>

<li><p><strong style="color:#DD5145">移除</strong><code>v-on.native</code>修飾符</p></li>

<li><p>父組件中綁定事件</p>

<pre><code class="vue">&lt;my-component

? v-on:close=&quot;handleComponentEvent&quot;

? v-on:click=&quot;handleNativeClickEvent&quot;

/&gt;</code></pre></li>

<li><p>子組件中聲明自定義事件</p>

<pre><code class="vue">&lt;script&gt;

? export default {

? ? emits: [&apos;close&apos;]

? }

&lt;/script&gt;</code></pre></li>

<li><p><strong style="color:#DD5145">移除</strong>過濾器(filter)</p></li>

</ul>

<blockquote>

<p>過濾器雖然這看起來很方便,但它需要一個自定義語法,打破大括號內表達式是 “只是 JavaScript” 的假設,這不僅有學習成本,而且有實現成本!建議用方法調用或計算屬性去替換過濾器。</p>

</blockquote>

<ul>

<li>&#8230;&#8230;</li>

</ul>

<!-- ##END MARKED WRAPPER## -->

? ? </div>

</body>

</html>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容