vue生命周期钩子及组件

生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

生命周期图示

vue生命周期钩子及组件
部分参考(https://segmentfault.com/a/1190000008010666?utm_source=tag-newest)
beforecreated:el 和 data 并未初始化
created:完成了 data 数据的初始化,el没有
beforeMount:完成了 el 和 data 初始化
mounted :完成挂载
另外在标红处,我们能发现el还是 {{message}},这里就是应用的 Virtual DOM(虚拟Dom)技术,先把坑占住了。到后面mounted挂载的时候再把值渲染进去。
vue生命周期钩子及组件
vue生命周期钩子及组件
这里我们在 chrome console里执行以下命令

app.message= ‘yes !! I do’;
下面就能看到data里的值被修改后,将会触发update的操作。
vue生命周期钩子及组件
destroy 相关
有关于销毁,暂时还不是很清楚。我们在console里执行下命令对 vue实例进行销毁。销毁完成后,我们再重新改变message的值,vue不再对此动作进行响应了。但是原先生成的dom元素还存在,可以这么理解,执行了destroy操作,后续就不再受vue控制了。

app.$destroy();
vue生命周期钩子及组件
这么多钩子函数,我们怎么用呢?
beforecreate : 举个例子:可以在这加个loading事件
created :在这结束loading,还做一些初始化,实现函数自执行
mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestroy: 你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容

组件

组件是可复用的 Vue 实例,且带有一个名字,每一个组件只能有一个根节点。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。可以在组件内部继续使用vue的其他方法比如:lifeCircle,filter,computed,在一个vue实例中 每一个组件都是独立存在的,它都有自己一套固定的生命周期函数和data以及其他vue的可操作属性和方法

组件的复用

你可以将组件进行任意次数的复用,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

data必须是一个函数

data 表示组件中使用的数据,组件中的数据必须是一个function,其返回值为data的值,(一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,如果 Vue 没有这条规则,点击一个按钮就可能会影响到其它所有实例)。

组件的组织

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。下面是通过 Vue.component 全局注册的:

Vue.component(‘my-component-name’, {
// … options …
})

局部组件与全局组件

在vue中使用组件需要先引入,通过在实例中使用components节点进行设置, 此种定义组件的方式叫局部组件
全局组件
// 参数一 组件的名字
// 参数二 一个对象 ,用来设置组件的属性
// 全局组件定义好之后可以直接使用 不需要在components中做注册
Vue.component(‘aaa’, {
template: <h5>这是一个组件</h5>
})
全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
注意:在vue中如果使用驼峰命名的时候 在template中使用组件的时候需要使用-把单词做分割

<div id="app">
    <Hello-World></Hello-World>//标签不区分大小写,因此可以hello-world
  </div>
  var app = new Vue({
      el: '#app',
      components: {
        HelloWorld: {
          template: `<h5>Hello World!</h5>`
        }
      }
    })

组件之间可以进行嵌套调用

通过this.$attrs可以获取到父组件传递过来的数据, 在使用局部组件的时候必须先进行注册,哪里使用就要在哪里进行注册


   <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>嵌套</title>
  <style>
    .list-item {
      border: 1px solid deeppink;
      padding: 0.2rem 1rem;
      margin: 0.5rem;
    }
  </style>
</head>
<body>
  <div id="app">
    <list-item :info="book" k="abc" b="1234" v-for="book in books"></list-item>
  </div>
  <script src="./lib/vue.js"></script>
  <script>

    const hello = {
      template: '<h3>Hi!!!!!!!!!!</h3>'
    }
    Vue.component('list-item', {
      template: `<div class="list-item">
        <h5 class="title">{{data.title}}</h5>
        <p class="desc">作者:{{data.author}}</p>
        <book-price :p="data.price"></book-price>
        <hello></hello>
      </div>`,
      data() {
        return {
          data: {
            title: '',
            author: '',
            price: '',
          }
        }
      },
      created() {
        // debugger;
        console.log(this.$attrs) // this.$attrs可以获取到外层组件传递过来的数据
        this.data = this.$attrs.info;
      },
      components: {
        hello,
      },
    })
    Vue.component('book-price', {
      template: `<div>
          <p :style="priceClass">¥{{price}}元</p>
          <hello></hello>
        </div>`,
      data() {
        return {
          priceClass: {
            color: 'red',
            fontSize: '1.5rem',
          },
          price: 0,
        }
      },
      created() {
        console.log(this.$attrs)
        this.price = this.$attrs.p;
      },
      components: {
        hello,
      },
    })
    var app = new Vue({
      el: '#app',
      data: {
        books: [{
          title: '三国演义',
          price: 45.98,
          author: '罗贯中',
        }, {
          title: '水浒传',
          price: 52.31,
          author: '施耐庵',
        }, {
          title: '从你的全世界路过',
          price: 29.82,
          author: '张嘉佳',
        }],
      }
    });
  </script>
</body>
</html>

组件传值

通过$attrs传递的属性值是没有在props数组中定义的属性
attrs是在vue2.4之后的版本新增的属性this.attrs是在vue2.4之后的版本新增的属性 this.props可以获取到所有的属性

vue开发常见传值方式

  1. 子组件传父组件传值
    VUE中子组件向父组件传值 使用事件派发
    在子组件内部使用this.$emit派发事件
    在父组件中使用@或v-on绑定事件
  2. 父组件传子组件传值
    VUE中父组件向子组件进行传值使用props,使用此种方式时在子组件中可以直接通过this.值的名称 进行获取
    下面的例子包含上面两种
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>组件之间传值</title>
</head>
<body>
  <div id="app">
    <h5>当前计数器的值为:{{c}}</h5>
    <counter @plusadd="plusAddHandle" :step1="2" step2="3" a="abc" b="我叫MT"></counter>
    <!-- <counter :step1="5"></counter>
    <counter :step1="7"></counter> -->
  </div>
  <script src="./lib/vue.js"></script>
  <script>
    Vue.component('counter', {
      template: `<button @click="plusHandle">当前计数值-{{count}}</button>`,
      data() {
        return {
          count: 1,
          step: 1,
        }
      },
      methods: {
        plusHandle() {
          this.count += this.step1;
          this.$emit('plusadd', this.count); // 派发自定义事件
        }
      },
      props: ['step1', 'step2'],
      created() {
        // debugger
        console.log(this.$props);
        console.log(this.$attrs);
        console.log(this.step1);
      }
    })
    var app = new Vue({
      el: '#app',
      data: {
        c: 0,
      },
      methods: {
        plusAddHandle(params) {
          this.c = params;
        }
      }
    })
  </script>
</body>
</html>
  1. 非父子组件传值
    用下面的方式:事件总线

动态组件-事件总线


在上述示例中,currentTabComponent 可以包括已注册组件的名字,或一个组件的选项对象。
为了解决非父子组件之间的传值问题,引入事件总线
在vue中使用一个空白的Vue对象作为一个EventBus,用来做事件的监听和派发
var eventBus=newVue();//使用一个空白的VUE实例作为中间媒介Vue.prototype.eventBus = new Vue(); // 使用一个空白的VUE实例作为中间媒介 Vue.prototype.eventBus = $eventBus; // 此种定义的属性可以在实例中进行访问

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>动态组件</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
    }
    .nav {
      background-color: deeppink;
      padding: 1.5rem 1rem;
      height: 60px;
    }
    .nav ul {
      margin: 0;
      padding: 0;
    }
    .nav ul li {
      float: left;
      margin: 1.5rem;
      list-style: none;;
    }
    .nav a {
      color: #fff;
      cursor: pointer;
    }
    .nav a.cur {
      color: greenyellow;
    }
    .product {
      border-bottom: 1px solid #ccc;
      padding: 0.2rem 0.5rem;
    }
  </style>
