# 生命周期和数据共享

# 1.import路径优化插件Path Autocomplete

set.json中添加以下设置

"path-autocomplete.pathMappings": {
    "@":"${folder}/src"
},
"path-autocomplete.extensionOnImport": true

这个插件的作用和webpack配置项resolve.alias的作用是一样的

resolve: {//配置模块解析
    // 创建important或者require的别名,可以将特定的文件或者目录解析成别名,导入时只需导入key即可
    alias: {
        // 告诉webpack,程序员写的代码中,@表示src这一层目录
        "@":resolve(__dirname, './src')
    }
}

# 2.自定义属性props

自定义属性节点props:['属性名','属性名'...],设置组件的自定义属性,外部导入组件时可以设置不同的值,增加组件的复用性,props中的数据,可以直接在模版结构中被使用

注意:props是只读的,不要直接修改props的值,虽然可以修改成功,但是终端仍然会报错

//方式一,数组形式
props: ["init"], 

//方式二, 若想进行更加精细化的设置,则需要设置成对象节点
props: {
    // 自定义属性A:{属性配置内容}
    // 自定义属性B:{属性配置内容}
    // 自定义属性C:{属性配置内容}
    init: {
        // 默认值
        default: 0,
        //type限制自定义属性的值类型,类型不一致时会报错
        type: Number,
        //必填项校验,外部调用时若不指定,即使设置了默认值也会报错
        required: true,
    },
}

# 3.样式冲突

特别是在单页面程序中容易出现,因为所有组件的样式都会被渲染到同一个HTML页面中,这是组件中设置的样式就会变成全局样式

  1. scoped创建组件样式范围
<style lang="less" scoped>

// scoped会自动的给当前组件模板添加自定义属性data-v-...,从而防止样式冲突
// 当前组件中的样式也会自动使用对应的属性选择器[data-v-...]

h3 {
  color: red;
}

</style>
  1. /deep/ 样式穿透
// h5[data-v-3c83f0b7]
// [data-v-3c83f0b7] h5

// 当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到 /deep/ 
// 样式穿透,本质上是限制属性的后代选择器
/deep/ h5 {
  color: pink;
}

# 4.生命周期LifeCycle

创建阶段:

beforeCreatecreated(重要)、beforeMountmounted(重要)

运行阶段:

beforeUpdateUpdated(重要)

销毁阶段:

beforeDestroydestroyed

<template>
  <div class="test-container">
    <h3 id="myh3">Test.vue 组件 --- {{ books.length }} 本图书</h3>
    <p id="pppp">message 的值是:{{ message }}</p>
    <button @click="message += '~'">修改 message 的值</button>
  </div>
</template>

<script>
export default {
  props: ["info"],
  data() {
    return {
      message: "hello vue.js",
      books: [],
    };
  },
  methods: {
    show() {
      console.log("调用了 Test 组件的 show 方法!");
    },
    initBookList() {
      let xhr = new XMLHttpRequest();
      xhr.open("GET", "http://www.liulongbin.top:3006/api/getbooks");
      xhr.send();

      xhr.addEventListener("load", () => {
        const result = xhr.responseText;
        // console.log(result);
        this.books = JSON.parse(result).data;
      });
    },
  },


  // 创建阶段
  beforeCreate() {
    // 这个阶段只初始化了 事件 和 生命周期函数,所以vue组件的自定义属性、数据、方法等都不可用,
    // 这个生命周期函数不常用
     console.log(this.info); // Cannot read properties of undefined (reading 'info')"
     console.log(this.message); //undefined
     this.show(); //this.show is not a function"
  },

  created() {
    // 这个阶段对组件的自定义属性props、数据data、方法methods等进行了初始化
    // 一般在这个生命周期函数使用Ajax请求数据
    // 很重要!很重要!很重要!

     console.log(this.info); // 这是在外部定义的变量
     console.log(this.message); //hello vue.js
     this.show(); //调用了 Test 组件的 show 方法!

    // 现在可以使用方法了,所以在这里调用Ajax
    this.initBookList();
  },

  beforeMount() {
    // 根据数据和模版,在内存中生成HTML结构,但是还没有渲染到浏览器当中,也不常用
     console.log('beforeMount');
    let dom = document.querySelector('.test-container');
    console.log(dom); // null
    console.log(this.$el); //undefined
  },

  mounted() {
    // 用内存中编译生成的HTML结构替换掉 el属性 指定的dom元素
    // 如果要操作当前组件渲染的DOM元素,也是在这一步操作,
    // 很重要!很重要!很重要!
    
    console.log('mounted');
    console.log(this.$el); // <div data-v-dc87507c...
    let dom = document.querySelector('.test-container');
    console.log(dom); // <div data-v-dc87507c... 
   
  },

    
  // 运行阶段
  beforeUpdate() {
    // 数据更新了,但是没有渲染到HTML页面
    
    // 点击修改message的值
    console.log('beforeUpdate'); 
    console.log(this.message); // hello vue.js~
    let dom = document.querySelector('#pppp');
    console.log(dom); // <div> hello vue.js </div>
    
  },

  updated() {
    // 根据最新的数据,完成HTML页面的渲染
    // 当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中
    // 很重要!很重要!很重要!
   
    // 点击修改message的值
    console.log('updated'); 
    console.log(this.message); // hello vue.js~
    let dom = document.querySelector('#pppp');
    console.log(dom); // <div> hello vue.js~ </div>
    
  },

  //  销毁阶段
  beforeDestroy() {
    //尚未销毁,所有数据都能操作,不常使用,不太重要
    console.log('beforeDestroy'); 
    this.message='111'
    console.log(this.message); // 111
   
  },

  destroyed() {
    // HTML中渲染的DOM已经被销毁,但是还能访问到组件的数据和方法,也不常用
    console.log("destroyed");
    this.message = "aaa";
    console.log(this.message); // aaa
    let dom = document.querySelector(".test-container");
    console.log(dom); // null
    this.show(); //调用了 Test 组件的 show 方法!
    console.log(this.info); //这是在外部定义的变量
  },
};
</script>

