11 表单
11.1 表单基本操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
form div {
height: 40px;
line-height: 40px;
}
form div:nth-child(4) {
height: auto;
}
form div span:first-child {
display: inline-block;
width: 100px;
}
</style>
</head>
<body>
<div id="app">
<form action="http://itcast.cn">
<div>
<span>姓名:</span>
<span>
<input type="text" v-model='uname'>
</span>
</div>
<div>
<span>性别:</span>
<span>
<input type="radio" id="male" value="1" v-model='gender'>
<label for="male">男</label>
<input type="radio" id="female" value="2" v-model='gender'>
<label for="female">女</label>
</span>
</div>
<div>
<span>爱好:</span>
<input type="checkbox" id="ball" value="1" v-model='hobby'>
<label for="ball">篮球</label>
<input type="checkbox" id="sing" value="2" v-model='hobby'>
<label for="sing">唱歌</label>
<input type="checkbox" id="code" value="3" v-model='hobby'>
<label for="code">写代码</label>
</div>
<div>
<span>职业:</span>
<select v-model='occupation' multiple>
<option value="0">请选择职业...</option>
<option value="1">教师</option>
<option value="2">软件工程师</option>
<option value="3">律师</option>
</select>
</div>
<div>
<span>个人简介:</span>
<textarea v-model='desc'></textarea>
</div>
<div>
<input type="submit" value="提交" @click.prevent='handle'>
</div>
</form>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
uname: 'lisi',
gender: 2,
hobby: ['2','3'],
occupation: ['2','3'],
desc: 'nihao'
},
methods: {
handle: function(){
console.log(this.desc)
}
}
});
</script>
</body>
</html>
11.2 表单域修饰符用法
v-model修饰符的使用
lazy:失去焦点是才会触发双向绑定,应用于<input>标签
number:应用于<input>标签,可以使String类型转换为number类型
trim:去除空格,应用于<input>标签
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model.number='age'>
<input type="text" v-model.trim='info'>
<input type="text" v-model.lazy='msg'>
<div>{{msg}}</div>
<button @click='handle'>点击</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
age: '',
info: '',
msg: ''
},
methods: {
handle: function(){
}
}
});
</script>
</body>
</html>
12 自定义指令
12.1 自定义指令基本用法
1 为何需要自定义指令?
内置指令不满足需求
2 自定义指令的语法规则(获取元素焦点)
Vue.directive('focus' {
inserted: function(el) {
// 获取元素的焦点
el.focus();
}
})
3 自定义指令用法
<input type="text" v-focus>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-focus>
<input type="text">
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.directive('focus', {
inserted: function(el){
el.focus();
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
}
}
});
</script>
</body>
</html>
12.2 带参数的自定义指令
带参数的自定义指令(改变元素背景色)
Vue.directive(‘color', {
inserted: function(el, binding) {
el.style.backgroundColor = binding.value.color;
}
})
指令的用法
<input type="text" v-color='{color:"orange"}'>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-color='msg'>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.directive('color', {
bind: function(el, binding){
el.style.backgroundColor = binding.value.color;
}
});
var vm = new Vue({
el: '#app',
data: {
msg: {
color: 'blue'
}
},
methods: {
handle: function(){
}
}
});
</script>
</body>
</html>
12.3 局部指令用法
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-color='msg'>
<input type="text" v-focus>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: {
color: 'red'
}
},
methods: {
handle: function(){
}
},
directives: {
color: {
bind: function(el, binding){
el.style.backgroundColor = binding.value.color;
}
},
focus: {
inserted: function(el) {
el.focus();
}
}
}
});
</script>
</body>
</html>
13 计算属性
1. 为何需要计算属性?
表达式的计算逻辑可能会比较复杂,使用计算属性可以使模板内容更加简洁
2. 计算属性的用法
computed: {
reversedMessage: function () {
return this.msg.split('').reverse().join('')
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<div>{{reverseString}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'Nihao'
},
computed: {
reverseString: function(){
return this.msg.split('').reverse().join('');
}
}
});
</script>
</body>
</html>
13.1 计算属性基本用法
计算属性与方法的区别
计算属性是基于它们的依赖进行缓存的
方法不存在缓存
使用缓存的好处:对于耗时间的操作,如果结果相同的话,不需要在重复之前的操作,直接从缓存中拿
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{reverseString}}</div>
<div>{{reverseString}}</div>
<div>{{reverseMessage()}}</div>
<div>{{reverseMessage()}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'Nihao',
num: 100
},
methods: {
reverseMessage: function(){
console.log('methods')
return this.msg.split('').reverse().join('');
}
},
computed: {
reverseString: function(){
console.log('computed')
var total = 0;
for(var i=0;i<=this.num;i++){
total += i;
}
return total;
}
}
});
</script>
</body>
</html>
14 侦听器
14.1 侦听器基本用法