</head>
<body>
  <div id="app">
    <navbar @pagchange="changePageHandle"></navbar>
    <component :is="currentPage"></component>
  </div>
  <script src="./lib/vue.js"></script>
  <script>


    const Nav = {
      template:  `
      <div class="nav">
        <ul>
          <li><a :class="this.selIndex==0? 'cur': ''" @click="navClick(0, 'Home')">首页</a></li>
          <li><a :class="this.selIndex==1? 'cur': ''" @click="navClick(1, 'List')">商品列表页</a></li>
          <li><a :class="this.selIndex==2? 'cur': ''" @click="navClick(2, 'Cart')">购物车【{{cartCount}}】</a></li>
          <li><a :class="this.selIndex==3? 'cur': ''" @click="navClick(3, 'Us')">关于我们</a></li>
        </ul>
      </div>
      `,
      data() {
        return {
          selIndex: 0,
          cartCount: 0
        }
      },
      methods: {
        navClick(index, page) {
          this.$emit('pagchange', page); // 子组件传值到父组件
          this.selIndex = index
        }
      },
      created() {
        this.$eventBus.$on('addToCartEvent', (id) => {
          console.log(id);
          console.log(this);
          this.cartCount += 1;
        })
      }
    }
    const Home = {
      template: `
      <div>
        <h1>我是首页</h1>
      </div>
      `
    }
    const List = {
      template: `
      <div>
        <div v-for="item in products" class="product">
          <h3>{{item.name}}</h3>
          <p>{{item.price}}<button @click="addToShopCart(item.id)">加入购物车</button></p>
        </div>
      </div>
      `,
      data() {
        return {
          products: [{
            id: 1,
            name: 'iphone18',
            price: 19999,
          }, {
            id: 2,
            name: 'vivo 32',
            price: 5200
          }, {
            id: 3,
            name: 'redmi 10',
            price: 3000,
          }]
        }
      },
      methods: {
        addToShopCart(id) {
          this.$eventBus.$emit('addToCartEvent', id); // 使用$eventBus对象派发一个事件
        }
      }
    }
    const Cart = {
      template: `
      <div>
        <h1>我是购物车</h1>
      </div>
      `
    }
    const Us = {
      template: `
      <div>
        <h1>关于我们</h1>
      </div>
      `
    }
    var $eventBus = new Vue(); // 使用一个空白的VUE实例作为中间媒介
    Vue.prototype.$eventBus = $eventBus; // 此种定义的属性可以在实例中进行访问
    var app = new Vue({
      el: '#app',
      components: {
        navbar: Nav,
        Home,
        List,
        Cart,
        Us
      },
      data: {
        currentPage: 'Home'
      },
      methods: {
        changePageHandle(page) {
          this.currentPage = page;
        }
      }
    })
  </script>
</body>
</html>