Vue.js从入门到精通

目录

Node(后端)中的MVC与前端MVVC之间的区别

v-cloak\v-text\v-html\v-bind

跑马灯效果

事件修饰符

model双向数据绑定 

v-model实现计算器的案例

vue中样式Class

vue中样式Style

v-for循环普通数组

v-for循环对象数组

v-for循环对象

v-for迭代数字

v-for循环key属性的使用

品牌案例

品牌案例——完成品牌列表的添加功能

品牌案例——根据id完成品牌的删除

品牌案例——根据关键字实现数组的过滤

过滤器——vue中全局过滤器的基本使用

 过滤器——定义私有过滤器

字符串的padstart方法使用

自定义按ff键修饰符 

指令——自定义全局指令让文本框获取焦点

 指令——使用钩子函数的第二个binding参数拿到传递的值

指令——定义私有指令 

指令——指令函数的简写形式 

生命周期函数——组件创建期间的4个钩子函数 

vue-resource发起get、post、jsonp请求

结合Node手写Jsonp服务器解析 jsonp原理

vue组件化开发

全局组件

局部组件

组件复用

表格作业

父传子

 子传父

弹框案例

弹框案例——组件布局和父传子完成

 弹框案例——点击隐藏弹框

弹框案例——子传父

插槽

   

匿名插槽 

具名插槽

 作用域插槽


Node(后端)中的MVC与前端MVVC之间的区别

MVC是后端的分层开发概念

mvc一般指MVC框架。 经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。

MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。

在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

MVVM模型相关框架(如Vue 、 React、AngularJS、Knockout、Polymer等)

与MVC的关系:

MVC是Model-View- Controller的简写。即模型-视图-控制器。 使开发更高效,结构更清晰,增加代码的复用性。主要就是解决mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。

el:指定要控制的区域

data:是个对象,指定了控制的区域内要用到的数据

methods:虽然带个s后缀,但是是个对象,这里可以自定义方法

如果要访问data上的数据,或者要访问methods中的方法,必须带this

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<!-- 1.导入vue包 -->
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<!-- 将来new的vue实例,会控制这个 元素的所有内容 --> 
		<div id="app">
			<p>{{msg}}</p>
		</div>
	</body>
	<script>
		// 2.创建一个vue的实例
		// 当我们导入包之后,在浏览器的内存中,多了一个vue构造函数
		//注意:我们new出来的这个vm对象,就是我们MVVM中的VM调度者
		var app = new Vue({
			el:"#app",//el 表示 当前我们new的这个vue实例,要控制页面上的哪个区
			//这里的data就是MVVM中的M,专门用来保存,每个页面的数据的
			data:{//data属性中,存放的是el中要用到的数据
				msg:'欢迎学习vue'//通过vue提供的指令,能把数据渲染到页面上,程序员不再手动操作DOM元素
			},
		})
	</script>
</html>

插值表达式

插值表达式  v-cloak  v-text  v-html  v-bind(缩写是:)  v-on(缩写是@)  v-model  v-for  v-if  v-show

v-cloak\v-text\v-html\v-bind

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	<script>
        /* 将带有 v-clock 属性的标签隐藏 */
        [v-clock] {
            display: none;
        }
	</script>
	</head>
	<body>
		<div id="app" >
			<!-- 使用v-cloak能够解决 插值表达式闪烁的问题 -->
			<p v-cloak>{{123}}</p>
			<h4 v-text="msg"></h4>
			<!-- 默认v-text没有闪烁问题 -->
			<!-- v-text会覆盖元素中原本的内容,但是 插值表达式 只会替换自己的这个占位符,不会把整个元素清空 -->
			
			
			<div>{{msg2}}</div>
			<div v-text="msg2"></div>
			<div v-html="msg2">121212</div>
			
			<!-- v-bind 是Vue中提供的用于绑定属性的指令 -->
			<!-- <input type="button" value="按钮" v-bind:title="mytitle+'123'"/> -->
			
			<!-- 注意:v-bind:指令可以被简写为:要绑定的属性 -->
			<!-- v-bind中,可以写合法的JS表达式 -->
			<!-- <input type="button" value="按钮" :title="mytitle+'123'"/ id='btn'> -->
			
			<!-- v-on:事件绑定机制 -->
			<!-- 可以用@代表v-on -->
			<input type="button" value="按钮" :title="mytitle+'123'" @click="show"/>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				msg:'123',
				msg2:'<h1>哈哈</h1>',
				mytitle:'这是一个自己定义的title'
			},
			methods:{//这个methods属性中定义了当前vue实例所有可用方法
				show:function(){
					alert('Hello')
				}
				
			}
		})
		
		// document.getElementById('btn').onclick=function(){
		// 	alert('Hello')
		// }
	</script>
</html>

效果图:

跑马灯效果

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<!-- 创建一个要控制的区域 -->
		<div id="app">
			<h4>{{msg}}</h4>
			<input type="button" value="浪起来" @click="lang"/>
			<input type="button" value="低调" @click="stop"/>
		</div>
	</body>
	<script>
		//注意:在app实例中,如果想要获取data上的数据,或者 想要调用methods中的方法,必须通过this.数据属性名
		// 或 this.方法名来进行访问,这里的this,表示我们New出来的app对象
		var app = new Vue({
			el:"#app",
			data:{
				msg:'猥琐发育,别浪~~!',
				IntervalId:null//在data上定义 定时器Id
			},
			methods:{
				lang(){//lang:function(){}
					// console.log(this.msg)
					
					if(this.IntervalId != null) return;
					
					
					this.IntervalId=setInterval(()=>{//function()
					//获取到头的第一个字符
					var start = this.msg.substring(0,1)//(位置,几个)
					//获取到 后面的所有字符
					var end=this.msg.substring(1)
					//重新拼接得到新的字符串并赋值给this.msg
					this.msg=end + start
					},400)
					//注意,app实例会监听自己身上的data 中所有数据改变,只要数据发生改变,就会自动
					//把最新数据从data上同步到中去;(好处:减少程序员对dom操作,只需关心数据,不需考虑如何渲染
				},
				stop() {//停止定时器
					clearInterval(this.IntervalId)
					//每当清楚了定时器之后,需要重新把intervalId设为null
					this.IntervalId=null;
				}
			}
		})
		
		//分析:
		//1.给[浪起来]按钮绑定一个点击事件 v-on
		//2.在按钮的事件处理函数中,写相关的业务逻辑代码:拿到msg字符串,
		//然后 调用 调用 字符串的subString方法来进行字符串的截取,把第一个字符截取出来放到最后一个
		//3.为了实现点击下按钮 自动截取的功能,需要把2步骤中的代码,放到一个定时器中
	</script>
</html>

效果图:

事件修饰符

.stop 阻止冒泡

.prevent 阻止默认事件

.capture 添加事件监听器使用事件捕获模式

.self 只当事件在该元素本身(比如不是子元素)触发时回调

