Vue核心:组件化编程(脚手架) - 万字总结精华整理

Vue基础:Vue基础
TodoList案例:Vue:实现TodoList案例
cd /d 目标文件夹

一、Vue组件化编程 components

1.1 传统方式编写应用的弊端

  • 引用导致依赖关系混乱,难以维护
  • html中的代码复用率不高
    在这里插入图片描述

1.2 组件式编写应用

  • 组件的概念:实现应用中局部功能代码和资源的集合

  • 组件不仅可以放css,html,js,还可以放mp4,mp3,文字格式等等

  • 组件的作用:复用编码,简化项目编码,提高运行效率
    在这里插入图片描述

在这里插入图片描述

二、非单文件组件

2.1 使用组件的步骤

  • 一个文件中包含n个组件
  • 缺点 : 样式不能跟着组件走

Vue中使用组件的三大步骤:

  1. 定义组件(创建组件)
  2. 注册组件
  3. 使用组件(写组件标签)

1.如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别
区别如下

  • el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
  • data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系
    备注:使用template可以配置组件结构

2.如何注册组件?

  • 局部注册:new Vue的时候传入components选项
  • 全局注册:Vue.component(‘组件名’,组件)

3.编写组件标签:
<school></school>

① 局部注册组件

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>一个重要的内置关系</title>
        <!-- 引入Vue -->
        <script type="text/javascript" src="../js/vue.js"></script>
   </head>
    <body>

        <div id="root">
			<hr>
			<!-- 第三步:编写组件标签 -->
			<school></school>
			<hr>
			<!-- 第三步:编写组件标签 -->
			<student></student>
           <student></student>
        </div>
    </body>

    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

       //第一步:创建school组件
        const school = Vue.extend({
    
     // 传入配置对象
            template:`
               <div>
                    <h2>学校名称:{
     
     {schoolName}}</h2>
                    <h2>学校地址:{
     
     {address}}</h2>
                </div>
           `,
            // el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
            data(){
    
     // data 函数式(普通函数),不能书写成对象时,使用对象式时,有引用关系
                return {
    
    
                    schoolName:'Tom',
                    address:'北京昌平'
                }
            },
       })

       //第一步:创建student组件
		const student = Vue.extend({
    
    
			template:`
				<div>
					<h2>学生姓名:{
     
     {studentName}}</h2>
					<h2>学生年龄:{
     
     {age}}</h2>
				</div>
			`,
			//data一定要写成return的形式,因为return能返回一个新的对象
			//而之前写的对象形式,如果组件被使用两次,那么两个组件实例使用的同一个data
			//a的data数据被修改时,因为数据代理,b中的data数据也被修改
			data(){
    
    
				return {
    
    
					studentName:'张三',
					age:18
				}
			}
		})

        new Vue({
    
    
         el: "#root",
         //第二步:注册组件(局部注册)
		  components:{
    
    
				school, // 组件名 或者 组件名 school:创建组件时的名字
				student
			}
        })


   </script>
</html>

在这里插入图片描述
② 全局注册组件
Vue.component(‘hello’,hello) // 组件的名字 组件的位置

<!DOCTYPE html>
<html>
   <head>
   	<meta charset="UTF-8" />
   	<title>基本使用</title>
   	<script type="text/javascript" src="../js/vue.js"></script>
   </head>
   <body>
   	<div id="root">
   		<!-- 第三步:编写组件标签 -->
   		<school></school>
   		<!-- 第三步:编写组件标签 -->
   		<student></student>
   	</div>

   	<div id="root2">
   		<!-- 第三步:编写组件标签 -->
   		<hello></hello>
   	</div>
   </body>

   <script type="text/javascript">
   	Vue.config.productionTip = false
      
   	//第一步:创建hello组件
   	const hello = Vue.extend({
    
    
   		template:`
   			<div>	
   				<h2>你好啊!{
     
     {name}}</h2>
   			</div>
   		`,
   		data(){
    
    
   			return {
    
    
   				name:'Tom'
   			}
   		}
   	})
   	
   	//第二步:全局注册组件
   	Vue.component('hello',hello) // 组件的名字 组件的位置

   	//创建vm
   	new Vue({
    
    
   		el:'#root2',
   	})
   </script>
</html>

2.2 组件的注意事项

1. 关于组件名:
(1)一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
(2)多个单词组成:
第一种写法(kebab-case命名):my-school ‘my-school’
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1)组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
(2)可以使用name配置项指定组件在开发者工具中呈现的名字

2. 关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注: 不用使用脚手架时,会导致后续组件不能渲染

3. 一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options

2.3 创建组件简写形式*推荐

单文件组件时,常用

const hello = {
    
    
	template:`
		<div>	
			<h2>你好啊!{
     
     {name}}</h2>
		</div>
	`,
	data(){
    
    
		return {
    
    
			name:'Tom'
		}
	}
}

2.4 组件的嵌套

  • 子组件(student 组件)应该写在 父组件(school组件)的上面
  • 父组件(school组件)中使用 子组件(student 组件)

① school 父组件嵌套 student 子组件

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>一个重要的内置关系</title>
        <!-- 引入Vue -->
        <script type="text/javascript" src="../js/vue.js"></script>
   </head>
    <body>

        <div id="root">
        	//子组件不再页面引用,而在父组件中引用
			<school></school>
        </div>
    </body>

    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

       //创建 student 组件
		const student = Vue.extend({
    
    
			template:`
				<div>
					<h2>学生姓名:{
     
     {studentName}}</h2>
					<h2>学生年龄:{
     
     {age}}</h2>
				</div>
			`,
			data(){
    
    
				return {
    
    
					studentName:'张三',
					age:18
				}
			},
		})

       //创建 school组件
        const school = Vue.extend({
    
     // 传入配置对象
        
            //使用子组件
            template:`
               <div>
                    <h2>学校名称:{
     
     {schoolName}}</h2>
                    <h2>学校地址:{
     
     {address}}</h2>
                    <student></student> 
                </div>
           `,
            data(){
    
     
                return {
    
    
                    schoolName:'Tom学校',
                    address:'北京昌平'
                }
            },
            components:{
    
    
				student,
			}
       })

        new Vue({
    
    
         el: "#root",
		  components:{
    
    
				school,
			}
        })

   </script>
</html>

在这里插入图片描述

②定义hello 组件,其与school平级

<div id="root">
	<school></school>
	<hello></hello>
</div>

//定义hello组件
const hello = Vue.extend({
    
    
	template:`<h1>{
     
     {msg}}</h1>`,
	data(){
    
    
		return {
    
    
			msg:'欢迎来和Tom一起学习!'
		}
	}
})

new Vue({
    
    
    el: "#root",
	components:{
    
    
		school,
        hello,
	}
})

开发中常用技巧 app组件