1. 侦听器的应用场景
数据变化时执行异步或开销较大的操作
2. 侦听器的用法
watch: {
firstName: function(val){
// val表示变化之后的值
this.fullName = val + this.lastName;
},
lastName: function(val) {
this.fullName = this.firstName + val;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<span>名:</span>
<span>
<input type="text" v-model='firstName'>
</span>
</div>
<div>
<span>姓:</span>
<span>
<input type="text" v-model='lastName'>
</span>
</div>
<div>{{fullName}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
firstName: 'Jim',
lastName: 'Green',
},
computed: {
fullName: function(){
return this.firstName + ' ' + this.lastName;
}
},
watch: {
}
});
</script>
</body>
</html>
14.2 验证用户名是否可用
需求:输入框中输入姓名,失去焦点时验证是否存在,如果已
经存在,提示从新输入,如果不存在,提示可以使用。
① 通过v-model实现数据绑定
② 需要提供提示信息
③ 需要侦听器监听输入信息的变化
④ 需要修改触发的事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<span>用户名:</span>
<span>
<input type="text" v-model.lazy='uname'>
</span>
<span>{{tip}}</span>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
uname: '',
tip: ''
},
methods: {
checkName: function(uname) {
var that = this;
setTimeout(function(){
if(uname == 'admin') {
that.tip = '用户名已经存在,请更换一个';
}else{
that.tip = '用户名可以使用';
}
}, 2000);
}
},
watch: {
uname: function(val){
this.checkName(val);
this.tip = '正在验证...';
}
}
});
</script>
</body>
</html>
15 过滤器基本用法
15.1 过滤器基本用法

格式化数据,比如将字符串格式化为首字母大写,将日期格式化为指定的格式等
自定义过滤器
Vue.filter(‘过滤器名称’, function(value){
// 过滤器业务逻辑
})
过滤器的使用
<div>{{msg | upper}}</div>
<div>{{msg | upper | lower}}</div>
<div v-bind:id=“id | formatId"></div>
局部过滤器
filters:{
capitalize: function(){}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model='msg'>
<div>{{msg | upper}}</div>
<div>{{msg | upper | lower}}</div>
<div :abc='msg | upper'>测试数据</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.filter('lower', function(val) {
return val.charAt(0).toLowerCase() + val.slice(1);
});
var vm = new Vue({
el: '#app',
data: {
msg: ''
},
filters: {
upper: function(val) {
return val.charAt(0).toUpperCase() + val.slice(1);
}
}
});
</script>
</body>
</html>
15.2 带参数的过滤器案例
带参数的过滤器
Vue.filter(‘format’, function(value, arg1){
// value就是过滤器传递过来的参数
})
过滤器的使用
<div>{{date | format(‘yyyy-MM-dd')}}</div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{date | format('yyyy-MM-dd hh:mm:ss')}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.filter('format', function(value, arg) {
function dateFormat(date, format) {
if (typeof date === "string") {
var mts = date.match(/(\/Date\((\d+)\)\/)/);
if (mts && mts.length >= 3) {
date = parseInt(mts[2]);
}
}
date = new Date(date);
if (!date || date.toUTCString() == "Invalid Date") {
return "";
}
var map = {
"M": date.getMonth() + 1,
"d": date.getDate(),
"h": date.getHours(),
"m": date.getMinutes(),
"s": date.getSeconds(),
"q": Math.floor((date.getMonth() + 3) / 3),
"S": date.getMilliseconds()
};
format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
var v = map[t];
if (v !== undefined) {
if (all.length > 1) {
v = '0' + v;
v = v.substr(v.length - 2);
}
return v;
} else if (t === 'y') {
return (date.getFullYear() + '').substr(4 - all.length);
}
return all;
});
return format;
}
return dateFormat(value, arg);
})
var vm = new Vue({
el: '#app',
data: {
date: new Date()
}
});
</script>
</body>
</html>
16 Vue 生命周期
生命周期钩子 ====> 生命周期函数

