一,前言
本篇文章的正文部分为渐近式介绍Vue基础中的OptionAPI和相关指令,在两大内容之外,本文结尾处会附上vue的特性。 ps:本文总字数过22000,足够详细,尽量新手向的同时也包含一些原理性知识,部分内容结合文档。
二,正文
基本流程
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{msg}}</h2>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
接下来,我们将将上述过程分开来解释
Step One. Vue应用的创建
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
}
},
methods: {
}
}
)
app.mount("#app")
</script>
首先,我们通过Vue.createApp()方法构建Vue的应用对象,它需要接收一个选项对象作为参数,并且该选项对象包含了OptionAPI(data,methods,computed....) 完整见下图
但在本文中,我们只探讨一些基本的OptionAPI:
- template
- data
- methods
- computed
- watch
Step Two. 应用实例的挂载
Vue作为国内广泛使用的渐进式框架,可以很好地与原生及其其他框架共同开发,而应用实例的挂在很好的体现了这一点,app.mount()方法可以将Vue应用在选定的DOM对象上,赋予DOM对象可以使用Vue的属性而不会影响到该DOM对象以外的内容,这样其他部分就可以使用原生JS开发而不会产生耦合。该例子中我们将应用实例挂载在id为“app”上的div元素上。
<script>
const app = Vue.createApp(...)
app.mount("#app")
</script>
Step Three. template介绍
<body>
<div id="app">
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: `<h2>{{msg}}</h2>`,
data() {
return {
msg: "Hello Vue"
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
如图,template接收DOM对象,这样Vue才能使用这些DOM对象渲染内容,为了更简便地开发,我们可以不在template中写任何内容,直接在挂载元素中写入是等价的。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{msg}}</h2>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
上述代码与实例代码是等价的。,一般都是直接在挂载元素中开发。
Step Four. data介绍
<body>
<div id="app">
<h2>{{msg}}</h2>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
如图,data属性需要传入一个函数,图中的写法为语法糖,是常规写法的简便写法。在vue3中必须传入函数。data中返回的对象会被 vue响应式系统劫持,你可以认为data返回的对象中所有内容被时刻监听着,只要发生改变,这种变化也会同步到虚拟DOM上而对渲染结果产生影响。而在挂载元素中,形如{{ }}的写法被称为mustache写法,在{{}}中我们可以填入有返回值的内容,比如三元表达式,函数,变量。不支持赋值语句这样的无返回值内容,也不支持if-else语句等。这些内容后续会详细解释。在该例子中,{{ msg }}中msg显然为变量,我们可以改变data中的msg来动态改变h2中的内容。这个过程类似JS中修改innerText属性,但mustache能力更强。
Step Five. methods介绍
在介绍methods前,我们先看以下案例:
<body>
<div id="app">
<h2>{{msg}}</h2>
<button @click="btnClick">按钮</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
btnClick: function () {
console.log("clicked");
}
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
先不用管"@click"语句,它就是一个绑定事件的指令,我们观察data中,我们新增了一个对象方法,而这个方法绑定在了按钮上,我们进入网页:
结果说明这样写其实是没什么问题的,但是为了数据与方法分离,同时为了更简便地写方法,我们引入了methods这个选项式API。
使用methods后,我们可以观察变化,这一切都是等价的:
<body>
<div id="app">
<h2>{{msg}}</h2>
<button @click="btnClick">按钮</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
}
},
methods: {
btnClick() {
console.log("clicked");
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
我们可以看到写在methods中后,这就实现了数据与方法分离的效果,减少了耦合性。这样以后修改更加简便,并且还可以使用新写法,形式上更有C的风格。在methods中的方法,我们可以通过this关键词来访问data中的数据:
<body>
<div id="app">
<h2>{{msg}}</h2>
<button @click="btnClick">按钮</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
}
},
methods: {
btnClick() {
console.log(this.msg);
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
Notes:
在methods中不允许使用箭头函数,使用箭头函数指向的对象为window,而在window中我们不可以获取到data中的内容,那么,this到底指向什么呢?
methods
对象内的函数中的this
关键字通常指向当前 Vue 实例。这意味着你可以通过this
访问 Vue 实例的所有数据和属性,包括data
、computed
、props
等。当你在
methods
中定义一个函数并调用它时,this
关键字将自动绑定到 Vue 实例上。这样你就可以在方法内部访问和修改数据属性。
Step Six. computed介绍
我们为什么需要计算属性呢?在介绍computed属性前,可以先看Mustache语法部分,再看下面的例子:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{ msg }}</h2>
<p>{{ getFullName() }}</p>
<p>{{ reverseGreeting() }}</p>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
firstName: 'John',
lastName: 'Doe',
greeting: 'Hello Vue!'
}
},
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName
},
reverseGreeting() {
return this.greeting.split('').reverse().join('');
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
在上面这个例子中,我们使用methods中的方法计算了复杂的data,接下来我们使用computed属性来替代methods方法。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{ msg }}</h2>
<p>{{ getFullName() }}</p>
<p>{{ reverseGreeting() }}</p>
<p>{{ fullName }}</p>
<p>{{ reverse }}</p>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
firstName: 'John',
lastName: 'Doe',
greeting: 'Hello Vue!'
}
},
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName
},
reverseGreeting() {
return this.greeting.split('').reverse().join('');
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
},
reverse() {
return this.greeting.split('').reverse().join('');
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
从形式上看,computed属性也就少个()而已,但区别还是很大的:computed中的内容存在缓存中。当firstName和lastName不发生变化时,调用fullName时就不会再计算一遍,而是直接读取。但methods中的方法不同,调用一次就计算一次。所以使用computed属性可以提升优化。
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
},
reverse() {
return this.greeting.split('').reverse().join('');
}
}
Step Seven. watch介绍
以下的写法为app.vue中的写法,其实本质上都差不多。
说到watch,其实就是为某个变量添加一个监听器,当这个变量发生变化时就调用一个方法,监听分为浅度侦听和深度侦听。如下图,我们为msg变量设定了一个侦听器,当按下按钮,msg内容改变,侦听器启动,执行console代码。
对于watch中的监听函数,他们的名字不能随意取,必须和监听的变量名一致。
同时,监听函数触发时会返回修改前的值和修改后的值,我们可以接收这两个值。如图的newvalue和oldvalue。
<template>
<h3>监听器</h3>
<p>{{ msg }}</p>
<button @click="updateHandler">更新数据</button>
</template>
<script>
export default{
data(){
return{
msg:"hello",
}
},
methods:{
updateHandler(){
this.msg = "world"
}
},
watch:{
msg(newvalue,oldvalue){
console.log(oldvalue,newvalue);
}
}
}
</script>
<style>
</style>
Mustache语法
在 Vue.js 中,Mustache 语法(也称为插值语法)是一种简单的文本替换机制,用于在模板中展示数据。这种语法使用一对花括号
{{ }}
包裹表达式,表达式的结果将在渲染时插入到 HTML 中相应的位置。
除了用作变量的显示,mustache语法也支持一些较为复杂的使用:
(1)动态属性
以下用字符串拼接为例
<div id="app">
{{ firstName + ' ' + lastName }}
</div>
<script>
const app = Vue.createApp({
data() {
return {
firstName: 'John',
lastName: 'Doe'
};
}
});
app.mount('#app');
</script>
(2)条件语句
mustache语法只支持三元来做判断,不能使用if-else。
<div id="app">
{{ isShow ? 'Visible' : 'Hidden' }}
</div>
<script>
const app = Vue.createApp({
data() {
return {
isShow:true
};
}
});
app.mount('#app');
</script>
(3)方法调用
以一个反转单词的方法为例,插值语法是支持调用函数的。
<div id="app">
{{ reverseGreeting() }}
</div>
<script>
const app = Vue.createApp({
data() {
return {
greeting: 'Hello Vue!'
};
},
methods:{
reverseGreeting() {
return this.greeting.split('').reverse().join('');
}}
});
app.mount('#app');
</script>
指令内容
这就是最新版本的所有指令了,其中,v-show,v-if,v-else,v-else-if组成了条件渲染部分,v-for大多用在迭代服务器传来的数据中,它可以依次渲染元素。v-bind用来绑定动态属性,v-on用来绑定事件,v-model用作表单处理。以上提及指令为最常用的指令,其他的指令大多是用来优化的,前期不太常用,但也会提及一部分。
(1)v-once
仅渲染元素和组件一次,并跳过之后的更新。在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。
<body>
<div id="app">
<h2 v-once>{{msg}}</h2>
<!-- v-once只会渲染一次,后续改变不会影响这个元素了。 -->
<button @click="changeInfo">改变信息</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
}
},
methods: {
changeInfo() {
this.msg = "Hello World"
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
进入网页后,多次点击按钮文本没发生改变,但是data中的值会发生改变,v-once只是作用在渲染上,而不是限制值的修改。
(2)v-text
<body>
<div id="app">
<h2 v-text="msg"></h2>
<!-- 区别在于mustache语法更加灵活,v-text用的较少 -->
<button @click="changeInfo">改变信息</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
}
},
methods: {
changeInfo() {
this.msg = "Hello World"
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
mustache语法可以完全替代这个指令,了解即可,没啥用。
(3)v-html
<body>
<div id="app">
<h2>{{ web }}</h2>
<h2 v-html="web">{{ web }}</h2>
<button @click="changeInfo">改变信息</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
web: "<a href='www.baidu.com'>百度</a>"
}
},
methods: {
changeInfo() {
this.msg = "Hello World"
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
v-html
的内容直接作为普通 HTML 插入—— Vue 模板语法是不会被解析的。如果你发现自己正打算用v-html
来编写模板,不如重新想想怎么使用组件来代替。
以上为vue.js文档内容,就作用上讲,v-html就是解析字符串中的内容,相当于innerHTML。但是在开发中用得不是很多,了解即可。
(4)v-pre
跳过该元素及其所有子元素的编译。
元素内具有 v-pre
,所有 Vue 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。这个用的就更少了,了解即可。
(5)v-cloak
在介绍v-cloak前,设想以下场景:当网站等待服务器发送数据时或者正在传输大量数据时,这会造成一定的延时,但是对于template模板却可以很快渲染完成,这时就出现骨架渲染完成但是数据却没有接收完成的现象,这是插值语法就无法渲染,这就会出现{{ msg }}这种按照原样渲染的模样,这对于用户而言是不友好的。所以我们采用v-cloak来控制这种未来得及链接数据的元素。
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<div id="app">
<h2 v-cloak>{{msg}}</h2>
<!-- 等待渲染完成后再显示,完成前使用style属性遮掩 -->
</div>
<script src="../static/vue.js"></script>
<script>
setTimeout(() => {
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue"
}
},
methods: {
}
}
)
app.mount("#app")
}, 3000);//延时3s
</script>
</body>
</html>
在上述代码中,我们为应用实例化延时3s来模拟接受数据的延时情况,添加v-cloak,当未接收到msg数据时,v-cloak会启用[v-cloak]{display:none},隐藏该元素,直到msg得到数据后才会取消掉该属性。
(6)v-bind
关于v-bind的语法糖:
<body>
<div id="app">
<h2>网站</h2>
<a v-bind:href="link">百度</a>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
link: "http://www.baidu.com"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
<body>
<div id="app">
<h2>网站</h2>
<a :href="link">百度</a>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
link: "http://www.baidu.com"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
以上的两种写法完全等价,v-bind:attr="" === :attr=""。
<1>绑定class
- 对象语法
- 数组语法
对象语法
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.active {
color: red;
}
</style>
<body>
<div id="app">
<!-- 最基本的绑定方法 -->
<h2 :class="classes">Hello</h2>
<button :class=" isActive?'active':''" @click="btnClick">我已阅读</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
classes: "a b c",
isActive: false
}
},
methods: {
btnClick() {
this.isActive = !this.isActive;
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
如图,观察h2的绑定class方式,classes为data中的一个变量,在calsses中,我们存有“a b c”三个类,这就是最基本的绑定类的方式,但这种绑定有个缺点:我们虽然可以通过修改classes的内容来间接的修改h2上的类,但是却不能动态的添加或者移除这个类。
比如我们想要实现在某种行为下附加这个类,而在其余时间不附加这个类,如果通过最基本的方式,就如button的class添加一致:通过三元运算符来判断是否启用active这个类别,这确实没问题,但是当这类动态类别数量较多时,代码可读性会很差。所以我们引入了对象语法。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.active {
color: red;
}
</style>
<body>
<div id="app">
<!-- 最基本的绑定方法 -->
<h2 :class="classes">Hello</h2>
<!-- 对象语法 -->
<button :class="classes" :class="{active:isActive}" @click="btnClick">我已阅读</button>
<!-- 静态class与动态class不会出现覆盖的问题,而是结合在一起 -->
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
classes: "a b c",
isActive: false
}
},
methods: {
btnClick() {
this.isActive = !this.isActive;
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
如图,对象语法很简洁,形如:
<div :class="{类别:是否启用}">
当然一个对象当然可以拥有很多元素,也就是说我们可以添加很多这样的键值对,同时控制很多对类别的启用和关闭。 这里就不演示了。
虽然说对象语法直接接收对象没问题,但是不代表对象语法只能通过对象赋值这种形式。只要返回的结果是一个可读取的对象,那这种方式就没问题,比如通过变量名引用对象,通过函数来获取对象,这都是可行的。
数组语法
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.active {
color: red;
}
</style>
<body>
<div id="app">
<button :class="classes" :class="['a','b',{'active':isActive}]" @click="btnClick">我已阅读</button>
<!-- 静态class与动态class不会出现覆盖的问题,而是结合在一起 -->
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
classes: "a b c",
isActive: false
}
},
methods: {
btnClick() {
this.isActive = !this.isActive;
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
数组语法可以直接写类名(加引号),变量(不加引号),也可以写对象。数组语法用的不是很多 ,了解即可。
<2>绑定style
CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名;
- 对象语法
- 数组语法
对象语法
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>动态绑定style</h2>
<p :style="{color:'red','font-size':'10px',backgroundColor:'black'}"></p>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
fontColor: "red"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
如上图,对象中的键为CSS Property,它不是字符串,所以不用带引号,对于CSS property,我们有两种方式引入:如'font-size'这种中间带'-'的类型,它是作为字符串出现的,需要带引号。而'backgroundColor'这种驼峰式命名法是作为非字符串出现的,是不用带引号的,注意甄别。
关于其他的,都和绑定class差不多,不管使用函数返回一个style对象,还是用个style的对象名来引用都是可行的。
数组语法
<div :style="[styleObjectA, styleObjectB]"></div>
文档实例:
<!-- 绑定 attribute -->
<img v-bind:src="imageSrc" />
<!-- 动态 attribute 名 -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc" />
<!-- 缩写形式的动态 attribute 名 (3.4+),扩展为 :src="src" -->
<img :src />
<!-- 动态 attribute 名的缩写 -->
<button :[key]="value"></button>
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName" />
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]"></div>
<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
<!-- 绑定对象形式的 attribute -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- prop 绑定。“prop” 必须在子组件中已声明。 -->
<MyComponent :prop="someThing" />
<!-- 传递子父组件共有的 prop -->
<MyComponent v-bind="$props" />
<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>
<3>动态绑定属性
形如:[attrName]="Object"
和上面的区别就是属性名变成了变量。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p :[name]="'active'"></p>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
name: 'class',
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
<4>绑定对象
批量处理属性与对应值
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<span v-bind="infos"></span>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue",
infos: {
title: "list",
age: 18,
gender: "male"
}
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
这种情况下,v-bind:不能省略,它会接收对象将其键作为属性名,值作为属性名所对应的值,支持嵌套对象。
(7)v-on
v-on的语法糖
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{msg}}</h2>
<button v-on:click="btnClick"></button>
<button @click="btnClick"></button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
msg: "Hello Vue"
}
},
methods: {
btnClick() {
console.log("Clicked");
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
其中,
<button v-on:click="btnClick"></button>
<button @click="btnClick"></button>
完全等价。
当然,你可以选取其他触发事件条件:mousedown,mouseup等等。
<1>v-on的使用
在上文中,我们已经提及了一种事件的绑定方法,v-on同样支持多种事件的绑定。
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
借此,我们通过v-on来构建一个计数器:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{counter}}</h2>
<button @click="btnUp">➕</button>
<button @click="btnDown">➖</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
counter: 10
}
},
methods: {
btnUp() {
this.counter++
},
btnDown() {
this.counter--
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
<2>参数传递
当方法不需要参数时,默认将事件对象作为参数,如果方法具有参数时,使用$event来访问事件对象。
当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的
$event
变量:v-on:click="handle('ok', $event)"
。
<3>修饰符
以下为文档内容截取:
<!-- 停止传播 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认事件 -->
<button @click.prevent="doThis"></button>
<!-- 不带表达式地阻止默认事件 -->
<form @submit.prevent></form>
<!-- 链式调用修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 按键用于 keyAlias 修饰符-->
<input @keyup.enter="onEnter" />
<!-- 点击事件将最多触发一次 -->
<button v-on:click.once="doThis"></button>
(8)条件指令
如图,条件渲染比较符合其他编程语言的习惯,这里就不赘述了,想要了解更多,可以看文档:
v-if文档
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>if测试</h3>
<div v-if="flag">真</div>
<div v-else>假</div>
<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-if="type === 'D'">D</div>
<div v-show="type==='A'"></div>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
flag: false,
type: "D"
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
其中,v-show的作用和v-if相似,v-show没有else的语句。
v-show与v-if的区别
当v-show条件为假时,v-show会为元素添加display:none的样式,而v-if条件为假时,元素被清除。如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show;如果不会频繁的发生切换,那么使用v-if;
(9)v-for
基于原始数据多次渲染元素或模板块。
<1>v-for的使用
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.item {
border: 2px solid black;
padding: 50px;
margin-top: 2px;
}
</style>
<body>
<div id="app">
<h2>电影列表</h2>
<ul>
<li style="list-style-type: none;" v-for="(item,index) in movies">{{ index }} {{ item }}</li>
</ul>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
movies: ["速度与激情", "星际穿越", "地球脉动", "哪吒"],
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
在python中,这种形式与enumerate()方法类似,可以同时获取到下标和对应元素。
<2>v-for支持类型
对于v-for而言,只要是可迭代对象,就可以作为v-for支持类型。
下面举数字,字符串和对象为例:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.item {
border: 2px solid black;
padding: 50px;
margin-top: 2px;
}
</style>
<body>
<div id="app">
<h2>电影列表</h2>
<ul>
<li v-for="item in 10">{{ item }}</li>
<li v-for="item in 'Hello Vue'">{{ item }}</li>
</ul>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.item {
border: 2px solid black;
padding: 50px;
margin-top: 2px;
}
</style>
<body>
<div id="app">
<h2>电影列表</h2>
<h2>学生列表</h2>
<div v-for="stu in students">
<div class="item">
<p>ID-{{ stu.id }}</p>
<p>Name-{{ stu.name }}</p>
<p>Age-{{ stu.age }}</p>
<p>Major-{{ stu.major }}</p>
</div>
</div>
<div v-for="(value,key,index) in product">
{{ index }} - {{ key }} - {{ value }}
</div>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
movies: ["速度与激情", "星际穿越", "地球脉动", "哪吒"],
//对象数组的渲染
students: [
{ id: 1, name: "Mike", age: 18, major: "Mathmatics" },
{ id: 2, name: "Jane", age: 18, major: "Art" },
{ id: 3, name: "Rian", age: 18, major: "IT" }
],
product: { id: 22, productName: "Little Prince", price: "39.8", amount: "1100" },
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
<3>template元素
template元素就像一个调整图层一样,它不会被渲染,它唯一的用处就是承载命令。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.item {
border: 2px solid black;
padding: 50px;
margin-top: 2px;
}
</style>
<body>
<div id="app">
<h2>电影列表</h2>
<h2>学生列表</h2>
<template v-for="stu in students">
<div class="item">
<p>ID-{{ stu.id }}</p>
<p>Name-{{ stu.name }}</p>
<p>Age-{{ stu.age }}</p>
<p>Major-{{ stu.major }}</p>
</div>
</template>
<template v-for="(value,key,index) in product">
{{ index }} - {{ key }} - {{ value }}
</template>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
movies: ["速度与激情", "星际穿越", "地球脉动", "哪吒"],
//对象数组的渲染
students: [
{ id: 1, name: "Mike", age: 18, major: "Mathmatics" },
{ id: 2, name: "Jane", age: 18, major: "Art" },
{ id: 3, name: "Rian", age: 18, major: "IT" }
],
product: { id: 22, productName: "Little Prince", price: "39.8", amount: "1100" },
}
},
methods: {
}
}
)
app.mount("#app")
</script>
</body>
</html>
<4>数组更新检测
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。
以上的方法都可以刷新视图,即影响渲染结果。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{msg}}</h2>
<ul>
<li v-for="item in movies">{{ item }}</li>
</ul>
<button @click="appendMovie">添加电影</button>
</div>
<script src="../static/vue.js"></script>
<script>
const app = Vue.createApp(
{
template: ``,
data() {
return {
movies: ["速度与激情", "星际穿越", "地球脉动", "哪吒"]
}
},
methods: {
appendMovie() {
this.movies.push("白蛇传");
}
}
}
)
app.mount("#app")
</script>
</body>
</html>
除了上面其中方法外,如filter(),slice(),map()这种创建新数组的方法不会被监控,即修改后必须得赋给原数组才可以引起变化。
<5>key
一般v-for都需要带上key作为该数据的唯一性标志,因为加上key后可以提高效率。
原理较为复杂,简单来说,就是使用了key可以使用diff算法来进行匹配key相同的VNode,而不是大量删除和修改来满足插入值的情况。
(10)v-model
v-model作为表单中最重要的指令之一,功能强大,内容比较多,这里就不赘述了,留待下篇文章讨论。这里可以提前了解一下:v-model就是可以双向绑定,当我们在input(text)中输入值时,它会实时传输给message,当我们修改message值的时候也会改变input(text)的内容,他们是互相影响的。lazy修饰词就是将实时变化改为失去焦点时变化。
<template>
<h3>表单</h3>
<input type="text" name="" id="" v-model="message">
<input type="checkbox" name="" id="" v-model="check">
<p>{{ message }}</p>
<p>{{ check }}</p>
<input type="text" name="" id="" v-model.lazy="message">
<p>{{ message }}</p>
</template>
<script>
export default{
data(){
return{
message:"",
check:false
}
},
watch:{
check(newvalue,oldvalue){
if(newvalue === true)
{
alert("网站协议");
}
}
}
}
</script>
<style>
</style>
三,总结
OptionAPI(选项式API)
- Template: 定义 Vue 实例的 HTML 模板。可以是一个字符串模板,也可以是一个外部文件的引用。
- Data: 定义 Vue 实例的数据属性。这些属性可以在模板中使用,也可以在方法中引用。
data
必须是一个函数,每次创建新实例时都会返回一个新的对象。- Methods: 定义可被组件内部使用的可复用函数。方法可以在模板中通过
.
符号调用,也可以通过事件监听器调用。- Computed: 定义依赖于其他数据属性的计算属性。计算属性会缓存结果,只有在其依赖的数据属性改变时才会重新计算。
- Watch: 监听数据属性的变化并执行相应的回调函数。可以用于执行异步操作或开销较大的计算任务。
指令
v-once:只渲染元素和组件一次。
v-text:更新元素的
textContent
。v-html:更新元素的
innerHTML
。v-pre:跳过 Vue 的编译过程。
v-cloak:隐藏未编译的 Mustache 语法直到实例准备就绪。
v-bind:动态绑定属性。
v-on:绑定事件监听器。
v-for:循环渲染列表或数组。
v-model:实现表单输入控件与数据的双向绑定。
v-if....:条件渲染。
v-show:切换元素的 CSS
display
属性。
四,附言
声明式与命令式
(1)声明式
定义:
- 声明式编程关注的是“做什么”,而不是“怎么做”。也就是说,你告诉计算机你想达到的目标或结果,而不是具体的步骤。
- 声明式编程通常涉及使用高级抽象,如函数式编程、逻辑编程、数据库查询语言等。
特点:
- 更加简洁,易于理解和维护。
- 通常更加接近人类的思考方式。
- 可以提高代码的可读性和可维护性。
- 适用于处理数据流和变换的场景。
示例: 在前端开发中,Vue.js 的模板系统就是一个典型的声明式编程的例子。例如,使用 v-if
指令来控制元素的显示或隐藏:
<div v-if="isVisible">Hello World</div>
这里我们声明了只有当 isVisible
为真时才显示这个元素,而不需要关心具体的实现细节。
(2)命令式
定义:
- 命令式编程关注的是“怎么做”,即明确地告诉计算机每一步应该做什么。
- 这种编程方式更接近底层硬件的操作,通常涉及更多的控制流语句(如
for
循环、if
语句等)。
特点:
- 更加灵活,可以精确控制程序的行为。
- 适用于需要复杂流程控制的场景。
- 通常需要编写更多的代码来完成相同的任务。
示例: 在命令式编程中,你可能需要显式地编写循环来遍历数组并修改每个元素:
let numbers = [1, 2, 3, 4, 5];
for (let i = 0; i < numbers.length; i++) {
numbers[i] *= 2;
}
- 声明式编程:适用于构建用户界面、处理数据流、执行查询等场景。
- 命令式编程:适用于需要精确控制流程、优化性能、处理复杂逻辑的情况。
MVVM(Model-View-ModelView)
我们可以看到ViewModel中有DOM Listeners,这个就是vue为什么能够在变量改变时快速改变渲染的原因,就和Qt中主循环效果差不多,一直循环等待修改。而Data Bindings可以参考Mustache语法,它其实就是使用Data Bindings实现的。
创作不易,点个赞鼓励一下呗~