作用: 用于管理应用里面所有的组件(vm之下,所有组件之上)

<div id="root">
	<app></app>
</div>		

//定义app组件
const app = Vue.extend({
    
    
	template:`
		<div>	
			<hello></hello>
			<school></school>
		</div>
	`,
	components:{
    
    
		school, // 引入 school 组件, 不用在引用student 引用父组件即可
		hello
	}
})

//创建vm
new Vue({
    
    
	el:'#root',
	//注册组件(局部)
	components:{
    
    app}
})

在这里插入图片描述

补充: 容器中也可什么都不写

//创建vm
new Vue({
    
    
	template:'<app></app>',
	el:'#root',
	//注册组件(局部)
	components:{
    
    app}
})

2.5 VueComponent构造函数

关于VueComponent

  1. school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的

  2. 我们只需要写<school/><school></school>,Vue解析时会帮我们创建school组件的实例对象,
    即Vue帮我们执行的:new VueComponent(options)

  3. 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent

  4. 关于 this 指向:
    (1) 组件配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】
    (2) new Vue(options)配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】

  5. VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)
    Vue的实例对象,以后简称vm

//不用看,只是怕后面复习不知道在说什么才粘贴进来的
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>VueComponent</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>

		<div id="root">
			<school></school>
			<hello></hello>
			school	
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		
		//定义school组件
		const school = Vue.extend({
    
    
			name:'school',
			template:`
				<div>
					<h2>学校名称:{
     
     {name}}</h2>	
					<h2>学校地址:{
     
     {address}}</h2>	
					<button @click="showName">点我提示学校名</button>
				</div>
			`,
			data(){
    
    
				return {
    
    
					name:'Tom',
					address:'北京'
				}
			},
			methods: {
    
    
				showName(){
    
    
					console.log('showName',this)
				}
			},
		})

		const test = Vue.extend({
    
    
			template:`<span>atguigu</span>`
		})

		//定义hello组件
		const hello = Vue.extend({
    
    
			template:`
				<div>
					<h2>{
     
     {msg}}</h2>
					<test></test>	
				</div>
			`,
			data(){
    
    
				return {
    
    
					msg:'你好啊!'
				}
			},
			components:{
    
    test}
		})


		console.log('@',school) // Vue 的 VueComponent的构造函数
		console.log('#',hello)  // 每次调用都会 创建一个新的 构造函数对象
      	 					    // 每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

		//创建vm
		const vm = new Vue({
    
    
			el:'#root',
			components:{
    
    school,hello}
		})
	</script>
</html>

在这里插入图片描述

vm 管理 一个一个的vc
在这里插入图片描述

2.6 Vue实例与组件实例

vc有的功能vm都有,vm有一个功能vc就没有,vm可以通过el决定来为哪个容器服务,vc不可以
在这里插入图片描述

2.7 重要的内置关系

  1. 一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
  2. 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法

① 原型相关知识

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>一个重要的内置关系</title>
		<!-- 引入Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>

		<div id="root">

		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		//定义一个构造函数
	     function Demo(){
    
    
			this.a = 1
			this.b = 2
		}
		//创建一个Demo的实例对象
		const d = new Demo()

		console.log(Demo.prototype) // 函数身上的 显示原型属性 

		console.log(d.__proto__) // 实例身上的 隐式原型属性  统统都指向的了一个对象 原型对象

		console.log(Demo.prototype === d.__proto__)

		//程序员通过显示原型属性操作原型对象,追加一个x属性,值为99
		Demo.prototype.x = 99

       console.log('@',d.__proto__.x) // 在顺着这条线去找 或者 console.log('@',d.x) 
		console.log('@',d) 

	</script>
</html>

在这里插入图片描述
② 内置关系 prototype.proto === Vue.prototype
解释: 按照原型链的指向,实例的隐式原型属性,应该指向其缔造者的原型对象。故VC的隐式原型属性指向VC的原型对象,且VC的隐式原型属性本应该指向Object的原型对象,但这里VC的隐式原型属性却指向Vue的原型对象
目的: 让组件实例对象(vc)可以访问到 Vue原型上的属性、方法

在这里插入图片描述

在Vue上的x能被组件school访问到

<div id="root">
	<school></school>
</div>
<script type="text/javascript">
	Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
	//在Vue上的x能被组件school访问到
	Vue.prototype.x = 99

	//定义school组件    本质 是VueComponent
	const school = Vue.extend({
    
    
		name:'school',
		template:`
			<div>
				<h2>学校名称:{
     
     {name}}</h2>	
				<button @click="showX">点我输出x</button>
			</div>
		`,
		data(){
    
    
			return {
    
    
				name:'Tom',
			}
		},
		methods: {
    
    
			showX(){
    
    
				//这里可以输出Vue上的x
				console.log(this)
				console.log(this.x)
			}
		},
	})

	//创建一个vm
	const vm = new Vue({
    
    
		el:'#root',
		data:{
    
    
			msg:'你好'
		},
		components:{
    
    school}
	})
</script>

三、单文件组件

安装提示插件:vetur

在这里插入图片描述
① Vue文件的基本结构

安装插件后快捷键:<v

<template>
	 // 组件的结构 
</template>

<script>
	// 组件交互相关的代码(数据、方法等等)
</script>

<style>
	// 组件的样式
</style>

② 组件的定义 基本使用(简单使用)

  • school 组件
<template>
	<div class="demo">
		<h2>学校名称:{
    
    {
    
    name}}</h2>
		<h2>学校地址:{
    
    {
    
    address}}</h2>
		<button @click="showName">点我提示学校名</button>	
	</div>
</template>

<script>
	 export default {
    
     // 默认暴露
		name:'School', // 组件的名字
		data(){
    
    
			return {
    
    
				name:'Tom', // 数据
				address:'北京昌平'
			}
		},
		methods: {
    
    
			showName(){
    
    
				alert(this.name)
			}
		},
	}
</script>

<style>
	.demo{
    
    
		background-color: orange;
	}
</style>
  • student 组件
<template>
	<div>
		<h2>学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生年龄:{
    
    {
    
    age}}</h2>
	</div>
</template>

<script>
	 export default {
    
    
		name:'Student',
		data(){
    
    
			return {
    
    
				name:'张三',
				age:18
			}
		}
	}
</script>
  • App组件
<template>
	// ③ 使用组件
	<div>
		<School></School>
		<Student></Student>
	</div>
</template>

<script>
	// ① 引入组件
	import School from './School.vue'
	import Student from './Student.vue'

	export default {
    
    
		name:'App', // 汇总所有的组件
       // ② 注册组件
		components:{
    
    
			School,
			Student
		}
	}
</script>

③ 创建vm:main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    
    
	el:'#app',
	//后续有介绍render函数
 	render: h => h(App)
})

④ 容器 index.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>练习一下单文件组件的语法</title>
	</head>
	<body>
		<!-- 准备一个容器 -->
		<div id="root">
       	<App></App>
       </div>
		<!-- Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
       <!-- 先让模板出来在 引入main.js 入口文件-->
		<script type="text/javascript" src="./main.js"></script>
	</body>
</html>

逻辑:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、Vue脚手架cli

cli官网

4.1 创建Vue脚手架

在cmd命令窗口下执行下列步骤
1.全局安装@vue/cli
npm install -g @vue/cli
(过程中不用回车,容易报错,实在不动了再回车试试,等很久的建议配置淘宝镜像)
2.创建项目
切换到要创建项目的目录>,使用命令创建项目
cd /d 目标目录
vue create xxx (xxx为自定义项目名)
3.选择运行环境(Vue2或3或自定义)
在这里插入图片描述

出现这个表示创建成功在这里插入图片描述

4.启动项目
cd vue_test
npm run serve
在这里插入图片描述
5.访问项目
可通过 http://localhost:8080/ 访问项目

4.2 Vue脚手架结构

在这里插入图片描述
分析index.html页面结构

//
<!DOCTYPE html>
<html lang="">
 <head>
   <meta charset="utf-8">
		<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
		<!-- 开启移动端的理想视口 -->
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
		<!-- 配置页签图标 -->
   <link rel="icon" href="<%= BASE_URL %>favicon.ico">
		<!-- 引入第三方样式 -->
		<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
		<!-- 配置网页标题 -->
   <title>硅谷系统</title>
 </head>
 <body>
		<!-- 当浏览器不支持js时noscript中的元素就会被渲染 -->
   <noscript>
     <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
   </noscript>
		<!-- 容器 -->
   <div id="app"></div>
   <!-- built files will be auto injected -->
 </body>
</html>

4.3 render函数

关于不同版本的Vue

  1. vue.js与vue.runtime.xxx.js的区别
    (1) vue.js是完整版的Vue,包含:核心功能+模板解析器
    (2) vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器

  2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
    render函数接收到的createElement函数去指定具体内容

① main.js如果直接这么写会报错

//引入Vue
import Vue from 'vue'
//引入App组件,它是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示
Vue.config.productionTip = false


//创建Vue实例对象---vm
new Vue({
    
    
	el:'#app',
	template:`<App></App>`,
	components:{
    
    App},
})

在这里插入图片描述
② 报错原因
引入的vue是阉割版的vue,里面不包含模板解析器
③ 解决方法
解决方法1:使用render(渲染)函数

  • render需要返回值
  • 可以接收参数
//render写成最终简化方式的简化步骤如下
// ① 完整写法
new Vue({
    
    
	el:'#app',
	render(createElement) {
    
     
		return createElement('h1','你好啊')
	}
})
// ② 没有使用到this可以写成箭头函数
new Vue({
    
    
	el:'#app',
	render:(createElement)=> {
    
     
		return createElement('h1','你好啊')
	}
})
// ③ 箭头函数左面含有一个参数 可以省略小括号
new Vue({
    
    
	el:'#app',
	render:createElement=> {
    
     
		return createElement('h1','你好啊')
	}
})
// ④ 箭头函数只有一句函数体,并且还return
new Vue({
    
    
	el:'#app',
	render:createElement=> createElement('h1','你好啊')
})
// ⑤ createElement 使用字母替代 render:q=> q('h1','你好啊')
new Vue({
    
    
	el:'#app',
	render:h=> h('h1','你好啊')
})

// 页面上成功返回
  • 和创建的基础代码 参数 其实还不同
    h1是HTML中的内置元素,里面需要写具体的内容,需要传递第二个参数
    如果使用的是组件 就不用内容
// 两个参数
new Vue({
    
    
	el:'#app',
	render:h=> h('h1','你好啊') 
})
// 一个参数
import App from './App.vue'

new Vue({
    
    
	el:'#app',
   // render函数完成了这个功能:将App组件放入容器中
	render: h => h(App) // 不加入引号 读取变量
})

解决方法2:引入完整版本的Vue

//引入Vue
import Vue from 'vue/dist/vue'

Vue.config.productionTip = false

new Vue({
    
    
	el:'#app',
	template:`<h1>你好啊</h1>`,
})

关于引入阉割版vue而不使用vue的原因
举例:

  • 引入完整版vue:买装修材料 + 买工人,装修完成后还得养着工人
  • 引入阉割版vue:买装修材料 + 雇工人,装修完成后不用养工人

但个人认为实在多此一举了,这个例子也不能让人信服,毕竟工人吃多少饭,完整版vue又能占多少空间?阉割与否就差那么一百多K,随便一张图片都几M了,真的省这么点大小的必要?多写这么个方法就为了解决这么个小问题太费劲了

4.4 修改默认配置

4.4.1 查看默认配置

vue.config.js配置文件

  1. 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
  2. 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

查看Vue的配置文件
vue把核心的配置文件隐藏了,怕用户修改错误,项目跑不起来程序,查看核心配置文件命令:vue inspect > output.js
运行这个命令后会出现 output.js 文件,默认进去报错,在开头加上const a = 就行
在这里插入图片描述
但要注意这个文件只是给你看,修改它并不能真的修改到配置

4.4.2 修改Vue的默认配置

  • 官网左侧栏中的都可以进行修改:https://cli.vuejs.org/zh/config/
  • 新建vue.config.js 文件 和 package.json同级,在vue.config.js中书写需要修改的配置,程序读取时,会把vue.config.js中程序员书写的相关内容,与vue的核心配置文件进行整合,最核心的配置修改不到,之后需要重新的启动npm run serve
//vue.config.js
module.exports = {
    
    
  pages: {
    
    
    index: {
    
    
      //入口
      entry: 'src/main.js',
    },
  },
  lintOnSave:false, //关闭语法检查
}

4.5 ref属性

  1. 被用来个元素或子组件注册引用信息(id的替代者)
    • 应用在html标签上获取真实DOM元素
    • 应用在组件标签上获取组件实例对象(vc)
  2. 使用方式:
    • 打标识: <h1 ref='xxx'>...</h1><School ref="xxx"></School>
    • 获取:this.&refs.xxx
<div>
	<h1 v-text="msg" id="title"></h1>
	<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
	<School ref="sch"/>
	<School id="sch"/>
</div>


<script>
	export default {
    
    
		name:'App',
		components:{
    
    },
		data() {
    
    
		},
		methods: {
    
    
			showDOM(){
    
    
				console.log(document.getElementById("title"))

				console.log(this.$refs.title) 	//1 真实DOM元素
				console.log(this.$refs.btn) 	//2 真实DOM元素
				console.log(this.$refs.sch) 	//3 School组件的实例对象(vc)
			}
		},
	}
</script>

1 输出h1元素
在这里插入图片描述
2 school 用 ref 输出vc实例
在这里插入图片描述

3 school 用 id 输出school组件
在这里插入图片描述

4.6 props配置项(重要)

  1. 功能:让组件接受外部传过来的数据
  2. 传递数据:<Demo name="xxx"/>
  3. 接收数据:
    (1)简单声明接受:props:[‘name’]
    (2)限制类型:props:{name:String}
    (3)限制类型、限制必要性、指定默认值:

备注: props是只读的,Vue底层会监测你是否有对props所接受而来的数据进行了修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,则重新复制一份数据到data中,再修改data中的数据即可

4.6.1 使用proprs

在app.vue中引入student 组件并传递参数

<div>
	<Student name="李四" sex="女" age="18"/>
</div>

student 中的组件数据不能写死,方便不同学生复用

<script>
	export default {
    
    
		name:'Student',
		data() {
    
    
			console.log(this)
			return {
    
    
				msg:'我是一名热爱学习的学生',
			}
		},
		// 1.简单声明接收
		props:['name','age','sex'] 
		
		// 2.接收的同时对数据进行类型限制
		props:{
    
    
			name:String,
			age:Number,
			sex:String
		} 
		// 3.接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
		props:{
    
    
			name:{
    
    
				type:String, //name的类型是字符串
				required:true, //name是必要的
			},
			age:{
    
    
				type:Number,
				default:99 //默认值
			},
			sex:{
    
    
				type:String,
				required:true
			}
		}	
	}
</script>

在这里插入图片描述

如果操作传入过来的数据
如:给年龄加一岁,使用普通参数传参会出现问题

<div>
	<h1>{
    
    {
    
    msg}}</h1>
	<h2>学生姓名:{
    
    {
    
    name}}</h2>
	<h2>学生性别:{
    
    {
    
    sex}}</h2>
	<h2>学生年龄:{
    
    {
    
    age + 1}}</h2>
	//直接这么做学生年龄的结果是181,而不是19
</div>

解决办法:
app.vue中绑定age 的值

  • age 表示的是字符串
  • :age 的值是运行 “18” js表达式 里面执行的结果
<div>
	<Student name="李四" sex="女" :age="18"/>
</div>

4.6.2 注意事项

1. 传入的参数不能随意声明

<script>
	export default {
    
    
		name:'Student',
		data() {
    
    
			console.log(this)
			return {
    
    
				msg:'我是一名热爱学习的学生',
			}
		},
		props:['name','age','sex','phone'] 
	}
</script>

在这里插入图片描述

2. 外部传递的参数不能直接修改
案例: 修改外部传递参数age的数值

<template>
	<div>
		<h1>{
    
    {
    
    msg}}</h1>
		<h2>学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生性别:{
    
    {
    
    sex}}</h2>
		<h2>学生年龄:{
    
    {
    
    age}}</h2>
		<h2>学生年龄:{
    
    {
    
    myAge+1}}</h2>
	</div>
</template>

<script>
	export default {
    
    
		name:'Student',
		data() {
    
    
			console.log(this)
			return {
    
    
				msg:'我是一名热爱学习的学生',
				//用myAge去接收age
				myAge:this.age // props 的优先级更高,能获取到
			}
		},
		methods: {
    
    
			updateAge(){
    
    
				this.myAge++
			}
		},
		//简单声明接收
		props:['name','age','sex'] 
	}
</script>

4.7 mixin配置项(混入/混合)

  1. 功能:可以把多个组件共用的配置提取成一个混入对象
  2. 使用方式:
    • 定义混入
    {
          
          
    	data(){
          
          ...},
    	methods: {
          
          ...}
    	...
    }
    
    • 使用混入
      全局混入:Vue.mixin(xxx)
      局部混入:minxins:[‘xxx’]

基本使用

① 观察 student.vue 和 school.vue可以看到有相同的代码段

student.vue

<template>
	<div>
		<h2 @click="showName">学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生性别:{
    
    {
    
    sex}}</h2>
	</div>
</template>

<script>

	export default {
    
    
		name:'Student',
		data() {
    
    
			return {
    
    
				name:'张三',
				sex:'男'
			}
		},
       methods: {
    
    
           showName(){
    
    
               alert(this.name)
           }
       },
	}
</script>

school.vue

<template>
	<div>
		<h2 @click="showName">学校名称:{
    
    {
    
    name}}</h2>
		<h2>学校地址:{
    
    {
    
    address}}</h2>
	</div>
</template>

<script>

	export default {
    
    
		name:'School',
		data() {
    
    
			return {
    
    
				name:'Tom学校',
				address:'北京',
			}
		},
       methods: {
    
    
           showName(){
    
    
               alert(this.name)
           }
       },
	}
</script>

在这里插入图片描述
② 新建mixin.js(文件名可以自定义)

//分别暴露
export const hunhe = {
    
    
	methods: {
    
    
		showName(){
    
    
			alert(this.name)
		}
	},
	mounted() {
    
    
		console.log('你好啊!')
	},
}
export const hunhe2 = {
    
    
	data() {
    
    
		return {
    
    
			x:100,
			y:200
		}
	},
}

③ 向student.vue 和 school.vue 中分别引入并应用

<script>
	//引入一个hunhe
	import {
    
    hunhe,hunhe2} from '../mixin'

	export default {
    
    
		name:'School',
		data() {
    
    
			return {
    
    
				name:'Tom学校',
				address:'北京',
				x:666
			}
		},
		//应用混合
		mixins:[hunhe,hunhe2],
	}
</script>

混合原则

  • Vue里没有而mixin中有的,将mixin中的混合给Vue
  • Vue和mixin都有的,以Vue中的优先
  • mounted 两者都有的,会都用,且mixin的会先使用
    生命周期不以任何人为主,都要,混合的生命周期在前

全局混合

main.js中引入,而Student.vue 和 School.vue都不进行引入
所有的vc 和 vm 都会得到

//main.js
import {
    
    hunhe,hunhe2} from './mixin'
Vue.mixin(hunhe)
Vue.mixin(hunhe2)

4.8 插件

  1. 功能:用于增强Vue
  2. 本质:包含install方法的一个对象,install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
  3. 定义插件:
对象.install = function(Vue, options){
    
    
	//1. 添加全局过滤器
	Vue.filter(....)
	
	//2. 添加全局指令
	Vue.directive(...)
	
	//3.配置全局混入
	Vue.mixin(...)
	
	//4. 添加实例方法
	Vue.prototype.$myMethod = function(){
    
    ...}
	Vue.prototype.$myProperty = xxx
}
  1. 使用插件:Vue.user()

定义一个Vue插件

export default {
    
    
	install(Vue,x,y,z){
    
    
		console.log(x,y,z)
		//全局过滤器
		Vue.filter('mySlice',function(value){
    
    
			return value.slice(0,4)
		})

		//定义全局指令
		Vue.directive('fbind',{
    
    
			//指令与元素成功绑定时(一上来)
			bind(element,binding){
    
    
				element.value = binding.value
			},
			//指令所在元素被插入页面时
			inserted(element,binding){
    
    
				element.focus()
			},
			//指令所在的模板被重新解析时
			update(element,binding){
    
    
				element.value = binding.value
			}
		})

		//定义混入
		Vue.mixin({
    
    
			data() {
    
    
				return {
    
    
					x:100,
					y:200
				}
			},
		})

		//给Vue原型上添加一个方法(vm和vc就都能用了)
		Vue.prototype.hello = ()=>{
    
    alert('你好啊')}
	}
}

main.js —> 引入、应用插件 在vm之前

//引入插件
import plugins from './plugins'

//应用(使用)插件  
//1,2,3为使用者传参
Vue.use(plugins,1,2,3)

//创建vm
new Vue({
    
    
	el:'#app',
	render: h => h(App)
})

4.9 scoped样式

  1. 作用:让样式在局部生效,防止样式冲突
  2. 写法:<style scoped>

① Student 和 School 组件书写了同类名样式,会发生冲突

// School 组件
	<div class="demo">
		<h2 class="title">学校名称:{
    
    {
    
    name}}</h2>
		<h2>学校地址:{
    
    {
    
    address}}</h2>
	</div>

<style>
	.demo{
    
    
		background-color: skyblue;
	}
</style>

// Student 组件
<template>
	<div class="demo">
		<h2 class="title">学生姓名:{
    
    {
    
    name}}</h2>
		<h2 class="atguigu">学生性别:{
    
    {
    
    sex}}</h2>
	</div>
</template>

<style >
	.demo{
    
    
		background-color: pink;
	}
</style>

// 组件样式之间发生冲突

② 由于app.js中,Student后引入,所以Student的样式会覆盖掉School的

//app.js
<template>
	<div>
		<School/>
		<hr>
		<Student/>
	</div>
</template>

<script>
	import School from './components/School'
	import Student from './components/Student'

	export default {
    
    
		name:'App',
		components:{
    
    School,Student}
	}
</script>

在这里插入图片描述
③ 解决办法:增加scoped属性

// School 组件
<style scoped>
	.demo{
    
    
		background-color: skyblue;
	}
</style>

// Student 组件
<style lang="less" scoped>
	.demo{
    
    
		background-color: pink;
	}
</style>

**④ 原理:**自动增加类名选择器,且名字随机
在这里插入图片描述

⑤ 注意

  • App组件中一般不写scoped,因为会在App中书写的样式,就是想让很多组件使用,没必要加scoped

补充:

  • style 标签中 lang属性可以写 less 或者是 css 样式
  • 如果只写less vue 脚手架处理不了less
  • 不写 lang 默认是css
  • 需要安装 npm i less-loader,注意和webpack 的版本是否冲突,安装 7版本 npm i less-loader@7
  • 查看版本 npm view webpack versions
<style lang="less" scoped>
	.demo{
    
    
		background-color: pink;
        <!-- less 嵌套 -->
		.atguigu{
    
    
			font-size: 40px;
		}
	}
</style>

五、TodoList案例

案例链接:TodoList案例

六、浏览器本地存储

在这里插入图片描述

6.1 localStorage

  • 读的结果不存在时是 null
  • 浏览器关闭,数据不会消失
  • 数据什么时候会消失
    • 引导了用户点击了删除按钮
    • 用户主动地清空缓存

LocalStorage的常用API

// 保存数据到 localStorage
localStorage.setItem('key', 'value');
//只能保存字符串形式,json对象需先转成json字符串
let p = {
    
    name:'张三',age:18}
localStorage.setItem('person',JSON.stringify(p))

// 从 localStorage 获取数据
let data = localStorage.getItem('key');

// 从 localStorage 删除保存的数据
localStorage.removeItem('key');

// 从 localStorage 删除所有保存的数据
localStorage.clear();

// 获取某个索引的Key
localStorage.key(index)

具体使用

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>localStorage</title>
	</head>
	<body>
		<h2>localStorage</h2>
		<button onclick="saveData()">点我保存一个数据</button>
		<button onclick="readData()">点我读取一个数据</button>
		<button onclick="deleteData()">点我删除一个数据</button>
		<button onclick="deleteAllData()">点我清空一个数据</button>

		<script type="text/javascript" >
			let p = {
    
    name:'张三',age:18}

			function saveData(){
    
    
				localStorage.setItem('msg','hello!!!')
				localStorage.setItem('msg2',666)
				localStorage.setItem('person',JSON.stringify(p))
			}
			function readData(){
    
    
				console.log(localStorage.getItem('msg'))
				console.log(localStorage.getItem('msg2'))

				const result = localStorage.getItem('person')
				console.log(JSON.parse(result))

				// console.log(localStorage.getItem('msg3'))
			}
			function deleteData(){
    
    
				localStorage.removeItem('msg2')
			}
			function deleteAllData(){
    
    
				localStorage.clear()
			}
		</script>
	</body>
</html>

6.2 SessionStorage

SessionStorage 主要用于临时保存同一窗口(或标签页)的数据,刷新页面时不会删除,关闭窗口或标签页之后将会删除这些数据
SessionStorage的常用API:

// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');
//只能保存字符串形式,json对象需先转成json字符串
let p = {
    
    name:'张三',age:18}
sessionStorage.setItem('person',JSON.stringify(p))

// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');

// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');

// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear();

// 获取某个索引的Key
sessionStorage.key(index)

具体使用

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>sessionStorage</title>
	</head>
	<body>
		<h2>sessionStorage</h2>
		<button onclick="saveData()">点我保存一个数据</button></br>
		<button onclick="readData()">点我读取一个数据</button></br>
		<button onclick="deleteData()">点我删除一个数据</button></br>
		<button onclick="deleteAllData()">点我清空一个数据</button>

		<script type="text/javascript" >
			let p = {
    
    name:'张三',age:18}

			function saveData(){
    
    
				sessionStorage.setItem('msg','hello!!!')
				sessionStorage.setItem('msg2',666)
				sessionStorage.setItem('person',JSON.stringify(p))
			}
			function readData(){
    
    
				console.log(sessionStorage.getItem('msg'))
				console.log(sessionStorage.getItem('msg2'))

				const result = sessionStorage.getItem('person')
				console.log(JSON.parse(result))

				// console.log(sessionStorage.getItem('msg3'))
			}
			function deleteData(){
    
    
				sessionStorage.removeItem('msg2')
			}
			function deleteAllData(){
    
    
				sessionStorage.clear()
			}
		</script>
	</body>
</html>

七、TodoList本地存储

案例链接:TodoList案例

