系列文章
vue 学习 01 —— 基本语法
vue 学习 02 —— vue-cli项目、Webpack、vue-router
vue 学习 03 —— ElementUI
文章目录
一、概述
Vue
Vue(读音 /vjuː/,类似于 view)是一套用于构建用户界面的渐进式框架,发布于2014年2月。与其他大型框架不同,Vue被设计为自底向上逐层应用。Vue的核心库只关注视图层(HTML、CSS、JS),不仅易于上手,还便于与第三方库(如vue-router:跳转、vue-resource:通信、vuex:管理)或既有项目整合。
- 轻量级、体积小:Vue.js压缩后只有20kb,而Angular有56kb,React有44kb。
- 移动优先,更适合移动端,比如移动端的Touch事件。
- 易上手,学习曲线平稳,文档齐全。
- 吸取了Angular(模块化优点)和React(虚拟Dom优点)的长处,并拥有自己独特的功能,例如计算属性。
- 开源,社区活跃度高…
官网:https://cn.vuejs.org/v2/guide/
MVVM模式的实现者
- Model:模型层,【在第一个Vue程序中表示着JavaScript对象】
- View:视图层,用于展示ViewModel或Model的数据【在第一个Vue程序中表示着表示Dom(HTML操作的元素)】
- ViewModel:连接视图和数据的中间件,前端开发者在这一层对从后端获取的Model数据进行转换处理,将其封装为符合View层的视图数据模型。Vue.js就是MVVM中的ViewModel层的框架。
在MVVM架构中,不允许数据和视图直接通信,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者。
ViewModel能观察到数据的变化,并对视图对应的内容进行更新,同时它还能监听到视图的变化,能通知数据发送改变。简单点说,就是当数据变化后,界面上就会及时改变。
二、第一个Vue程序
1、新建一个文件夹vue/vue-first
2、用IDEA打开这个文件
3、新建文件 chapter-1/demo1.html
4、编写代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
{{message}}
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
let vm = new Vue({
el:"#app",
//model层 数据,JS中{}表示一个对象,[]表示数组
data:{
message:"hello,vue"
}
})
</script>
</body>
</html>
5、打开页面,可直接修改内容。
说明:在没有刷新页面的情况下,我们完成了页面数据的修改。以前如果想要修改一般是通过DOM来操作标签的内容,现在Vue利用ViewModel将数据和视图双向绑定,当数据被修改时,视图也会即时改变。
三、Vue基本语法
1、v-bind
前面我们已经创建了第一个Vue应用,看起来和Thymeleaf 模板引擎很像,数据和DOM已经建立了关联,所有东西都是响应式的,界面可以实时更新。
我们还能使用v-bind来绑定元素
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<span v-bind:title="message">鼠标悬停试试~</span>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
let vm = new Vue({
el:"#app",
//model层 数据
data:{
message:"hello,vue"
}
})
</script>
</body>
</html>
这种v-bind
带有前缀v-
的指令,都属于Vue提供的。该指令是将这个元素节点(span)的title
和Vue的实例message
绑定。
我们可以再去浏览器的JavaScript控制台,输入app.message=“新内容”,将title的值改变。
效果:
2、v-if和v-else
就是判断语句,效果直接看代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue判断语句</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 如果ok等于true,显示YES,否则显示NO。这里ok可以写成ok==true -->
<h1 v-if="ok">YES</h1>
<h1 v-else>NO</h1>
</div>
<script type="text/javascript">
// 创建好一个对象Vue,绑定至元素app(#app是ID选择器)
let vm = new Vue({
el:'#app',
data:{
ok: true
}
});
</script>
</body>
</html>
效果:
下面演示下v-else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue判断语句</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 如果ok等于true,显示YES,否则显示NO。这里ok可以写成ok==true -->
<h1 v-if="type==='A'">A</h1>
<h1 v-else-if="type==='B'">B</h1>
<h1 v-else="type==='C'">C</h1>
</div>
<script type="text/javascript">
// 创建好一个对象Vue,绑定至元素app(#app是ID选择器)
let vm = new Vue({
el:'#app',
data:{
type: 'A'
}
});
</script>
</body>
</html>
效果:
3、v-for
遍历指令:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue循环语句</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- v-for指令进行遍历,从数组items中取出每一个元素,元素放在item -->
<!-- 可以改成 (item, index) in items,其中index是下标 -->
<li v-for="item in items">
<!-- 显示item的message -->
{{item.message}}
</li>
</div>
<script type="text/javascript">
let vm = new Vue({
// 绑定至元素app(#app是ID选择器)
el:'#app',
//数据是一个对象(花括号表示一个对象{})
data: {
//对象中有一个数组(方括号表示数组[])
items: [
//数组有三个元素,每个元素都是对象。对象内容是字符串
{message: 'Java'},
{message: '前端'},
{message: '运维'}
]
}
});
</script>
</body>
</html>
效果:
4、v-on
v-on可将元素绑定到事件上。
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>绑定事件</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- v-on的click将绑定至method1方法,单击按钮时触发 -->
<button v-on:click="method1">按钮</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message: '这是一条消息'
},
//所有的方法要写在methods对象中
methods: {
//方法名为method1的效果是 弹出一个对话框,对话框内容来自message
method1: function () {
alert(this.message);
}
}
});
</script>
</body>
</html>
效果:
5、v-model
Vue.js是一个MVVM框架,能双向绑定,即当数据变化时视图也会相应变化。
我们可以使用v-model
对<input>、<textarea>、<select>
标签进行双向绑定。
注意:v-model会忽略标签的value、checked、selected的初始值,它总是根据Vue实例里的data属性值来确定。
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>双向绑定</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- input、textarea、select都可以双向绑定 -->
<div>
<!-- 输入文本的内容会及时修改后面文字 -->
<input type="text" v-model="message"/> {{message}}
</div>
<!-- 单选框绑定:将选择的内容显示在后面 -->
<div>
性别:
<input type="radio" name="sex" value="男" v-model="sex">男
<input type="radio" name="sex" value="女" v-model="sex">女<br>
选中的性别:{{sex}}
</div>
<!-- 下拉框:将选中的内容显示在后面 -->
<div>
下拉框:
<select v-model="select">
<!-- 第一个选项用作提示,不能选中 -->
<option value="" disabled>--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
选中的值:{{select}}
</div>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message: '该内容会及时改变',//input value的默认内容
sex: '女',//radio 默认选中女性
select: '',//select 默认是第一个内容,即 请选择
}
});
</script>
</body>
</html>
效果:
6、Vue.component
组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
注册一个全局组件语法格式如下:
Vue.component(tagName, options)
tagName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:
<tagName></tagName>
在实际开发中并不会用以下方式开发组件,而是采用vue-cli创建.vue模板文件的方式,下面只是为了便于理解什么是组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- myComponent是自定义的标签; v-for对模板进行循环; v-bind将item绑定到param1上 -->
<!-- 相当于从items中取出的每个元素放在item里,将item传入param1,即myComponent标签 -->
<!-- item是实参,param1是形参 -->
<component v-for="item in items" v-bind:param1="item"></component>
</div>
<script>
/** 定义一个Vue组件:
* 标签名是component(不要用驼峰命名),模板(内容)是template,形式参数props(用于接收实参)
*/
Vue.component("component",{
props: ['param1'],
template: '<li>{{param1}}</li>'
});
let vm = new Vue({
el: '#app',
data: {
items: ["Java", "前端", "运维"]
}
});
</script>
</body>
</html>
效果:
7、Axios异步通信
1、什么是Axios
Axios是一个开源的可以用在浏览器端和NodeJS的异步通信框架,她的主要作用就是实现Ajax异步通信,其特点如下:
- 在浏览器中创建XMLHttpRequests
- 从node.js创建Http请求
- 支持Promis API【JS中的链式编程】
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF(跨站请求伪造)
GitHub:
中文文档:http://www.axios-js.com/
2、为什么要用Axios
由于Vue.js
是一个视图层框架,且其作者(尤雨溪)严格遵守Soc(关注度分离原则),因此Vue.js
并不包含Ajax的通信功能,为了解决这个问题,其作者单独开发了一个名为vue-resource
的插件,但在Vue 2.0后停止了维护并推荐使用Axios框架。一般不会用jQuery,因为它操作Dom太频繁了。
3、使用方法
菜鸟教程:https://www.runoob.com/vue2/vuejs-ajax-axios.html
{
"name":"love似baby",
"url": "http://baidu.com",
"page": "1",
"isNonProfit":"true",
"address": {
"street": "天安门",
"city":"北京",
"country": "中国"
},
"links": [
{
"name": "B站",
"url": "https://www.bilibili.com/"
},
{
"name": "4399",
"url": "https://www.4399.com/"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Axios</title>
<!-- 在线Axios -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 显示Json数据 -->
<div>{{info.name}}</div>
<div>{{info.address.street}}</div>
<div>{{info.links.name}}</div>
</div>
<script type="text/javascript">
let vm = new Vue({
el:'#app',
//注意这里的data是函数
data () {
return{
info: null
}
},
//钩子函数
mounted(){
axios
.get('./data.json')//通过GET方式请求
.then(response=>(this.info = response.data))//response.data将JSON数据给info(对象)
.catch(function (error) { // 请求失败处理
console.log(error);
});
}
})
</script>
</body>
</html>
效果:
8、计算属性
计算属性的重点就是属性,它首先是一个属性且具有计算能力,这里的计算就是个函数。简单的说,它就是一个能够将计算结果缓存起来的属性。
下面进行演示:写两个方法,一个在methods里,一个在computed里。调用时,methods里的是作为方法,所以需要括号,而computed里的是计算属性,本质是属性,所以不用括号。
同时,computed里的是一种缓存,最后的演示效果中,我们反复调用,它不会更新值。当我们对其进行更改后,它会刷新。(类似MyBatis的缓存,进行update后,值就会改变)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<div>{{currentTime1()}}</div>
<div>{{currentTime2}}</div>
</div>
<script type="text/javascript">
let vm = new Vue({
el:'#app',
data: {
message: "hello, vue"
},
methods: {
currentTime1: function () {
return new Date().toLocaleString();//返回当前时间戳
}
},
//计算属性【这里面的方法不能和methods的重名,会优先调用methods的】
computed: {
currentTime2: function () {
return new Date().toLocaleString();//返回当前时间戳
}
}
})
</script>
</body>
</html>
效果:
结论
调用方法时,每次都需要进行计算,这就会产生系统开销。如果结果是不经常变化的,就可以考虑用缓存。计算属性的主要特性就是将不经常变化的计算结果缓存起来,节约系统开销。
9、slot
在Vue.js
中,通过<slot>
插槽来承载分发内容的出口,可以应用在组合组件的场景中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>插槽</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<my>
<my-title slot="my-title" v-bind:param1="title" ></my-title>
<my-items slot="my-items" v-for="item in myItems" v-bind:param2="item"></my-items>
</my>
</div>
<script type="text/javascript">
//slot:插槽,实现动态拔插,相当于slot位置可以插入任意其他的内容。
Vue.component("my",{
template:
'<div>' +
'<slot name="my-title"></slot>'+
'<ul>' +
'<slot name="my-items"></slot>' +
'</ul>' +
'</div>'
});
//下面两个组件插入到上面的组件的两个插槽<slot>里
Vue.component("my-title", {
props: ['param1'],
template:
'<div>{{param1}}</div>'
});
Vue.component("my-items", {
props: ['param2'],
template:
'<li>{{param2}}</li>'
});
let vm = new Vue({
el:'#app',
data: {
title: '书籍列表',
myItems: ['Java', 'Python', 'Linux']
},
})
</script>
</body>
</html>
效果:
10、自定义事件
父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!
我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用
$on(eventName)
监听事件 - 使用
$emit(eventName)
触发事件
另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>插槽</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<my>
<my-title slot="my-title" v-bind:param1="title" ></my-title>
<!-- v-on可简写为 @,v-bind可简写为 : -->
<!-- (item, index)中,index是内置的属性,是每个item的下标 -->
<!-- 把函数removeItems绑定到remove上,remove是组件my-items的函数。 -->
<my-items slot="my-items" v-for="(item,index) in myItems"
v-bind:param2="item" v-bind:param3="index" v-on:remove="removeItems(index)"></my-items>
</my>
</div>
<script type="text/javascript">
//slot:插槽,实现动态拔插,相当于slot位置可以插入任意其他的内容。
Vue.component("my",{
template:
'<div>' +
'<slot name="my-title"></slot>'+
'<ul>' +
'<slot name="my-items"></slot>' +
'</ul>' +
'</div>'
});
//下面两个组件插入到上面的组件里
Vue.component("my-title", {
props: ['param1'],
template: '<div>{{param1}}</div>'
});
Vue.component("my-items", {
//param2:各个书籍项目,param3:接收的下标index
props: ['param2', 'param3'],
//绑定按钮到remove函数,即点击按钮就会触发remove函数。
template: '<li>{{param3}}--{{param2}} <button v-on:click="remove">删除</button></li>',
//单独的data函数,不会影响到vm对象的data
data:function(){
return{
counter: 0
}
},
methods: {
//remove函数会触发removeItems函数(事件)
remove: function () {
console.log("触发了remove()"+(this.counter++));
//props:父组件传数据给子组件,子组件传给父组件: $emit(eventName) 触发事件、 $on(eventName) 监听事件
this.$emit('remove');
}
}
});
let vm = new Vue({
el:'#app',
data: {
title: '书籍列表',
myItems: ['Java', 'Python', 'Linux']
},
methods: {
removeItems: function (index) {
console.log("删除了"+this.myItems[index]);
this.myItems.splice(index,1);//一次删除一个元素
}
}
})
</script>
</body>
</html>
效果: