JavaWeb 学习笔记 9:Vue
1.快速开始
Vue 是一个前端框架,可以作用于浏览器或者 Node.js,它的优点是可以实现视图(View)和数据模型(Model)的双向绑定。
Vue 的这种双向绑定类似于很多年前我用 C# 开发桌面应用的体验。
来看一个示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="input">{{input}}
</div>
</body>
<script>
const { createApp } = Vue
createApp({
data() {
return {
input: ""
}
}
}).mount('#app')
</script>
</html>
这里使用一个 CDN 引入 vue 的 js 文件:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
vue.global.js
提供了一个全局的Vue
对象,我们可以利用这个对象完成数据绑定:
const { createApp } = Vue
createApp
需要传入一个 js 对象,这个对象的data
属性接收一个方法,该方法返回的 js 对象的属性就是 Vue 的数据模型(Model):
createApp({
data() {
return {
input: ""
}
}
}).mount('#app')
也就是说,这里创建的数据模型是input
,初始值是空字符串。
data(){...}
是一种匿名方法作为 js 对象属性的简写形式,和下面的方式是等价的:
createApp({
data: function () {
return {
input: ""
}
}
}).mount('#app')
最后的.mount()
方法会将创建好的 Vue 的数据模型绑定到指定的 Html DOM 节点上(在这个示例中是id
为app
的div
),目标节点及其子节点都可以使用相应的数据模型。
要实现数据模型和具体 Html 视图标签的绑定,要定义一个v-model
属性:
<input type="text" v-model="input">
实现绑定后,这个标签的值改变后数据模型就会改变,数据模型改变后标签的值也会改变,这就是双向绑定。
要在 Html 中直接(动态)显示数据模型的值,可以使用一个叫做”插值表达式“的东西:
{{input}}
页面被加载后,这个插值表达式会被 Vue 自动替换为数据模型的值,且数据模型的值改变后同样会更新。
最终的效果类似下面这样:
输入框中输入任意值后,输入框后边的插值表达式的地方会随之改变。
2.常用指令
2.1.v-bind
前面已经介绍了使用v-model
将数据模型绑定到标签的值,如果需要将数据模型绑定到标签的属性,就需要使用v-bind
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
SearchEngine: {{se}}<br/>
<select v-model="se">
<option value="http://baidu.com">百度</option>
<option value="https://cn.bing.com">Bing</option>
<option value="http://google.com">Google</option>
</select>
<a v-bind:href="se" target="_blank">Go Search</a>
</div>
</body>
<script>
const { createApp } = Vue
createApp({
data() {
return {
se: "http://google.com"
}
}
}).mount('#app')
</script>
</html>
在上面这个示例中,用一个select
标签选择不同的搜索引擎,并且这个标签和se
模型绑定,还使用一个额外的链接(a
标签)实现跳转到选择的搜索引擎(新标签页)。因为超链接需要根据我们选定的搜索引擎跳转到不同的地址,所以其href
属性必须是动态的,传统方式可能需要一些 js 判断 select
标签的当前值并对超链接的 href
属性进行修改,但现在只需要用v-bind
将href
属性绑定到模型se
上即可,因为模型se
已经和select
的值进行了绑定。
最终的效果就是选择不同的搜索引擎后点击 Go Search 即会跳转到相应的搜索引擎主页:
此外,属性绑定还有一种简写方式:
<a :href="se" target="_blank">Go Search</a>
和下面的形式是等效的:
<a v-bind:href="se" target="_blank">Go Search</a>
2.2.v-on
除了可以绑定属性,还可以将 Vue 中定义的方法绑定到 DOM 事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button v-on:click="clickHappen()">
click
</button>
</div>
</body>
<script>
const {createApp} = Vue
createApp({
methods: {
clickHappen: function () {
console.log("click...")
}
}
}).mount('#app')
</script>
</html>
需要注意的是,DOM 事件 并不等同于 Html 事件,比如 DOM 事件
click
对应 Html 事件onclick
。
在createApp
的初始化参数中,用methods
属性定义 Vue 的自定义方法(事件处理器),因为可以是多个方法,所以methods
的值是一个 js 对象,对象的属性名称是自定义方法名,值是匿名方法:
createApp({
methods: {
clickHappen: function () {
console.log("click...")
}
}
}).mount('#app')
绑定事件很简单:
<button v-on:click="clickHappen()">
也可以使用一种简写方式:
<button @click="clickHappen()">
2.2.1.内联事件处理器
对于简单的内容,可以使用一种”内联事件处理器“的方式绑定事件,比如下面这个示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button @click="countPlus">
Count is: {{ count }}
</button>
</div>
</body>
<script>
const {createApp} = Vue
createApp({
data: function () {
return {
count: 0
}
},
methods: {
countPlus: function () {
this.count++;
}
}
}).mount('#app')
</script>
</html>
Vue 中定义的事件处理器countPlus
的作用仅仅是让数据模型count
自增,对于这种简单的事件处理,可以使用内联事件处理器的写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button @click="count++">
Count is: {{ count }}
</button>
</div>
</body>
<script>
const {createApp} = Vue
createApp({
data: function () {
return {
count: 0
}
}
}).mount('#app')
</script>
</html>
2.3.v-if
可以用v-if
控制标签是否显示:
<div id="app">
<div v-if="count < 5">小于5</div>
<div v-else-if="count == 5">等于5</div>
<div v-else>大于5</div>
<button @click="count++">
Count is: {{ count }}
</button>
</div>
这个示例是在之前示例基础上,添加3个div
标签,用来显示当前数据模型count
的值是否大于5,这里使用v-if
、v-else-if
、v-else
属性控制所在标签在什么条件下才显示。
2.3.1.v-show
除了v-if
外,还有一个v-show
标签,有类似的效果:
<div id="app">
<div v-show="count < 5">小于5</div>
<div v-show="count == 5">等于5</div>
<div v-show="count > 5">大于5</div>
<button @click="count++">
Count is: {{ count }}
</button>
</div>
这两个示例在页面显示上效果完全相同,但它们的实现细节有所不同。
对于 v-show
,它是控制是否显示的 css 样式来显示或隐藏标签实现的:
对于 v-if
,它是直接添加或删除 DOM 节点的方式来实现的:
2.4.v-for
之前展示过一个搜索引擎筛选的示例,是用硬编码的方式实现的select
标签的option
标签,现在可以用v-for
循环遍历数据模型来生成 option
标签:
<body>
<div id="app">
SearchEngine: {{se}}<br/>
<select v-model="se">
<option v-for="engine in engines" :value="engine.url">{{engine.name}}</option>
</select>
<a :href="se" target="_blank">Go Search</a>
</div>
</body>
<script>
const { createApp } = Vue
createApp({
data() {
return {
se: "http://google.com",
engines: [
{name: "百度", url: "http://baidu.com"},
{name: "Bing", url: "https://cn.bing.com"},
{name: "Google", url: "http://google.com"}
]
}
}
}).mount('#app')
</script>
就像时候后端的语言一样,有时候在循环遍历的时候我们希望获取当前的数组元素下标,在使用 Vue 的v-for
时可以这样:
<option v-for="(engine,i) in engines" :value="engine.url">{{i}}-{{engine.name}}</option>
效果:
这种写法和 Python 的风格类似。
3.生命周期
Vue 有以下生命周期阶段:
Vue 官方的生命周期图示:
一般我们只需要定义和使用 mounted
回调方法即可:
<script>
const {createApp} = Vue
createApp({
data: function () {
return {
count: 0
}
},
mounted: function (){
console.log("Vue is ready...")
}
}).mount('#app')
</script>
mounted
回调被执行时意味着 Vue 已经加载完毕。
4.案例:商品列表
用 Vue 可以简化页面数据的填充工作,这里以之前使用的示例项目login-demo为例进行说明。
之前是使用 Axios 获取数据后,用 JQuery 操作 DOM 对象,进行遍历填充。虽然 JQuery 简化了一部分操作,但操作 DOM 对象依然是一个繁琐的工作。
使用 Vue,我们只需要将需要填充的数据定义成数据模型:
createApp({
data() {
return {
brands: [],
username: ""
};
},
// ...
})
// ...
然后使用这个数据模型绑定标签:
<table id="brandsTable" border="1" cellspacing="0" width="800">
<tr>
<th>序号</th>
<th>品牌名称</th>
<th>企业名称</th>
<th>排序</th>
<th>品牌介绍</th>
<th>状态</th>
<th>操作</th>
</tr>
<tr align="center" v-for="(brand,index) in brands">
<td>{{index+1}}</td>
<td>{{brand.brandName}}</td>
<td>{{brand.companyName}}</td>
<td>{{brand.ordered}}</td>
<td>{{brand.description}}</td>
<td v-if="brand.status==0">启用</td>
<td v-else>禁用</td>
<td><a :href="brand.editUrl">修改</a>
<a :href="brand.deleteUrl">删除</a></td>
</tr>
</table>
现在 Vue 的数据模型和标签已经绑定好了,只要数据模型中有内容,表格就会自动填充。
所以剩下的工作就是在 mounted
回调中异步加载数据:
mounted() {
let _vue = this;
axios({
method: "POST",
url: "/login-demo/brand/list",
data: {}
})
.then((resp) => {
_vue.brands = resp.data;
_vue.brands.forEach((brand) => {
brand.editUrl = "/login-demo/brand/edit?id=" + brand.id;
brand.deleteUrl = "/login-demo/brand/delete?id=" + brand.id;
});
});
axios({
method: "GET",
url: "/login-demo/user/info",
data: {}
}).then((resp)=>{
_vue.username = resp.data.username;
});
}
这里有一个小技巧,在createApp
的事件处理器(自定义方法)或者生命周期回调中,可以使用this.attrName
获取 Vue 的数据模型,但是如果嵌套了对象,比如上边的axios()
,在axios
的方法(这里是then
)中,this
显然指向的是axios
对象本身,如果想要获取外部的句柄,就需要在外部定义一个局部变量,这里用_vue
表示。
可以用类似的方式简化其它页面,比如品牌添加页。这里不再赘述,感兴趣的可以查看完整示例。
The End,谢谢阅读。
本文的完整示例代码可以从这里获取。