八、组件的自定义事件 子组件给父组件传

  • 给组件使用
  • 给谁绑的事件,就找谁触发事件
    在这里插入图片描述

8.1 自定义事件–绑定

① 基本代码
School.vue

<template>
	<div class="school">
		<h2>学校名称:{
    
    {
    
    name}}</h2>
		<h2>学校地址:{
    
    {
    
    address}}</h2>
	</div>
</template>

<script>
	export default {
    
    
		name:'School',
		props:['getSchoolName'],
		data() {
    
    
			return {
    
    
				name:'Tom学校',
				address:'北京',
			}
		},
	}
</script>

<style scoped>
	.school{
    
    
		background-color: skyblue;
		padding: 5px;
	}
</style>

Student.vue

<template>
	<div class="student">
		<h2>学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生性别:{
    
    {
    
    sex}}</h2>
	</div>
</template>

<script>
	export default {
    
    
		name:'Student',
		data() {
    
    
			return {
    
    
				name:'张三',
				sex:'男',
			}
		},
	}
</script>

<style lang="less" scoped>
	.student{
    
    
		background-color: pink;
		padding: 5px;
		margin-top: 30px;
	}
</style>

App.vue

<template>
	<div class="app">
		<h1>{
    
    {
    
    msg}}</h1>

		<School/>
		<Student/>
	</div>
</template>

<script>
	import Student from './components/Student'
	import School from './components/School'

	export default {
    
    
		name:'App',
		components:{
    
    School,Student},
		data() {
    
    
			return {
    
    
				msg:'你好啊!',
			}
		},
	}
</script>

<style scoped>
	.app{
    
    
		background-color: gray;
		padding: 5px;
	}
</style>

在这里插入图片描述
② School 组件有个按钮,点击按钮把学校名交给App,即子组件传递给父组件
App.vue

<template>
	<div class="app">
       <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
		<School :getSchoolName="getSchoolName"/>
		<Student/>
	</div>
</template>

<script>

	export default {
    
    
		name:'App',
		components:{
    
    School,Student},
		methods: {
    
    
			getSchoolName(name){
    
    
				console.log('App收到了学校名:',name)
			},
		},
	}
</script>

School.vue

<template>
	<div class="school">
		<button @click="sendSchoolName">把学校名给App</button>
	</div>
</template>

<script>
	export default {
    
    
		props:['getSchoolName'],
		methods: {
    
    
			sendSchoolName(){
    
    
				this.getSchoolName(this.name)
			}
		},
	}
</script>

在这里插入图片描述
③ 把学生名给App 换一种方式:使用自定义事件

自定义绑定事件方式一:v-on / @

App.vue

<template>
   <div class="app">
   	<h1>{
    
    {
    
    msg}}</h1>

   	<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
   	//<School :getSchoolName="getSchoolName"/>

   	<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
   	//<Student v-on:atguigu="getStudentName"/>
   	<Student @atguigu="getStudentName"/>
   </div>
</template>

<script>
   export default {
    
    
   	methods: {
    
    
   		getSchoolName(name){
    
    
   			console.log('App收到了学校名:', name)
   		}
   	},
   }
</script>

Student.vue

<template>
   <div class="student">
   	<button @click="sendStudentlName">把学生名给App</button>
   </div>
</template>

<script>
   export default {
    
    
   	methods: {
    
    
   		sendStudentlName(){
    
    
   			//触发Student组件实例身上的atguigu事件
   			this.$emit('atguigu',this.name) 
   		},
   	},
   }
</script>

二者之间的异同点

  • propes 和 自定义事件 都需要两个回调
  • propes给了 School组件一个方法,School组件调用了这个方法
  • 自定义事件并没有给 Student 组件什么方法,只是绑定了一个自定义事件,做为自定义事件的回调在使用
  • Student 组件触发自定义事件会传参

自定义绑定事件方式二:$ref

  • 麻烦但灵活性强
  • 如:定时器,等五秒后在绑定自定义事件

app.vue
在app组件里面,通过app的vc(this.$refs.student),就可以获取到Student组件的实例对象

	<div class="app">

		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
		<!-- <Student @atguigu="getStudentName" @demo="m1"/> -->

		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
		<Student ref="student"/>
	</div>

<script>
	export default {
    
    
		// app 挂载完毕时触发mounted
		mounted() {
    
     
          	// this.$refs.student 是 Student组件的实例对象  
          	
          	//这里相当于同时在student上绑定事件,并等待事件触发
          	//$on 当atguigu 被触发的时候 触发回调
			this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件
           	// this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件(一次性) 触发一次后就不能再触发了

			//定时器	触发事件后3秒后再启用getStudentName函数
			setTimeout(()=>{
    
    
				this.$refs.student.$on('atguigu',this.getStudentName)},3000)
		
		},
		methods: {
    
    
			getSchoolName(name){
    
    
				console.log('App收到了学校名:', name)
			}
		},
	}
</script>

触发事件时传递多个参数

// 方式一

school.vue
<script>
		methods: {
    
    
			sendStudentlName(){
    
    
				this.$emit('atguigu',this.name,666,888,900)
			},
		},
</script>

app.vue
<script>
		methods: {
    
    
			getStudentName(name,x,y,z){
    
    
				console.log('App收到了学生名:',name,x,y,z)
			},
		},
	}
</script>
开发中的方式
// 方式一 :
	把数据包装成一个对象传递过去
school.vue
<script>
		methods: {
    
    
			sendStudentlName(){
    
    
				this.$emit('atguigu',{
    
    })
			},
		},
</script>

// 方式二:
	es6 写法  正常传递,接收
school.vue
<script>
		methods: {
    
    
			sendStudentlName(){
    
    
				this.$emit('atguigu',this.name,666,888,900)
			},
		},
</script>

app.vue
<script>
		methods: {
    
    
           // name 正常结构,其他的参数不管传递多少,整理到params数组上
			getStudentName(name,...params){
    
    
				console.log('App收到了学生名:',name,params)
			},
		},
	}
</script>

8.2 解绑事件

Student.vue

<template>
	<div class="student">
		<button @click="add">点我number++</button>
		<button @click="unbind">解绑atguigu事件</button>

</template>

<script>
	export default {
    
    

		methods: {
    
    
			unbind(){
    
    
				this.$off('atguigu') //解绑一个自定义事件
				// this.$off(['atguigu','demo']) //解绑多个自定义事件
				// this.$off() //解绑所有的自定义事件
			}
		},
	}
</script>

案例: app在收到Student传入的姓名后,将姓名呈现在页面上

方法一:使用@或v-on

<template>
	<div class="app">
		<h1>{
    
    {
    
    msg}},学生姓名是:{
    
    {
    
    studentName}}</h1>

		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
		<Student @atguigu="getStudentName" @demo="m1"/>
	</div>
</template>