.once 事件只触发一次

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
		<style>
			.inner{
				height:150px;
				background-color:pink;
				
			}
			.outer{
				padding: 40px;
				background-color: antiquewhite;
			}
		</style>
	</head>
	<body>
		<div id="app">
			
			<!-- .stop 阻止冒泡 
			 <div class="inner" @click="divHandler">
				<input type="button" value="戳他" @click.stop="btnHandler"/>
			</div> 
			console先输出按钮事件 再输出div事件-->
			
			<!-- .prevent 阻止默认事件
			<a href="http://www.baidu.com" @click.prevent="linkClick">有问题去百度</a> -->
			
			<!--.capture 添加事件监听器使用事件捕获模式
 			<div class="inner" @click.capture="divHandler">
					<input type="button" value="戳他" @click.stop="btnHandler"/>
				</div>
			console先输出div 再输出按钮 -->
			
			<!-- .self 只有点击当前元素时候,才会触发事件处理函数
			<div class="inner" @click.self="divHandler">
					<input type="button" value="戳他" @click.stop="btnHandler"/>
			</div> -->
			
			<!-- .once 事件只触发一次
			<a href="http://www.baidu.com" @click.prevent.once="linkClick">有问题去百度</a> -->
			
			<!-- .self只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡行为
			<div class="outer" @click="div2Handler">
				<div class="inner" @click.self="divHandler">
					<input type="button" value="戳他" @click="btnHandler"/>
				</div> 
			</div> -->
			
			
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				
			},
			methods:{
				divHandler(){
					console.log('这是触发了 inner div的事件')
				},
				btnHandler(){
					console.log('这是触发了 btn按钮 的点击事件')
				},
				linkClick(){
					console.log('触发了链接的点击事件')
				},
				div2Handler(){
					console.log('这是触发了 outer div的事件')
				}
			}
		})
	</script>
</html>

效果图:

 

 

model双向数据绑定 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<h4>{{msg}}</h4>
			
			<!-- v-bind 只能实现数据的单项绑定,从m自动绑定到v -->
			<!-- <input type="text" v-bind:value="msg" style="width: 100%;"/> -->
			
			<!-- 使用v-model 指令,可以实现 表单元素和 Model中数据的双向绑定 -->
			<!-- 注意:v-model 只能运用在 表单元素中 -->
			<!-- input(radio,text,address,email....)  select  checkbox  textarea -->
			<input type="text" style="width: 100%;" v-model="msg"/>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				msg:'哈哈哈哈哈哈哈哈哈或或或或或或或或或或',
				
			},
			methods:{
				
			},
			
		})
	</script>
</html>

效果图:

v-model实现计算器的案例

只能运用于表单元素

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<input type="text" v-model="n1"/>
			
			<select v-model="opt">
				<option value="+">+</option>
				<option value="-">-</option>
				<option value="*">*</option>
				<option value="/">/</option>
			</select>	
			<input type="text" v-model="n2"/>
			
			<input type="button" value="=" @click="calc"/>
			
			<input type="text" v-model="result"/>
			
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				n1:0,
				n2:0,
				result:0,
				opt:'+'
			},
			methods:{
				 calc(){//计算器算数的方法
				// 	//逻辑:
				// 	// switch(this.opt){
				// 	// 	case'+':
				// 	// 	this.result=parseInt(this.n1)+parseInt(this.n2)
				// 	// 		break;
				// 	// 	case'-':
				// 	// 	this.result=parseInt(this.n1)-parseInt(this.n2)
				// 	// 		break;
				// 	// 	case'*':
				// 	// 	this.result=parseInt(this.n1)*parseInt(this.n2)
				// 	// 		break;
				// 	// 	case'/':
				// 	// 	this.result=parseInt(this.n1)/parseInt(this.n2)
				// 	// 		break;
				// 	}
				
				//注意:这是投机取巧的方式,正式开发中尽量少用
				 var codeStr='parseInt(this.n1)'+this.opt+'parseInt(this.n2)'
				 this.result=eval(codeStr)
					
				}
			}
			
		})
	</script>
</html>

效果图:

vue中样式Class

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
		<style>
			.red{
				color: red;
			}
			.thin{
				font-weight: 200;
			}
			.italic{
				font-style: italic;/* 斜字体 */
			}
			.active{
				letter-spacing: 0.5em;/* 字间距  只对英文有作用*/
			}
		</style>
	</head>
	<body>
		<div id="app">
			<!-- Class绑定,v-bind:class 或 :class -->
			<!--- 渲染 'red', 'italic' 样式,注意要加上单引号,不然是获取data中的值 --> 
			<!-- 第一种使用方式,直接传递一个数组,注意:这里的class需要用 v-bind做数据绑定 -->
			   <h1 :class="['red', 'italic']">数组表达式</h1>
			   
			<!-- 在数组中使用三元表达式 -->
			   <h1 :class="['red', 'italic',flag?'active':'null']">数组中三元表达式</h1>
			
			<!-- 在数组中使用 对象代替三元表达式,提高代码的可读性 -->
			<!-- 如果flag为true则应用active,否则不应用 -->
			   <h1 :class="['red', 'italic',{'active':flag}]">数组中嵌套对象</h1>
			
			<!-- 在为class使用v-bind 绑定对象的时候,对象的属性是类名,由于对象的属性可引号,也可不带引号,所以 不写引号;
			属性的值 是一个标识符 -->
			   <h1 :class="{red:false,thin:true,italic:true,active:false}">数组中直接对象</h1>
			   <h1 :class="classObj">数组中直接对象</h1>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				flag:true,
				classObj:{red:false,thin:true,italic:true,active:false}
			},
			methods:{}
		})
	</script>
	
</html>

效果图:

vue中样式Style

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- 对象就是无需键值对的集合 -->
			<!-- <h1 :style="styleObj1">这是一个H1</h1> -->
			
			<!-- 在style中通过数组,引用多个data上的样式对象 -->
			<h1 :style="[styleObj1,styleObj2]">这是一个H1</h1>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				styleObj1:{color:'red','font-weight':200},
				styleObj2:{'font-style':'italic'}
			},
			methods:{}
		});
	</script>
</html>

v-for循环普通数组

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
<!-- 		<p>{{list[0]}}</p>
			<p>{{list[1]}}</p>
			<p>{{list[2]}}</p> -->
			<!-- <p v-for="item in list">{{item}}</p> -->
			<p v-for="(item,i) in list">索引值:i---每一项:{{item}}</p>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				list:[1,2,3,4,5,6]
			},
		})
	</script>
</html>

效果图:

v-for循环对象数组

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- <p v-for="user in list">Id:{{user.id}}--------name:{{user.name}}</p> -->
			<p v-for="(user,i) in list">Id:{{user.id}}---name:{{user.name}}---索引号:{{i}}</p>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				list:[
					{id:1,name:'n1'},
					{id:2,name:'n2'},
					{id:3,name:'n3'},
					{id:4,name:'n4'}
				]
			},
		})
	</script>
</html>

v-for循环对象

v-for要会使用key属性(只接受string/number)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- 注意:在遍历对象身上的键值对的时候,除了有 val key 在第三个位置还有一个 索引 -->
			<p v-for="(val,key,i) in user">值是:{{val}}---键是:{{key}}--索引{{i}}</p>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				user:{
					id:1,
					name:'小瑶',
					gender:'女'
				}
			}
		})
	</script>
</html>

效果图:

v-for迭代数字

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- in 后面我们放过 普通数组, 对象数组, 对象, 还可以放数字 -->
			<!-- 注意:如果使用 v-for 迭代数字的话,前面的count值从1开始 -->
			<p v-for="count in 10">这是第{{count}}次循环</p>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{}
		})
	</script>
</html>

 效果图:

v-for循环key属性的使用

