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>
-
-
-
组件通信
根据原理的不同区别
-
父组件向子组件传递数据
-
父组件自定义属性并传递给子组件
-
核心
<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, // 必传且是Number default: 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的原型上 -
-
创建两个兄弟组件,之后发现两个兄弟组件中可以拿到相互间的数据
-
发送数据的组件通过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. 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>
-
-
-
-
-
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,就能拿到祖先组件了,里面的数据和方法都能拿到
-
不过后代组件中数据不能直接改祖先组件中数据
- 但可以通过调用祖先组件中的方法来间接修改
-
-
-
最好是当成无响应式效果
-
-
步骤
-
祖先组件通过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标签是没有任何作用的
-
-
-
实例上的方法
-
$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>
-
-
-
-
动态组件
-
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=“footer”+name=“footer”
-
方法2*:
- 不能取默认 default
-
-
根据组件间关系
- 不相关组件-如兄弟间,或不同父组件中不同子组件
- 祖先后代关系
- 父组件–>子组件关系
- 子组件–>父组件关系
查看实例对象的原型
-
使用console.log()/console.dir()打印出对象实例
-
在对象A即
obj.[[Prototype]]属性中点击展开,查看[[Prototype]].constructor的名称为TheFunction1,那么对象A的第1个原型就是TheFunction1.prototype- [[Prototype]]属性后面的名称不太准确,所以要用它的constructor属性的名称来指代;
-
依第2步找下去,就能拿到对象A的第2个原型到第n个原型,直到null;
父子组件的生命周期
- 渲染阶段
- 更新阶段