<script>

	export default {
    
    
		data() {
    
    
			return {
    
    
				msg:'你好啊!',
				studentName:''
			}
		},
		methods: {
    
    
			getStudentName(name,...params){
    
    
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			}
		},
	}
</script>

方法二:使用$ref

<template>
	<div class="app">
		<h1>{
    
    {
    
    msg}},学生姓名是:{
    
    {
    
    studentName}}</h1>

		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
		<Student ref="student"/>
	</div>
</template>

<script>

	export default {
    
    
		data() {
    
    
			return {
    
    
				msg:'你好啊!',
				studentName:''
			}
		},
		methods: {
    
    
           getStudentName(name,...params){
    
    
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			}
		},
		mounted() {
    
    
			this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件
		},
	}
</script>

注意:

  • 不能将 getStudentName 以普通函数的方法写在$on的回调函数中
    • 因为谁触发了 atguigu 事件,事件当中会掉的this就是谁,所以此时是Student组件的vc
  • 可以getStudentName 以箭头函数的方法写在$on的回调函数中,但一般不这么写
    • 箭头函数没有实例对象,向上查找,找到 mounted 钩子,此时是App
<script>

	export default {
    
    
		data() {
    
    
			return {
    
    
				msg:'你好啊!',
				studentName:''
			}
		},
		/* methods: {
           getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			}
		},*/ 
		//以下这么写不行
		/*
		mounted() {
			this.$refs.student.$on('atguigu',function(name,...params){
				...
			}) //绑定自定义事件
           	console.log(this) // 此时是this 是Student组件的vc
		},
		*/ 
		//以下这么写可以,但一般没必要
		mounted() {
    
    
			this.$refs.student.$on('atguigu',(name,...params)=>) //绑定自定义事件
           	console.log(this) // 此时是this 是Student组件的vc
		},
	}

8.3 组件绑定事件默认不使用内置事件

// 这么写会被默认当做自定义事件
<Student ref="student" @click="show"/> 

//加上native 原生的,本来的,才会使用到内置事件
<Student ref="student" @click.native="show"/> 

九、全局事件总线 重要:任意组件间通信

9.1 基本使用

总结:在这里插入图片描述
理解:

  • 在x这个组件上绑定事件,通过其进行数据中转
  • 想获取事件的就向X上绑定事件,想传递数据的就$emit这个事件并携带参数
  • 如图中A组件向x绑定demo事件,D通过demo事件向A传递数据666
    在这里插入图片描述
    ① 基本代码

Student.vue

<template>
	<div class="student">
		<h2>学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生性别:{
    
    {
    
    sex}}</h2>
	</div>
</template>

<script>
	export default {
    
    
		name:'Student',
		data() {
    
    
			return {
    
    
				name:'张三',
				sex:'男',
			}
		}
	}
</script>

School.vue

<template>
	<div class="school">
		<h2>学校名称:{
    
    {
    
    name}}</h2>
		<h2>学校地址:{
    
    {
    
    address}}</h2>
	</div>
</template>

<script>
	export default {
    
    
		name:'School',
		data() {
    
    
			return {
    
    
				name:'Tom',
				address:'北京',
			}
		},
	}
</script>

App.vue

<template>
	<div class="app">
		<h1>{
    
    {
    
    msg}}</h1>
		<School/>
		<Student/>
	</div>
</template>

<script>
	import Student from './components/Student'
	import School from './components/School'

	export default {
    
    
		name:'App',
		components:{
    
    School,Student},
		data() {
    
    
			return {
    
    
				msg:'你好啊!',
			}
		}
	}
</script>

② 实现让所有组件都能看到 组件$bus(即总线)
放在main.js中的 beforeCreate
main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

//创建vm
new Vue({
    
    
	el:'#app',
	render: h => h(App),
	beforeCreate() {
    
    
		//绑定在Vue的实例对象上才可以让所有组件“看到”
		//$bus = this则是想为$bus绑定一个Vue实例对象,使得$bus可以使用$on,$off,$emit
		Vue.prototype.$bus = this //全局事件总线
	},
})

③ Student.vue 传给 School 数据
School.vue

<script>
	export default {
    
    
		mounted() {
    
    
			this.$bus.$on('hello',(data)=>{
    
    
				console.log('我是School组件,收到了数据',data)
			})
		},
		beforeDestroy() {
    
     
			this.$bus.$off('hello') // 销毁 $bus上的事件
			//之前不用销毁是因为事件都绑定在足尖上,组件一销毁,事件也跟着销毁
		},
	}
</script>

Student.vue

