学习Vue的第一天
一、初识Vue
1.我的第一个Vue项目
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- {{ }}差值表达式 -->
<!-- 差值表达式里面为js表达式所以第二个h1标签没有报错 -->
<!--
一、js表达式:一个表达式可以产生一个值,可以在任何需要的地方使用
1.a
2.a+b
3.main()
4.x = y ? 1 : 2 (三元表达式)
二、js代码(语句)
1.if()
2.for()
-->
<div>
<h1>{{ message.toUpperCase() }}</h1>
<h1>{{ Date.now() }}</h1>
</div>
</body>
</html>
<script>
const app = new Vue({
el:"div",
data:{
message:'hello word'
}
})
</script>
2.模板语法(v-bind ===> :)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>{{ message }}</h1>
<!--这里直接把这个url字符串当做js表达式去执行-->
<a v-bind:href="url">去百度</a>
<a :href="url">还是去百度</a> <!-- v-bind ====> : -->
</div>
</body>
</html>
<script>
const app = new Vue({
el:"div",
data:{
message:'hello word',
url:'http://www.baidu.com'
}
})
</script>
3.数据绑定(v-model)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!--
v-bind:数据从data流向页面,不能从页面流向data
v-model:双向的 -->
<div>
单向数据绑定<input type="text" v-bind:value="message">
<br>
双向数据绑定<input type="text" v-model:value="message">
<!--注意v-model只能用于表单类元素(就是有value值的属性)-->
<!--简写-->
<br>
单向数据绑定<input type="text" :value="message">
<br>
双向数据绑定<input type="text" v-model="message">
</div>
</body>
</html>
<script>
const app = new Vue({
el:"div",
data:{
message:'hello word',
url:'http://www.baidu.com'
}
})
</script>
4.el和data的两种写法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<div>
<h1>{{ message }}</h1>
</div>
</body>
</html>
<script>
const app = new Vue({
// el:"div", //el的第一种写法
//data第一种写法 对象式
// data:{
// message:'hello word',
// }
//data第二种写法 函数式
data:function(){ //Vue自己调用的
console.log(this); //指向Vue实例对象
return{
message:'hello world'
}
}
});
setTimeout(() => { //一秒后才把data挂载上去
app.$mount('div'); //el的第二种写法
},1000)
</script>
5.MVVM模型
6.defineProperty
6.1追加属性操作
<script>
var person = {
name:'猴王',
city:'花果山'
}
Object.defineProperty(person,'age',{ //这样写不可被枚举(遍历)
value:18,
})
console.log(person);
console.log(Object.keys(person)); //遍历 键名形成数组
</script>
6.2基本操作
<script>
var person = {
name:'猴王',
city:'花果山'
}
Object.defineProperty(person,'age',{ //这样写不可被枚举(遍历)
value:18,
enumerable:true, //可以被枚举 三个属性默认均为false
writable:false, //值不可以被修改 三个属性默认均为false
configurable:true, //是否可以被删除 三个属性默认均为false
})
console.log(person);
console.log(Object.keys(person)); //遍历 键名形成数组
</script>
6.3进阶操作
原本我们用defineProperty追加进去的属性值是不可以被修改的,需要追加get和set两个函数才能完成更好的修改
<script>
var number = 18
var person = {
name:'猴王',
city:'花果山'
}
Object.defineProperty(person,'age',{ //这样写不可被枚举(遍历)
// value:18,
// enumerable:true, //可以被枚举 三个属性默认均为false
// writable:false, //值不可以被修改 三个属性默认均为false
// configurable:true, //是否可以被删除 三个属性默认均为false
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get:function(){
console.log('有人读取了get属性');
return number;
},
//当有人修改person的age属性时,set函数(setter)就会被调用,接受到的值就是age的值
set(value){
console.log('有人修改了set属性值是',value);
number=value;
}
})
console.log(person);
</script>
7.数据代理
最简单的数据代理
<script>
//1.最简单的数据代理
let objone = {x:100}
let objtwo = {y:200}
Object.defineProperty(objtwo,'x',{
get(){
return objone.x;
},
set(value){
objone.x = value
}
})
</script>
Vue中的数据代理
data.name通过getter到页面中的name这是加载网页就执行了的
接下来验证修改页面中的name,会不会通过setter改变data.name,Vue在实例化后,data更名叫_data了
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>{{ message }}</h1>
</div>
</body>
</html>
<script>
const app = new Vue({
el:"div",
data:{
message:'hello world'
}
})
</script>
8.事件处理(v-on ====> @)
8.1点击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<button v-on:click="showInfoOne">按钮1(不传参数)</button>
<button @click="showInfoTwo(2,50)">按钮2(简写传参数)</button>
</div>
</body>
</html>
<script>
const app = new Vue({
data(){
return {
}
},
methods:{
showInfoOne(){alert(666)},
showInfoTwo(a,b){alert(a+''+b)}
}
})
app.$mount('div');
</script>
8.2键盘事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<input @keyup.enter="showDown">
</div>
</body>
</html>
<script>
const app = new Vue({
data(){
return {
}
},
methods:{
showDown(event){
//手动判断按下的是不是回车
//if(event.code !== 13) return;
//key是按键的名字,keycode是编码
// console.log(event.key,event.keyCode);
console.log(event.target.value);
},
}
})
app.$mount('div');
</script>
9.事件修饰符
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.demo01{
margin: 20px 0 0 0 ;
height: 50px;
width: 100px;
background-color: aqua;
}
.box1{
margin: 20px 0 0 0 ;
height: 100px;
width: 200px;
background-color: aqua;
}
.box2{
height: 50px;
width: 100px;
background-color: red;
}
.list{
height: 200px;
width: 200px;
overflow: auto; /*溢出了增加滚动条*/
background-color: khaki;
}
li{
height: 100px;
width: 200px;
}
</style>
</head>
<body>
<div>
<!-- 1.prevent事件修饰符 阻止默认行为-->
<a href="http://www.baidu.com/" @click.prevent="showInfoOne">阻止去百度</a>
<!-- 2.stop事件修饰符 阻止事件冒泡-->
<!-- 因为div和button都是有点击事件的,如果这里不阻止事件冒泡啊,
点击button也会触发div的事件,就会出现两次弹窗-->
<div class="demo01" @click="showInfoOne">
<button @click.stop="showInfoOne">按钮</button>
</div>
<!-- 3.once事件修饰符 事件只触发一次-->
<button @click.once="showInfoOne">按钮</button>
<!-- 4.capture事件修饰符 事件的捕获模式-->
<!-- 注意这是一个嵌套模式,并没有阻止冒泡,我们点击box2应该是输出2再输出1-->
<!-- 点击box2后捕获就是先通过box1再找到box2,冒泡再是box2冒泡到box1事件执行两次-->
<!-- 添加capture后通过事件捕获机制执行,所以先输出1再输出2-->
<div class="box1" @click.capture=showMsg(1)>
div1
<div class="box2" @click=showMsg(2)>
div2
</div>
</div>
<!-- 5.self事件修饰符 只有操作当前元素才会触发事件(个人理解跟阻止冒泡差不多)-->
<div class="demo01" @click.self="showInfoOne">
<button @click="showInfoOne">按钮</button>
</div>
<!-- 6.passive事件修饰符,事件的默认行为立即执行不用等待回调函数执行完毕 -->
<!-- 如果不加修饰,就会等下方for循环执行完毕,进度条才往下开始走 -->
<ul class="list" @wheel="gunlun"> <!--wheel滚轮事件-->
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {}
},
methods: {
showInfoOne(event) {
//1.阻止默认行为
// event.preventDefault();
//2.防止事件冒泡
// event.stopPropagation();
alert(666)
},
showMsg(a){
console.log(a);
},
gunlun(){
for(let i = 0;i < 10000;i++){
console.log('###');
}
console.log('tmd累死辣子算了');
}
}
})
app.$mount('div');
</script>
10.计算属性(computed)
10.1demo01
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{ fullName() }}</span>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {
firstName:'张',
lastName:'三'
}
},
methods:{
//如果全部写在差值表达式中不友好
fullName(){
//return this.firstName + '' + this.lastName;
//return app.firstName + '' + app.lastName;
return app._data.firstName + '' + app._data.lastName;
}
}
})
app.$mount('div');
</script>
10.1demo02
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{ fullName }}</span>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {
firstName: '张',
lastName: '三'
}
},
methods: {},
computed: {
fullName: {
get() {
console.log('get被调用了');
return this.firstName + '-' + this.lastName;
},
set(value) {
console.log('set被调用了',value);
var arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
app.$mount('div');
</script>
11.监视属性(watch)
11.1demo01
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>今天天气{{ info }}</h1>
<button @click="changeWeather">点击切换天气</button>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {flag:true}
},
methods: {
changeWeather(){
this.flag=!this.flag;
console.log('天气切换成功');
}
},
computed: {
info(){
return this.flag ? '炎热' : '凉爽';
}
}
})
app.$mount('div');
</script>
11.2demo02监视
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>今天天气{{ info }}</h1>
<button @click="changeWeather">点击切换天气</button>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {flag:true}
},
methods: {
changeWeather(){
this.flag=!this.flag;
}
},
// watch:{
// flag:{
// //当flag发生改变这个handler函数自动调用
// immediate:true, //初始化时自动调用一下
// handler(oldValue,newValue){
// console.log('我tm被修改了',oldValue,newValue);
// }
// }
// },
computed: {
info(){
return this.flag ? '炎热' : '凉爽';
}
}
})
app.$mount('div');
app.$watch('flag',{ //在methods中我们使用的是简写,这里面必须带单引号
immediate:true, //初始化时自动调用一下
handler(oldValue,newValue){
console.log('我tm被修改了',oldValue,newValue);
}
})
</script>
11.3深度监视(deep属性)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>a的值是{{ numbers.a }}</h1>
<button @click="numbers.a++">点击我让a的值加+1</button><br>
<h1>b的值是{{ numbers.b }}</h1>
<button @click="numbers.b++">点击我让b的值加+1</button>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {
numbers: {
a: 1,
b: 1
}
}
},
methods: {
},
watch: {
// 'numbers.a':{ //对象里面的key是字符串,用简写方式有.必须加引号
// handler(){
// console.log('我是a,我被改变了');
// }
// }
numbers:{
deep:true, //深度检测属性
handler(){
console.log('number被改变了');
}
}
},
})
app.$mount('div');
</script>
11.4简写
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>今天天气{{ info }}</h1>
<button @click="changeWeather">点击切换天气</button>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {flag:true}
},
methods: {
changeWeather(){
this.flag=!this.flag;
}
},
// watch:{
// //简写情况下 不可以写其他配置对象
// flag(oldValue,newValue){
// console.log('我tm被修改了',oldValue,newValue);
// }
// },
computed: {
info(){
return this.flag ? '炎热' : '凉爽';
}
}
})
app.$mount('div');
app.$watch('flag',function(oldValue,newValue){
//简写情况下 不可以写其他配置对象
console.log('我tm被修改了',oldValue,newValue);
})
</script>
11.5用监视属性写10那个案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{ fullName }}</span>
</div>
</body>
</html>
<script>
const app = new Vue({
data(){
return {
firstName:'张',
lastName:'三',
fullName:'张三'
}
},
watch:{
firstName(value){
//写箭头函数没有this指向往外找就是firstName的this
//普通函数的this指向window 就不能指向app了
setTimeout(() => {
this.fullName = value + this.lastName;
},1000)
},
lastName:{
handler(value){
this.fullName = this.firstName + value;
}
}
}
})
app.$mount('div');
</script>
12.条件渲染(v-show,v-if)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<!-- v-shou是使用display属性进行一个隐藏和显示调节 -->
<!-- v-if是把东西销毁了然后重新创建 -->
<h1 v-show="false">hello world!</h1>
<h1 v-show="true">hello world!</h1>
<button @click='n++'>点击我让n的值加1</button>
<span>当前n的值是{{ n }}</span>
<h1 v-if='n == 1'>Auguler</h1>
<h1 v-else-if='n == 2'>React</h1>
<h1 v-else-if='n == 3'>Vue</h1>
<h1 v-else>n=0或者大于3执行</h1>
<hr>
<template v-if='n == 4'>
<!-- 这个标签不会破坏页面结构 -->
<h2>现在n=4</h2>
<h2>现在n=4</h2>
</template>
</div>
</body>
</html>
<script>
const app = new Vue({
data() {
return {
n: 0
}
}
})
app.$mount('div');
</script>
13.列表渲染
13.1demo01
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="kk">
<h1>人员信息</h1>
<ul>
<!-- 绑定的key值必须是唯一的 我们这里直接绑定序列 -->
<li v-for='(p,index) of persons' :key='index'>
{{ index }}-{{ p.name }}-{{ p.age}}
<!-- 遍历的是数组,index我理解为下标 -->
</li>
</ul>
<h1>车辆信息</h1>
<ul>
<li v-for="(value,index) of cars" :key='index'>
{{ index }}-{{ value }}
<!-- 遍历的是对象,index我理解为下标对应的键名 -->
</li>
</ul>
<h1>字符串信息</h1>
<ul>
<li v-for='(char,index) of str' :key='index'>
{{index}}-{{char}}
</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data: function() {
return {
persons: [{
id: 001,
name: '张三',
age: 18,
},
{
id: 002,
name: '李四',
age: 19,
},
{
id: 003,
name: '王五',
age: 20,
}
],
cars: {
name: '奔驰Rs8',
price: '200万',
color: '深海枫蓝'
},
str: 'hello'
}
}
})
app.$mount('.kk');
</script>
13.2demo02(key值)
出错效果图(我们这里key值绑定的index,破坏了顺序就出毛病)
出错原理
解决方案
我们把我们数据中学的唯一标识id绑定到key就解决
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="kk">
<h1>人员信息</h1>
<button type="button" @click="add()">点击我添加一个老六</button>
<ul>
<li v-for='(p,index) of persons' :key='p.id'>
{{ index }}-{{ p.name }}-{{ p.age}}
<input/>
</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data: function() {
return {
persons:
[
{id: 001,name: '张三',age: 18,},
{id: 002,name: '李四',age: 19,},
{id: 003,name: '王五',age: 20,}
],
}
},
methods:{
add(){
const p = {id:004,name:'老六',age:40}
this.persons.unshift(p); //在数组最前方放一个p
}
}
})
app.$mount('.kk');
</script>
13.3demo03(列表过滤)
13.4watch监视过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="kk">
<h1>人员信息</h1>
<ul>
这是查询框<input type="text" v-model="keyWord"/>
<li v-for='(p,index) of filePersons' :key='p.id'>
{{ index }}-{{ p.name }}-{{ p.age}}
<input/>
</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data: function() {
return {
keyWord:'',
persons:
[
{id: 001,name: '张三',age: 18,},
{id: 002,name: '李四',age: 19,},
{id: 003,name: '王五',age: 20,}
],
filePersons:[]
}
},
watch:{
keyWord:{
immediate:true, //初始化时自动调用一下,初始化让filePersons有数据
handler(value){
this.filePersons = this.persons.filter((p) => {
return p.name.indexOf(value) !== -1;
//indexOf返回-1表示不存在
})
}
}
}
})
app.$mount('.kk');
</script>
13.5computed计算过滤
效果和13.1一模一样
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="kk">
<h1>人员信息</h1>
<ul>
这是查询框<input type="text" v-model="keyWord"/>
<li v-for='(p,index) of filePersons' :key='p.id'>
{{ index }}-{{ p.name }}-{{ p.age}}
<input/>
</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data: function() {
return {
keyWord:'',
persons:
[
{id: 001,name: '张三',age: 18,},
{id: 002,name: '李四',age: 19,},
{id: 003,name: '王五',age: 20,}
],
}
},
computed:{
filePersons(){
//这个return是filePersons返回值
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1;
})
}
}
})
app.$mount('.kk');
</script>
13.6列表排序
扩展知识
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="kk">
<h1>人员信息</h1>
<ul>
这是查询框<input type="text" v-model="keyWord" />
<button @click="sortType = 2">升序</button>
<button @click="sortType = 1">降序</button>
<button @click="sortType = 0">原顺序</button>
<li v-for='(p,index) of filePersons' :key='p.id'>
{{ index }}-{{ p.name }}-{{ p.age}}
</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data: function() {
return {
sortType: 0, //0原顺序 1降序 2升序
keyWord: '',
persons: [
{id: 001,name: '张三',age: 28,},
{id: 002,name: '李四',age: 19,},
{id: 003,name: '王五',age: 30,},
{id: 004,name: '老六',age: 25,}
],
}
},
computed: {
filePersons() {
let arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1;
})
// this.sortType = 0 if语句不执行 直接上方arr返回出去就是原顺序
if (this.sortType) {
console.log(this.sortType);
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;
})
}
return arr;
}
}
})
app.$mount('.kk');
</script>
14.Vue检测数据原理
14.1对象
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
</html>
<script>
let data = {
name:'猴王',
address:'花果山'
}
const obs = new Observer(data);
// console.log(obs);
let app = {};
app._data = data = obs;
function Observer(obj){
//将对象中的属性变成一个数组
const keys = Object.keys(obj);
// console.log(keys); //(键名)
keys.forEach((k) => {
//this指向Observer的实例对象obs
Object.defineProperty(this,k,{
get() {
//传入的对象属性对应的值交出去
return obj[k];
},
set(value){
console.log('key被修改了,我要去解析模板,生成虚拟DOM');
obj[k] = value;
}
})
})
}
</script>
14.2set方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>姓名:{{ student.name }}</h1>
<h1>性别:{{ student.sex }}</h1>
<h1>地址:{{ student.address }}</h1>
</div>
</body>
</html>
<script>
const app = new Vue({
data:function(){
return {
student:{
name:'猴王',
address:'花果山'
}
}
}
})
app.$mount('div');
</script>
失败效果(根本原因我们最先没有写sex这个数据,没有他的get和set方法)
使用set追加(target目标,key属性)
两种set方式均可,数据就变成响应式的了,即可敲刚刚失败的代码完成修改
14.3数组操注意事项
14.4总结
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<h1>学生信息</h1>
<h3>姓名:{{ student.name }}</h3>
<h3>年龄:{{ student.age }}</h3>
<button @click="student.age++">点击我年龄+1</button>
<button @click="addSex">点击我添加性别默认男</button>
<!-- 使用v-if没有性别时默认不显示 -->
<h3 v-if="student.sex">性别:{{ student.sex }}</h3>
<h3>地址:{{ student.address }}</h3>
<h3>爱好</h3>
<button type="button" @click="addHobby">添加一个爱好</button>
<button type="button" @click="updateHobby">修改第一个爱好为开车</button>
<button type="button" @click="removeSmoke">删除抽烟这个爱好</button>
<ul>
<li v-for="(p,index) of hobby" :key="index">{{ p }}</li>
</ul>
<h2>朋友们</h2>
<button @click="addFriend">点击我添加一个朋友</button>
<button @click="updateFriendsTwoName">修改第二个朋友的年龄为50</button>
<ul>
<li v-for="(p,index) of friends" :key="index">
{{ p.name }}-{{ p.age }}
</li>
</ul>
</div>
</body>
</html>
<script>
const app = new Vue({
data:function(){
return {
student:{
age:18,
name:'猴王',
address:'花果山'
},
hobby:['抽烟','喝酒','洗脚'],
friends:[
{name:'猴子',age:18},
{name:'猴儿',age:20}
]
}
},
methods:{
addSex(){
//Vue.set(app._data.student,'sex','男');
//this指向Vue实例化对象app _data是做了数据代理直接省略
Vue.set(this.student,'sex','男');
},
addFriend(){
this.friends.unshift({name:'猴天棒',age:30});
},
updateFriendsTwoName(){
this.friends[1].age = 50;
},
addHobby(){
this.hobby.push('打牌');
},
updateHobby(){
// Vue.set(this.hobby,0,'开车');
//从第0个开始到1个删除,并插入一个开车
this.hobby.splice(0,1,'开车');
},
removeSmoke(){
this.hobby = this.hobby.filter((h) => {
return h!=='抽烟'
})
}
}
})
app.$mount('div');
</script>
15.收集表单数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<!-- 表单提交有个默认刷新跳转 这里阻止掉默认行为-->
<form @submit.prevent ="tijiao">
账号:<input type="text" v-model='account'><br><br>
密码:<input type="text" v-model='password'><br><br>
性别:
男<input type="radio" name='sex' v-model="sex" value="male">
女<input type="radio" name='sex' v-model="sex" value="female"><br><br>
<!-- type=number是控制只能输入数字 通过事件修饰符为数字-->
年龄<input type="number" v-model.number="age"><br><br>
爱好:
抽烟<input type="checkbox" v-model='hobby' value='smoken'>
洗脚<input type="checkbox" v-model='hobby' value='xijio'>
喝酒<input type="checkbox" v-model='hobby' value='hejiu'><br><br>
所属地区
<select v-model='city'>
<option value="">请选择city</option>
<option value="huaguoshan">花果山</option>
<option value="shuiliantonf">水帘洞</option>
<option value="bajiaodong">芭蕉洞</option>
</select><br><br>
其他信息:
<textarea></textarea><br><br>
<input type="checkbox" v-model="agree">阅读并接受<a href="www.baidu.com" >《用户协议》</a>
<button>注册</button>
</form>
</div>
</body>
</html>
<script>
const app = new Vue({
data:function(){
return {
account:'',
password:'',
sex:'female',
age:'',
hobby:[],
city:'huaguoshan',
agree:''
}
},
methods:{
tijiao(){
let str = this._data;
let json = JSON.stringify(str);
console.log(json);
let obj = JSON.parse(json);
console.log(obj);
}
}
})
app.$mount('div');
</script>
16.生命周期
16.1渐入渐出文字
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>欢迎学习Vue</h1>
<h1 :style="{opacity}">欢迎学习Vue</h1>
</div>
</body>
</html>
<script type="text/javascript">
const app = new Vue({
data(){
return{
opacity:1,
}
},
mounted(){
setInterval(() => {
this.opacity -= 0.01;
if(this.opacity <=0 ){
this.opacity = 1;
}
},16)
}
})
app.$mount('#app');
</script>
17.非单文件组件
17.1原生vue写法
— 17.1-3都是同样的效果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="root">
<h1>学校名称:{{schoolName}}</h1>
<h1>学校地址:{{schoolCity}}</h1>
<hr>
<h1>学生:{{studentName}}</h1>
<h1>年龄:{{sex}}</h1>
</div>
</body>
</html>
<script>
const app = new Vue({
data(){
return {
schoolName:"猴猴学院",
schoolCity:"重庆花果山",
studentName:"猴王",
sex:18
}
}
})
app.$mount(".root");
</script>
17.2非单文件组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="root">
<school></school>
<hr>
<student></student>
</div>
</body>
</html>
<script>
const xuexiao = Vue.extend({
template:`
<div>
<h1>学校名称:{{schoolName}}</h1>
<h1>学校地址:{{schoolCity}}</h1>
</div>`,
data(){
return {
schoolName:"猴猴学院",
schoolCity:"重庆花果山",
}
}
})
const xuesheng = Vue.extend({
template:`
<div>
<h1>学生:{{studentName}}</h1>
<h1>年龄:{{sex}}</h1>
</div>`,
data(){
return {
studentName:"猴王",
sex:18
}
}
})
new Vue({
el:".root",
components:{
school:xuexiao,
student:xuesheng
}
})
</script>
17.3组件的嵌套
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<school></school>
</div>
</body>
</html>
<script>
const student = Vue.extend({
template : `<div>
<hr>
<h1>学生 : {{ studentName }}</h1>
<h1>年龄 : {{ studentSex }}</h1>
</div>`,
data(){
return {
studentName:'猴王',
studentSex:18
}
}
});
const school = Vue.extend({
template : `<div>
<h1>学校名称 : {{ schoolName }}</h1>
<h1>学校地址 : {{ schoolCity }}</h1>
<student></student>
</div>`,
data(){
return {
schoolName:'猴猴学院',
schoolCity:'重庆花果山',
}
},
components:{
student,
}
});
const app = new Vue({
data:function(){
return {}
},
components:{
school,
}
})
app.$mount('#root')
</script>
18.单文件组件
18.1编写App、School和Student的Vue文件还有main的js/html文件
App.vue代码
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import Student from 'Stundet.vue';
import School from 'School.vue';
export default {
name:'App',
components:{
Student:Student,
School:School
}
}
</script>
School.Vue代码
<template>
<div class="demo">
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ city }}</h1>
<button @click="showName()">点击我提示学校名称</button>
</div>
</template>
<script>
//暴露方式一
export default {
name: 'School',
//暴露方式一
// export default Vue.extend({
data: function() {
return {
name: "猴猴学院",
city: "重庆花果山",
}
},
methods: {
showName() {
alert(this.name);
}
},
}
</script>
<style>
.demo {
background-color: orange;
}
</style>
Student代码
<template>
<div>
<h1>学生:{{name}}</h1>
<h1>年龄:{{sex}}</h1>
</div>
</template>
<script>
export default{
name:"Student"
data:function(){
return {
studentName:"猴王",
sex:18
}
}
}
</script>
main.js文件代码`
import App from './App.vue';
const app = new Vue({
components:{App}
})
app.$mount('#root');
main.html文件代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root"></div>
<App></App>
<!-- 只能写在这个后面 因为必须先让模板出来才找得到root这个节点 -->
<script src="main.js"></script>
</body>
</html>
18.2创建Vue脚手架
配置淘宝镜像源
npm config set registry https://registry.npm.taobao.org
全局安装vue
npm install -g @vue/cli
创建第一个vue项目
vue create 项目名
只需要将18.1的三个vue文件分别放入即可完成我们的最基本的vue项目
效果图
19.属性
19.1ref属性
App.vue代码另外的基于脚手架没动
<template>
<div>
<h1 v-text="message" ref="title"></h1>
<button @click="showMessage">先点击我再点击上方h1标签打印666</button>
<School/>
</div>
</template>
<script>
import School from "./components/School.vue";
export default {
name:"App",
components:{School:School},
data:function() {
return {
message:'欢迎来到猴猴学院!'
}
},
methods:{
showMessage(){
console.log(this);
this.$refs.title.onclick = function(){
console.log(666)
}
}
}
}
</script>
19.2props属性
新建Student.vue文件
<template>
<div>
<h1 v-text="msg"></h1>
<h2>姓名: {{ name }}</h2>
<h2>性别: {{ sex }}</h2>
<h2>年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Student",
data(){
return {
msg:'我是猴猴学院的学生'
}
},
// props:['name','sex','age'] 最简单的声明接受
//类型限制
// props:{
// name:String,
// sex:String,
// age:Number
// }
//对数据类型默认值和必要性的限制
props:{
name:{
type:String,
require:true
},
sex:{
type:String,
default:'母'
},
age:{
type:Number,
require: true
}
}
}
</script>
19.3mixin混合
两个vue文件都写上mixins这个配置对象实现代码的复用。
19.4插件
20.TODO-list案例
20.1静态页面
App.vue(不包含css)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader></MyHeader>
<MyList></MyList>
<MyFooter></MyFooter>
</div>
</div>
</div>
</template>
<script>
import MyHeader from './components/MyHeader';
import MyList from './components/MyList';
import MyFooter from './components/MyFooter';
export default {
name:'App',
components:{MyFooter,MyHeader,MyList}
}
</script>
MyHeader.vue(不包含css)
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
</div>
</template>
<script>
export default {
name:'MyHeader',
}
</script>
MyList(不包含css)
<template>
<ul class="todo-main">
<li>
<label>
<input type="checkbox"/>
<span>xxxxx</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
<li>
<label>
<input type="checkbox"/>
<span>yyyy</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
</ul>
</template>
<script>
import MyItem from './MyItem';
export default {
name:"MyList",
components:{MyItem}
}
</script>
MyItem(不包含css)
<template>
</template>
<script>
export default {
name:'MyItem',
}
</script>
MyFooter(不包含css)
<template>
<div class="todo-footer">
<label>
<input type="checkbox"/>
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button class="btn btn-danger">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'MyFooter',
}
</script>
效果图(未保存没有)
20.2添加功能
主要思路:
1.初始化的数据和需要新增的方法写在App.vue中,然后通过标签把数据给MyHeader.vue和MyList.vue
App.vue(不包含css)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader :addTodo="addTodo"></MyHeader>
<MyList :todos="todos"></MyList>
<MyFooter></MyFooter>
</div>
</div>
</div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import MyList from "./components/MyList";
import MyFooter from "./components/MyFooter";
export default {
name: "App",
components: { MyFooter, MyHeader, MyList },
data: function () {
return {
todos: [
{ id: "001", title: "抽烟", done: "true" },
{ id: "002", title: "喝酒", done: "false" },
{ id: "003", title: "打牌", done: "true" },
],
};
},
methods:{
addTodo(todoObj){
this._data.todos.unshift(todoObj);
}
}
};
</script>
MyHeader.vue(不包含css)
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="inval" @keyup.enter="add"/>
</div>
</template>
<script>
import { nanoid} from "nanoid";
export default {
name:'MyHeader',
data:function(){
return{
inval:'',
}
},
props:['addTodo'],
methods:{
//1.获取用户输入的数据
//通过event事件获取事件发生改变的值
// add(event){
// console.log(event.target.value);
// }
//2.通过vue的data数据改变获取
add(){
//借用nanoid库生成一些随机不唯一的id
const todoObj = {id:nanoid(),title:this.inval,done:false};
this.addTodo(todoObj)
}
}
}
</script>
MyList(不包含css)
<template>
<ul class="todo-main">
<MyItem v-for="todoObj in todos" :key="todoObj.id" :TodoObjs="todoObj"/>
</ul>
</template>
<script>
import MyItem from "./MyItem";
export default {
name: "MyList",
components: { MyItem },
props:['todos']
};
MyItem(不包含css)
<template>
<div>
<li>
<label>
<input type="checkbox" ref="input"/>
<span>{{TodoObjs.title}}</span>
</label>
<button class="btn btn-danger" style="display: block">删除</button>
</li>
</div>
</template>
<script>
export default {
name: "MyItem",
props:['TodoObjs'],
mounted:function(){
// 检测一下done的值决定input框是否打钩
if(this.TodoObjs.done=='true'){
this.$refs.input.checked="true";
}
}
};
</script>
效果图:
20.3勾选功能
思路:将MyItem中的id给MyList后给App.vue执行
App.vue(不包含css)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader :addTodo="addTodo"></MyHeader>
<MyList :todos="todos" :checkTodo="checkTodo"></MyList>
<MyFooter></MyFooter>
</div>
</div>
</div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import MyList from "./components/MyList";
import MyFooter from "./components/MyFooter";
const App = {
components: { MyFooter, MyHeader, MyList },
data: function () {
return {
todos: [
{ id: "001", title: "抽烟", done: "true" },
{ id: "002", title: "喝酒", done: "false" },
{ id: "003", title: "打牌", done: "true" },
],
};
},
methods:{
//1.添加数据
addTodo(todoObj){
this._data.todos.unshift(todoObj);
},
//2.选中和取消input函数
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done;
})
}
}
};
export default App;
</script>
MyHeader.vue(不包含css)
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认"
v-model="inval" @keyup.enter="todo"/>
</div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
name:'MyHeader',
data:function(){
return{
inval:'',
}
},
props:['addTodo'],
methods:{
//1.获取用户输入的数据
//通过event事件获取事件发生改变的值
// add(event){
// console.log(event.target.value);
// }
//2.通过vue的data数据改变获取
todo(){
//借用nanoid库生成一些随机不唯一的id
const todoObj = {id:nanoid(),title:this.inval,done:false};
this.addTodo(todoObj)
this.inval='';
}
}
}
</script>
MyList.vue(不包含css)
<template>
<ul class="todo-main">
<MyItem
v-for="todoObj in todos"
:key="todoObj.id"
:TodoObjs="todoObj"
:checkTodo="checkTodo"
/>
</ul>
</template>
<script>
import MyItem from "./MyItem";
export default {
name: "MyList",
components: { MyItem },
props:['todos','checkTodo']
};
</script>
MyItem.vue(不包含css)
<template>
<div>
<li>
<label>
<input type="checkbox" ref="input" @change="handlechecked(TodoObjs.id)"/>
<span>{{TodoObjs.title}}</span>
</label>
<button class="btn btn-danger" style="display: block">删除</button>
</li>
</div>
</template>
<script>
export default {
name: "MyItem",
props:['TodoObjs','checkTodo'],
mounted:function(){
// 检测一下done的值决定input框是否打钩
if(this.TodoObjs.done=='true'){
this.$refs.input.checked="true";
}
},
methods:{
handlechecked(id){
this.checkTodo(id)
}
}
};
</script>
20.4删除和统计功能
App.vue(不包含css)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader :addTodo="addTodo"></MyHeader>
<MyList :todos="todos" :checkTodo="checkTodo" :delete_todo="delete_todo"></MyList>
<MyFooter :todos="todos"></MyFooter>
</div>
</div>
</div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import MyList from "./components/MyList";
import MyFooter from "./components/MyFooter";
const App = {
components: { MyFooter, MyHeader, MyList },
data: function () {
return {
todos: [
{ id: "001", title: "抽烟", done: true },
{ id: "002", title: "喝酒", done: false },
{ id: "003", title: "打牌", done: true },
],
};
},
methods:{
//1.添加数据
addTodo(todoObj){
this._data.todos.unshift(todoObj);
},
//2.选中和取消input函数
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done;
})
},
//3.删除功能
delete_todo(id){
this.todos = this.todos.filter((todo)=>{
//过滤出所有你需要的新数据
return todo.id !== id;
})
}
}
};
export default App;
</script>
MyList.vue(不包含css)
<template>
<ul class="todo-main">
<MyItem
v-for="todoObj in todos"
:key="todoObj.id"
:TodoObjs="todoObj"
:checkTodo="checkTodo"
:delete_todo="delete_todo"
/>
</ul>
</template>
<script>
import MyItem from "./MyItem";
export default {
name: "MyList",
components: { MyItem },
props:['todos','checkTodo',"delete_todo"]
};
</script>
MyItem.vue(不包含css)
<template>
<div>
<li @mouseover="btn_hover" @mouseleave="btn_out">
<label>
<input type="checkbox" ref="input" @change="handlechecked(TodoObjs.id)"/>
<span>{{TodoObjs.title}}</span>
</label>
<button class="btn btn-danger" ref="button" style="display: none;" @click="delete_button(TodoObjs.id)">删除</button>
</li>
</div>
</template>
<script>
export default {
name: "MyItem",
props:['TodoObjs','checkTodo','delete_todo'],
mounted:function(){
// 检测一下done的值决定input框是否打钩
if(this.TodoObjs.done==true){
this.$refs.input.checked="true";
}
},
methods:{
//勾选的功能
handlechecked(id){
this.checkTodo(id)
},
//鼠标引入li显示删除按钮
btn_hover(){
this.$refs.button.style.display="block";
},
//鼠标移除li隐藏删除按钮
btn_out(){
this.$refs.button.style.setProperty("display","none");
},
//删除功能
delete_button(id){
if(confirm("你确定删除吗?")){
this.delete_todo(id)
}
}
}
};
</script>
MyFooter.vue(不包含css)
<template>
<div class="todo-footer">
<label>
<input type="checkbox"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{todos.length}}
</span>
<button class="btn btn-danger" >清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'MyFooter',
props:['todos'],
computed:{
doneTotal(){
//pre是上一个返回值 current数组中正在处理的元素
return this.todos.reduce((pre,current) => {
//如果正在执行的数组的done值为真+1
return pre + (current.done ? 1 : 0 );
//0是初始值哦
} ,0)
}
}
}
</script>
效果图
20.5全选和全不选清除已完成的功能
App.vue(不包含css)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader :addTodo="addTodo"></MyHeader>
<MyList
:todos="todos"
:checkTodo="checkTodo"
:delete_todo="delete_todo"
:ClearAllTodo="ClearAllTodo"
></MyList>
<MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :ClearAllTodo="ClearAllTodo"></MyFooter>
</div>
</div>
</div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import MyList from "./components/MyList";
import MyFooter from "./components/MyFooter";
const App = {
components: { MyFooter, MyHeader, MyList },
data: function () {
return {
todos: [
{ id: "001", title: "抽烟", done: true },
{ id: "002", title: "喝酒", done: false },
{ id: "003", title: "打牌", done: true },
],
};
},
methods:{
//1.添加数据
addTodo(todoObj){
this._data.todos.unshift(todoObj);
},
//2.选中和取消input函数
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done;
})
},
//3.删除功能
delete_todo(id){
this.todos = this.todos.filter((todo)=>{
//过滤出所有你需要的新数据
return todo.id !== id;
})
},
//4.全选和全不选
checkAllTodo(done){
this.todos.forEach((item)=>{
item.done = done;
})
},
//5.清除已经完成
ClearAllTodo(){
this.todos = this.todos.filter((todo)=>{
//留下done值为false的
return !todo.done;
})
}
}
};
export default App;
</script>
MyFooter.vue(不包含css)
<template>
<div class="todo-footer" v-show="todos.length">
<label>
<!-- 这里的checked的是根据计算属性isAll来决定要不要加进元素里,并不是isAll就是checked的属性值-->
<input type="checkbox" ref="checkboxstatus" :checked="isAll" @change="checkboxdisplay"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{todos.length}}
</span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'MyFooter',
props:['todos','checkAllTodo','ClearAllTodo'],
computed:{
doneTotal(){
//pre是上一个返回值 current数组中正在处理的元素
return this.todos.reduce((pre,current) => {
//如果正在执行的数组的done值为真+1
return pre + (current.done ? 1 : 0 );
//0是初始值哦
} ,0)
},
isAll(){
if(this.todos.length == this.doneTotal && this.todos.length > 0) return true;
}
},
methods:{
checkboxdisplay(){
//因为我们要等dom页面渲染完成才可以拿到$refs所以我们嵌套一个this.$nextTick(() => {})函数
this.$nextTick(() => {
this.checkAllTodo(this.$refs.checkboxstatus.checked);
});
},
clearAll(){
this.ClearAllTodo();
}
}
}
</script>
21.组件自定义事件
21.1组件自定义事件_绑定
App.vue(不包含css)
<template>
<div class="app">
<h1>{{message}}</h1>
<School :getSchoolName="getSchoolName"></School>
<Student v-on:houwang="getStudentName" ref="student"></Student>
</div>
</template>
<script>
import Student from './components/Stundet.vue';
import School from './components/School.vue';
export default {
name:'App',
data:function(){
return {
message:'hello world!'
}
},
components:{
Student:Student,
School:School
},
methods:{
getSchoolName(name){
console.log("已经收到学校的名称:"+name);
},
getStudentName(name){
console.log("自定义组件被触发,收到学生名:"+name);
}
},
mounted(){
console.log("回调钩子被调用");
this.$refs.student.$on("houwang",this.getStudentName);
}
}
</script>
School.vue(不包含css)
<template>
<div class="school">
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ address }}</h1>
<button @click="sendSchoolName">把学校名给App.vue</button>
</div>
</template>
<script>
export default {
name: 'School',
data: function() {
return {
name: "猴猴学院",
address: "重庆花果山",
}
},
props:['getSchoolName'],
methods:{
sendSchoolName(){
this.getSchoolName(this.name);
}
}
}
</script>
Student.vue(不包含css)
<template>
<div class="student">
<h1>学生:{{name}}</h1>
<h1>年龄:{{sex}}</h1>
<button @click="sendStudentName">把学生名给App.vue</button>
</div>
</template>
<script>
export default {
name:"Student",
data:function(){
return {
name:"猴王",
sex:18
}
},
methods:{
sendStudentName(){
//触发Student组件实例身上的houwang事件
this.$emit("houwang",this.name);
}
}
}
</script>
效果图
21.2组件自定义事件_解绑
App.vue(不包含css)
<template>
<div class="app">
<h1>{{message}}</h1>
<School :getSchoolName="getSchoolName"></School>
<Student v-on:houwang="getStudentName" ref="student" @test="test1"></Student>
</div>
</template>
<script>
import Student from './components/Stundet.vue';
import School from './components/School.vue';
export default {
name:'App',
data:function(){
return {
message:'hello world!'
}
},
components:{
Student:Student,
School:School
},
methods:{
getSchoolName(name){
console.log("已经收到学校的名称:"+name);
},
getStudentName(name){
console.log("自定义组件被触发,收到学生名:"+name);
},
test1(){
console.log("这是一个测试事件");
}
},
mounted(){
console.log("回调钩子被调用");
this.$refs.student.$on("houwang",this.getStudentName);
}
}
</script>
Student.vue(不包含css)
<template>
<div class="student">
<h1>学生:{{name}}</h1>
<h1>年龄:{{sex}}</h1>
<h1 @click="add">{{number}}</h1>
<button @click="sendStudentName">把学生名给App.vue</button>
<button @click="unbind">解绑组件自定义事件</button>
<button @click="destory">销毁当前Student组件的实例</button>
</div>
</template>
<script>
export default {
name:"Student",
data:function(){
return {
name:"猴王",
sex:18,
number:0
}
},
methods:{
sendStudentName(){
//触发Student组件实例身上的houwang事件
this.$emit("houwang",this.name);
this.$emit("test")
},
unbind(){
// this.$off('houwang'); //单个组件自定义事件解绑
console.log("解绑houwang和test两个组件自定义事件");
this.$off(['houwang','test']); //多个组件自定义事件解绑
//this.$off() //全部事件解绑
},
add(){
console.log("add方法被调用");
this.number++;
},
destory(){
this.$destroy(); // 销毁当前Student组件的实例,并且销毁所有事件
console.log("所有事件失效");
}
}
}
</script>
22.全局事件总线
22.1在Vue.prototype挂载参数和组件
main.js代码
import Vue from 'vue'
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//全局事件总线 1
Vue.prototype.x1 = {a:1,b:2}
//全局事件总线 2
//先创建一个空组件
const Demo = Vue.extend({})
//对其进行实例化
const demo = new Demo()
//挂载到全局
Vue.prototype.x2 = demo
new Vue({
render: h => h(App),
//全局事件总线 3 (最推荐的方式)
beforeCreate(){
Vue.prototype.$bus = this //安装全局事件总线
}
}).$mount('#app')
App.vue代码(不包含css)
<template>
<div class="app">
<h1>{{message}}</h1>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student.vue';
import School from './components/School.vue';
export default {
name:'App',
data:function(){
return {
message:'hello world!'
}
},
components:{
Student:Student,
School:School
},
}
</script>
Student.vue(不包含css)
<template>
<div class="student">
<h1>学生:{{name}}</h1>
<h1>年龄:{{sex}}</h1>
<h1>{{number}}</h1>
<button @click="sendStudentName">点击我把学生名发送给School组件</button>
<button @click="sendStudentSex">点击我把学生年龄发送给School组件</button>
</div>
</template>
<script>
export default {
name:"Student",
data:function(){
return {
name:"猴王",
sex:18,
number:0
}
},
methods:{
sendStudentName(){
this.x2.$emit("name",this.name)
},
sendStudentSex(){
this.$bus.$emit("sex",this.sex)
}
}
}
</script>
School.vue(不包含css)
<template>
<div class="school">
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ address }}</h1>
</div>
</template>
<script>
export default {
name: 'School',
data: function() {
return {
name: "猴猴学院",
address: "重庆花果山",
}
},
mounted(){
console.log("我是School组件的回调钩子拿到的x1参数:",this.x1);
//全局事件总线 2
//先绑定一个自定义事件
this.x2.$on("name",(data)=>{
console.log("我是School组件的回调钩子拿到的x2参数:",data);
}),
//全局事件总线 3
this.$bus.$on("sex",(data)=>{
console.log("我是School组件的回调钩子拿到的$bus参数:",data);
})
}
}
</script>
22.3pubsub事件订阅与发布
安装pubsub-js库
npm i pubsub-js
App.vue代码
<template>
<div class="app">
<h1>{{message}}</h1>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student.vue';
import School from './components/School.vue';
export default {
name:'App',
data:function(){
return {
message:'hello world!'
}
},
components:{
Student:Student,
School:School
},
}
</script>
School.vue
<template>
<div class="school">
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ address }}</h1>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name: 'School',
data: function() {
return {
name: "猴猴学院",
address: "重庆花果山",
}
},
mounted(){
//这里如果写普通函数 this指向pubsub这个插件为undefined
this.PubsubId = pubsub.subscribe('hello',(MessageName,MessageContent) => {
console.log(`收到来自${MessageName}的消息:${MessageContent}`);
console.log(this);
})
},
beforeDestroy(){
pubsub.unsubcribe(this.PubsubId)
}
}
</script>
Student.vue
<template>
<div class="student">
<h1>学生:{{name}}</h1>
<h1>年龄:{{sex}}</h1>
<h1>{{number}}</h1>
<button @click="sendStudentName">点击我把学生名发送给School组件</button>
</div>
</template>
<script>
import pubsub from "pubsub-js"
export default {
name:"Student",
data:function(){
return {
name:"猴王",
sex:18,
number:0
}
},
methods:{
sendStudentName(){
pubsub.publish('hello',666)
}
}
}
</script>
效果图
23.TodoList案例进阶版本
App.vue(不包含css)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader @addTodo="addTodo"></MyHeader>
<MyList
:todos="todos"
:ClearAllTodo="ClearAllTodo"
></MyList>
<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @ClearAllTodo="ClearAllTodo"></MyFooter>
</div>
</div>
</div>
</template>
<script>
import pubsub from "pubsub-js";
import MyHeader from "./components/MyHeader";
import MyList from "./components/MyList";
import MyFooter from "./components/MyFooter";
const App = {
components: { MyFooter, MyHeader, MyList },
data: function () {
return {
//第一次打开初始数据为空 后面到todos里面查找
todos: JSON.parse(localStorage.getItem('todos')) || []
};
},
methods:{
//添加数据
addTodo(todoObj){
this._data.todos.unshift(todoObj);
},
//选中和取消input函数
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done;
})
},
//更新数据
updateTodo(id,title){
this.todos.forEach((item)=>{
if(item.id === id ) item.title = title;
})
},
//删除功能
// _占位符因为pubsub要接受一个消息名字的参数
delete_todo(_,id){
this.todos = this.todos.filter((todo)=>{
//过滤出所有你需要的新数据
return todo.id !== id;
})
},
//全选和全不选
checkAllTodo(done){
this.todos.forEach((item)=>{
item.done = done;
})
},
//清除已经完成
ClearAllTodo(){
this.todos = this.todos.filter((todo)=>{
//留下done值为false的
return !todo.done;
})
}
},
watch:{
todos:{
//开启深度监视,因为在保存在本地存储中的todos数据是一个json对象,done属性保存在第二层
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
},
mounted(){
this.$bus.$on("checkTodo",this.checkTodo);
this.$bus.$on("updateTodo",this.updateTodo);
// this.$bus.$on("delete_todo",this.delete_todo)
this.pubsubId = pubsub.subscribe("delete_todo",this.delete_todo);
},
beforeDestroy(){
this.$bus.$off("checkTodo")
// this.$bus.$off("delete_todo")
pubsub.unsubscribe("delete_todo")
}
};
export default App;
</script>
MyHeader.vue(不包含css)
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认"
v-model="inval" @keyup.enter="todo"/>
</div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
name:'MyHeader',
data:function(){
return{
inval:'',
}
},
//第一个版本是通过app.vue传递一个函数过来用于接受,现在是把addTodo写成自定义组件
// props:['addTodo'],
methods:{
//1.获取用户输入的数据
//通过event事件获取事件发生改变的值
// add(event){
// console.log(event.target.value);
// }
//2.通过vue的data数据改变获取
todo(){
//借用nanoid库生成一些随机不唯一的id
const todoObj = {id:nanoid(),title:this.inval,done:false};
//第一个版本是通过app.vue传递一个函数过来用于接受,现在是把addTodo写成自定义组件
// this.addTodo(todoObj)
this.$emit('addTodo',todoObj);
this.inval='';
}
}
}
</script>
MyList.vue(不包含css)
<template>
<ul class="todo-main">
<MyItem
v-for="todoObj in todos"
:key="todoObj.id"
:TodoObjs="todoObj"
/>
</ul>
</template>
<script>
import MyItem from "./MyItem";
export default {
name: "MyList",
components: { MyItem },
props:['todos']
};
</script>
Myitemr.vue(不包含css)
<template>
<div>
<li @mouseover="btn_hover" @mouseleave="btn_out">
<label>
<input type="checkbox" ref="input" :checked="TodoObjs.done" @change="handlechecked(TodoObjs.id)"/>
<span v-show="!TodoObjs.isEdit">{{TodoObjs.title}}</span>
<input type="text"
v-show="TodoObjs.isEdit"
:value="TodoObjs.title"
@keydown.enter="InputBlur(TodoObjs,$event)"
@blur="InputBlur(TodoObjs,$event)"
ref="InputTitle">
</label>
<button class="btn btn-danger" ref="button" style="display: none;" @click="delete_button(TodoObjs.id)">删除</button>
<button v-show="!TodoObjs.isEdit" class="btn btn-Edit" ref="Edit" style="display: none;" @click="handleEdit(TodoObjs)">编辑</button>
</li>
</div>
</template>
<script>
import pubsub from "pubsub-js";
export default {
name: "MyItem",
props:['TodoObjs'],
methods:{
//勾选的功能
handlechecked(id){
// this.checkTodo(id)
this.$bus.$emit("checkTodo",id)
},
//鼠标引入li显示删除按钮
btn_hover(){
this.$refs.button.style.display="block";
this.$refs.Edit.style.display="block";
},
//鼠标移除li隐藏删除按钮
btn_out(){
this.$refs.button.style.setProperty("display","none");
this.$refs.Edit.style.setProperty("display","none");
},
//编辑功能
handleEdit(TodoObjs){
if(TodoObjs.hasOwnProperty("isEdit")){
TodoObjs.isEdit = true;
}else{
this.$set(TodoObjs,"isEdit",true);
}
//点编辑立即得到焦点
//this.$nextTick是等待dom结构渲染完毕才执行
this.$nextTick(function(){
//如果不写在这个函数里面这个代码会和修改显示属性的代码一起执行
//产生修改的dom结构还没有生效,页面上找不到这个input输入框
this.$refs.InputTitle.focus();
})
},
//编辑框失去焦点 修改数据传送值
InputBlur(TodoObjs,e){
TodoObjs.isEdit = false;
if(!e.target.value.trim()) return alert("输入的数据不能为空");
this.$bus.$emit("updateTodo",TodoObjs.id,e.target.value);
},
//删除功能
delete_button(id){
if(confirm("你确定删除吗?")){
// this.delete_todo(id)
// this.$bus.$emit("delete_todo",id)
pubsub.publish("delete_todo",id);
}
}
}
};
</script>
MyFooterr.vue(不包含css)
<template>
<div class="todo-footer" v-show="todos.length">
<label>
<!-- 这里的checked的是根据计算属性isAll来决定要不要加进元素里,并不是isAll就是checked的属性值-->
<input type="checkbox" ref="checkboxstatus" :checked="isAll" @change="checkboxdisplay"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{todos.length}}
</span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'MyFooter',
props:['todos'],
computed:{
doneTotal(){
//pre是上一个返回值 current数组中正在处理的元素
return this.todos.reduce((pre,current) => {
//如果正在执行的数组的done值为真+1
return pre + (current.done ? 1 : 0 );
//0是初始值哦
} ,0)
},
isAll(){
if(this.todos.length == this.doneTotal && this.todos.length > 0) return true;
}
},
methods:{
checkboxdisplay(){
//因为我们要等dom页面渲染完成才可以拿到$refs所以我们嵌套一个this.$nextTick(() => {})函数
this.$nextTick(() => {
this.$emit('checkAllTodo',this.$refs.checkboxstatus.checked);
});
},
clearAll(){
this.$emit('ClearAllTodo')
}
}
}
</script>