vue

一、 vue 有多少个生命周期,每个作用是什么?

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <link rel="stylesheet" href="">
    <script src="https://cdn.bootcss.com/vue/2.2.1/vue.min.js"></script>
</head>
<body>
    
    <div id="app">
        <template>
            <div>
              <p>{{ total }}</p>
              <button-counter v-on:increment="incrementTotal"></button-counter>
              <button-counter v-on:increment="incrementTotal"></button-counter>
              <hr>
              <button onclick="vm.$destroy()">此按钮用来销毁实例</button>
            </div>
        </template>
    </div>

    <script>

        Vue.component('button-counter', {
            template: '<button @click="increment">我是按钮{{ counter }}</button>',
            data: function() {
                return {
                    counter: 0
                }
            },
            methods: {
                increment(){
                    this.counter ++;
                    this.$emit('increment')
                }
            }
        })
        
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    total: 0
                }
            },
            methods: {
                incrementTotal(){
                    this.total ++;
                }
            },
            // 组件生命周期
            beforeCreate() {   // 在实例创建之后,在数据初始化之前被调用
                console.log('beforeCreated-1')
            },
            created() { // 在数据初始化之后被调用,如果你的页面进来的时候就调用接口,那么created是第一选择
                console.log('created-2') 
            },
            beforeMount() { // 在数据渲染之前被调用
                console.log('beforeMount-3') 
            },
            mounted() { // 实例创建完成、数据初始化、渲染页面数据后,才被调用
                console.log('mounted-4') 
            },
            beforeUpdate() { // 在数据改变时被调用
                console.log('beforeUpdate-5')
            },
            updated() { // 数据被更新之后
                console.log('updated-6')
            },

            //keep-alive生命周期有两个 (activated , deactivated)
            activated() { // keep-alive 组件激活时调用,也就是说在路由切换时被调用,注意要配合keep-alive使用才会被调用, <keep-alive>  
                // 可以看下别人写的 https://www.cnblogs.com/sysuhanyf/p/7454530.html
                
            },
            deactivated(){// keep-alive 组件停用时调用,也可以理解成在路由切换的会自动停用组件, 

                // 可以看下别人写的 https://www.cnblogs.com/sysuhanyf/p/7454530.html
              
            },
            beforeDestroy() { // 实例销毁前被调用
                console.log('beroreDestroy-9')
            },
            destroyed() { //实例销毁后被调用
                console.log('destroyed-10')
            }
        })

    </script>

</body>
</html>

|

二、 vue路由有多少个钩子,每个作用是什么?

1. beforeEach 全局路由钩子,只要页面路由改变就会执行,钩子有是三个参数 to 、from 、next
export default {
    name: 'app',
    data() {
      return {
      }
    },
    mounted() {
      this.$router.beforeEach((to, from, next) => {
        console.log(to, from)
        next()
      })
    }
  }

to 要去哪个页面 返回信息如下图
from 是我从哪个页面过来的,和to参数格式一样,只不过path不同
next 是一个执行的参数 必须要加上next(),不然路由不会跳转

image.png
2. afterEach 全局路由钩子 有两个参数 to 、from

这两个参数和beforeEach相同,唯一的区别是afterEach钩子是路由跳转之后才被执行

this.$router.afterEach((to, from) => {
      console.log(to, from)
})
3. beforeEnter 这个一般情况下用不到,在路由页面配置 beforeEnter,在进入这个路由后执行
routes: [{
      path: '/Hello',
      name: 'Hello',
      component: Hello
    },
    {
      path: '/index',
      component: index,
      children: [
        { path: 'world', component: world ,
          beforeEnter: (to, from, next) => {
            // 执行逻辑
            next()
          }
        },
        { path: 'world_childen', component: world_childen },
      ]
    }
  ]
4. beforeRouteEnter 组件私有钩子

组件私有的钩子,组件被调用事执行,在beforeRouteEnter这个钩子里面没有this
因为在这个钩子执行的时候组件的实例还没有创建,所以打印this是undefined
在next里面使用vm.name可以代替 this。
to 返回的信息是当前页面路由信息
from 返回的是从哪里过来的,和beforeEach 有点差异