# Vue生命周期总结
1.初始化阶段
beforeCreate(){ //1.生命周期中第一个函数,该函数在执行时Vue实例仅仅完成了自身事件的绑定和生命周期函数的初始化工作,Vue实例中还没有 Data el methods相关属性
console.log("beforeCreate: "+this.msg);
},
created(){ //2.生命周期中第二个函数,该函数在执行时Vue实例已经初始化了data属性和methods中 相关方法
console.log("created: "+this.msg);
},
beforeMount(){//3.生命周期中第三个函数,该函数在执行时Vue将El中指定作用范围作为模板编译
console.log("beforeMount: "+document.getElementById("sp").innerText);
},
mounted(){//4.生命周期中第四个函数,该函数在执行过程中,已经将数据渲染到界面中并且已经更新页面
console.log("Mounted: "+document.getElementById("sp").innerText);
}
2.运行阶段
beforeUpdate(){//5.生命周期中第五个函数,该函数是data中数据发生变化时执行 这个事件执行时仅仅是Vue实例中data数据变化页面显示的依然是原始数据
console.log("beforeUpdate:"+this.msg);
console.log("beforeUpdate:"+document.getElementById("sp").innerText);
},
updated(){ //6.生命周期中第六个函数,该函数执行时data中数据发生变化,页面中数据也发生了变化 页面中数据已经和data中数据一致
console.log("updated:"+this.msg);
console.log("updated:"+document.getElementById("sp").innerText);
},
3.销毁阶段
beforeDestory(){//7.生命周期第七个函数,该函数执行时,Vue中所有数据 methods componet 都没销毁
},
destoryed(){ //8.生命周期的第八个函数,该函数执行时,Vue实例彻底销毁
}
beforeCreate 在实例初始化之后,数据观测和事件配置之前被调用。
created 在实例创建完成后被立即调用。
beforeMount 在挂载开始之前被调用。
mounted el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
beforeUpdate 数据更新时调用,发生在虚拟DOM打补丁之前。
updated 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
beforeDestroy 实例销毁之前调用。
destroyed 实例销毁后调用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<button @click='update'>更新</button>
<button @click='destroy'>销毁</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: '生命周期'
},
methods: {
update: function(){
this.msg = 'hello';
},
destroy: function(){
this.$destroy();
}
},
beforeCreate: function(){
console.log('beforeCreate');
},
created: function(){
console.log('created');
},
beforeMount: function(){
console.log('beforeMount');
},
mounted: function(){
console.log('mounted');
},
beforeUpdate: function(){
console.log('beforeUpdate');
},
updated: function(){
console.log('updated');
},
beforeDestroy: function(){
console.log('beforeDestroy');
},
destroyed: function(){
console.log('destroyed');
}
});
</script>
</body>
</html>
17 图书管理
17.1 变异方法和替换数组
1. 变异方法(修改原有数据)
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
2. 替换数组(生成新的数组)
filter()
concat()
slice()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<span>
<input type="text" v-model='fname'>
<button @click='add'>添加</button>
<button @click='del'>删除</button>
<button @click='change'>替换</button>
</span>
</div>
<ul>
<li :key='index' v-for='(item,index) in list'>{{item}}</li>
</ul>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
fname: '',
list: ['apple','orange','banana']
},
methods: {
add: function(){
this.list.push(this.fname);
},
del: function(){
this.list.pop();
},
change: function(){
this.list = this.list.slice(0,2);
}
}
});
</script>
</body>
</html>
17.2 动态响应式数据处理
Vue.set(vm.items, indexOfItem, newValue)
vm.$set(vm.items, indexOfItem, newValue)
参数一表示要处理的数组名称
参数二表示要处理的数组的索引
参数三表示要处理的数组的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<ul>
<li v-for='item in list'>{{item}}</li>
</ul>
<div>
<div>{{info.name}}</div>
<div>{{info.age}}</div>
<div>{{info.gender}}</div>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
list: ['apple', 'orange', 'banana'],
info: {
name: 'lisi',
age: 12
}
},
});
vm.$set(vm.list, 1, 'lemon');
vm.$set(vm.info, 'gender', 'female');
</script>
</body>
</html>
17.3 图书管理案例-常用特性应用场景
1. 图书列表
实现静态列表效果
基于数据实现模板效果
处里每行的操作按钮
2.添加图书
实现表单的静态效果
添加图书表单域数据绑定
添加按钮事件绑定
实现添加业务逻辑
3. 修改图书
修改信息填充到表单
修改后重新提交表单
重用添加和修改的方法
4. 删除图书
删除按钮绑定事件处理方法
实现删除业务逻辑
5. 常用特性应用场景
过滤器(格式化日期)
自定义指令(获取表单焦点)
计算属性(统计图书数量)
侦听器(验证图书存在性)
生命周期(图书数据处理)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.grid {
margin: auto;
width: 530px;
text-align: center;
}
.grid table {
border-top: 1px solid #C2D89A;
width: 100%;
border-collapse: collapse;
}
.grid th,td {
padding: 10;
border: 1px dashed #F3DCAB;
height: 35px;
line-height: 35px;
}
.grid th {
background-color: #F3DCAB;
}
.grid .book {
padding-bottom: 10px;
padding-top: 5px;
background-color: #F3DCAB;
}
.grid .total {
height: 30px;
line-height: 30px;
background-color: #F3DCAB;
border-top: 1px solid #C2D89A;
}
</style>
</head>
<body>
<div id="app">
<div class="grid">
<div>
<h1>图书管理</h1>
<div class="book">
<div>
<label for="id">
编号:
</label>
<input type="text" id="id" v-model='id' :disabled="flag" v-focus>
<label for="name">
名称:
</label>
<input type="text" id="name" v-model='name'>
<button @click='handle' :disabled="submitFlag">提交</button>
</div>
</div>
</div>
<div class="total">
<span>图书总数:</span>
<span>{{total}}</span>
</div>
<table>
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key='item.id' v-for='item in books'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date | format('yyyy-MM-dd hh:mm:ss')}}</td>
<td>
<a href="" @click.prevent='toEdit(item.id)'>修改</a>
<span>|</span>
<a href="" @click.prevent='deleteBook(item.id)'>删除</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.directive('focus', {
inserted: function (el) {
el.focus();
}
});
Vue.filter('format', function(value, arg) {
function dateFormat(date, format) {
if (typeof date === "string") {
var mts = date.match(/(\/Date\((\d+)\)\/)/);
if (mts && mts.length >= 3) {
date = parseInt(mts[2]);
}
}
date = new Date(date);
if (!date || date.toUTCString() == "Invalid Date") {
return "";
}
var map = {
"M": date.getMonth() + 1,
"d": date.getDate(),
"h": date.getHours(),
"m": date.getMinutes(),
"s": date.getSeconds(),
"q": Math.floor((date.getMonth() + 3) / 3),
"S": date.getMilliseconds()
};
format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
var v = map[t];
if (v !== undefined) {
if (all.length > 1) {
v = '0' + v;
v = v.substr(v.length - 2);
}
return v;
} else if (t === 'y') {
return (date.getFullYear() + '').substr(4 - all.length);
}
return all;
});
return format;
}
return dateFormat(value, arg);
})
var vm = new Vue({
el: '#app',
data: {
flag: false,
submitFlag: false,
id: '',
name: '',
books: []
},
methods: {
handle: function(){
if(this.flag) {
this.books.some((item) => {
if(item.id == this.id) {
item.name = this.name;
return true;
}
});
this.flag = false;
}else{
var book = {};
book.id = this.id;
book.name = this.name;
book.date = 2525609975000;
this.books.push(book);
this.id = '';
this.name = '';
}
this.id = '';
this.name = '';
},
toEdit: function(id){
this.flag = true;
console.log(id)
var book = this.books.filter(function(item){
return item.id == id;
});
console.log(book)
this.id = book[0].id;
this.name = book[0].name;
},
deleteBook: function(id){
this.books = this.books.filter(function(item){
return item.id != id;
});
}
},
computed: {
total: function(){
return this.books.length;
}
},
watch: {
name: function(val) {
var flag = this.books.some(function(item){
return item.name == val;
});
if(flag) {
this.submitFlag = true;
}else{
this.submitFlag = false;
}
}
},
mounted: function(){
var data = [{
id: 1,
name: '三国演义',
date: 2525609975000
},{
id: 2,
name: '水浒传',
date: 2525609975000
},{
id: 3,
name: '红楼梦',
date: 2525609975000
},{
id: 4,
name: '西游记',
date: 2525609975000
}];
this.books = data;
}
});
</script>
</body>
</html>
18 Vue中组件(Component)
18.1 组件作用
组件作用: 用来减少Vue实例对象中代码量,日后在使用Vue开发过程中,可以根据 不能业务功能将页面中划分不同的多个组件,然后由多个组件去完成整个页面的布局,便于日后使用Vue进行开发时页面管理,方便开发人员维护。
18.2 组件使用
18.2.1 全局组件注册
说明:全局组件注册给Vue实例,日后可以在任意Vue实例的范围内使用该组件
Vue.component('login',{
template:'<div><h1>用户登录</h1></div>'
});
<login></login>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('button-counter', {
data: function(){
return {
count: 0
}
},
template: '<button @click="handle">点击了{{count}}次</button>',
methods: {
handle: function(){
this.count += 2;
}
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
注意:
1.Vue.component用来开发全局组件 参数1: 组件的名称 参数2: 组件配置{} template:''用来书写组件的html代码 template中必须有且只有一个root元素
2.使用时需要在Vue的作用范围内根据组件名使用全局组件
3.如果在注册组件过程中使用 驼峰命名组件的方式 在使用组件时 必须将驼峰的所有单词小写加入-线进行使用
18.2.2 组件注册注意事项-上
data必须是一个函数
组件模板内容必须是单个跟元素
模板字符串需要浏览器提供支持(ES6语法)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('button-counter', {
data: function(){
return {
count: 0
}
},
template: `
<div>
<button @click="handle">点击了{{count}}次</button>
<button>测试123</button>
</div>
`,
methods: {
handle: function(){
this.count += 2;
}
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
18.2.3 组件注册注意事项-下
组件命名方式
短横线方式
Vue.component('my-component', { /* ... */ })
驼峰方式
Vue.component('MyComponent', { /* ... */ })
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<hello-world></hello-world>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('HelloWorld', {
data: function(){
return {
msg: 'HelloWorld'
}
},
template: '<div>{{msg}}</div>'
});
Vue.component('button-counter', {
data: function(){
return {
count: 0
}
},
template: `
<div>
<button @click="handle">点击了{{count}}次</button>
<button>测试123</button>
<HelloWorld></HelloWorld>
</div>
`,
methods: {
handle: function(){
this.count += 2;
}
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
18.2.4 局部组件注册
说明:通过将组件注册给对应Vue实例中一个components属性来完成组件注册,这种方式不会对Vue实例造成累加
18.2.4.1 第一种开发方式
let login ={
template:'<div><h2>用户登录</h2></div>'
};
const app = new Vue({
el: "#app",
data: {},
methods: {},
components:{
login:login
}
});
<login></login>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<hello-world></hello-world>
<hello-tom></hello-tom>
<hello-jerry></hello-jerry>
<test-com></test-com>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('test-com',{
template: '<div>Test<hello-world></hello-world></div>'
});
var HelloWorld = {
data: function(){
return {
msg: 'HelloWorld'
}
},
template: '<div>{{msg}}</div>'
};
var HelloTom = {
data: function(){
return {
msg: 'HelloTom'
}
},
template: '<div>{{msg}}</div>'
};
var HelloJerry = {
data: function(){
return {
msg: 'HelloJerry'
}
},
template: '<div>{{msg}}</div>'
};
var vm = new Vue({
el: '#app',
data: {
},
components: {
'hello-world': HelloWorld,
'hello-tom': HelloTom,
'hello-jerry': HelloJerry
}
});
</script>
</body>
</html>
18.2.4.2 第二种开发方式
<template id="loginTemplate">
<h1>用户登录</h1>
</template>
let login ={
template:'#loginTemplate'
};
const app = new Vue({
el: "#app",
data: {},
methods: {},
components:{
login:login
}
});
<login></login>
19 组件间数据交互
19.1 父组件向子组件传值-基本使用
1. 组件内部通过props接收传递过来的值
Vue.component(‘menu-item', {
props: ['title'],
template: '<div>{{ title }}</div>'
})
2. 父组件通过属性将值传递给子组件
<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>
作用:props用来给组件传递相应静态数据或者是动态数据的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{pmsg}}</div>
<menu-item title='来自父组件的值'></menu-item>
<menu-item :title='ptitle' content='hello'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('menu-item', {
props: ['title', 'content'],
data: function() {
return {
msg: '子组件本身的数据'
}
},
template: '<div>{{msg + "----" + title + "-----" + content}}</div>'
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
ptitle: '动态绑定属性'
}
});
</script>
</body>
</html>
19.1.1 通过在组件上声明静态数据传递给组件内部
let login = {
template:"<div><h1>欢迎:{{ userName }} 年龄:{{ age }}</h1></div>",
props:['userName','age']
}
const app = new Vue({
el: "#app",
data: {},
methods: {},
components:{
login
}
});
<login user-name="小陈" age="23"></login>
# 总结:
1.使用组件时可以在组件上定义多个属性以及对应数据
2.在组件内部可以使用props数组生命多个定义在组件上的属性名 日后可以在组件中通过{{ 属性名 }} 方式获取组件中属性值
19.1.2 通过在组件上声明动态数据传递给组件内部
const login = {
template:'<div><h2>欢迎: {{ name }} 年龄:{{ age }}</h2></div>',
props:['name','age']
}
const app = new Vue({
el: "#app",
data: {
username:"小陈陈",
age:23
},
methods: {},
components:{
login
}
});
<login :name="username" :age="age"></login>
19.2 props属性名规则
在props中使用驼峰形式,模板中需要使用短横线的形式
字符串形式的模板中没有这个限制
Vue.component(‘menu-item', {
// 在 JavaScript 中是驼峰式的
props: [‘menuTitle'],
template: '<div>{{ menuTitle }}</div>'
})
<!– 在html中是短横线方式的 -->
<menu-item menu-title=“nihao"></menu-item>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{pmsg}}</div>
<menu-item :menu-title='ptitle'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('third-com', {
props: ['testTile'],
template: '<div>{{testTile}}</div>'
});
Vue.component('menu-item', {
props: ['menuTitle'],
template: '<div>{{menuTitle}}<third-com testTile="hello"></third-com></div>'
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
ptitle: '动态绑定属性'
}
});
</script>
</body>
</html>
19.3 父组件向子组件传值-props属性值类型
字符串 String
数值 Number
布尔值 Boolean
数组 Array
对象 Object
:pnum='12'需要这样写,否则类型是string类型而不是number类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{pmsg}}</div>
<menu-item :pstr='pstr' :pnum='12' pboo='true' :parr='parr' :pobj='pobj'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
父组件向子组件传值-props属性值类型
*/
Vue.component('menu-item', {
props: ['pstr','pnum','pboo','parr','pobj'],
template: `
<div>
<div>{{pstr}}</div>
<div>{{12 + pnum}}</div>
<div>{{typeof pboo}}</div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
<span>{{pobj.name}}</span>
<span>{{pobj.age}}</span>
</div>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
pstr: 'hello',
parr: ['apple','orange','banana'],
pobj: {
name: 'lisi',
age: 12
}
}
});
</script>
</body>
</html>
19.4 prop的单向数据流
单向数据流:所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。---摘自官网
19.5 子组件向父组件传值
子组件通过自定义事件向父组件传递信息
<button v-on:click='$emit("enlarge-text", 0.1) '>扩大字体</button>
父组件监听子组件的事件
<menu-item v-on:enlarge-text='fontSize += $event'></menu-item>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
<menu-item :parr='parr' @enlarge-text='handle'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('menu-item', {
props: ['parr'],
template: `
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
<button @click='$emit("enlarge-text")'>扩大父组件中字体大小</button>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple','orange','banana'],
fontSize: 10
},
methods: {
handle: function(){
this.fontSize += 5;
}
}
});
</script>
</body>
</html>
19.6 子组件向父组件传值-携带参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('menu-item', {
props: ['parr'],
template: `
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
<button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>
<button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小</button>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple','orange','banana'],
fontSize: 10
},
methods: {
handle: function(val){
this.fontSize += val;
}
}
});
</script>
</body>
</html>
19.7 非父子组件间传值
1. 单独的事件中心管理组件间的通信
var eventHub = new Vue()
2. 监听事件与销毁事件
eventHub.$on('add-todo', addTodo)
eventHub.$off('add-todo')
3. 触发事件
eventHub.$emit(‘add-todo', id)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>父组件</div>
<div>
<button @click='handle'>销毁事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var hub = new Vue();
Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
hub.$emit('tom-event', 1);
}
},
mounted: function() {
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
</body>
</html>
19.8 组件中定义数据和事件使用
19.8.1 组件中定义属于组件的数据
const login = {
template:'<div><h1>{{ msg }} 教育</h1><ul><li v-for="item,index in lists">{{ index }}{{ item }}</li></ul></div>',
data(){
return {
msg:"hello",
lists:['java','spring','springboot']
}
}
}
19.8.2 组件中事件定义
const login={
template:'<div><input type="button" value="点我触发组件中事件" @click="change"></div>',
data(){
return {
name:'小陈'
};
},
methods:{
change(){
alert(this.name)
alert('触发事件');
}
}
}
# 总结
1.组件中定义事件和直接在Vue中定义事件基本一致 直接在组件内部对应的html代码上加入@事件名=函数名方式即可
2.在组件内部使用methods属性用来定义对应的事件函数即可,事件函数中this 指向的是当前组件的实例
19.8.3 向子组件中传递事件并在子组件中调用该事件
在子组件中调用传递过来的相关事件必须使用 this.$emit('函数名') 方式调用
const login = {
template:"<div><h1>教育 {{ uname }}</h1> <input type='button' value='点我' @click='change'></div>",
data(){
return {
uname:this.name
}
},
props:['name'],
methods:{
change(){
this.$emit('aaa');
}
}
}
const app = new Vue({
el: "#app",
data: {
username:"小陈"
},
methods: {
findAll(){
alert('Vue 实例中定义函数');
}
},
components:{
login,
}
});
<login @find="findAll"></login>
20 插槽
20.1 插槽基本用法
插槽位置
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
插槽内容
<alert-box>Something bad happened.</alert-box>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
20.2 具名插槽用法
1. 插槽定义
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
2. 插槽内容
<base-layout>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</base-layout>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<base-layout>
<p slot='header'>标题信息</p>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot='footer'>底部信息信息</p>
</base-layout>
<base-layout>
<template slot='header'>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
具名插槽
*/
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
20.3 作用域插槽用法
应用场景:父组件对子组件的内容进行加工处理
1. 插槽定义
<ul>
<li v-for= "item in list" v-bind:key= "item.id" >
<slot v-bind:item="item">
{{item.name}}
</slot>
</li>
</ul>
2. 插槽内容
<fruit-list v-bind:list= "list">
<template slot-scope="slotProps">
<strong v-if="slotProps.item.current">
{{ slotProps.item.text }}
</strong>
</template>
</fruit-list>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style type="text/css">
.current {
color: orange;
}
</style>
<body>
<div id="app">
<fruit-list :list='list'>
<template slot-scope='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</fruit-list>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('fruit-list', {
props: ['list'],
template: `
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'orange'
},{
id: 3,
name: 'banana'
}]
}
});
</script>
</body>
</html>
20.4 购物车案例
1. 按照组件化方式实现业务需求
根据业务功能进行组件化划分
标题组件(展示文本)
列表组件(列表展示、商品数量变更、商品删除)
结算组件(计算商品总额)
1. 功能实现步骤
实现整体布局和样式效果
划分独立的功能组件
组合所有的子组件形成整体结构
逐个实现各个组件功能
标题组件
列表组件
结算组件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {
}
.container .cart {
width: 300px;
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration:none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="" @click.prevent='sub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="" @click.prevent='add(item.id)'>+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
changeNum: function(id, event){
this.$emit('change-num', {
id: id,
type: 'change',
num: event.target.value
});
},
sub: function(id){
this.$emit('change-num', {
id: id,
type: 'sub'
});
},
add: function(id){
this.$emit('change-num', {
id: id,
type: 'add'
});
},
del: function(id){
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function() {
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
if(val.type=='change') {
this.list.some(item=>{
if(item.id == val.id) {
item.num = val.num;
return true;
}
});
}else if(val.type=='sub'){
this.list.some(item=>{
if(item.id == val.id) {
item.num -= 1;
return true;
}
});
}else if(val.type=='add'){
this.list.some(item=>{
if(item.id == val.id) {
item.num += 1;
return true;
}
});
}
},
delCart: function(id) {
var index = this.list.findIndex(item=>{
return item.id == id;
});
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>