key保证数据的唯一性,让data和页面的数据关联起来,不给写key,只管用v-for循环渲染

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			
			<div>
				<label>Id:
					<input type="text" value="id"/>
				</label>
				
				<label>Name:
					<input type="text" value="name"/>
				</label>
				
					<input type="button" value="添加" @click="add"/>
			</div>
			
			<!-- 注意:v-for循环时,key只能使用number获取string -->
			<!-- 注意:key在使用的时候,必须使用v-bind 属性绑定的形式,指定key的值-->
			<!-- 在组件中,使用v-for循环时,或许一些特殊情况中,
			如果v-for有问题,必须在使用v-for的同时指定唯一的字符串或数字 类型:key值 -->
			<p v-for="item in list" :key="item.id">
			<input type="checkbox">{{item.id}}---{{item.name}}
			</p>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				id:'',
				name:'',
				list:[
					{id:1,name:'哈哈'},
					{id:2,name:'呵呵'},
					{id:3,name:'嘿嘿'},
					{id:4,name:'嘻嘻'},
					{id:5,name:'讷讷'}
				]
			},
			methods:{
				add(){//添加方法
					this.list.push({this.id,this.name}])
				}
			}
		})
	</script>
</html>

效果图:

品牌案例

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
		<link rel="stylesheet" href="css/bootstrap.css"/>
	</head>
	<body>
		<div id="app">
			<div class="panel panel-primary">
			<div class="panel-heading">
				<h3 class="panel-title">添加品牌</h3>
				</div>
			<div class="panel-body form-inline">
				<label>
					Id:
					<input type="text" class="form-control" v-model="id"/>
				</label>
				
				<label>
					Name:
					<input type="text" class="form-control" v-model="name"/>
				</label>
				
				<!-- 在vue中,使用事件绑定机制,为元素制定处理函数的时候,如果加了小括号,就可以给函数传参 -->
				<input type="button" value="添加" class="btn-primary" @click="add()"/>
				
				<label>
					搜索名称关键字:
					<input type="text" class="form-control" v-model="keywords"/>
				</label>
			</div>
			</div>
			
			
			
			<table class="table table-bordered table-hover table-striped">
			<thead>
				<tr>
					<th>Id</th>
					<th>Name</th>
					<th>Ctime</th>
					<th>Operation</th>
				</tr>
			</thead>
			<tbody>
				<!-- 之前,v-for中的数据,都是直接从data上的list中直接渲染过来的 -->
				<!-- 现在,自定义一个search方法,同时把所有的关键字,通过传参的形式,传递给search方法 -->
				<!-- 在search方法内部,通过执行for循环,把所有符合搜索关键字的数据,保存到一个新数组中,返回 -->
				<tr v-for="item in search(keywords)" :key="item.id">
					<td>{{item.id}}</td>
					<!-- <td v-text="{{item.name}}"></td> -->
					<td>{{item.name}}</td>
					<td>{{item.ctime}}</td>
					<td>
						<a href="" @click.prevent='del(index)'>删除</a>
					</tr>
				</tr>
			</tbody>
			</table>
		</div>
	</body>
	<script>
		var app = new Vue({
			el:"#app",
			data:{
				 id:'',
				 name:'',
				 keywords:'',
				 list:[
				 	 {id:1,name:'奔驰',ctime:new Date()},
				 	 {id:2,name:'宝马',ctime:new Date()}
				 ]
			},
			methods:{
				add(){//添加方法
					// console.log()
					// 分析:
					// 1.获取id和name,直接从data上面获取
					// 2.组织出一个对象
					// 3.把这个对象,调用数组的 相关方法 ,添加到当前data上的list中
					// 4.在vue中,已经实现了数据的双向绑定,每当我们修改了data中的数据,vue会默认监听这个数据的改动,
					// 自动把最新的数据,应用到页面上
					
					// 5.当我们意识到上面的第四步的时候,就证明大家已经入门vue了,我们更多是在进行 app中 
					// Model数据的操作,同时,在操作model数据的时候,指定的业务逻辑操作
					var car={id:this.id,name:this.name,ctime:new Date()}
					this.list.push(car)
					this.id=this.name=' '
				},
				del(id){//根据id删除数据
					//分析:
					// 1.如何根据id找到要删除这一项的索引
					// 2.如果找到了,直接调用数组的splice方法
					
					/*this.list.some((item,i)=>{
						if(item.id==id){
							this.list.splice(i,1)
							//在数组的some方法中,如果return ture 就会立即终止这个数组的后序循环
							return true;
						}
					})*/
					
					var index=this.list.findIndex(item=>{
						if(item.id==id){
							return true;
						}
					})
					// console.log(index)
					this.list.splice(index,1)
				} ,
				/*del(index) {
					// 对话框
					var bool = confirm('确定是否删除?')
					// console.log(bool);
					if (bool) {
						this.list.splice(index, 1);
						// 重新赋值本地存储的list变量
						localStorage.setItem('list', JSON.stringify(this.list));
					}
					}*/
				search(keywords){//根据关键字,进行数据的搜索
				
				/*forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
				
				注意: forEach() 对于空数组是不会执行回调函数的。
				var newList=[]
					this.list.forEach(item=>{
						if(item.name.indexOf(keywords)!=-1){//不等于-1意思就是包含
							newList.push(item)
						}
					})
					return newList*/
				
				/*注意: forEach(无法终止) some(通过true可以终止) filter(进行过滤,把查到的返回一个新数组) 
				findIndex(找到对应对象索引) 这些都属于数组的新方法
				都会对数组中的每一项进行遍历,执行相关的操作*/
				var newList=this.list.filter(item=>{
					// if(item.name.indexOf(keywords)!=-1)
					
					//注意,es6中,为字符串提供了一个新方法,叫做string.prototype.includes('要包含的字符串')
					//如果包含,则返回true,否则返回false
					//contain
					if(item.name.includes(keywords)){
						return item
					}
				})
				return newList
				}
			}
		})
		
		//过滤器的定义语法
		// Vue.filter('过滤器的名称',function(){})
		
		//过滤器中的function,第一个参数已经被规定死了,永远都是 过滤器 管道符前面 传递过来的数据
		Vue.filter('过滤器的名称',function(data){
			return data+'123'
		})
	</script>
</html>

<!-- |:管道符,管道符前的值(name)要交给过滤器处理 -->
<!-- 过滤器调用时候的格式   {{name |过滤器的名称}}  --> 

效果图:

品牌案例——完成品牌列表的添加功能

add(){//添加方法
					// console.log()
					// 分析:
					// 1.获取id和name,直接从data上面获取
					// 2.组织出一个对象
					// 3.把这个对象,调用数组的 相关方法 ,添加到当前data上的list中
					// 4.在vue中,已经实现了数据的双向绑定,每当我们修改了data中的数据,vue会默认监听这个数据的改动,
					// 自动把最新的数据,应用到页面上
					
					// 5.当我们意识到上面的第四步的时候,就证明大家已经入门vue了,我们更多是在进行 app中 
					// Model数据的操作,同时,在操作model数据的时候,指定的业务逻辑操作
					var car={id:this.id,name:this.name,ctime:new Date()}
					this.list.push(car)
					this.id=this.name=' '
				}

效果图:

品牌案例——根据id完成品牌的删除

del(id){//根据id删除数据
					//分析:
					// 1.如何根据id找到要删除这一项的索引
					// 2.如果找到了,直接调用数组的splice方法
					
					/*this.list.some((item,i)=>{
						if(item.id==id){
							this.list.splice(i,1)
							//在数组的some方法中,如果return ture 就会立即终止这个数组的后序循环
							return true;
						}
					})*/
					
					var index=this.list.findIndex(item=>{
						if(item.id==id){
							return true;
						}
					})
					// console.log(index)
					this.list.splice(index,1)
				} ,
				/*del(index) {
					// 对话框
					var bool = confirm('确定是否删除?')
					// console.log(bool);
					if (bool) {
						this.list.splice(index, 1);
						// 重新赋值本地存储的list变量
						localStorage.setItem('list', JSON.stringify(this.list));
					}
					}*/