export default {
        data() {
          return {
             name: 'web_珂珂'
          }
        },
        beforeRouteEnter(to, from, next){   
          console.log(this) // undefined
          next(vm => {
            console.log(vm.name) // web_珂珂
          });
        }
}
5. beforeRouteUpdate 组件私有钩子,只有在组件被多次调用并且路由有变的的时候才会执行

比如: A页面调用了当前组件,B页面也调用了当前组件,但是他们的路由变化了,beforeRouteUpdate就会被执行
beforeRouteUpdate this是指向实例的
html:

// 类似于这样 两个router-link都调用world组件,但是参数不同,就会被执行
<router-link to="/index/world">导航二</router-link>
<router-link to="/index/world?name=web_keke">测试beforeRouteUpdate钩子</router-link>
<script>
    export default {
        data() {
          return {
             name: 'web_珂珂'
          }
        },
        beforeRouteUpdate(to, from, next){
            console.log(this.name) // web_珂珂
            console.log(to,from)
            next()
        }
    }
</script>
6. beforeRouteLeave 从当前组件的跳转别的路由页面执行,就是离开的时候执行
    export default {
        data() {
        },
        beforeRouteLeave (to, from, next) {
            console.log(to, from)
            next()
        }
    }

三、computed计算属性与methods方法的区别?

computed 在数据没有更新的情况下,computed缓存数据,下次进来还是读取缓存的数据。
而methods方法会每次都会执行,不会走缓存,举个例子:

<template>
    <div>
        <!--  computed  -->
        我是computed 只要数据没有更新,我会读取缓存,不会重新计算
        <p>
            数量:{{ computedCount }}
        </p>
        <p>
            数量:{{ computedCount }}
        </p>
        <p>
            数量:{{ computedCount }}
        </p>
        <hr>
        
        <!--  方法  -->
        我是methods方法,每次都会重新调用methodsCount()函数
        <p>
            数量:{{ methodsCount() }}
        </p>
        <p>
            数量:{{ methodsCount() }}
        </p>
        

    </div>

</template>

<script>
export default {
    data() {
      return {
         count: 1
      }
    },
    // 计算
    computed: {
        computedCount(){
            return this.count + 100
        }
    },
    // 方法
    methods: {
        methodsCount(){
            return this.count + 100
        }
    }
}
</script>

image.png

四、自定义指令 directive

