一、概述
1.1 关于Vue的说法
- vue 是一套构建用户界面的流行的渐进式前端框架。
- vue 只关注视图层, 采用自底向上增量开发的设计。
- vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
- vue是基于MVVM模式的具体实现。
- vue 的核心库只关注视图层。
- 便于与第三方库或既有项目整合。
- vue的其他扩展库可以高效的进行前后台分离的开发模式,常用的开发库又称为vue全家桶。
Vue在线教程:https://cn.vuejs.org/v2/guide/index.html
1.2 下载和安装
- 运行环境:nodejs
下载地址:https://nodejs.org/en/download/
- 开发工具:visual code studio
下载地址:https://visual-studio-code.en.softonic.com/
- vue核心库
下载地址:https://cn.vuejs.org/v2/guide/installation.html
1.3 Hello World
第一步:新建一个html页面,引入vue核心库。
<script src="js/vue.js" type="text/javascript"></script>
第二步:编写vue代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue" type="text/javascript"></script> -->
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>1-基本绑定</h4>
<div id="app">
{{ message }}
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
}
})
</script>
</body>
</html>
第三步:在浏览器上打开该html页面即可看到下面效果。
二、Vue对象详解
2.1 创建Vue对象
每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的。一个vue应用至少包含一个vue实例对象。
基本语法:
new Vue({
// 选项
})
2.2 选项详解
Vue实例可以包含以下选项:
- 数据属性与方法:当前实例闭包内的全局变量以及全局方法。
- 操作dom:用于生成dom或绑定dom。
- 生命周期钩子函数:用于在vue组件或vue实例在创建以及渲染的过程中植入用户的可执行程序。
- 资源:引入组件或实例所引用的指令、组件、过滤器等。
- 其他:name属性、minxin混合等。
常用的数据属性与方法:
- data:Vue 实例的数据对象, Vue 将会递归将 data 的属性转换为 getter/setter方法,从而对data进行赋值。
- props:props 可以是数组或对象,用于接收来自父组件的数据,访问时与data基本一致,只有组件具备,vue实例对象不具备。
- computed:计算属性,内容为返回计算结果的函数。
- methods:实例或组件的内置函数。
- watch:观察对象,当数据变化时可触发watch中的内置函数。
操作dom:
- el:只在vue实例中生效,用于绑定实例对象对应的dom对象的id属性。
- template:vue实例对象或组件的模板语法,模板中除标准的html语法还包括vue的自定义指令以及表达式等。
- render:以创建dom对象的方式声明dom结构,在vue中不常用。
声明周期钩子函数:在vue对象以及组件从创建、渲染、数据改变等周期内,提供给用户的对外函数接口。
- created:function() {} 在vue对象创建时被调用。
资源类属性:
- directives:本组件包含的用户自定义指令集合。
- filters:本组件包含的过滤器。
- components:本组件或本实例包含的组件。
其他属性:
- Name:标注当前组件名称
- minxin:用于将当前数据属性混入到组件配置属性中。
- parent:指定当前组件的父实例(组件)。
2.3 data属性
2.3.1 data属性用法
实例中的data对应的是一个对象。例如:
new Vue({
data: {
message: 'hello vue'
}
})
组件中的data对应的是一个函数,需要在函数中声明当前组件的数据属性。
Vue.component({
data: function() {
return {
message: 'hello vue component'
}
}
})
2.3.2 data属性的使用方式
可以使用this关键字访问vue实例属性,也可以使用实例变量访问。例如:
let app = new Vue({
el: '#app',
data: {
message: 'hello vue'
},
methods: function() {
setMessage: function() {
this.message = 'hello java'
console.log(app.message)
// 在methods中扩展vue实例属性,vue无法识别
// this.newMessage = 'hello react'
}
}
})
2.3.3 vue实例对外暴露的属性和方法
$el :获取vue实例对应的dom对象;
$data :获取vue实例的data对象;
例如:
new Vue({
el: '#app',
data: {
message: 'hello vue'
},
methods: {
getAttribute: function() {
console.log(this.$el === document.getElementById('app')) // true
this.$data.message = "新数据";
}
}
})
三、基本功能介绍
3.1 基本数据绑定
vue实现了mvvm模式,用户只需要配置view、model,vue则直接实现了viewmodel,并直接展现视图。
基本语法:
{{js表达式}}
js表达式的特点:
- 内容为单句js语句。
- 表达式不支持赋值(如 var I = 1);
- 表达式支持三目运算符。
- 在表达式中可以访问组件的属性与方法,省略this调用。
- 表达式支持使用原生js对象(如Date等)。
3.2 元素属性绑定
通过vue可以将model层的数据与元素的基本属性绑定,从而对元素属性赋值。
基本语法:
<标签名 v-bind:属性名="js表达式">
标签内容...
</标签名>
例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>2-元素属性绑定</h4>
<div id="app">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
message: '页面加载于' + new Date().toLocaleString()
}
})
</script>
</body>
</html>
v-bind指令缩写为:
<span :title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
3.3 条件判断
vue中提供了类似于if、else的语法来实现逻辑判断。
基本语法:
<标签名 v-if="实例对象属性">
标签内容...
</标签名>
如果v-if取值为true,则加载v-if标签内容;如果如v-if取值为false,则无需加载。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>3-条件判断</h4>
<div id="app">
<p v-if="seen">现在您看到我了</p>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
seen: true
}
})
</script>
</body>
</html>
3.4 循环
v-for模板指令来循环vue实例中的属性。
基本语法:
<标签名 v-for="变量名 in 实例对象属性">
标签内容...
</标签名>
实例对象属性应该是数组或集合类型。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>4-循环</h4>
<div id="app">
<ul>
<li v-for="todo in todoList">
{{todo.text}}
</li>
</ul>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
todoList: [
{text: 'java'},
{text: 'php'},
{text: 'android'},
{text: 'ios'},
]
}
})
</script>
</body>
</html>
3.5 数据双向绑定
单向绑定:view中的显示数据与vue实例中的属性绑定即使单项绑定,此时vue实例中的属性变化会同步到view的显示数据中。
双向绑定:用于表单标签(例如:文本框),表单标签的内容与绑定的vue实例中的属性双向同步。双向绑定意味着任意一方改变都会影响另一方的取值。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>5-数据双向绑定</h4>
<div id="app">
<input v-model="message" />
<button v-on:click="setValue">设置值</button>
<button v-on:click="getValue">获取值</button>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
message: ''
},
methods: {
setValue() {
this.message = '呵呵'
},
getValue() {
alert(this.message);
}
}
})
</script>
</body>
</html>
3.6 计算属性
计算属性函数用于返回计算结果。在此函数内可以访问vue组件的属性与方法。
计算函数一般在组件或实例对象的computed中声明,然后在模板中直接访问该函数即可。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>7-计算属性</h4>
<div id="app">
出生日期:<input type="date" v-model="birthday"/><br/>
<!-- v-model属性绑定了age函数,该input的内容会根据age函数返回值发生变化 -->
年龄:<input type="number" v-model="age"/><br/>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
birthday: '2012-12-12'
},
computed: {
// 只要data中任意属性发生变化,都会触发该函数
// 该函数的返回值作为v-model="age"文本框的内容
age: function() {
return new Date().getFullYear() - new Date(this.birthday).getFullYear()
}
}
})
</script>
</body>
</html>
计算属性与函数的区别?
- 相同点:
在模板中使用的方式相同,传递参数的方式相同。
- 不同点:
1)函数在methods中声明,计算属性在computed中声明;
2)计算属性需要返回结果,而函数非必须返回结果;
3)计算属性是基于依赖进行缓存的,意味着只有影响计算结果的属性发生改变的时候才会重新计算,而函数每次调用都会运算;
3.7 观察属性
观察属性:当data发生变化时,vue会调用观察属性绑定的函数,因此也叫监听器。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>8-观察属性</h4>
<div id="app">
出生日期:<input type="date" v-model="birthday"/><br/>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
birthday: '2012-12-12'
},
watch: {
// 监听属性名字与data属性名字相同
// 当data发生变化时,该函数会自动触发
birthday: function() {
alert(vue.birthday)
}
}
})
</script>
</body>
</html>
监听器函数封装在watch属性中,函数名与监听的数据属性名必须相同。只有在数据属性变化时,监听器函数将会被调用。
3.8 设置样式
3.8.1 通过数据属性设置样式
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<style>
.ba {
color: red;
}
.cr {
background: aqua;
}
</style>
</head>
<body>
<h4>9-设置样式</h4>
<div id="app">
<!-- 如果error或warn属性为true,那么就设置class属性为ba或cr -->
<!-- <div v-bind:class="{ba:error, cr:warn}">样式</div> -->
<div v-bind:class="bb">样式</div>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
error: true,
warn: true,
// 如果ba为true,则返回ba;如果ba为false则不返回
// 如果cr为true,则返回cr;如果cr为false则不返回
bb: {ba:false, cr:true}
}
})
</script>
</body>
</html>
3.8.2 通过函数设置样式
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<style>
.ba {
color: red;
}
.cr {
background: aqua;
}
</style>
</head>
<body>
<h4>10-通过函数计算样式</h4>
<div id="app">
<span v-bind:class="getClass">样式1</span>
<span v-bind:style="getStyle">样式2</span>
</div>
<script>
let st = {
name: 'jacky',
sex: 0
}
let vue = new Vue({
el: '#app',
computed: {
getClass: function() {
// 返回class名字(ba或cr)
return {ba:st.sex == 1, cr:st.sex == 0}
},
getStyle: function() {
// 返回样式单
return "color:blue;font-size:30px;"
}
}
})
</script>
</body>
</html>
3.9 事件处理
v-on指令:监听 DOM 事件,并在触发时运行用户编写的js代码。v-on可以绑定几乎全部的事件类型。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>11-事件处理</h4>
<div id="app">
<button v-on:click="minus">-</button>
{{count}}
<button v-on:click="add">+</button>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add: function() {
this.count++
},
minus: function() {
if (this.count > 0) {
this.count--
}
}
}
})
</script>
</body>
</html>
v-on指令缩写为@
<button @click="minus">-</button>
{{count}}
<button @click="add">+</button>
3.10 函数传参
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>11-参数传递</h4>
<div id="app">
<button v-on:click="add(2, $event)">+2</button>
{{count}}
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
// e是dom的event对象
add: function(num, e) {
this.count = num + this.count;
}
}
})
</script>
</body>
</html>
上面v-on指令的取值处直接调用add函数并传递参数,在模板中可以使用$event获取到event对象,并传递给响应函数。
3.11 表单
vue通过v-model指令将dom元素与数据属性双向绑定,其工作原理:
1)监听用户的输入事件以更新数据;
2)监听数据更新dom元素取值;
vue通过以下监听事件来监控dom元素的属性变化。
dom元素 | 监听属性 | 监听事件 |
---|---|---|
text\textarea | value | input |
checkbox\radio | checked | change |
select | value | change |
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>13-表单</h4>
<div id="app">
<form>
<!-- 如果没有lazy,那么修改输入框内容,span标签内容也会发生变化 -->
<!-- 如果指定了lazy,那么修改输入框内容,span标签内容不会马上发生变化,只有输入框失去焦点后,span标签内容才会发生变化 -->
<input v-model.lazy="msg" v-on:change="getLength"/>
<span>{{msg}}</span>
<br/>
<input v-model.number="age" type="number" />
<br/>
</form>
</div>
<script>
let vue = new Vue({
el: '#app',
data: {
msg: 'hello',
age: 0
},
methods: {
getLength: function() {
console.log("msg length is " + this.msg.length)
}
}
})
</script>
</body>
</html>
我们可以在vmodel后面指定修饰符:
vmodel.lazy:一般适用于text,将数据属性更新的事件由input转变为使用 change 事件进行同步。
vmodel.number:将用户的输入值转为数值类型。
vmodel.trim:自动过滤用户输入的首尾空白字符。
例如:
<input vmodel.lazy="message" />
<input vmodel.number="age" />
<input vmodel.trim="msg" />
它们也可以组合在一起使用:
<input vmodel.lazy.trim="message" />
3.12 组件
组件是可复用的 Vue 实例,一个组件映射着一个MVVM模型,声明一个组件后,在其他实例或组件中使用组件标签直接调用即可完成组件的显示。一个大型的工程由多个可视化可视化界面,每个界面都是由多个组件组成。
3.12.1 组件注册
组件的注册:声明组件模板内置函数。
基本语法:
Vue.component('组件名', {
data: function() {
return {
// 组件全局变量
}
},
template: 'html模板',
methods: {
// 方法属性
},
computed: {
// 计算属性
}
})
与实例对象不同,组件中的data是一个函数,函数的返回值为全局变量。
与实例对象不同,组件中很少使用el,都是用template声明模板,在模板中要包含根节点。
// 注册组件
Vue.component('button-counter', {
// 定义组件中的全局变量
// 该变量在组件中可以访问
data: function() {
return {
count: 0
}
},
// 定义组件模板
template: '<button v-on:click="count++">{{count}}</button>'
})
组件引用:
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
注意:如果组件名是多个英文组成的字符串,那么引入组件时候多个英文之间使用横杠分割。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<script>
</script>
</head>
<body>
<h4>14-组件</h4>
<div id="app">
<!-- 使用组件 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
// 注册组件
Vue.component('button-counter', {
// 定义组件中的全局变量
// 该变量在组件中可以访问
data: function() {
return {
count: 0
}
},
// 定义组件模板
template: '<button v-on:click="count++">{{count}}</button>'
})
// 在实例中应用组件
let vue = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
3.12.2 props属性
props属性主要用于父组件向子组件中传递参数。props中声明的属性是全局属性,可以在方法、计算属性中访问。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<script>
</script>
</head>
<body>
<h4>15-props属性</h4>
<div id="app">
<!-- 普通属性传递 -->
<blog-component blog-title="测试"></blog-component>
<!-- 组件属性传递 -->
<blog-component v-bind:blog-title="posts[0].title"></blog-component>
</div>
<script>
// 定义一个组件
Vue.component('blog-component', {
props: ['blogTitle'],
template: '<h3>{{blogTitle}}</h3>'
})
// 在实例中应用组件
let vue = new Vue({
el: '#app',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
</script>
</body>
</html>
Post的命名规则 :在定义prop时可以使用camelCase和kebab-case两种方式。但在模板中引入组件时,由于html是忽略大小写的,因此只能使用kebab-case方式引入。
在声明属性时可以设定属性的数据类型,如果传入的属性与类型不符合,将会提示相关的错误信息。
Vue.component('blog-component', {
props: {
blogTitle: String
},
template: '<h3>{{blogTitle}}</h3>'
})
上面程序指定blogTitle的类型为String,那么传入属性必须是字符串类型,否则报错。
下面是常见的数据类型:
3.12.3 子组件捕获事件
当触发子组件的事件处理函数后,需要调用父组件的响应函数进行响应。
在父组件中通过v-on:method方式注册自定义处理函数,例如:
<blog-post v-on:enlarge-text="处理函数"></blog-post>
在子组件中使用 emit方法的参数名对应着v-on事件名。
Vue.component('blog-post', {
props: ['post'],
template: // $emit方法传入事件名称来触发一个事件
`<div class="blog-post">
<h3>{{post.title}}</h3>
<button @click="$emit('enlarge-text')">Enlarge text</button>
<div v-html="post.content"></div>
</div>`
})
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<script>
</script>
</head>
<body>
<h4>16-子组件事件捕获</h4>
<div id="app">
<div :style="{fontSize: postFontSize + 'em'}">
<!--
参数说明:
v-for:循环父组件的posts属性
key: 唯一表示每个item
post:父组件传递给子组件的参数,该名字与子组件的post属性相同
v-on:定义一个监听器,该监听器用来监听enLarge-text事件。
有了该事件,父组件就会接收该事件并更新postFontSize属性
-->
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post"
v-on:enlarge-text="postFontSize += 0.1"></blog-post>
</div>
</div>
<script>
// 子组件
Vue.component('blog-post', {
props: ['post'],
template: // $emit方法传入事件名称来触发一个事件
`<div class="blog-post">
<h3>{{post.title}}</h3>
<button @click="$emit('enlarge-text')">Enlarge text</button>
<div v-html="post.content"></div>
</div>`
})
// 父组件
let vue = new Vue({
el: '#app',
data: {
posts: [
{ id: 1, title: 'My journey with Vue', content: 'this is content' },
{ id: 2, title: 'Blogging with Vue', content: 'this is content' },
{ id: 3, title: 'Why Vue is so fun', content: 'this is content' }
],
postFontSize: 1
}
})
</script>
</body>
</html>
3.12.4 vmodel在组件中的使用
使用自定义事件处理v-model的双向绑定。一般用于子组件中的输入框与父组件中的属性双向绑定。
需求:在vue实例中定义searchText属性,然后在子组件中定义一个input搜索框,搜索框内容发生变化的时候,改变vue实例的searchText属性的值。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
</head>
<body>
<h4>17-vmodel在组件中的应用</h4>
<div id="app">
<!--
在组件中使用v-model就相当于:
<custom-input v-bind:value="searchText"
v-on:input="searchText = $event"></custom-input>
上面代码执行了两个操作:
1)v-bind:value 实现父组件的searchText参数与子组件value属性之间的数据绑定
2)v-on:input 定义一个监听器,该监听器用来监听input事件。当子组件中的input内容发生改变,
那么就会就会把新的内容($event)设置到父组件的searchText参数中。
-->
<custom-input v-model="searchText"></custom-input>
{{searchText}}
</div>
<script>
// 子组件
Vue.component('custom-input', {
props: ['value'],
// v-on:input用于监控用户输入,当input事件被触发的时候,
// 将新的值($event.target.value)通过自定义的input事件抛出
template: `
<input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" />
`
})
// 父组件
let vue = new Vue({
el: '#app',
data: {
searchText: '呵呵'
}
})
</script>
</body>
</html>
3.13 插槽
插槽:子组件中的模板接口,它可以在父组件中填充子组件的模板。
定义插槽:
<slot></slot>
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<script>
</script>
</head>
<body>
<h4>18-插槽</h4>
<div id="app">
<alert-box>
Something bad happened.
</alert-box>
</div>
<script>
// 子组件
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
// 父组件
let vue = new Vue({
el: '#app'
})
</script>
</body>
</html>
3.14 动态组件
当组件的内容不确定时,使用动态组件技术选择加载的组件。例如:选择Posts的时候显示Posts组件,选择Archive时候显示Archive组件。
3.14.1 创建动态组件
第一步:在data对象中定义一个属性,该属性指定要加载的组件名;
let vue = new Vue({
el: '#app',
data: {
currentComponent: 'component-2'
}
})
第二步:使用component引用组件。
<component v-bind:is="currentComponent"></component>
上面<component>
元素是vue 里面的一个内置组件,v-bind:is 决定哪个组件被渲染成动态组件。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript"></script>
<script>
</script>
</head>
<body>
<h4>19-动态组件</h4>
<div id="app">
<component v-bind:is="currentComponent"></component>
</div>
<script>
// 子组件
Vue.component('component-1', {
template: `
<h3>组件1...</h3>
`
})
Vue.component('component-2', {
template: `
<h3>组件2...</h3>
`
})
// 父组件
let vue = new Vue({
el: '#app',
data: {
currentComponent: 'component-2'
}
})
</script>
</body>
</html>
3.14.2 动态组件缓存问题
当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。这时候可以使用<keep-alive>
标签。使用<keep-alive>
修饰的组件会保持在内存中,下次使用组件时候直接显示,避免重新渲染的问题。
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>