品牌案例——根据关键字实现数组的过滤

search(keywords){//根据关键字,进行数据的搜索
				
				/*forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
				
				注意: forEach() 对于空数组是不会执行回调函数的。
				var newList=[]
					this.list.forEach(item=>{
						if(item.name.indexOf(keywords)!=-1){//不等于-1意思就是包含
							newList.push(item)
						}
					})
					return newList*/
				
				/*注意: forEach(无法终止) some(通过true可以终止) filter(进行过滤,把查到的返回一个新数组) 
				findIndex(找到对应对象索引) 这些都属于数组的新方法
				都会对数组中的每一项进行遍历,执行相关的操作*/
				var newList=this.list.filter(item=>{
					// if(item.name.indexOf(keywords)!=-1)
					
					//注意,es6中,为字符串提供了一个新方法,叫做string.prototype.includes('要包含的字符串')
					//如果包含,则返回true,否则返回false
					//contain
					if(item.name.includes(keywords)){
						return item
					}
				})
				return newList
				}

效果图:

过滤器——vue中全局过滤器的基本使用

过滤器:

vue.js允许自己定义过滤器,可作一些常见的文本格式化。

过滤器可以用在两个地方:花括号插值和v-bind表达式。

过滤器应该被添加在javascript表达式的尾部,由“管道”符指示

说明:结构是{ 值 | js表达式 },值是通过js表达式处理过后的值

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/v2.6.10/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<p>{{msg | msgFormat('疯狂','123') | test}}</p>
		</div>
	</body>
	<script>
		
		//定义一个vue全局的过滤器,名字叫做msgFormat
		Vue.filter('msgFormat',function(msg,arg,arg2){
			//字符串的 replace方法,第一个参数,除了可写一个 字符串之外,还可以定义一个正则
			return msg.replace(/嗯嗯/g,arg+arg2)
			// return msg.replace(/嗯嗯/g,'喔喔')
		})
		
		Vue.filter('test',function(msg){
			return msg+'============'
		})
		
		var app = new Vue({
			el:"#app",
			data:{
				msg:'哈哈哈哈哈嗯嗯哈哈哈哈或或或,嗯嗯或或或或或或或嗯嗯'
			}
		})
	</script>
</html>

效果图:

替换后效果                                                                                                                                         

<td>{{item.ctime | dateFormat('')}}</td>
//全局的过滤器,进行时间的格式化
//所谓的全局过滤器,就是所有的app实例都共享的
Vue.filter('dateFormat',function(dateStr,pattern=""){
			//根据给定的时间字符串,得到特定的时间
			var dt=new Date(dateStr)
			
			//yyyy-mm-dd
			var y=dt.getFullYear()
			var m=dt.getMonth()+1
			var d=dt.getDate()//getDay 星期几
			
			// return y+'-'+m+'-'+d
			// return `${y}-${m}-${d}`
			
			if(pattern.toLowerCase()==='yyyy-mm-dd'){
				return `${y}-${m}-${d}`
			}else{
				var hh=dt.getHours()
				var mm=dt.getMinutes()
				var ss=dt.getSeconds()
				
				return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
			}
		})