vue 有他们自己内置的指令如v-model, 还可以自己定义一些指令(directive),自定义指令可以写成局部的,也可以写成全局的,下面有个回车搜索的案例(这个还是我之前的一个朋友教我的-_-"),用于多个页面搜索框输入直接回车就可以调用方法

钩子函数钩子函数参数

1. 全局指令

全局的写法我在main.js里面写的,在任何组件都可以用

Vue.directive('keyup-enter',{
    inserted(el, binding) {
        let $inputs = el.getElementsByTagName('input')
        Array.from($inputs).forEach( $item => {
            $item.addEventListener('keydown', event => {
                let e = event || window.event
                if (e.keyCode === 13) {
                  if (binding.value) {
                    setTimeout(() => {
                      binding.value()
                    }, 100)
                  }
                }
            })
        })
    }
})

然后在组件中调用keyup-enter指令,
把指令绑定到最外层的div上面 如,然后在输入框输入文本按回车

<template>
 <!-- 这里绑定指令 v-keyup-enter -->  
<div v-keyup-enter="getName"> 
        名称:
        <input type="text" v-model="name">
        年龄:
        <input type="text" v-model="age">
    </div>
</template>

<script>

    export default {
      data() {
        return {
          name: '珂珂',
          age: '26'
        }
      },
      methods: {
        getName() {
            console.log(this.name) // 珂珂
            console.log(this.age) // 26
        }   
      }
    }

</script>
image.png

2. 局部指令

比如有个需求,要求页面一进来,就让某一样文本,添加一个颜色,并且让这个文字翻转,或者只有第一次需要添加颜色,这中情况使用指令是最合适的
v-reverse 就是自定义的指令名称

<template>
    <div>
        <p v-reverse>{{ message }}</p>
    </div>
</template>

<script>

    export default {
      data() {
        return {
          message: '"-_-我是菜鸟',
        }
      },
      directives: {
        reverse: {
            inserted(el) {
                el.innerHTML = el.innerHTML.split('').reverse().join(''); // 鸟菜是我-_-"
                el.style.color = 'red';
                console.log(el) // <p style="color: red;">鸟菜是我-_-"</p>
            }
          }
        }
    }
</script>

image.png

五、 keep-alive 缓存

详解 keep-alive

keep-alive有两个生命周期 activated和deactivated,activated是组件被缓存的时候执行,deactivated是离开缓存组件的时候执行。

keep-alive属性 如: <keep-alive include="Hello"> 缓存Hello组件
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例

在Hello中切换tab到内容二选项,然后在跳转的world组件后,在切回到Hello组件中,你会发现还是刚才的内容二的文本,他并没有显示初始化的内容一的文本,这样就实现了缓存,另外keep-alive属性include 设置的名称是你要缓存的组件中写的那个name名称,如果名字不统一,缓存则无效。
如果想缓存多个组件,可以以逗号分隔,如: <keep-alive include="Hello, world">

缓存方式一、

app.vue

<template>
  <div id="app">
    <div class="nav">
      <ul>
        <router-link 
          v-for="(item, index) in arr" 
          :key="index" 
          :to="item.routerPath">
            {{item.routerName}}
            <br><br><br>
        </router-link>
      </ul>
    </div>
    <div class="view-box">
       // 这里缓存组件Hello
      <keep-alive include="Hello">
        <router-view></router-view>
      </keep-alive>
    </div>
  </div>
</template>
<script>
  export default {
    name: 'app',
    data() {
      return {
       active: '/Hello',
       arr: [
          {
            routerName: '我是hello组件',
            routerPath: '/Hello'
          },
          {
            routerName: '我是world组件',
            routerPath: '/world'
          }
        ]
      }
    }
  }

</script>

Hello.vue

<template>
  <div class="hello">
        <ul>
        <li v-for="(item, index) in navArr" :key="index">
          <button 
            @click="tab(item.tab)" 
            :class="{ active : tabShow == item.tab }"
            >
            {{ item.name }}
          </button>
        </li>
      </ul>
      <div class="box">
        <div :is="tabShow"></div>
      </div>
  </div>
</template>

<script>

  let A = {
    template:`<h3>我是内容一的信息11111</h3>`
  }
  let B = {
    template:`<h3>我是内容二的信息22222222222</h3>`
  }

  export default {
    name:'Hello', // 这个name要和 keep-alive中include 设置的名字统一,否则无效
    data () {
      return {
        tabShow:'A',
        navArr: [
          {
            name: '内容一',
            tab: 'A'
          },
          {
            name: '内容二',
            tab: 'B'
          }
        ]
      }
    },
    components: {
        'A': A,
        'B': B
    },
    methods: {
        tab(currentShowTab){
            this.tabShow = currentShowTab;
        }
    },
    activated(){ // 组件被缓存的时候执行
      console.log('组件被缓存的时候执行')
    },
    deactivated(){ // 离开缓存组件的时候执行
      console.log('离开缓存组件的时候执行')
    }
  }

</script>

缓存方式二、

配合router路由去动态缓存, 这种方式 keep-alive 就是根据路由配置里面来实现的
index.js (router下的index.js文件)

import Vue from 'vue'
import Router from 'vue-router'
import Hello from '../components/Hello'
import index from '../components/index'
import world from '../components/world'

Vue.use(Router)

export default new Router({
  routes: [{
      path: '/Hello',
      name: 'Hello',
      component: Hello,
      meta: {
        keepAlive: true //  设置为缓存
      }
    },
    {
      path: '/world',
      name: 'world',
      component: world,
      meta:{
         keepAlive: false  //设置为不缓存
      } 
    }
  ]
})

然后修改app.vue

<template>
  <div id="app">
    <div class="nav">
      <ul>
        <router-link v-for="(item, index) in arr" :key="index" :to="item.routerPath">
          {{item.routerName}}
          <br><br><br>
        </router-link>
      </ul>
    </div>
    <div class="view-box">
      // 修改为根据路由配置来实现缓存某些页面
      <keep-alive>
        <router-view v-if="$route.meta.keepAlive"></router-view>
      </keep-alive>
      <router-view v-if="!$route.meta.keepAlive"></router-view>
    </div>
  </div>
</template>
image.png

推荐阅读更多精彩内容