# 5.组件间的数据共享

# 父组件 -> 子组件

通过自定义属性props,父组件的创建子组件的实例是可以通过v-bind指令给自定义属性传递数据

// Parent.vue
<!-- 父组件给子组件传递数据,通过父组件给只读自定义属性props赋值 -->
 <Left :msg='sendMsgForSon'></Left>
<script>
data(){
	return {
		sendMsgForSon:'this is from father\'s message'
	}
}
</script>


// Son.vue 
<p>msg 的值是:{{ msg }}</p>
<script>
props:['msg']
</script>

# 子组件 -> 父组件

通过$emit('事件名',事件对象e)在子组件Son中触发自定义事件,然后附加参数 “事件对象e” 会传给父组件中注册的自定义事件监听器的回调函数,如果直接把数据传过来的话,那么回调getCount(e)中的e就是对应的数据

// Parent.vue
<Son @changeCount='getCount'></Son>
<script>
data(){
    return {
      count:0
    }
},
methods:{
    getCount(e){
      console.log('获得了来自子组件Right的数据!');
      // 将e的值赋给这个组件的count
      this.count = e;
    }
}
</script>


// Son.vue
<script>
data() {
    return {
        count: 0
    };
},
methods:{
   add(){
        this.count++;
        // console.log('调用了自增方法');
        // 触发自定义事件changeCount,将自增的值传给父组件App
        this.$emit('changeCount',this.count);
    }
}
</script>

其实vue事件指令v-on:所监听事件的触发,使用的也是这个原理,例如(下面的是伪代码,但是原理就是这样的):

this.$emit('click',{
    clickX:'',
    ClickY:'',
    target: 对应的dom元素
})

# 兄弟组件/没有引用关系的组件间的数据传递

首先需要创建一个中转站componentBus.js,命名随意

//componentBus.js

import Vue from 'vue';
// 作为信息中转站,只需要导出一个Vue实例即可
export default new Vue();

然后通过$emit('事件名',事件对象e)在发送组件Send中调用bus模块的实例触发自定义事件

// Send.vue

// 导入中转站bus
import bus from '@/components/componentBus.js'
export default {
    methods:{
        send()
            //调用bus模块的实例触发自定义事件
            bus.$emit('share',this.sendMsgForBrother);
        }
    }
}

最后在接收组件Receive中通过事件对象e来接收,如果直接把数据传过来的话,那么回调getCount(e)中的e就是对应的数据

// Receive.js

// 导入中转bus
import bus from '@/components/componentBus.js'

// 在事件初始化之后接受来自Send的数据

export default {
    data() {
        return {
            message:''
        };
    },
    // 在created生命周期函数中注册该自定义事件监听器,获取相应数据
    created(){
        // 注意,这里一定要用箭头函数,因为this指向的问题,不用的话也需要在事件函数外部先声明this的值
        bus.$on('share',(e)=>{
            console.log('Send的分享方法被触发了');
            // 给接收数据的变量赋值
            this.message = e;
        })
    }
}