20230421----重返学习-vue基础-查看实例对象的原型-父子组件的生命周期
day-054-fifty-four-20230421-vue基础-查看实例对象的原型-父子组件的生命周期
vue基础
依据作用范围创建组件
- 局部组件
- 局部组件的创建
- 全局组件
-
创建一个组件()
-
一般在
/src/components/
文件夹中创建 -
使用大驼峰命名法
<template><div class="two-page">two-page</div> </template><script> export default {name: "TwoPage", }; </script><style> </style>
-
-
在
/src/main.js
中,导入组件import TwoPage from "./components/TwoPage.vue"
-
在
/src/main.js
中,声明导入的组件成为全局组件- 语法: Vue.component(组件名称,组件对象)
Vue.component("TwoPage",TwoPage)
-
使用全局组件
- 可以在当前项目所有的vue文件组件中使用
-
一般在template标签中使用
<template><!-- 大驼峰风格方式 --><TwoPage></TwoPage><!-- 连接符风格使用/串式风格 --><two-page></two-page> </template>
-
- 可以在当前项目所有的vue文件组件中使用
-
组件通信
根据原理的不同区别
-
父组件向子组件传递数据
-
父组件自定义属性并传递给子组件
-
核心
<template><one-page :numMsg="num" str="hello"></one-page> </template>
-
例子
<template><OnePage :numMsg="num" :str="str"></OnePage><OnePage :numMsg="num" str="字符串"></OnePage></div> </template><script> import OnePage from "@/components/OnePage.vue"; export default {name: "AboutView",components: {OnePage,},data() {return {num: 200,str: "父组件字符串变量",};}, }; </script>
-
-
子组件通过 props 接收
-
子组的props接收方式有两种
-
数组(不可以进行格式校验)
<template><div><div class="one-page">OnePage---{{ numMsg }}---{{ str }}</div></div> </template> <script> export default {name: "OnePage",props: ["numMsg", "str"], }; </script>
-
对象(可以进行格式校验))
<template><div><div class="one-page">OnePage---{{ numMsg }}---{{ str }}</div></div> </template><script> export default {props: {numMsg: {type: Number, //检测数据类型required: true, //不能为空;即表示不添加该属性,default: 200, //取默认值},str: {type: [Number, String],required: true,default: "aaa",},}, }; </script>
-
可进一步处理
<template><div><div class="one-page">OnePage---{{ numMsg }}---{{ str }}</div></div> </template><script> export default {props: {numMsg: {type: Number, // 基础类型检测, null意味着任何类型都行required: true, // 必传且是Numberdefault: 101, // 数字有默认值},str: {type: [String, Number], // 多种类型default: function () {// 数组、默认值是一个工厂函数返回对象console.log("默认的调用");let res = "默认字符串";return res;},isValid(value) {// 自定义验证函数let res = value > 100 || typeof value === "string";return res;},},}, }; </script>
-
-
-
接收完成后,子组件可以直接使用,但是不允许修改(修改会报警告性错误)
<template><div><input type="text" v-model="str" /><div class="one-page">OnePage---{{ numMsg }}---{{ str }}</div></div> </template> <script> export default {props: {numMsg: {type: Number, //检测数据类型required: true, //不能为空;即表示不添加该属性,default: 200, //取默认值},str: {type: [Number, String],required: true,default: "aaa",},}, }; </script>
-
-
子组件向父组件传递数据
- 原理是发布订阅及组件事件冒泡的方式
- 父子组件传递数据时,只能由父组件直接流向子组件,不能由子组件直接流向父组件。
- 这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
- 父子组件传递数据时,只能由父组件直接流向子组件,不能由子组件直接流向父组件。
- 步骤
-
父组件自定义一个
事件parent-custom-event
-
向父组件的事件池中添加绑定了
事件parent-custom-event
的方法parentFunction
<three-page @parent-custom-event="parentFunction"></three-page> export default{methods:{parentFunction(...args){console.log(args);}} }
-
-
子组件通过
vue实例对象.$emit()
来执行自定义事件parent-custom-event
并且传入要回调的参数,以便将参数传递给父组件- 调用子组件的事件池中的事件,但一般子组件并没有绑定,故而冒泡到父组件,被父组件接收到
- 如果子组件也通过
vue实例对象.$on()
为自定义事件parent-custom-event
绑定了方法childFunction
,那么子组件上方法childFunction
也会被调用。但如果不阻止事件冒泡,父组件依旧可以监听到。
- 如果子组件也通过
this.$emit("parent-custom-event",10,20,this.msg);
-
一般会在子组件的事件中调用,当然也可以在子组件生命周期中调用
<button @click="send">点击按钮触发$emit</button> export default {data(){return {msg:"qqq"}},methods:{send(){this.$emit("parent-custom-event",10,20,this.msg);;}} }
- 调用子组件的事件池中的事件,但一般子组件并没有绑定,故而冒泡到父组件,被父组件接收到
-
由于子组件是在父组件内,子组件执行的
自定义事件parent-custom-event
冒泡到了父组件,故而父组件监听到了事件parent-custom-event
,便会调用父组件上的方法parentFunction
,而在方法parentFunction
中,可以拿到子组件上为自定义事件parent-custom-event
传入的参数。
-
- 原理是发布订阅及组件事件冒泡的方式
-
兄弟组件通信
- 父子组件或子父组件也都可以使用
- 任何组件的通信—发布订阅
- 实际上就是创建了一个vue实例对象来当成中间消息代理IntermediateMessageBroker,它维护着事件类型与订阅者的联系。发送数据的vue组件为发布者,通过调用事件发布了消息–即数据。而接收数据的组件为订阅者,它监听着事件类型,并有着一个专门的事件用于处理对应事件类型被触发后传递的消息。
- 步骤:
-
在/src/main.js中创建一个新的vue实例,并将这个vue实例对象添加到vue原型上
- 这个vue实例实际上是一个发布订阅对象,这个流程中主要用到的也是vue实例中的发布订阅相关属性
- vue实例上与发布订阅相关的方法是通过原型拿到的,实际上是在Vue.prototype上的( e m i t , emit, emit,on,$off等)
- 如果不绑定到vue原型上,后续如果其它要使用,就需要导入再使用
let EventBus=new Vue()//EventBus的原型链:EventBus实例 --> Vue.prototype($emit,$on,$off) --> Object.prototype --> null Vue.prototype.$bus=EventBus; //EventBus添加到Vue的原型上
- 这个vue实例实际上是一个发布订阅对象,这个流程中主要用到的也是vue实例中的发布订阅相关属性
-
创建两个兄弟组件,之后发现两个兄弟组件中可以拿到相互间的数据
-
发送数据的组件通过this. b u s . bus. bus.emit()中触发某个自定义事件发送数据
- 是通过this.$bus拿到事件巴士对应的vue实例组件
- 如果事件巴士对应的vue实例组件没有绑定到vue原型上,那么就要引入并使用
this.$bus.$emit("AAA",100,200)
-
例子
<template><div><button @click="send">点击按钮发送数据</button></div> </template><script> export default {data() {return {msg: "qqq",};},methods: {send() {this.$bus.$emit("AAA", 100, 200, '来自APage');// $emit("XXX", 100, 200, '来自APage') 执行事件池中的方法,并且传递参数},}, }; </script>
- 是通过this.$bus拿到事件巴士对应的vue实例组件
-
要接收数据的组件通过this. b u s . bus. bus.on()中监听某个自定义事件接收数据
- 是通过this.$bus拿到事件巴士对应的vue实例组件
- 如果事件巴士对应的vue实例组件没有绑定到vue原型上,那么就要引入并使用
- 要记得监听与绑定
- 一般是在生命周期中进行监听与解除绑定的
- created()做监听,好像beforeCreate更早,不过不能访问到methods中定义的方法;
- beforeDestroy()中做解除绑定,提高性能
- 一般是在生命周期中进行监听与解除绑定的
this.$bus.$on("AAA",(...args)=>{console.log(args)})
-
例子
<template><div><h1>BBBBBBBBBBB</h1></div> </template><script> export default {name: "BPage",created(){this.$bus.$on('AAA',this.showa)//$on向事件池中添加自定义事件},data() {return {msg: "qqq",};},methods: {showa(...args) {console.log(`this.$bus-->`, this.$bus);console.log(`兄弟组件中的args-->`, args);},}, }; </script>
- 是通过this.$bus拿到事件巴士对应的vue实例组件
-
-
-
vue实例属性-类似于DOM树文档来查找
-
$parent 获取父元素的数据/方法
this.$parent
-
$root 获取根组件的数据/方法
this.$root
-
$children 获取子元素的数据/方法
this.$children
- 一般在mounted钩子函数及之后获取,要有下标
- 不能直接判断那个子元素是自己所需的子元素
- 但可以通过子元素实例上的数据拿到
-
$refs 作用1:获取子组件的方法和数据/作用2:获取DOM元素
- 作用分类
- 作用1:获取子组件的方法和数据(组件标签)
- 作用2:获取DOM元素(普通标签)
- 作用是依据DOM中绑定的是什么来区分的,这个可以主动决定要用那个作用
-
ref绑定的是组件,那么拿到的是vue实例对象
-
vue实例对象里的某个属性上会有vue组件对应DOM元素
<a-page ref="apage"></a-page> mounted(){//必须在mounted 钩子里获取子组件,父子组件生命周期console.log(this.$refs.apage); }
-
-
ref绑定的是普通标签,那么拿到的DOM元素
<h1 ref="hone">1111</h1> mounted(){console.log(this.$refs.hone); }
-
- 作用分类
-
-
祖先后代
- 祖先组件传递数据,默认会失去响应式效果
- 但是如果传递的是对象,对象本身是非响应式的,但对象里面的值如果是响应式的,对象里面的数据不会失去响应式
- 所以也有人定义一个自定义属性并直接传一个this,就能拿到祖先组件了,里面的数据和方法都能拿到
- 不过后代组件中数据不能直接改祖先组件中数据
- 但可以通过调用祖先组件中的方法来间接修改
- 不过后代组件中数据不能直接改祖先组件中数据
- 所以也有人定义一个自定义属性并直接传一个this,就能拿到祖先组件了,里面的数据和方法都能拿到
- 最好是当成无响应式效果
- 但是如果传递的是对象,对象本身是非响应式的,但对象里面的值如果是响应式的,对象里面的数据不会失去响应式
- 步骤
-
祖先组件通过provide发送数据
provide(){,return{n:this.n,m:this.m,//祖先组件传递数据,默认会失去响应式效果obj:this.obj//但是传递的是对象,对象里面的值不会失去响应式效果}}
-
例子
<script> export default {data() {return {n: "12",m: 20,obj: {name: "fang",age: 18,},};},provide() {return {n: this.n,m: this.m,obj: this.obj,};}, }; </script>
或
provide:{n:this.n,//祖先组件传递数据,默认会失去响应式效果m:this.m,obj:this.obj //但是传递的是对象,对象里面的值不会失去响应式效果}
-
例子
<script> export default {data() {return {n: "12",m: 20,obj: {name: "fang",age: 18,},};},provide:{n: this.n,m: this.m,obj: this.obj,} }; </script>
-
-
后代组件通过inject接收
inject:["n","m","obj"],//后代组件接收参数
<script> export default {name: "FangTwoPage",inject: ["n", "m", "obj"], //后代组件接收参数 }; </script>
-
调用时把它当成一个计算属性就行了
<template><div class="two-page"><h1>FangTwoPage</h1><h2>{{n}}--{{m}}--{{obj}}</h2></div> </template> <script> export default {name: "FangTwoPage",inject: ["n", "m", "obj"], //后代组件接收参数 }; </script>
-
-
- 祖先组件传递数据,默认会失去响应式效果
-
vuex
vue组件的其它相关知识
-
修饰符
- 事件.native 在父组件中为子组件上添加事件如click等,触发事件,没有.native无法触发
- 无法在组件直接使用事件,需要加.native修饰符来实现,以便在组件的根元素上监听原生事件
- .native修饰符相当于对于该事件,把vue组件转化成一个普通的HTML标签,并且对普通的HTML标签是没有任何作用的
- 无法在组件直接使用事件,需要加.native修饰符来实现,以便在组件的根元素上监听原生事件
- 事件.native 在父组件中为子组件上添加事件如click等,触发事件,没有.native无法触发
-
实例上的方法
- $mount(“#box”)=====>手动挂载 el 可以替换原来的挂载
- s e t ( 要修改的数据 , 属性名,属性值 ) / set(要修改的数据,属性名,属性值)/ set(要修改的数据,属性名,属性值)/set(要修改的数据,属性名,属性值) ====> 任何时候添加对象属性,页面都会更新
- $destroy()====>销毁 destoryed/beforeDestroy
- $delete(vm.jsona,属性名)====>任何时候删除对象属性,页面都会更新
- $forceUpdate() 强制更新
- $nextTick() 相当于Promise.then
- vue响应式数据变动时
template-->虚拟DOM --->真实DOM
template-->虚拟DOM --->真实DOM
流程,等整个流程都完毕,最后执行
- vue响应式数据变动时
-
样式私有化
- 默认在vue组件中style标签填写样式,是全局样式
- 在style标签上添加scoped的属性,就会变成私有样式
-
原理: 是给标签添加一个唯一属性,样式设置的时候也添加了唯一属性
<template><div class="two-page"><h1>fang-two-page</h1><h2>fang-two-page</h2></div> </template> <style scoped> div h2{background: skyblue;color: red; } h1 {color: blue; } </style>
-
渲染时变成
<template><div data-v-592ad9b2="" class="two-page"><h1 data-v-592ad9b2="">fang-two-page</h1><h2 data-v-592ad9b2="">fang-two-page</h2></div> </template><style> div h2[data-v-592ad9b2]{background: skyblue;color: red; } h1[data-v-592ad9b2] {color: blue; } </style>
-
- 在style标签上添加scoped的属性,就会变成私有样式
- 默认在vue组件中style标签填写样式,是全局样式
-
动态组件
-
component标签+component标签的is属性
<template><button @click="flag='APage'">AA</button><button @click="flag='BPage'">BB</button><component :is="flag"></component> <template> <script> import FangTwoPage from "@/components/FangTwoPage.vue"; import FangPage1 from "@/components/FangPage1.vue";export default {name: "HomeView",components: {// HelloWorld,FangTwoPage,FangPage1}, }; </script>
-
-
- 普通插槽
- 在
设置插槽的组件
中使用<slot></slot>
设置插槽要插入的位置。- slot标签内的内容是该插槽如果不传值时的默认值。
- 在
使用插槽的盒子组件
中于设置插槽组件
内部中添加内容。 - 就会发现插槽组件内部的东西展示在
设置插槽的组件
中的<slot></slot>
的位置。
- 在
- 具名插槽
- 方法1:使用属性slot="header"指定插槽名
- 在
设置插槽的组件
中使用<slot name="插槽名"></slot>
设置插槽要插入的位置,并定义插槽名。- slot标签内的内容是该插槽如果不传值时的默认值。
- 在
使用插槽的盒子组件
中于设置插槽组件
内部中添加内容。 - 就会发现插槽组件内部的东西展示在
设置插槽的组件
中的对应name属性slot插槽所在的位置
- 在
- 方法2:使用指令 v-slot 简写 #
- #default 默认,可以不写name
- 方法1:使用属性slot="header"指定插槽名
- *作用域插槽
- 方法1:
- 可以不写slot=“footer”+name=“footer”
- 方法2*:
- 不能取默认 default
- 方法1:
- 普通插槽
根据组件间关系
- 不相关组件-如兄弟间,或不同父组件中不同子组件
- 祖先后代关系
- 父组件–>子组件关系
- 子组件–>父组件关系
查看实例对象的原型
- 使用console.log()/console.dir()打印出对象实例
- 在对象A即
obj.[[Prototype]]
属性中点击展开,查看[[Prototype]].constructor的名称为TheFunction1,那么对象A的第1个原型就是TheFunction1.prototype- [[Prototype]]属性后面的名称不太准确,所以要用它的constructor属性的名称来指代;
- 依第2步找下去,就能拿到对象A的第2个原型到第n个原型,直到null;
父子组件的生命周期
- 渲染阶段
- 更新阶段