效果图:

 过滤器——定义私有过滤器

		<div id="app2">
			<h3>{{  dt | dateFormat }}</h3>
		</div>
		var app2= new Vue({
			el:"#app2",
			data:{
				dt:new Date()
			},
			methods:{},
			filters:{//定义私有过滤器  过滤器有两个条件  【过滤器名称和处理函数】
			//过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致,这时候优先调用私有过滤器
				dateFormat:function(dateStr,pattern=''){
					//根据给定的时间字符串,得到特定的时间
					var dt=new Date(dateStr)
					
					//yyyy-mm-dd
					var y=dt.getFullYear()
					var m=dt.getMonth()+1
					var d=dt.getDate()//getDay 星期几
					
					// return y+'-'+m+'-'+d
					// return `${y}-${m}-${d}`
					
					if(pattern.toLowerCase()==='yyyy-mm-dd'){
						return `${y}-${m}-${d}`
					}else{
						var hh=dt.getHours()
						var mm=dt.getMinutes()
						var ss=dt.getSeconds()
						
						return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~~~`
				}
			}
			}
		})

效果图:

字符串的padstart方法使用

.toString().padStart(2,'0')

toString:先转化为字符串

padStart:处理时间格式补零。一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。

padStart() 用于头部补全,padEnd() 用 于尾部补全。

var d=dt.getDate().toString().padStart(2,'0')//getDay 星期几
filters:{//定义私有过滤器  过滤器有两个条件  【过滤器名称和处理函数】
			//过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致,这时候优先调用私有过滤器
				dateFormat:function(dateStr,pattern=''){
					//根据给定的时间字符串,得到特定的时间
					var dt=new Date(dateStr)
					
					//yyyy-mm-dd
					var y=dt.getFullYear()
					var m=(dt.getMonth()+1).toString().padStart(2,'0')//填充两位,以0为填充
					var d=dt.getDate().toString().padStart(2,'0')//getDay 星期几
					
					// return y+'-'+m+'-'+d
					// return `${y}-${m}-${d}`
					
					if(pattern.toLowerCase()==='yyyy-mm-dd'){
						return `${y}-${m}-${d}`
					}else{
						var hh=dt.getHours().toString().padStart(2,'0')
						var mm=dt.getMinutes().toString().padStart(2,'0')
						var ss=dt.getSeconds().toString().padStart(2,'0')
						
						return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~~~`
				}
			}
			}

效果图:

自定义按ff键修饰符 

<label>
Name:
<!-- <input type="text" class="form-control" v-model="name" @keyup.enter="add"/> -->
<input type="text" class="form-control" v-model="name" @keyup.f2="add"/>//F2
</label>
//自定义全局修饰符
		Vue.config.keyCodes.f2=113

效果:按f2可以直接添加

指令——自定义全局指令让文本框获取焦点

//使用vue.directive()定义全局的指令
//其中:参数1:指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,
//在调用的时候,必须 在指令前 加上 v- 前缀进行调用
//参数2,是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作

Vue.directive('focus',{
	bind:function(el){//每当绑定到元素上的时候,会立即函数,执行这个bind,只执行一次
	//注意:在每个 函数中,第一个参数,永远是el,表示被绑定了指令的那个元素,这个el参数,是一个原生的js对象
	//在元素 刚绑定了指令的时候,还没有插入到dom中去,这时候,调用 focus方法没有作用
	//因为,一个元素,只有插入dom之后,才能获取焦点
	// el.focus()
	},//绑定时
	inserted:function(el){//inserted 表示元素 插入到DOM中的时候,会执行inserted 函数
		el.focus()
	},//插入时
	updated:function(el){//当VNode更新的时候,会执行updated,可能会触发多次
				
	}
})

 效果图:

 指令——使用钩子函数的第二个binding参数拿到传递的值

value:把它计算

expression:原样输出

 直接在这改颜色 v-color=" ' ' "

<label>
搜索名称关键字:
<!-- 注意:Vue中所有的指令,在调用的时候,都以v-开头 -->
<input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color="'yellow'"/>
</label>
//自定义一个 设置字体颜色的指令
Vue.directive('color',{

//样式,只要通过指令绑定了元素,不管这个元素有没有插入页面中,这个元素肯定有了一个内联的样式
//将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
			
       bind:function(el,binding){
			el.style.color='red'
			//和样式相关的操作,一般都可以在bind执行
			//console.log(binding.name)
				
			// console.log(binding.expression)
			// console.log(binding.value)
				
			el.style.color=binding.value
				
			}
		})

效果图:

指令——定义私有指令 

<div id="app2">
		<h3 v-color="'green'" v-fontweight="900">{{  dt | dateFormat }}</h3>
</div>
var app2= new Vue({
			el:"#app2",
			data:{
				dt:new Date()
			},
			methods:{},
			filters:{{//定义私有过滤器
            ...
},
            directives:{//自定义私有指令
			'fontweight':{
				bind:function(el,binding){
					el.style.fontWeight=binding.value
				}
			}

效果图:

指令——指令函数的简写形式 

<div id="app2">
	<h3 v-color="'green'" v-fontweight="900" v-fontsize="'50px'">{{  dt | dateFormat }}</h3>
</div>
var app2= new Vue({
			el:"#app2",
			data:{
				dt:new Date()
			},
			methods:{},
			filters:{{//定义私有过滤器
            ...
},
            directives:{//自定义私有指令
			'fontweight':{
				...
			}
			'fontsize':function(el,binding){//注意,这个function等同于把代码写到了bind和update中去
				el.style.fontSize=parseInt(binding.value)+'px'
			}

效果图:

生命周期函数——组件创建期间的4个钩子函数 

官方解释:每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做**生命周期钩子**的函数,这给了用户在不同阶段添加自己的代码的机会。

主要的生命周期函数: 

一、创建期间的生命周期函数

(1)beforeCreate

(2)Create

(3)beforeMount

(4)Mount

运行期间的生命周期函数

(1)beforeUpdate

(2)updated

销毁期间的声明周期函数

(1)beforeDestroy

(2)destroy

在这里插入图片描述

 (1)beforeCreate:

beforeCreate(){
		//这是我们遇到的第一个生命周期函数,表示实例被完全创建出来之前,会执行它
		 console.log(this.msg)
		 this.show()
		// 注意:在beforeCreate生命周期函数执行时,data和methods还未初始化
			},

(2)created()

created(){
		//这是遇到的第二个生命周期
		//在created中,data和 methods都已经被初始化好了
		//如果调用methods中的方法,或者操作data中的数据,最早只能在created中操作
		console.log(this.msg)
		this.show()
		}

效果图:

 (3)beforeMount()

beforeMount(){
        //这是遇到的第三个生命周期,表示 模板已经在内存中编辑完成了,但尚未把模板渲染到 页面中
	    console.log(document.getElementById('h3').innerText)
	    //在beforeMount执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
			},

(4)Mount()

mounted(){
		//这是遇到的第四个生命周期,表示,内存中的模板,已经真实的挂载到了页面中
		//用户已经可以看到渲染好的页面了
		console.log(document.getElementById('h3').innerText)
		//注意:mounted是实例创建期间的最后一个生命函数,当执行完mounted,实际已经
		//被完全创建好了,此时,如果没有其他操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
				
			}

运行期间的生命周期函数

(1)beforeUpdate()

//接下来是运行中的两个事件
beforeUpdate(){
		//这时候,表示,我们的界面还没有被更新【数据被更新了吗?数据肯定被更新了】
		console.log('界面上元素的内容:'+document.getElementById('h3').innerText)
		console.log('data中的msg数据是:'+this.msg)
		//得出结论:当执行beforeUpdate时,页面中显示的数据还是旧的,此时data数据是新的,页面尚未和 最新的数据保持同步
			}

(2)updated()

updated(){
		console.log('界面上元素的内容:'+document.getElementById('h3').innerText)
		console.log('data中的msg数据是:'+this.msg)
		//updated事件执行的时候,页面和data数据已经保持同步了,都是最新的
			}

销毁期间的声明周期函数

(1)beforeDestroy

(2)destroy

vue-resource发起get、post、jsonp请求

vue-resource是一个通过XMLHttpRequrest或JSONP技术实现异步加载服务端数据的Vue插件,提供了一般的 HTTP请求接口和RESTful架构请求接口,并且提供了全局方法和Vue组件实例方法。

vue-resource的安装及使用
安装
github地址:GitHub - vue-resource: The HTTP client for Vue.js

在线cdn地址:https://cdn.jsdelivr.net/npm/vue-resource@1.5.3

注: 要使用vue-resource的时候直接在代码中引用就可以了;一是可以去上面的github链接中下载并引用,二是直接通过cdn进行在线的引用。

使用
在vue-resource中,使用ajax请求一般用 this.$http.get   ,通过  .then  来拿到返回的数据,包括一个成功的属性和一个失败的属性,这里需要注意的是后者是可选的。

get发起请求

<body>
    <div id="app">
        <input type="button" value="get请求" @click="getInfo">
    </div>
 
    <!--导入vue.js-->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!--vue-resource依赖于vue,导包的时候要注意先后顺序-->
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.3"></script>
    <script>
        var vm = new Vue({
            el: "#app",
            data: {},
            methods: {
                getInfo() {
                    // 当发起get请求之后,通过.then来设置成功的回调函数                                
                        this.$http.get('http://vue.studyit.io/api/getlunbo').then(function(result){
                        console.log(result);
                    });
                }
            }
        });
    </script>
</body>

post发起请求

 postInfo() {//发起post请求   application/x-www-form-urlencoded
           //手动发起的post请求默认没有表单格式,所以有的服务器处理不了
           //通过post方法的第三个参数,{ emulateJSON: true }设置提交的内容类型为普通表单数据格式
           this.$http.post('http://vue.studyit.io/api/post', {}, { emulateJSON: true }).then(result => {
           console.log(result.body);
         });
         }

 jsonp发起请求

jsonpInfo() {
       this.$http.get('http://vue.studyit.io/api/jsonp').then(result => {
       console.log(result.body);
                    });
                }

结合Node手写Jsonp服务器解析 jsonp原理

什么是jsonp

同源策略 

浏览器同源策略的限制,XmlHttpRequest只允许请求当前源(相同域名、协议、端口)的资源。

-1)jsonp只支持get请求

-2)不受同源策略限制 ,兼容性好 不需要XMLHttpRequest(ActiveX)支持,通过js回调函数返回结果

-3)不能解决 不同域的两个页面之间js调用的问题

jsonp原理

动态添加一个script标签,get链接上发送回调函数名称 传给服务器 服务器获取回调函数名称 返回这个函数名称的JS函数(拼装函数) json数据作为函数的实际参数传入 返回的数据是js函数  在前端调用回调函数( json数据传入) 执行回调函数

vue组件化开发

组件化思想

页面拆分成一个个小的,可复用的组件
让代码更加方便组织和管理

组件化思想图片

全局组件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		
		<div id="app">
				<head-nav></head-nav>
		</div>
		
		<template id="tmp1">
			<div>
				<h1>全局组件</h1>
				<p>这是p标签</p>
			</div>
		</template>
	</body>
	<script>
		// 注意:定义组件时,组件名称可以是驼峰式,或者带-命名方式
		// 调用组件的时候,组件名称只能是带-方式
		// 组件只能绑定了vue对象的标签内去使用
		//组件只能有一个根标签
		Vue.component('headNav', {
			//component组件
			//template模板
			template:'#tmp1' 
			// '<div><h1>全局组件</h1><p>这是p标签</p></div>'
			// <div>
			// 	<h1>全局组件</h1>
			// 	<p>这是P标签</p>
			// </div>
			// '
			// template: '#com'

		})
		
		
		var app = new Vue({
			el: "#app",
			data: {},

		})
		
		var app2 = new Vue({
			el: "#app2",
			data: {},
		
		})
	</script>
	
</html>

局部组件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		
		<div id="app">
				<head-nav></head-nav>
		</div>
		
		<template id="tmp1">
			<div>
				<h1>全局组件</h1>
				<p>这是p标签</p>
			</div>
		</template>
	</body>
	<script>
	
		var app = new Vue({
			el: "#app",
			data: {},
			components:{
				"headNav":{
					template:'#tmp1'
				}
			}
		})
	
	</script>
	
</html>

组件复用

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- 2、给属性赋值 -->
				<my-header title="分类"></my-header>
				
				<my-header title="购物车"></my-header>
		</div>
		
		<template id="tmp1">
			<div>
				<!-- 3、在组件中用胡子语法使用这个属性 -->
				<h1>{{title}}</h1>
				<p>这是内容</p>
			</div>
		</template>
	</body>
	<script>
		var app = new Vue({
			el: "#app",
			data: {
				
			},
			components:{
				myHeader:{
					template:'#tmp1',
					//1、声明myHeader这个组件的标签属性
					props:["title"]
				}
			}
		})
	</script>
</html>

同一个组件可以用不同的文字来区分

效果图:

 复用(2)

<div id="app">
			<!-- 2、给属性赋值 -->
				<my-header :title="title1"></my-header>
				
				<my-header :title="title2"></my-header>
		</div>
var app = new Vue({
			el: "#app",
			data: {
				title1:"分类1",
				title2:"购物车2"
			},

表格作业

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
		<style>
			table,th,td{
				border: 1px solid #000;
			}
			table{
				border-collapse: collapse;
			}
			th,td{
				min-width: 100px;
				line-height: 50px;
				text-align: center;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<table>
				<tr>
					<th v-for="i in titles">{{i}}</th>
				</tr>
				<tr v-for="item in arr" :key="item">
					<td>{{item.name}}</td>
					<td>{{item.math}}</td>
					<td>{{item.chinese}}</td>
					<td>{{item.english}}</td>
					<td>{{item.math+item.chinese+item.english}}</td>
				</tr>
				<tr>
					<td>平均分</td>
					<td>{{average("math")}}</td>
					<td>{{average("chinese")}}</td>
					<td>{{average("english")}}</td>
					<td>{{average("math")+average("chinese")+average("english")}}</td>
				</tr>
			</table>
			
		</div>
	</body>
	<script>
		var app = new Vue({
			el: "#app",
			data: {
				titles:["姓名","数学","语文","英语","总分"],
				arr:[
					{name:'赵云',math:97,chinese:89,english:67},
					{name:'马超',math:97,chinese:89,english:67},
					{name:'关羽',math:97,chinese:89,english:67},
					{name:'黄忠',math:97,chinese:89,english:67},
					{name:'花木',math:97,chinese:89,english:67},
				]
			},
			computed:{
				average(){
					
					
					return function(subj){
						let sum=this.arr.reduce((pre,current)=>{
							return pre+current[subj]
						},0)
						
						return sum/this.arr.length
					}
				}
			}
		})
	</script>
</html>

效果图:

父传子

先定义后赋值

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- 2、赋值属性 
                app的数据可以直接在里面使用
                {{parentTitle}}与:title="parentTitle"效果一样-->
			<child-comp :title="parentTitle" :cont="parentContent" :vue-obj="parentObj"></child-comp>
		</div>
		
		<template id="tmp1">
			<div>属性
				<!-- 3、在子组件中使用这个 -->
				<h1>{{title}}标题</h1>
				<p>{{cont}}</p>
				<p>{{vueObj.name}}的年龄是{{vueObj.age}}</p>
			</div>
		</template>
		
	</body>
	<script>
		// 父传子的步骤总结
		let childComp={
			template:"#tmp1",
			// 1、声明属性
			props:['title','cont','vueObj']
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				
				parentTitle:'父组件的title',
				parentContent:'父组件的content',
				parentObj:{
					name:"Vue",
					age:6
				}
			},
			components:{
				childComp
			}
		})
	</script>
</html>

效果图:

子传父

第一步:声明属性
第二步:自定义事件
第三步:触发自定义事件,让函数执行

---------------------------------------------------------------------

单项数据流跟双向数据流没关系,这样的角度只是数据的流向而已,也就是说根据父传子,可以顺利往下传,子传父不提供这个方法,单项数据流只支持从父组件传到子组件。

为什么要这样设置呢?是关于安全性问题,这样谁都能修改父组件,要修改父组件数据,如要动用父亲的财产,父亲可以给一个银行卡,从银行卡动用,就如同可以提供一个方法给你修改,直接在methods中用changeNum()

使用这个组件的时候,可以传方法,@fn相当于一个事件,这个叫自定义事件,事件名是fn,事件要触发的是changNum

由于changNum在app上面,可以直接调用

修改不了num,可以触发事件
this.$emit()是专门来触发自定义事件的,触发了就能调用,调用就能+1————this.$emit(事件名)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		<!-- 本质不是子修改父的数据,而是子的方法可以调用父的方式,帮父修改了 -->
		<div id="app">
			<!--2、 @fn 自定义事件  @事件名=事件被触发时要执行的函数-->
			<child-comp :num="parentNum" @fn="changeNum"></child-comp>
			
		</div>
		
		<template id="tmp1">
			<div>
				<p>{{num}}</p>
				<button @click="add()">按钮</button>
			</div>
		</template>
		
	</body>
	<script>
		//单项数据流(一般只支持数据从父组件传递到子组件)
		let childComp={
			template:"#tmp1",
			props:["num"],
			methods:{
				add(){
					//修改父组件中的parentNum
					//触发自定义事件fn
					// $emit()专门用来触发自定义事件,调用对应函数
					// this.$emit(事件名,参数1,参数2...)
					// 3、触发自定义事件,让changeNum这个函数来执行
					this.$emit("fn")
				}
			}
}
		
		var app = new Vue({
			el: "#app",
			data: {
				parentNum:20
			},
			components:{
				childComp
			},
			methods:{
				//1、在父组件中准备一个修改这个数据的方法
				changeNum(){
					this.parentNum +=1
				}
			}
		})
	</script>
</html>

效果图:

弹框案例

弹框案例——组件布局和父传子完成

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	<style>
		*{
			margin: 0;
			padding: 0;
		}
		html,body,#app{
			width: 100%;
			height: 100%;
		}
		.wrapper{
			display: flex;
			justify-content: center;
			align-items: center;
			width: 100%;
			height: 100%;
			background-color: gray
			position: fixed;
			top:0;
			left:0;
		}
		.content{
			background-color:lemonchiffon;
			width: 250px;
			height: 160px;
		}
		.title{
			height: 40px;
			line-height: 40px;
			text-align: center;
		}
		.msg{
			display: flex;
			align-items: center;
			justify-content: center;
			box-sizing: border-box;
			padding: 5px 10px;
			border-top: 1px solid #eee;
			border-bottom: 1px solid #eee;
			height: 80px;
			text-align: center;
			color: gray;
			font-size: 14px;
		}
		.bottom{
			display: flex;
			height: 40px;
			line-height: 40px;
			text-align: center;
		}
		.bottom div{
			flex:1;
			color: green;
		}
		.bottom div:nth-of-type(1){
			border-right: 1px solid #eee;
			color: red;
		}
		
	</style>
	</head>
	<body>
		<div id="app">
			<p>点击结果:{{clickResult}}</p>
			
			<wrapper :text-obj="textObj"></wrapper>

		</div>
		
	
	<template id="tmp1">
			<div class="wrapper">
				<div class="content">
				<p class="title">{{textObj.title}}</p>
				<div class="msg">{{textObj.msg}}</div>
				<div class="bottom">
				<div>{{textObj.cancelTxt}}</div>
				<div>{{textObj.submitTxt}}</div>
			</div>

	</template>
	</body>
	<script>
		let wrapper={
			template:"#tmp1",
			props:["textObj"]
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				textObj:{
					title:'bug提示',
					msg:'亲,你还有38478个Bug,是否要处理?',
					cancelTxt:'忽略,下班',
					submitTxt:'加班处理'
				},
				clickResult:"",
				},
				components:{
					wrapper
				}
		})
	</script>
</html>

效果图:

 弹框案例——点击隐藏弹框

<div @click="handleClick">{{textObj.cancelTxt}}</div>
<div @click="handleClick">{{textObj.submitTxt}}</div>
		let wrapper={
			template:"#tmp1",
			data(){
				return{
					isShow:true
				}
			},
			props:["textObj"],
			methods:{
				handleClick(){
					this.isShow=false
				}
			}
		}

弹框案例——子传父

<wrapper :text-obj="textObj" @fn="changeResult"></wrapper>

...
<div @click="handleClick(textObj.cancelTxt)">{{textObj.cancelTxt}}</div>
<div @click="handleClick(textObj.submitTxt)">{{textObj.submitTxt}}</div>

	<script>
		let wrapper={
			template:"#tmp1",
			data(){
				return{
					isShow:true
				}
			},
			props:["textObj"],
			methods:{
				handleClick(val){
					this.isShow=false
					this.$emit("fn",val)
				}
			}
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				textObj:{
					title:'bug提示',
					msg:'亲,你还有38478个Bug,是否要处理?',
					cancelTxt:'忽略,下班',
					submitTxt:'加班处理'
				},
				clickResult:"",
				},
				components:{
					wrapper
				},
				methods:{
					changeResult(val){
						this.clickResult = val
					}
				}
		})

效果图:

插槽

1. 为什么使用slot

> slot翻译为插槽,组件的插槽:
>
> 1. 组件的插槽也是为了让我们封装的组件更加具有扩展性。
> 2. 让使用者可以决定组件内容的一些内容到底展示什么。

2. 如何在组件中使用slot呢?

> 如何去封装这类的组件呢?
>
> 1. 它们也很多区别,但是也有很多共性。
> 2. 如果,我们每一个单独去封装一个组件,显然不合适:比如每个页面都返回,这部分内容我们就要重复去封装。
> 3. 但是,如果我们封装成一个,好像也不合理:有些左侧是菜单,有些是返回,有些中间是搜索,有些是文字,等等
>
> 如何封装合适呢?**抽取共性,保留不同**
>
> 1. 最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
> 2. 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
> 3. 是搜索框,还是文字,还是菜单。由调用者自己来决定。

3、插槽的基本使用

在子组件中,使用特殊的元素`<slot>`就可以为子组件开启一个插槽。

<template id="template1">
    <div>
      <slot>我是插槽中的默认内容!!</slot>
    </div>
</template>

匿名插槽 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		<!-- 1、组件标签内部书写html标签的时候,默认不渲染 
		2、需要把html这些标签展示在组件内部,就需要用到slot标签来接受
		3、slot标签 的出现的位置,就是将来这些html标签所在的位置
		4、由于没有起名字name属性,则称为匿名插槽-->
		<div id="app">
			<comp>
				<a herf="#">这是a链接标签</a>
				<button>按钮</button>
			</comp>
		</div>
		
		<template id="tmp1">
			<div>
				<slot></slot>
				<h1>这是标题标签</h1>
			</div>
		</template>
	</body>
	<script>
		let comp={
			template:"#tmp1"
		}
		
		var app = new Vue({
			el: "#app",
			data: {},
			components:{
				comp
			}
		})
	</script>
</html>

具名插槽

插槽加上name称为具名插槽

> 当子组件的功能复杂时,子组件的插槽可能并非是一个。
>
> 1. 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
> 2. 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
> 3. 这个时候,我们就需要给插槽起一个名字
>
> 如何使用具名插槽呢?
>
> 1. 非常简单,只要给slot元素一个name属性即可
> 2. `<slot name='myslot'></slot>`

		<div id="app">
			<comp>
				
				<a herf="#" slot="link">这是a链接标签</a>
				<button slot="btn">按钮</button>
			</comp>
		</div>
		
		<template id="tmp1">
			<div>
				<slot name="link"></slot>
				<h1>这是标题标签</h1>
				<slot name="btn"></slot>
			</div>
		</template>

效果图:

作用域插槽


// 有name的属于具名插槽,没有name属于匿名插槽
<app>
  <div slot="a">xxxx</div>
  <div slot="b">xxxx</div>
</app>
<slot name="a"></slot>
<slot name="b"></slot>
 
普通插槽渲染的位置是在它的父组件里面,而不是在子组件里面
作用域插槽渲染是在子组件里面

> 默认情况下,父组件使用子组件,插槽数据默认是拿父组件的数据,而不是子组件拿数据。

> **作用域插槽**在父组件使用我们的子组件时, 插槽的数据从子组件中拿到数据,而不是从父组件拿到。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		
		<!-- 组件标签comp内部想要去使用data数据时,不能直接使用,原因是不在作用域范围内
		
		1、把组件中的data中的数据传到对应的slot标签属性  :mynum="num",作为属性的接收
		2、在slot所对应的标签上添加slot-scope属性,值是一个对象(名字自己起),这个对象接收了它所对应的slot标签的标签属性
		3、在这个标签上使用插值语法{{obj.mynum}} 就能使用到子组件中的data数据-->
		
		<div id="app">
			<comp>
				<button slot="btn" slot-scope="obj">按钮{{obj.mynum}}、{{obj.tit}}</button>
			</comp>
		</div>
		
		<template id="tmp1">
			<div>
				
				<h1>这是标题标签</h1>
				<slot name="btn" :mynum="num" tit="哈哈"></slot>
				
			</div>
		</template>
		
	</body>
	<script>
		let comp={
			template:"#tmp1",
			data(){
				return{
					num:20
				}
			}
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				num:30
				
			},
			components:{
				comp
			}
		})
	</script>
</html>

 效果图:

作用域插槽的四种写法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./js/vue.js"></script>
	</head>
	<body>
		
		<!-- 组件标签comp内部想要去使用data数据时,不能直接使用,原因是不在作用域范围内
		
		1、把组件中的data中的数据传到对应的slot标签属性  :mynum="num",作为属性的接收
		2、在slot所对应的标签上添加slot-scope属性,值是一个对象(名字自己起),这个对象接收了它所对应的slot标签的标签属性
		3、在这个标签上使用插值语法{{obj.mynum}} 就能使用到子组件中的data数据-->
		
		<div id="app">
			<!-- <comp>
				<button slot="btn" slot-scope="obj">按钮{{obj.mynum}}、{{obj.tit}}</button>
			</comp> -->
			
			<comp>
				<template slot="btn" slot-scope="obj">
				<button >按钮{{obj.mynum}}、{{obj.tit}}</button>
				</template>
			</comp>
			
			<!-- <comp v-slot:btn="obj">
				<button >按钮{{obj.mynum}}、{{obj.tit}}</button>
			</comp> -->
			
			<!-- <comp>
				<template v-slot:btn="obj">
				<button >按钮{{obj.mynum}}、{{obj.tit}}</button>
				</template>
			</comp> -->
		</div>
		
		<template id="tmp1">
			<div>
				
				<h1>这是标题标签</h1>
				<slot name="btn" :mynum="num" tit="哈哈"></slot>
				
			</div>
		</template>
		
	</body>
	<script>
		let comp={
			template:"#tmp1",
			data(){
				return{
					num:20
				}
			}
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				num:30
				
			},
			components:{
				comp
			}
		})
	</script>
