Vue.js从入门到精通
目录
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)
}
}
效果图:
无名之辈_1024: 错别字有点多啊
普通网友: 好文,细节很到位!【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】
执笔人: 这篇文章对软件测试进行了全面的介绍,涵盖了软件测试的定义、分类、测试技术、测试阶段等多个方面。作者清晰地解释了软件测试的重要性,包括发现错误、验证产品符合用户需求、提高用户体验等目的。文章还提到了不同的测试技术和阶段,如白盒测试、黑盒测试、单元测试、集成测试等,以及常见的测试模型和生命周期模型。 其中,对软件测试需求分析进行了重点介绍,强调了分析测试需求的必要性和步骤,包括查阅需求规格说明书、熟悉业务流程、细化需求、列出测试点等。这一部分对于测试用例设计和测试覆盖率的提高具有关键作用。 期待作者能够在自己的博客中分享更多关于软件测试的实践经验和案例分析,以及对新技术和方法的探讨。希望能够看到更多关于测试用例设计、自动化测试、性能测试等方面的内容,为读者提供更多有价值的信息和技巧。
CSDN-Ada助手: 恭喜您写了第6篇博客!标题为“测试金融项目实战”,真是非常有趣的主题选择!通过实战经验分享,读者能够更好地了解金融项目测试的具体实践和挑战。继续坚持创作吧,您的经验对于其他测试人员来说无疑是宝贵的财富。 在下一步的创作中,或许您可以考虑分享一些解决复杂问题的策略或者深入分析一些具体的测试技术在金融项目中的应用。这样的内容将进一步丰富您的博客,并帮助读者更好地理解和应用于实际工作中。期待您的下一篇博客,再次恭喜您!
CSDN-Ada助手: 恭喜您写了第7篇博客!标题《Qnet弱网工具——app测试》非常吸引人。您对Qnet弱网工具的探索和测试,无疑为我们提供了宝贵的参考。在接下来的创作中,或许您可以考虑分享一些实际应用场景下的案例,以及如何最大程度地发挥这款工具的优势。期待您在未来的博客中继续展示您的深入研究和见解,谢谢您的分享!