> 文章列表 > 20230421----重返学习-vue基础-查看实例对象的原型-父子组件的生命周期

20230421----重返学习-vue基础-查看实例对象的原型-父子组件的生命周期

20230421----重返学习-vue基础-查看实例对象的原型-父子组件的生命周期

day-054-fifty-four-20230421-vue基础-查看实例对象的原型-父子组件的生命周期

vue基础

依据作用范围创建组件

  • 局部组件
    • 局部组件的创建
  • 全局组件
    1. 创建一个组件()

      • 一般在/src/components/文件夹中创建

      • 使用大驼峰命名法

        <template><div class="two-page">two-page</div>
        </template><script>
        export default {name: "TwoPage",
        };
        </script><style>
        </style>
        
    2. /src/main.js中,导入组件

      import TwoPage from "./components/TwoPage.vue"
      
    3. /src/main.js中,声明导入的组件成为全局组件

      • 语法: Vue.component(组件名称,组件对象)
      Vue.component("TwoPage",TwoPage)
      
    4. 使用全局组件

      • 可以在当前项目所有的vue文件组件中使用
        • 一般在template标签中使用

          <template><!-- 大驼峰风格方式 --><TwoPage></TwoPage><!-- 连接符风格使用/串式风格 --><two-page></two-page>
          </template>
          

组件通信

根据原理的不同区别

  1. 父组件向子组件传递数据

    1. 父组件自定义属性并传递给子组件

      • 核心

        <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>
        
    2. 子组件通过 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>
      
  2. 子组件向父组件传递数据

    • 原理是发布订阅及组件事件冒泡的方式
      • 父子组件传递数据时,只能由父组件直接流向子组件,不能由子组件直接流向父组件。
        • 这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
    • 步骤
      1. 父组件自定义一个事件parent-custom-event

        • 向父组件的事件池中添加绑定了事件parent-custom-event方法parentFunction

          <three-page @parent-custom-event="parentFunction"></three-page>
          export default{methods:{parentFunction(...args){console.log(args);}}
          }
          
      2. 子组件通过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);;}}
          }
          
      3. 由于子组件是在父组件内,子组件执行的自定义事件parent-custom-event冒泡到了父组件,故而父组件监听到了事件parent-custom-event,便会调用父组件上的方法parentFunction,而在方法parentFunction中,可以拿到子组件上为自定义事件parent-custom-event传入的参数。

  3. 兄弟组件通信

    • 父子组件或子父组件也都可以使用
    • 任何组件的通信—发布订阅
      • 实际上就是创建了一个vue实例对象来当成中间消息代理IntermediateMessageBroker,它维护着事件类型与订阅者的联系。发送数据的vue组件为发布者,通过调用事件发布了消息–即数据。而接收数据的组件为订阅者,它监听着事件类型,并有着一个专门的事件用于处理对应事件类型被触发后传递的消息。
    • 步骤:
      1. 在/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的原型上
        
      2. 创建两个兄弟组件,之后发现两个兄弟组件中可以拿到相互间的数据

        1. 发送数据的组件通过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>
            
        2. 要接收数据的组件通过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>
            
  4. 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);
          }
          
  5. 祖先后代

    • 祖先组件传递数据,默认会失去响应式效果
      • 但是如果传递的是对象,对象本身是非响应式的,但对象里面的值如果是响应式的,对象里面的数据不会失去响应式
        • 所以也有人定义一个自定义属性并直接传一个this,就能拿到祖先组件了,里面的数据和方法都能拿到
          • 不过后代组件中数据不能直接改祖先组件中数据
            • 但可以通过调用祖先组件中的方法来间接修改
      • 最好是当成无响应式效果
    • 步骤
      1. 祖先组件通过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>
          
      2. 后代组件通过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>
          
  6. vuex

vue组件的其它相关知识

  • 修饰符

    1. 事件.native 在父组件中为子组件上添加事件如click等,触发事件,没有.native无法触发
      • 无法在组件直接使用事件,需要加.native修饰符来实现,以便在组件的根元素上监听原生事件
        • .native修饰符相当于对于该事件,把vue组件转化成一个普通的HTML标签,并且对普通的HTML标签是没有任何作用的
  • 实例上的方法

    1. $mount(“#box”)=====>手动挂载 el 可以替换原来的挂载
    2. s e t ( 要修改的数据 , 属性名,属性值 ) / set(要修改的数据,属性名,属性值)/ set(要修改的数据,属性名,属性值)/set(要修改的数据,属性名,属性值) ====> 任何时候添加对象属性,页面都会更新
    3. $destroy()====>销毁 destoryed/beforeDestroy
    4. $delete(vm.jsona,属性名)====>任何时候删除对象属性,页面都会更新
    5. $forceUpdate() 强制更新
    6. $nextTick() 相当于Promise.then
      • vue响应式数据变动时 template-->虚拟DOM --->真实DOM
      • template-->虚拟DOM --->真实DOM流程,等整个流程都完毕,最后执行
  • 样式私有化

    • 默认在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>
      
  • 插槽

    1. 普通插槽
      1. 设置插槽的组件中使用<slot></slot>设置插槽要插入的位置。
        • slot标签内的内容是该插槽如果不传值时的默认值。
      2. 使用插槽的盒子组件中于设置插槽组件内部中添加内容。
      3. 就会发现插槽组件内部的东西展示在设置插槽的组件中的<slot></slot>的位置。
    2. 具名插槽
      • 方法1:使用属性slot="header"指定插槽名
        1. 设置插槽的组件中使用<slot name="插槽名"></slot>设置插槽要插入的位置,并定义插槽名。
          • slot标签内的内容是该插槽如果不传值时的默认值。
        2. 使用插槽的盒子组件中于设置插槽组件内部中添加内容。
        3. 就会发现插槽组件内部的东西展示在设置插槽的组件中的对应name属性slot插槽所在的位置
      • 方法2:使用指令 v-slot 简写 #
        • #default 默认,可以不写name
    3. *作用域插槽
      • 方法1:
        • 可以不写slot=“footer”+name=“footer”
      • 方法2*:
        • 不能取默认 default

根据组件间关系

  1. 不相关组件-如兄弟间,或不同父组件中不同子组件
  2. 祖先后代关系
  3. 父组件–>子组件关系
  4. 子组件–>父组件关系

查看实例对象的原型

  1. 使用console.log()/console.dir()打印出对象实例
  2. 在对象A即obj.[[Prototype]]属性中点击展开,查看[[Prototype]].constructor的名称为TheFunction1,那么对象A的第1个原型就是TheFunction1.prototype
    • [[Prototype]]属性后面的名称不太准确,所以要用它的constructor属性的名称来指代;
  3. 依第2步找下去,就能拿到对象A的第2个原型到第n个原型,直到null;

父子组件的生命周期

  • 渲染阶段
  • 更新阶段

进阶参考