</html>

todolist样式结构-组件化-数据渲染

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" href="css/index.css">
		<script src="./js/vue.js"></script>
	</head>
	<body>
		<div id="app">
			<header>
				<div class="wrap">
					<h1>ToDoList</h1>
					<input type="text" placeholder="请输入..."/>
				</div>
			</header>
			

			<con-comp type="doing" title="正在进行" :arr="arr"></con-comp>
			<con-comp type="finished" title="已经完成" :arr="arr"></con-comp>
			<div class="footer">
				Copyright ©2022 todolist.cn clear
			</div>
		</div>
		
		
		<template id="tmp1">
			<div class="wrap">
			<div class="hd">
				<h3>{{title}}</h3>
				<div class="count">2</div>
			</div>
			
			<ul class="bd">
				<li v-for="item in arr" v-show="type=='doing'?!item.isFinished:item.isFinished">
					<div class="l">
						<input type="checkbox"/>
					</div>
					<input type="text" v-model="item.content"/>
					<div class="r">
						<div>-</div>
					</div>
				</li>
			</ul>
			</div>
		</template>
		
	</body>
	<script>
		let conComp={
			template:"#tmp1",
			props:["title","arr","type"]
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				arr:[
					{id:1,content:"内容1",
					isFinished:true,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
					{id:2,content:"内容2",
					isFinished:true,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
					{id:3,content:"内容3",
					isFinished:false,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
				]
			},
			components:{
				conComp
			}
		})
	</script>
</html>

根据isFinished的值做对应的展示

两种情况用三眼运算符,你是不是doing 是就给你这个

<li v-for="item in arr" v-show="type=='doing'?!item.isFinished:item.isFinished">
<body>
<con-comp type="doing" title="正在进行" :arr="arr"></con-comp>
<con-comp type="finished" title="已经完成" :arr="arr"></con-comp>

let conComp={
			template:"#tmp1",
			props:["title","arr","type"]
		}
</body>
<script>


	var app = new Vue({
			el: "#app",
			data: {
				arr:[
					{id:1,content:"内容1",
					isFinished:true,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
					{id:2,content:"内容2",
					isFinished:true,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
					{id:3,content:"内容3",
					isFinished:false,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
				]
			},
</script>

效果图:

数据条数的渲染

根据titile的doing来计算 计算属性 计Arr.length算arr数据

相当于在数组中过滤出来,寻找false把它放在this.arr.filter中,this.arr.filter() 这个东西最终返回一个数组 然后返回new 看它有多少条数据

计算属性需要return

<con-comp 
			type="doing" 
			title="正在进行" 
			:arr="arr"
			:doingCounts="doingCounts"
			></con-comp>
	<script>
		let conComp={
			template:"#tmp1",
			props:["title","arr","type","doingCounts"]
		}
		
		var app = new Vue({
			el: "#app",
			data: {
				arr:[
					{id:1,content:"内容1",
					isFinished:true,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
					{id:2,content:"内容2",
					isFinished:true,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
					{id:3,content:"内容3",
					isFinished:false,  // 用来区分是否完成
					isShowBorder:false,//用来区分是否有边框
					},
				]
			},
			components:{
				conComp
			},
			computed:{
				doingCounts(){
					//计算的是未完成的条数
					//在arr数组中过滤出 isFinished为false的元素(找出false元素
					let newArr=this.arr.filter(item=>{
						return item.isFinished==false
					})
					
					return newArr.length
				}
			}
		})
	</script>

因为里面arr也有,而arr已经传过来了,所以computed直接放在conComp中,可以不需要写在con-comp中写:doingCounts="doingCounts"

效果图:

  

但是上下效果一样,我们应该再写一个finishedCount

				finishedCounts(){
					//计算的是未完成的条数
					//在arr数组中过滤出 isFinished为false的元素
					let newArr=this.arr.filter(item=>{
						return item.isFinished==true
					})
					
					return newArr.length
				}

由于两代码重复的有点多,因此写成

				counts(){
					let newArr=this.arr.filter(item=>{
						return this.type=='doing'?!item.isFinished:item.isFinished
					})
					
					return newArr.length
				}

然后上面直接写counts

 效果图:

点击checkbox切换任务状态

点击checkbox切换任务状态
第一:给checkbox添加一个点击事件

第二:定义一下事件:修改对应的isfinished,取反,这是子传父
 

//父
methods:{
				changeType(index){
					this.arr[index].isFinished=!this.arr[index].isFinished
				}
			}
//子
			methods:{
					clickCheckbox(key){
				//修改该数据的isfinished属性,取反
				this.$emit("fn",key)
				}
			}
			<con-comp 
			type="doing" 
			title="正在进行" 
			:arr="arr"
			@fn="changeType"
			></con-comp>
			
			<con-comp 
			type="finished" 
			title="已经完成" 
			:arr="arr"
			@fn="changeType"
			></con-comp>

效果图:

todolist边框展示

在Index.css写类

			<con-comp 
			type="doing" 
			title="正在进行" 
			:arr="arr"
			@fn="changeType"
			@fn2="showBorderFather"
			></con-comp>
			
			<con-comp 
			type="finished" 
			title="已经完成" 
			:arr="arr"
			@fn="changeType"
			@fn2="showBorderFather"
			></con-comp>
//子
			methods:{
				changeType(index){
					this.arr[index].isFinished=!this.arr[index].isFinished
				},
				showBorderFather(index){
					this.arr[index].isShowBorder=!this.arr[index].isShowBorder
				}
			}
//父
			methods:{
				changeType(index){
					this.arr[index].isFinished=!this.arr[index].isFinished
				},
				showBorderFather(index){
					this.arr[index].isShowBorder=!this.arr[index].isShowBorder
				}
			}

todolist删除数据

//父
			methods:{
				changeType(index){
					this.arr[index].isFinished=!this.arr[index].isFinished
				},
				showBorderFather(index){
					this.arr[index].isShowBorder=!this.arr[index].isShowBorder
				},
				delFather(index){
					this.arr.splice(index,1);
				}
			}

fn3

			<con-comp 
			type="doing" 
			title="正在进行" 
			:arr="arr"
			@fn="changeType"
			@fn2="showBorderFather"
			@fn3="delFather"
			></con-comp>
			
			<con-comp 
			type="finished" 
			title="已经完成" 
			:arr="arr"
			@fn="changeType"
			@fn2="showBorderFather"
			@fn3="delFather"
			></con-comp>
//子
			methods:{
					clickCheckbox(key){
				//修改该数据的isfinished属性,取反
				this.$emit("fn",key)
				},
				showBorderMt(key){
					this.$emit("fn2",key)
				},
				del(key){
				this.$emit("fn3",key)
			}
			}

效果图:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值