vue2.x基础

82 阅读1分钟

一、 生命周期

生命周期钩子组件状态最佳实践
beforeCreate实例初始化之后,this指向创建的实例,不能访问到data、computed、watch、methods上的方法和数据常用于初始化非响应式变量
created实例创建完成,可访问data、computed、watch、methods上的方法和数据,未挂载到DOM,不能访问到el属性,el属性,ref属性内容为空数组常用于简单的ajax请求,页面的初始化
beforeMount在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数--
mounted实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,$ref属性可以访问常用于获取VNode信息和操作,ajax请求
beforeupdate响应式数据更新时调用,发生在虚拟DOM打补丁之前适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
updated虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作避免在这个钩子函数中操作数据,可能陷入死循环
beforeDestroy实例销毁之前调用。这一步,实例仍然完全可用,this仍能获取到实例常用于销毁定时器、解绑全局事件、销毁插件对象等操作
destroyed实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁--

注意:

  1. created阶段的ajax请求与mounted请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态
  2. mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick
  3. vue2.0之后主动调用$destroy()不会移除dom节点,作者不推荐直接destroy这种做法,如果实在需要这样用可以在这个生命周期钩子中手动移除dom节点
<template>
    <div>
        <h3>单组件</h3>
        <el-button @click="value += 1">更新 {{value}}</el-button>
        <el-button @click="handleDestroy">销毁</el-button>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                value: 1
            }
        },
        beforeCreate() {
            this.compName = 'hello'
            console.log(`--${this.compName}--beforeCreate`)
        },
        created() {
            console.log(`--${this.compName}--created`)
        },
        beforeMount() {
            console.log(`--${this.compName}--beforeMount`)
        },
        mounted() {
            console.log(`--${this.compName}--mounted`)
        },
        beforeUpdate() {
            console.log(`--${this.compName}--beforeUpdate`)
        },
        updated() {
            console.log(`--${this.compName}--updated`)
        },
        beforeDestroy() {
            console.log(`--${this.compName}--beforeDestroy`)
        },
        destroyed() {
            console.log(`--${this.compName}--destroyed`)
        },
        methods: {
            handleDestroy() {
                this.$destroy()
            }
        }
    }
  </script>

二、自定义组件的v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的model 选项可以用来避免这样的冲突。

子组件:

<template>
  <label>
    自定义input:
    <input
      v-bind="$attrs"
      :value="value"
      @input="onInput($event.target.value)"
    >
  </label>
</template><script>
export default {
  props: ['value'],
  watch: {
    value(newVal, oldVal) {
      console.log('newVal type = ' + typeof newVal)
      console.log('oldVal type = ' + typeof oldVal)
      console.log('watch-child-value', newVal)
    }
  },
  methods: {
    onInput(value) {
      console.log('onInput')
      this.$emit('input', value)
    }
​
  }
}
</script>

父组件:

<template>
  <child-component v-model="currValue" />
</template><script>
import childComponent from './child-component'
export default {
  components: { childComponent },
  data() {
    return {
      currValue: ''
    }
  },
  watch: {
    currValue(val) {
      console.log('watch-value:', val)
      console.log('watch-value type:', typeof val)
      // this.currValue = parseInt(val) // 这是一串死循环代码
    }
  }
}
</script>

注意:

  1. 理论上,input event -> parent value change ->child value change -> input event,是一个死循环。当child value change时,会判定前后的值是否相等,如果相等则不会触发input event。
  2. 为了避免上面的死循环,不能在上面的过程中修改value值。

三、自定义插槽

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。插槽

定义插槽

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

使用插槽

<div class="container">
  <header>
    <!-- 我们希望把页头放这里 -->
  </header>
  <main>
    <!-- 我们希望把主要内容放这里 -->
  </main>
  <footer>
    <!-- 我们希望把页脚放这里 -->
  </footer>
</div>

定义插槽参数

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

使用插槽参数

<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

四、动态组件 & 异步组件

    <component v-bind:is="currentTabComponent"></component>

    <!-- 失活的组件将会被缓存!-->
    <keep-alive>
      <component v-bind:is="currentTabComponent"></component>
    </keep-alive>
    <script>
    import childComponent from './child-component.vue'
    export default {
      components: { asyncComponent: () => import('./async-component.vue'), childComponent },
    }
</script>
    Vue.component('async-example', function (resolve, reject) {
      setTimeout(function () {
        // 向 `resolve` 回调传递组件定义
        resolve({
          template: '<div>I am async!</div>'
        })
      }, 1000)
    })

    const AsyncComponent = () => ({
      // 需要加载的组件 (应该是一个 `Promise` 对象)
      component: import('./MyComponent.vue'),
      // 异步组件加载时使用的组件
      loading: LoadingComponent,
      // 加载失败时使用的组件
      error: ErrorComponent,
      // 展示加载时组件的延时时间。默认值是 200 (毫秒)
      delay: 200,
      // 如果提供了超时时间且组件加载也超时了,
      // 则使用加载失败时使用的组件。默认值是:`Infinity`
      timeout: 3000
    })

五、Vue通信

  1. 父子组件通信

    1. 通过事件:v-on指令和$emit
    2. 通过属性:props
    3. 语法糖:v-model
    4. 直接访问实例:$parent和$refs
    5. 访问内置属性:$attrs和$listeners
    6. 注入:provide和inject
    
  2. 事件总线:EventBus

  3. 状态管理库:VueX

  4. 定义全局变量

    Vue.prototype.GLOBAL = {}
    ​
    export const Global = {}
    
  5. 通过本地存储:cookie / localStorage / sessionStorage

  6. 路由传参

六、路由传参

  1. 路径传参
// 跳转
this.$router.push({ path: `/details/${id}`,})
// 获取参数
this.$route.params.id
  1. 通过路由属性中的name来确定匹配的路由,通过params来传递参数
// 跳转
const id = '12345'
this.$router.push({ name: 'userDetail', params: { id }})
// 获取参数
this.$route.params.id
  1. 使用path来匹配路由,然后通过query来传递参数
// 跳转
const id = '12345'
this.$router.push({ name: 'userDetail', query: { id }})
// 获取参数
this.$route.query.id

七、自定义指令

参考自定义指令

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})