<template>
	<div class="student">
		<h2>学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生性别:{
    
    {
    
    sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	export default {
    
    
		methods: {
    
    
			sendStudentName(){
    
    
				this.$bus.$emit('hello',this.name)
			}
		},
	}
</script>

9.2 TodoList事件总线

案例链接:TodoList案例

十、消息订阅与发布 任意组件间通信

10.1 总结

在这里插入图片描述

10.2 理解

  • 谁需要数据谁订阅,谁提供数据谁发布
    在这里插入图片描述

10.3 消息订阅与发布的第三方库PubSubJS

使用 PubSubJS

  1. 在线文档: https://github.com/mroderick/PubSubJS

  2. 安装插件: npm i pubsub-js

  3. 相关语法
    (1) import PubSub from ‘pubsub-js’ // 引入
    (2) PubSub.subscribe(‘msgName’, functon(msgName, data){ })
    (3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
    (4) PubSub.unsubscribe(token): 取消消息的订阅

具体案例

  • School 组件订阅消息
  • Student 发布消息

School.vue

<template>
	<div class="school">
		<h2>学校名称:{
    
    {
    
    name}}</h2>
		<h2>学校地址:{
    
    {
    
    address}}</h2>
	</div>
</template>

<script>
	import pubsub from 'pubsub-js'
	export default {
    
    
		name:'School',
		data() {
    
    
			return {
    
    
				name:'Tom',
				address:'北京',
			}
		},
		mounted() {
    
    
			// 执行的回调函数中,第一个参数是 函数名(即hello),第二个参数是传过来的 数据
			// subscribe 订阅 
			//每个订阅会返回唯一性id
			this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
    
     
				
				// 如果是普通函数,使用 pubsub 库,则this是undefined  
              	//故应使用箭头函数,则this是vc School 
              	console.log(this) 
              	
               	console.log('有人发布了hello消息,heVlo消息的回调执行了',msgName,data)
           })
		},
		beforeDestroy() {
    
    
			//销毁要用唯一性id销毁
			pubsub.unsubscribe(this.pubId) // 解绑
			订阅
		},
	}
</script>

Student.vue

<template>
	<div class="student">
		<h2>学生姓名:{
    
    {
    
    name}}</h2>
		<h2>学生性别:{
    
    {
    
    sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	import pubsub from 'pubsub-js'
	export default {
    
    
		name:'Student',
		data() {
    
    
			return {
    
    
				name:'张三',
				sex:'男',
			}
		},
		mounted() {
    
    
		},
		methods: {
    
    
			sendStudentName(){
    
    
				pubsub.publish('hello',666) // publish发布hello,并携带数据666
			}
		},
	}
</script>

在这里插入图片描述

十一、TodoList编辑功能

TodoList案例博客
$nextTick 重要

  1. 语法:`this.$nextTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调
  3. 在改变数据后要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

十二、过度与动画

在这里插入图片描述

12.1 动画

  • 让谁有动画的效果就用transition标签给包裹起来

  • template 中所实现的名字是固定

    • 三个样式的类名

    • v-enter 进入的起点 反之对应 v-leave 离开的起点

    • v-enter-active 进入过程中 反之对应 v-leave-active 离开过程中

      • enter进入时的动画 active激活 进入时要激活的样式
      • leave离开 离开时要激活的样式
  • v-enter -to 进入的终点 反之对应 v-leave-to 离开的终点

  • v 可以替换成过度时的名字

  • template 加上appear属性可以使得页面在渲染时就直接呈现动画效果

app.vue

<template>
	<div>
		<Test/>
		<Test2/>
		<Test3/>
	</div>
</template>

<script>
	import Test from './components/Test'
	import Test2 from './components/Test2'
	import Test3 from './components/Test3'

	export default {
    
    
		name:'App',
		components:{
    
    Test,Test2,Test3},
	}
</script>

test.vue
动画效果页面左边缘向右出现出现,向左离开
① 基本代码

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<h1 v-show="isShow">你好啊!</h1>
	</div>
</template>

<script>
	export default {
    
    
		name:'Test',
		data() {
    
    
			return {
    
    
				isShow:true
			}
		},
	}
</script>

<style scoped>
	h1{
    
    
		background-color: orange;
	}
</style>

在这里插入图片描述
② 实现

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<!-- transition 过度 appear 呈现  hello 过度时起的名字-->
		<transition name="hello" appear>
			<h1 v-show="isShow">你好啊!</h1>
		</transition>
	</div>
</template>

<script>
	export default {
    
    
		name:'Test',
		data() {
    
    
			return {
    
    
				isShow:true
			}
		},
	}
</script>

<style scoped>
	h1{
    
    
		background-color: orange;
	}
	/* css3知识 */
	/* 使用动画 两个样式的类名  linear 匀速 reverse 反转*/
	.hello-enter-active{
    
    
		animation: atguigu 0.5s linear;
	}

	.hello-leave-active{
    
    
		animation: atguigu 0.5s linear reverse;
	}
	/* 动画定义一个关键帧  名字可以随意 */
	@keyframes atguigu {
    
     
		from{
    
    
			/* 来 */
			transform: translateX(-100%); 
		}
		to{
    
    
			/* 来到 */
			transform: translateX(0px);
		}
	}
</style>

在这里插入图片描述
在这里插入图片描述

12.2 过度

实现效果类同动画(0到100%)
test2.vue —> test1.vue 的效果

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<!-- transition 过度 appear 呈现  hello 过度时起的名字-->
		<transition name="hello" appear>
			<h1 v-show="isShow">你好啊!</h1>
		</transition>
	</div>
</template>

<script>
	export default {
    
    
		name:'Test',
		data() {
    
    
			return {
    
    
				isShow:true
			}
		},
	}
</script>

<style scoped>
	h1{
    
    
		background-color: orange;
	}
	/* 进入的起点、离开的终点 */
	.hello-enter,.hello-leave-to{
    
    
		transform: translateX(-100%);
	}
	/* 来的激活时的样式,离开时激活的样式 */
	.hello-enter-active,.hello-leave-active{
    
    
		transition: 0.5s linear;
	}
	/* 进入的终点、离开的起点 */
	.hello-enter-to,.hello-leave{
    
    
		transform: translateX(0);
	}
</style>

12.3 多个元素同样的过度效果

错误写法一:

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<!-- transition 过度 appear 呈现  hello 过度时起的名字-->
		<transition name="hello" appear>
			<h1 v-show="isShow">你好啊!</h1>
			<h1 v-show="isShow">我很好谢谢!</h1>
		</transition>
	</div>
</template>

只有一个展示
在这里插入图片描述
错误写法二:

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<!-- transition 过度 appear 呈现  hello 过度时起的名字-->
		<transition-group name="hello" appear>
			<h1 v-show="isShow">你好啊!</h1>
			<h1 v-show="isShow">我很好谢谢!</h1>
		</transition-group >
	</div>
</template>

在这里插入图片描述
正确方式一:

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<!-- transition 过度 appear 呈现  hello 过度时起的名字-->
		<!-- 真是列表中使用 v-for 生成 key 值--> 
		<transition-group name="hello" appear>
			<h1 v-show="isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">我很好谢谢!</h1>
		</transition-group >
	</div>
</template>

正确方式二:
使用transition标签,使用div标签把两个h1标签包裹起来 但是两个展示效果不能互斥,互斥无法实现,互斥只能使用 transition-group 显示取反

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<!-- transition 过度 appear 呈现  hello 过度时起的名字-->
		<!-- 真是列表中使用 v-for 生成 key 值--> 
		<transition-group name="hello" appear>
           <div>
               <h1 v-show="isShow" key="1">你好啊!</h1>
				<h1 v-show="isShow" key="2">我很好谢谢!</h1>
   		</div>
		</transition-group >
	</div>
</template>

在这里插入图片描述

12.4 集成第三方动画

npm-animate库链接
在这里插入图片描述
① 基本代码

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<transition-group >
			<h1 v-show="!isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">我很好谢谢你</h1>
		</transition-group>
	</div>
</template>

<script>
	import 'animate.css'
	export default {
    
    
		name:'Test',
		data() {
    
    
			return {
    
    
				isShow:true
			}
		},
	}
</script>

<style scoped>
	h1{
    
    
		background-color: orange;
	}
</style>

② 使用

  • 安装 npm install animate.css
  • 引入 import ‘animate.css’ 样式
<template>
   <div>
   	<button @click="isShow = !isShow">显示/隐藏</button>
   	<!-- 首页中有很多样式和动画,直接左侧还有演示直接复制即可 -->
   	<transition-group 
   		appear
   		name="animate__animated animate__bounce" 
   		enter-active-class="animate__swing"
   		leave-active-class="animate__backOutUp"
   	>
   		<h1 v-show="!isShow" key="1">你好啊!</h1>
   		<h1 v-show="isShow" key="2">我很好谢谢你</h1>
   	</transition-group>
   </div>
</template>

12.5 TodoList过度与动画

TodoList案例博客

猜你喜欢

转载自blog.csdn.net/m0_51487301/article/details/126141970