<!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 href="https://github.com/vuejs/vue-next/graphs/commit-activity">2600+次提交</a>、<a href="https://github.com/vuejs/rfcs/tree/master/active-rfcs">30+个RFC</a>、<a href="https://github.com/vuejs/vue-next/pulls?q=is%3Apr+is%3Amerged+-author%3Aapp%2Fdependabot-preview+">600+次PR</a>、<a href="https://github.com/vuejs/vue-next/graphs/contributors">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>……</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>……</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>…… </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>……</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 <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
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…)中<strong style="color:#DD5145">可以访问到</strong>setup中的属性、方法。</li>
<li>但在setup中<strong style="color:#DD5145">不能访问到</strong>Vue2.x配置(data、methos、computed…)。</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><div>{{xxx}}</div></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, 'count', {
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 = 'tom' </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 'vue'
setup(){
...
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
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)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
/* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',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(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})</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>===><code>setup()</code></li>
<li><code>created</code>=======><code>setup()</code></li>
<li><code>beforeMount</code> ===><code>onBeforeMount</code></li>
<li><code>mounted</code>=======><code>onMounted</code></li>
<li><code>beforeUpdate</code>===><code>onBeforeUpdate</code></li>
<li><code>updated</code> =======><code>onUpdated</code></li>
<li><code>beforeUnmount</code> ==><code>onBeforeUnmount</code></li>
<li><code>unmounted</code> =====><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>如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。 </p></li>
<li><p>如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> 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"><template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'Demo',
setup(){
// let keyword = ref('hello') //使用Vue准备好的内置ref
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track() //告诉Vue这个value值是需要被“追踪”的
return value
},
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //告诉Vue去更新界面
},delay)
}
}
})
}
let keyword = myRef('hello',500) //使用程序员自定义的ref
return {
keyword
}
}
}
</script></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:'奔驰',price:'40万'})
provide('car',car)
......
}</code></pre></p></li>
<li><p>后代组件中:</p>
<p><pre><code class="js">setup(props,context){
......
const car = inject('car')
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"><teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport></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 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))</code></pre></li>
<li><p>使用<code>Suspense</code>包裹组件,并配置好<code>default</code> 与 <code>fallback</code></p>
<pre><code class="vue"><template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template></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('MyButton', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
//注册全局指令
Vue.directive('focus', {
inserted: el => 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"><my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/></code></pre></li>
<li><p>子组件中声明自定义事件</p>
<pre><code class="vue"><script>
export default {
emits: ['close']
}
</script></code></pre></li>
<li><p><strong style="color:#DD5145">移除</strong>过滤器(filter)</p></li>
</ul>
<blockquote>
<p>过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。</p>
</blockquote>
<ul>
<li>……</li>
</ul>
<!-- ##END MARKED WRAPPER## -->
</div>
</body>
</html>