准备 开发工具及插件
IDE:vscode,WebStorm
插件:Auto Close Tag、Auto Rename Tag、Live Server
通过“!”快速生成html模板
正式学习
安装vue
通过CDN的方式导入vue
<script src="" target="_blank">https://unpkg.com/vue@3/dist/vue.global.js"></script>
模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层组件实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应性系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。
插值
文本 {{}}
数据绑定最常见的形式就是使用“Mustache” (双大括号) 语法的文本插值:
<span>Message: {{ msg }}</span>
Mustache 标签将会被替代为对应组件实例中 msg property 的值。无论何时,绑定的组件实例上 msg property 发生了改变,插值处的内容都会更新。
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
<spanv-once>这个将不会改变: {{ msg }}</span>
原始 HTML及v-html
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html 指令:
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <spanv-html="rawHtml"></span></p>
Attribute(属性) v-bind
Mustache 语法不能在 HTML attribute 中使用,然而,可以使用 v-bind 指令:
<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>(缩写)
如果绑定的值是 null 或 undefined,那么该 attribute 将不会被包含在渲染的元素上。
对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:
<button v-bind:disabled="isButtonDisabled">按钮</button>
如果 isButtonDisabled 的值是 truthy[1],那么 disabled attribute 将被包含在内。如果该值是一个空字符串,它也会被包括在内,与 <button disabled=""> 保持一致。对于其他 falsy[2] 的值,该 attribute 将被省略。
使用 JavaScript 表达式
迄今为止,在我们的模板中,我们一直都只绑定简单的 property 键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
这些表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式:-->
{{ var a = 1 }}
<!-- 流程控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
动态参数(属性值可以变换)
也可以在指令参数中使用 JavaScript 表达式,方法是用方括号括起来:
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributename]="url"> ... </a>
属性值 attributename 不能有大写
这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的组件实例有一个 data property attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href。
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
在这个示例中,当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus
缩写
v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v- 前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的单页面应用程序 (SPA - single page application) 时,v- 前缀也变得没那么重要了。因此,Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:
v-bind 缩写
<!-- 完整语法 -->
<av-bind:href="url"> ... </a>
<!-- 缩写 -->
<a:href="url"> ... </a>
<!-- 动态参数的缩写 -->
<a:[key]="url"> ... </a>
v-on 缩写
<!-- 完整语法 -->
<a v-on:click="doSomething"> ... </a>
<!-- 缩写 -->
<a @click="doSomething"> ... </a>
<!-- 动态参数的缩写 -->
<a @[event]="doSomething"> ... </a>
它们看起来可能与普通的 HTML 略有不同,但 : 与 @ 对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们。
条件渲染
v-if
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
<h1 v-if="awesome">Vue is awesome!</h1>
也可以用 v-else 添加一个“else 块”:
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
在 <template> 元素上使用 v-if 条件渲染分组
因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。
1
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-else
你可以使用 v-else 指令来表示 v-if 的“else 块”:
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
v-else-if
v-else-if,顾名思义,充当 v-if 的“else-if 块”,并且可以连续使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
与 v-else 的用法类似,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
v-show
另一个用于条件性展示元素的选项是 v-show 指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
1
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 display CSS property。
注意,v-show 不支持 <template> 元素,也不支持 v-else。
v-if vs v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
列表渲染
在 v-for 里使用值的范围
v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div id="range" class="demo">
<span v-for="n in 10" :key="n">{{ n }} </span>
</div>
用 v-for 把一个数组映射为一组元素
我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
<ul id="array-rendering">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
Vue.createApp({
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
}).mount('#array-rendering')
在 v-for 块中,我们可以访问所有父作用域的 property。v-for 还支持一个可选的第二个参数,即当前项的索引。
<ul id="array-with-index">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
Vue.createApp({
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
}).mount('#array-with-index')
你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
在 v-for 里使用对象
你也可以用 v-for 来遍历一个对象的 property。
<ul id="v-for-object" class="demo">
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
Vue.createApp({
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
}
}).mount('#v-for-object')
你也可以提供第二个的参数为 property 名称 (也就是键名 key):
<li v-for="(value, name) in myObject">
{{ name }}: {{ value }}
</li>
还可以用第三个参数作为索引:
<li v-for="(value, name, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
维护状态
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的 key attribute:
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
提示
不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
在 <template> 中使用 v-for
类似于 v-if,你也可以利用带有 v-for 的 <template> 来循环渲染一段包含多个元素的内容。比如:
<ul>
<template v-for="item in items" :key="item.msg">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for 与 v-if 一同使用
TIP
注意我们不推荐在同一元素上使用 v-if 和 v-for。更多细节可查阅风格指南。
当它们处于同一节点,v-if 的优先级比 v-for 更高,这意味着 v-if 将没有权限访问 v-for 里的变量:
<!-- 这将抛出一个错误,因为“todo” property 没有在实例上定义 -->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
可以把 v-for 移动到 <template> 标签中来修正:
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
事件处理
监听事件
我们可以使用 v-on 指令 (通常缩写为 @ 符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为 v-on:click="methodName" 或使用快捷方式 @click="methodName"
例如:
<div id="app">
<button @click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
Vue.createApp({
data() {
return {
counter: 0
}
}
}).mount('#app')
事件处理方法
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。
例如:
<div id="app">
<!-- `greet` 是在下面定义的方法名 -->
<button @click="greet">Greet</button>
</div>
Vue.createApp({
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// `methods` 内部的 `this` 指向当前活动实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM event
if (event) {
alert(event.target.tagName)
}
}
}
}).mount('#app')
内联处理器中的方法
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:
<div id="app">
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>
</div>
Vue.createApp({
methods: {
say(message) {
alert(message)
}
}
}).mount('#app')
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
// ...
methods: {
warn(message, event) {
// 现在可以访问到原生事件
if (event) {
event.preventDefault()
}
alert(message)
}
}
#多事件处理器
事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
Submit
</button>
methods: {
one(event) {
// 第一个事件处理器逻辑...
},
two(event) {
// 第二个事件处理器逻辑...
}
}
按键修饰符
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许为 v-on 或者 @ 在监听键盘事件时添加按键修饰符:
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input @keyup.enter="submit" />
你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
<input @keyup.page-down="onPageDown" />
在上述示例中,处理函数只会在 $event.key 等于 'PageDown' 时被调用。
#按键别名
Vue 为最常用的键提供了别名:
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
#系统修饰键
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
提示
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
例如:
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。
#.exact 修饰符
.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
#鼠标按钮修饰符
.left
.right
.middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮
表单输入绑定
官方文档地址:https://v3.cn.vuejs.org/guide/forms.html
基础用法
你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理。
v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值。它将始终将当前活动实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。
对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组织文字过程中得到更新。如果你也想响应这些更新,请使用 input 事件监听器和 value 绑定来替代 v-model。
文本 (Text)
<input v-model="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>
多行文本 (Textarea)
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br />
<textarea v-model="message" placeholder="add multiple lines"></textarea>
插值在 textarea 中不起作用,请使用 v-model 来代替。
<!-- bad -->
<textarea>{{ text }}</textarea>
<!-- good -->
<textarea v-model="text"></textarea>
#复选框 (Checkbox)
单个复选框,绑定到布尔值:
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
多个复选框,绑定到同一个数组:
<div id="v-model-multiple-checkboxes">
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
<br />
<span>Checked names: {{ checkedNames }}</span>
</div>
Vue.createApp({
data() {
return {
checkedNames: []
}
}
}).mount('#v-model-multiple-checkboxes')
Class 与 Style 绑定
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
对象语法
我们可以传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class:
<div :class="{ active: isActive }"></div>
上面的语法表示 active 这个 class 存在与否将取决于 data property isActive 的 truthiness。
你可以在对象中传入更多字段来动态切换多个 class。此外,:class 指令也可以与普通的 class attribute 共存。当有如下模板:
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
和如下 data:
data() {
return {
isActive: true,
hasError: false
}
}
渲染的结果为:
<div class="static active"></div>
当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 "static active text-danger"。
绑定的数据对象不必内联定义在模板里:
<div :class="classObject"></div>
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
使用vite创建项目
1.安装NODEJS
登录网站nodejs官方网站:http://nodejs.cn/download/
下载 安装长期支持版本
2.使用vite创建项目
vite官方网站
https://vitejs.cn/guide/#scaffolding-your-first-vite-project
在命令行中执行命令:npm init vite@latest
选择创建 vue-js 项目
Install Volar in your IDE for a better DX
运行项目:
cd projectName
npm install
npm run dev
VUE生命周期
生命周期图示
下图展示了实例的生命周期。我们不需要立马弄明白所有的东西,不过随着不断学习和使用,它的参考价值会越来越高
https://cn.vuejs.org/assets/lifecycle.16e4c08e.png
示例代码:
<template>
<div>
{{ testval }}
<button @click="clickMe">点我</button>
</div>
</template>
<script>
export default {
data() {
return {
testval: "testval",
};
},
methods: {
clickMe() {
this.testval = "testval2";
},
},
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("created");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("mounted");
},
beforeUpdate() {
console.log("beforeUpdate");
},
updated() {
console.log("updated");
},
beforeUnmount() {
console.log("beforeUnmount");
},
unmounted() {
console.log("unmounted");
},
};
</script>
<style></style>
watch的使用
watch:用于监听data里面的数据是否被修改
watch在监听的时候,可以有二次参数,第一次参数为更新的数据,第二个参数为之前的旧数据
data() {
return {
text: '旧数据',
}
},
watch: {
text: function (newData, oldData) {
//newData是更新后的数据
//oldData是旧数据
console.log(newData, oldData)
}
}
watch只会监听第一层,而对象的数据修改了,但对象它的首地址没有修改,所以watch判定它没有发生数据的变化,从而监听不到,所以我们要使用deep 开启深层监听
data(){
return {
text:{
name:'张三',
age:21
},
}
},
watch: {
//当想要监听对象的时候,想要开启深度监听
//deep:true
text: {
//此时旧数据不记录,oldval和newval指向同一块地址,所有值一样
handler:function (newval) {
console.log(newval)
},
deep:true
}
}
computed的使用
1.定义
computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相关依赖发生改变时才会进行更新
<template>
<div>
{{ "今天天气是:" + weather + ",时间是:" + new Date().toISOString() }}
<br />
{{ dayInfo }}
</div>
</template>
<script>
export default {
data() {
return {
weather: "晴天",
};
},
computed: {
dayInfo() {
return (
"今天天气是:" + this.weather + ",时间是:" + new Date().toISOString()
);
},
},
}
</script>
自定义组件(emit事件,props组件属性,slot,组件v-model)
组件是带有名称的可复用实例
装Vue vscode snippets用于代码提示
本例我们开发一个对话框组件作为实例:
在components文件夹里,创建一个MyDialog.vue文件
<template>
<div class="dialog-bg">
<div class="dialog">
<div>{{ message }}</div>
<div><input type="text" v-model="modelValue" @keydown.enter="submit" @keydown.esc="cancel" /></div>
<div class="btn-group">
<button @click="submit">确定</button>
<button @click="cancel">取消</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
modelValue: {
type: String,
default: "",
},
message: {
type: String,
default: "",
},
},
data() {
return {
};
},
created() {
console.log("created");
},
beforeUnmount() {
console.log("beforeUnmount");
},
unmounted() {
console.log("unmounted");
},
methods: {
submit() {
this.$emit("update:model-value", this.modelValue);
this.$emit("submit",this.modelValue)
},
cancel(){
this.$emit("cancel")
}
},
};
</script>
<style scoped>
.dialog-bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
text-align: center;
}
.dialog {
width: 300px;
padding: 20px;
margin: 0 auto;
margin-top: 200px;
background-color: #e4e4e4;
}
.btn-group {
margin-top: 50px;
display: flex;
justify-content: space-around;
}
</style>
在App.vue中引入该组件并显示出来
<template>
{{ testVal }}
<button @click="testClick">显示对话框</button>
<my-dialog
v-if="showDialog"
title="测试标题"
v-model="testVal"
@submit="dialogSubmit"
@cancel="dialogCancel"
>
<template v-slot:top="slotParam" ><div style="color:red">我的标题-{{slotParam.title}}</div></template>
<!-- <template #top="slotParam" ><div style="color:red">我的标题-{{slotParam.title}}</div></template> -->
<div>请填写修改的内容</div>
</my-dialog>
</template>
<script >
import MyDialog from "./components/MyDialog.vue";
export default {
components: {
MyDialog,
},
data() {
return {
testVal: "hello",
showDialog: false,
};
},
methods: {
testClick() {
this.showDialog = true;
},
dialogSubmit() {
this.showDialog = false;
},
dialogCancel() {
this.showDialog = false;
},
},
};
</script>
<style></style>
emit事件(子组件到父组件)
props组件属性(父到子)
slot插槽(父到子)
默认
命名,多个slot插槽
slot 子传信息到父
v-model组件数据双向绑定
父到子传递信息
双向传递信息(事件触发子到父传递信息)
组合式 API 基础
既然我们知道了为什么,我们就可以知道怎么做。为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup
带 ref、reactive 的响应式变量
ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值:
import { ref,reactive } from 'vue'
const counter = ref(0)
const user = reactive({name:"张三",age:13})
console.log(counter) // { value: 0 }
console.log(counter.value) // 0
counter.value++
console.log(counter.value) // 1
将值封装在一个对象中,看似没有必要,但为了保持 JavaScript 中不同数据类型的行为统一,这是必须的。这是因为在 JavaScript 中,Number 或 String 等基本类型是通过值而非引用传递的
在 setup 内注册生命周期钩子
为了使组合式 API 的功能和选项式 API 一样完整,我们还需要一种在 setup 中注册生命周期钩子的方法。这要归功于 Vue 导出的几个新函数。组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 看起来会像 onMounted。
这些函数接受一个回调,当钩子被组件调用时,该回调将被执行。
让我们将其添加到 setup 函数中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'
// 在我们的组件中
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
onMounted(getUserRepositories) // 在 `mounted` 时调用 `getUserRepositories`
return {
repositories,
getUserRepositories
}
}
现在我们需要对 user prop 的变化做出反应。为此,我们将使用独立的 watch 函数。
#watch 响应式更改
就像我们在组件中使用 watch 选项并在 user property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch 函数执行相同的操作。它接受 3 个参数:
一个想要侦听的响应式引用或 getter 函数
一个回调
可选的配置选项
下面让我们快速了解一下它是如何工作的
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
每当 counter 被修改时,例如 counter.value=5,侦听将触发并执行回调 (第二个参数),在本例中,它将把 'The new counter value is:5' 记录到控制台中。
以下是等效的选项式 API:
export default {
data() {
return {
counter: 0
}
},
watch: {
counter(newValue, oldValue) {
console.log('The new counter value is: ' + this.counter)
}
}
}
独立的 computed 属性
与 ref 和 watch 类似,也可以使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性。让我们回到 counter 的例子:
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
这里我们给 computed 函数传递了第一个参数,它是一个类似 getter 的回调函数,输出的是一个只读的响应式引用。为了访问新创建的计算变量的 value,我们需要像 ref 一样使用 .value property。
使用组合式API编写组件
使用组合式api重新编写MyDialog.vue
创建新文件MyDialogSetup.vue
<template>
<div class="dialog-bg">
<div class="dialog">
<slot name="top" :title="title">{{ title }}</slot>
<div>
<slot></slot>
</div>
<div>
<input
type="text"
v-model="modelValue"
@keydown.enter="submit"
@keydown.esc="cancel"
/>
</div>
<div class="btn-group">
<button @click="submit">确定</button>
<button @click="cancel">取消</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onBeforeUnmount, onUnmounted } from "vue";
//定义属性
let props = defineProps({
modelValue: {
type: String,
default: "",
},
title: {
type: String,
default: "",
},
});
//事件触发器
let emit = defineEmits({});
function submit() {
emit("update:model-value", props.modelValue);
emit("submit", props.modelValue);
}
function cancel() {
emit("cancel");
}
//生命周期
onBeforeUnmount(() => {
console.log("beforeUnmount");
});
onUnmounted(() => {
console.log("unmounted");
});
</script>
<style scoped>
.dialog-bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
text-align: center;
}
.dialog {
width: 300px;
padding: 20px;
margin: 0 auto;
margin-top: 200px;
background-color: #e4e4e4;
}
.btn-group {
margin-top: 50px;
display: flex;
justify-content: space-around;
}
</style>
VUE路由
1.安装路由
在命令行中运行:
npm install vue-router@4
2.路由配置
在main.js 或者main.ts中添加路由配置
import { createApp } from 'vue'
import App from './App.vue'
//引入路由
import { createRouter, createWebHashHistory } from 'vue-router'
//路由配置
let routes = [
{ path: '/', component: () => import('../views/Login.vue') },
{ path: '/test', component: () => import('../views/Test.vue') },
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
let app = createApp(App);
app.use(router);//添加路由
app.mount('#app');
在App.vue中添加路由视图
template>
<router-view></router-view>
</template>
3.路由基本方法
参数的传递和获取
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()//获取上层路由传递的参数
function pushWithQuery(query) {
//路由跳转
router.push({
name: 'search',
query: { //传递query参数
...route.query,//上一层路由参数
},
})
}
二级路由
App.vue
./views/Main.vue
PINIA的使用
官方网站https://pinia.vuejs.org/
1.安装pinia
在命令行中运行
npm install pinia
在main中添加pinia
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia' //引入pinia
let app = createApp(App);
app.use(createPinia());//注册使用pinia
app.mount('#app');
2.定义store
创建 TestStore.js 文件
import { defineStore } from 'pinia'
import { storeToRefs } from "pinia";
//第一个参数test 相当于为 id ,并且它需要一个唯一名称
export const testStore = defineStore('test', {
//state相当于data数据
state: () => {
return {
count:0,
};
},
//相当于方法methods
actions: {
increment() {
this.count++
},
},
//相当于computed,计算属性
getters: {
testCount(state){return state.count * 2},
}
})
3.使用store
在vue文件中使用自定的store:
<template>
{{counter}}
<br/>
{{testCount}}
<button @click="testClick">点我</button>
</template>
<script>
import { testStore } from "./stores/TestStore";
import { storeToRefs } from "pinia";
//第一种使用方法
const counter = testStore();
//第二种使用方法:如果不使用storeToRefs,将会失去实时渲染
let {count,testCount} = storeToRefs(counter)
function testClick(){
counter.increment();
//或者
count.value ++
}
</script>