系列文章只是基础内容的整理和记录,不具备深度。

一、refs:DOM 和 子组件

与 React 的 ref 一样的概念,基本的使用方式也都差不多,主要用于获取 DOM 元素或者是子组件。

声明方式:在组件上通过 ref="refName" 指定名称

调用方式:在 js 中通过 this.$refs.refName 可以拿到 DOM,如果是子组件则拿到的类型则是子组件 vue 组件类型。

DOM 节点

this.$refs.refNamebeforeCreatecreatedbeforeMount 三个生命周期中是无法拿到值,拿到的都是 undefined,只有当组件执行到 mounted 生命周期之后,才能拿到正确的值。

如果是直接获取的 DOM 节点,则输出值类似于:

1.jpg

Vue 子组件

如果是 vue 子组件则输出内容就特别多了,本身 vue一个组件对象内容就很多.

同时,父组件能够通过拿到子组件的 ref 来直接操作子组件内部的方法,这在某种业务交互特别复杂的情况下是非常需要的

微信截图_20181228001021.png

上面截图是在父组件的 mounted 方法中执行了下面代码:

mounted() {
  console.log(this.$refs.title);
  console.log(this.$refs.sub);
  this.$refs.sub.log();
},

而子组件定义如下:

  const Sub = {
    template: `<p>sub</p>`,
    methods: {
      log() {
        console.warn('父组件直接操作子组件的方法');
      }
    }
  }

在父组件中使用如下:

    template: `
      <div>
        <h1 ref="title">{{title}}</h1>
        <Sub ref="sub"/>
      </div>
    `

可以发现,虽然 log() 方法是在子组件中的,但是我在父组件中通过 this.$refs.sub.log() 就能够调用子组件的 log() 方法。这种方式可以认为破坏了组件的一些特性,比如单项数据流、造成组件间的耦合等,但在某些情况下还是很方便的。

而子组件拿到的内容不再是一个 DOM 节点,而是实实在在的 vue 对象,主要内容如下:

属性名说明
$parent当前组件对象的父组件
$children子组件列表
$root获取 vue 的示例 vm 对象
$el获取DOM

3.jpg

上面完整的示例如下:

const Sub = {
    template: `<p>sub</p>`,
    methods: {
      log() {
        console.warn('父组件直接操作子组件的方法');
      }
    }
  }
  const App = {
    components: {Sub},
    props:['title'],
    beforeCreate() {
      console.log(this.$refs.title);
    },
    created() {
      console.log(this.$refs.title);
    },
    beforeMount() {
      console.log(this.$refs.title);
    },
    mounted() {
      console.log(this.$refs.title);
      console.log(this.$refs.sub);
      this.$refs.sub.log();
    },
    template: `
      <div>
        <h1 ref="title">{{title}}</h1>
        <Sub ref="sub"/>
      </div>
    `
  }
  new Vue({
    el: '#app',
    components: {App},
    template: `<App :title="text"/>`,
    data() {
      return {
        text: 'test title'
      }
    }
  });

条件渲染

条件渲染如果是 v-if 是无法立即执行的,由 vue 维护一个队列,因此更新可能是异步的,(v-show 因为没有销毁 DOM ,一直都在,不在讨论之内),这就涉及到,如果状态变更后直接通过 ref 获取 DOM 可能是会报错的。

示例模板代码:

<input  ref="input" v-if="show" />

组件 mounted 周期做的事情

mounted() {
    this.show = true;
    this.$refs.input.focus();    
}

如果一定要进行上面的这种操作,则需要使用 vue 提供的 nextTick 方法,保证在更新完成后再进行一些操作。

mounted() {
    this.show = true;
    this.$nextTick(() => {
        this.$refs.input.focus();
    })
}