5

组件

  • 组件可以扩展 HTML 元素,封装可重用的代码

  • 在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能

  • 在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。

使用组件

注册一个全局组件

<div id="example">
<!--web组件的定义脱离了一般的dom元素的写法,相当于自定义了元素-->
  <my-component></my-component>
</div>
// 注册全局组件,指定之前设定的元素名,然后传入对象
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
  el: '#example'
})

局部注册组件

不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用

//将传入给组件的对象单独写
var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  //通过components语法创建局部组件
  //将组件仅仅放在这个vue实例里面使用
  components: {
    // <my-component> 将只在父模板可用
    'my-component': Child
  }
})

DOM模板解析说明

当使用 DOM 作为模版时(例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,

因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。
尤其像这些元素 <ul> , <ol>, <table> , <select> 限制了能被它包裹的元素, <option> 只能出现在其它元素内部。

<!--这种是不行的,会报错-->
<table>
  <my-row>...</my-row>
</table>
<!--要通过is属性来处理-->
<table>
  <tr is="my-row"></tr>
</table>

data必须是函数

使用组件时,大多数可以传入到 Vue 构造器中的选项可以在注册组件时使用,有一个例外: data 必须是函数。 实际上

//这样会报错,提示data必须是一个函数
Vue.component('my-component', {
  template: '<span>{{ message }}</span>',
  data: {
    message: 'hello'
  }
})
<div id="example-2">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // data 是一个函数,因此 Vue 不会警告,
  // 但是我们为每一个组件返回了同一个对象引用,所以改变其中一个会把其他都改变了
  data: function () {
    return data
  }
})
new Vue({
  el: '#example-2'
})

避免出现同时改变数据的情况

//返回一个新的对象,而不是返回同一个data对象引用
data: function () {
  return { //字面量写法会创建新对象
    counter: 0
  }
}

构成组件

组件意味着协同工作,通常父子组件会是这样的关系:

  • 组件 A 在它的模版中使用了组件 B 。它们之间必然需要相互通信

  • 父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件

然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。

在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。
父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。

prop

使用prop传递数据

  • 组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。

  • 使用 props 把数据传给子组件。

  • prop 是父组件用来传递数据的一个自定义属性

  • 子组件需要显式地用 props 选项声明 “prop”

<div id="example-2">
    <!--向这个组件传入一个字符串-->
    <child message="hello!"></child>
</div>
    Vue.component('child', {
        // 声明 props,用数组形式的对象
        props: ['message'],
        // 就像 data 一样,prop 可以用在模板内
        // 同样也可以在 vm 实例中像 “this.message” 这样使用
        template: '<span>{{ message }}</span>'
    });
    new Vue({
        el: '#example-2'
    })

动态prop

用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件

<div id="example-2">
<!--使用v-modal实现双向绑定-->
    <input v-model="parentMsg">
    <br>
    <!--需要注意这里使用短横线的变量,因为在html下是使用短横线变量的,但是在vue下使用驼峰变量-->
    <!--将父组件的parentMsg和子组件的my-message进行绑定-->
    <child v-bind:my-message="parentMsg"></child>
</div>
    Vue.component('child', {
        // 声明 props
        props: ['my-message'], 
        template: '<span>{{ myMessage }}</span>' //如果写my-message会报错,需要转换为驼峰写法
    });
    new Vue({
        el: '#example-2',
        data: {
            parentMsg: ''
        }
    })

短横线和驼峰写法

HTML 特性不区分大小写。当使用非字符串模版时,prop的名字形式会从 camelCase 转为 kebab-case(短横线隔开)

  • 在javascript里面使用驼峰写法,但是在html里面需要转成短横线写法

  • 反之亦然,vue会自动处理来自html的短横线写法转为驼峰写法

字面量语法和动态语法

<!-- 默认只传递了一个字符串"1" -->
<comp some-prop="1"></comp>

<!-- 用v-bind实现传递实际的数字 -->
<comp v-bind:some-prop="1"></comp>

单向数据流

  • prop 是单向绑定的

  • 当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。

  • 每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop 。如果你这么做了,Vue 会在控制台给出警告。

通常有两种改变 prop 的情况:

  1. prop 作为初始值传入,子组件之后只是将它的初始值作为本地数据的初始值使用

定义一个局部 data 属性,并将 prop 的初始值作为局部数据的初始值。

<div id="example-2">
<!--这里用短横线写法-->
    <child initial-counter="10"></child>
</div>
    Vue.component('child', {
        props: ['initialCounter'],//这里用驼峰写法
        data: function () { //转为一个局部变量,写一个data对象给组件使用
            return {counter: this.initialCounter}
        },
        template: '<span>{{ counter }}</span>'
    });
    new Vue({
        el: '#example-2'
    })
  1. prop 作为需要被转变的原始值传入。

定义一个 computed 属性,此属性从 prop 的值计算得出。

//例子没有写完,但是根据第一个例子可以知道利用computed的手法原理其实跟写一个data差不多
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}    

注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

prop验证

组件可以为 props 指定验证要求,当组件给其他人使用时这很有用。

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    propA: Number,
    // 多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

自定义事件

每个 Vue 实例都实现了事件接口(Events interface)

  • 使用 $on(eventName) 监听事件

  • 使用 $emit(eventName) 触发事件

  • 父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

<div id="counter-event-example">
    <p>{{ total }}</p>
    <!--监听子组件的事件触发,监听increment1事件,处理程序为incrementTotal事件-->
    <button-counter v-on:increment1="incrementTotal"></button-counter>
    <!--关键在于这里v-on绑定的是一个子组件的事件,并且赋值了一个父组件的方法给他,那么子组件里面就可以使用这个方法-->
    <button-counter v-on:increment1="incrementTotal"></button-counter>
</div>
    Vue.component('button-counter', {
        //监听click事件,处理程序为increment(子组件定义的方法)
        template: '<button v-on:click="increment">{{ counter }}</button>',
        //每一个counter都是独立的对象属性
        data: function () {
            return {
                counter: 0
            }
        },
        //子组件的方法
        methods: {
            increment: function () {
                this.counter += 1;
                //在子组件里面直接触发之前监听的increment1事件来执行父组件的方法
                this.$emit('increment1');
            }
        },
    })
    new Vue({
        el: '#counter-event-example',
        data: {
            total: 0
        },
        //父组件的方法
        methods: {
            incrementTotal: function () {
                this.total += 1
            }
        }
    })

1.组件之间因为作用域不同的关系,所以相互独立,所以子组件想要使用父组件的方法的话需要做一个新的监听映射

给组件绑定原生事件

<!--代替.on,这么就能够绑定原生js的事件了-->
<my-component v-on:click.native="doTheThing"></my-component>

使用自定义事件的表单输入组件

自定义事件也可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。
所以要让组件的 v-model 生效,它必须:

  • 接受一个 value 属性

  • 在有新的 value 时触发 input 事件

<!--直接使用v-model,v-modal默认处理input事件-->
<input v-model="something">

<!--v-modal是语法糖,翻译过来原理是这样:-->
<!--绑定一个value,然后监听input事件,通过获取input的输入来不断改变绑定的value的值,满足了v-modal的触发条件就可以实现v-modal了-->
<input v-bind:value="something" v-on:input="something = $event.target.value">

一个非常简单的货币输入:

<!--绑定一个v-model为price,其实是绑定了一个value-->
<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
  template: '\
    <span>\
      $\
      <input\
        ref="input"\ //注册为input,是DOM的节点元素
        v-bind:value="value"\ //v-model的value(也是prop)
        v-on:input="updateValue($event.target.value)"\ //封装更新value的函数
      >\
    </span>\
  ',
  props: ['value'], //父组件将绑定的value传给子组件
  methods: {
    // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制
    updateValue: function (value) {
      var formattedValue = value //对值进行处理
        // 删除两侧的空格符
        .trim()
        // 保留 2 小数位和2位数
        .slice(0, value.indexOf('.') + 3)
      // 如果值不统一,手动覆盖以保持一致,为了保持输入框显示内容跟格式化内容一致
      if (formattedValue !== value) {
      //因为注册是一个input元素,所以this.$refs 就是input元素
        this.$refs.input.value = formattedValue
      }
      //手动触发input事件,将格式化后的值传过去,这是最终显示输入框的输出
      this.$emit('input', Number(formattedValue))
    }
  }
})
//实例化vue实例的
new Vue({
        el: '#aa', //要绑定一个vue实例,例如包裹一个id为aa的div
        data:{
            price:'' //v-model要有数据源
        }
    })

ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 ref

这是一个比较完整的例子:

<div id="app">
<!--有3个组件,分别不同的v-model-->
  <currency-input 
    label="Price" 
    v-model="price"
  ></currency-input>
  <currency-input 
    label="Shipping" 
    v-model="shipping"
  ></currency-input>
  <currency-input 
    label="Handling" 
    v-model="handling"
  ></currency-input>
  <currency-input 
    label="Discount" 
    v-model="discount"
  ></currency-input>
  
  <p>Total: ${{ total }}</p>
</div>
Vue.component('currency-input', {
  template: '\
    <div>\
      <label v-if="label">{{ label }}</label>\
      $\
      <input\
        ref="input"\  // 这些没什么特别,引用注册为input DOM元素
        v-bind:value="value"\  
        v-on:input="updateValue($event.target.value)"\
        v-on:focus="selectAll"\  //这里多了focus事件监听,焦点在的时候全选,也只是多了处理而已,对整体逻辑理解没啥影响
        v-on:blur="formatValue"\ //这里多了blur事件监听,焦点离开的时候格式化
      >\
    </div>\
  ',
  props: {  //多个prop传递,因为prop是对象,只要是对象格式就行
    value: {
      type: Number,
      default: 0
    },
    label: {
      type: String,
      default: ''
    }
  },
  mounted: function () { //这是vue的过渡状态,暂时忽略不影响理解
    this.formatValue()
  },
  methods: {
    updateValue: function (value) {
      var result = currencyValidator.parse(value, this.value)
      if (result.warning) {
      // 这里也使用了$refs获取引用注册信息
        this.$refs.input.value = result.value
      }
      this.$emit('input', result.value)
    },
    formatValue: function () {
      this.$refs.input.value = currencyValidator.format(this.value) //这里注意下,这个this是prop传递过来的,也相当于这个组件作用域
    },
    selectAll: function (event) { //event可以获取原生的js事件
      // Workaround for Safari bug
      // http://stackoverflow.com/questions/1269722/selecting-text-on-focus-using-jquery-not-working-in-safari-and-chrome
      setTimeout(function () {
          event.target.select()
      }, 0)
    }
  }
})

new Vue({
  el: '#app',
  data: {
    price: 0,
    shipping: 0,
    handling: 0,
    discount: 0
  },
  computed: {
    total: function () {
      return ((
        this.price * 100 + 
        this.shipping * 100 + 
        this.handling * 100 - 
        this.discount * 100
      ) / 100).toFixed(2)
    }
  }
})

非父子组件通信

在简单的场景下,使用一个空的 Vue 实例作为中央事件总线:

var bus = new Vue()

// 触发组件 A 中的事件
bus.$emit('id-selected', 1)

/*
通过on来监听子组件的事件来实现传递
*/

// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
})

使用Slot分发内容

为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为 内容分发 (或 “transclusion” 如果你熟悉 Angular)

编译作用域

组件作用域简单地说是:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。
假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。

<!-- 无效 -->
<child-component v-show="someChildProperty"></child-component>

如果要绑定子组件内的指令到一个组件的根节点,应当在它的模板内这么做:

Vue.component('child-component', {
  // 有效,因为是在正确的作用域内
  template: '<div v-show="someChildProperty">Child</div>',
  data: function () {
    return { //因为这个属性在当前组件内编译(创建了)
      someChildProperty: true
    }
  }
})

类似地,分发内容是在父组件作用域内编译。

单个Slot

  • 除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃。

  • 当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。

  • 备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。

<!--父组件模版:-->
<div id="aa">
    <h1>我是父组件的标题</h1>
    <!--子组件的作用域内编译,宿主元素为空,且没有要插入的内容-->
    <my-component></my-component>
    <my-component>
        <p>这是一些初始内容</p>
        <p>这是更多的初始内容</p>
    </my-component>
</div>
    Vue.component('my-component', { 
    //my-component 组件有下面模板
        template: '\
            <div>\
                <h2>我是子组件的标题</h2> \
                <slot> \  //有slot插口,所以没有被父组件丢弃
                只有在没有要分发的内容时才会显示。\
                </slot> \
            </div> \
        '
    })
    new Vue({
        el: '#aa',
    })

渲染结果:

<div id="aa"><h1>我是父组件的标题</h1>
    <div>
        <h2>我是子组件的标题</h2>
        <!--这里是直接插入,没有使用DOM元素-->
        只有在没有要分发的内容时才会显示。
    </div>
    <div>
        <h2>我是子组件的标题</h2>
        <p>这是一些初始内容</p>
        <p>这是更多的初始内容</p>
    </div>
</div>

有名字的Slot

  • <slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。

  • 仍然可以有一个匿名 slot ,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。

<div id="aa">
    <app-layout>
        <!--这是header-->
        <h1 slot="header">这里可能是一个页面标题</h1>
        <p>主要内容的一个段落。</p>
        <p>另一个主要段落。</p>
        <!--这是footer-->
        <p slot="footer">这里有一些联系信息</p>
    </app-layout>
</div>
    Vue.component('app-layout', {
        template: '\
            <div class="container"> \
                <header> \  //找到名字叫header的slot之后替换内容,这里替换的是整个DOM
                    <slot name="header"></slot> \
                </header> \
                <main> \ //因为slot没有属性,会将内容插入到slot的所在的DOM位置
                    <slot></slot> \
                </main> \
                <footer>\  //跟header类似
                    <slot name="footer"></slot> \
                </footer> \
            </div> \
        '
    });
    new Vue({
        el: '#aa',
    })

渲染结果为:

<div class="container">
  <header>
    <h1>这里可能是一个页面标题</h1>
  </header>
  <main>
    <p>主要内容的一个段落。</p>
    <p>另一个主要段落。</p>
  </main>
  <footer>
    <p>这里有一些联系信息</p>
  </footer>
</div>

作用域插槽(vue2.1)

  • 作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。

  • 在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样

  • 在父级中,具有特殊属性 scope 的 <template> 元素,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 prop 对象

<div id="parent" class="parent">
    <child>
    <!--接收从子组件中传递的prop对象(这个就是作用域插槽)-->
        <template scope="props">
            <span>hello from parent</span>
            <!--使用这个prop对象-->
            <span>{{ props.text }}</span>
        </template>
    </child>
</div>
    Vue.component('child', {
        props: ['props'], //这个写不写都可以,作用域插槽固定会接收prop对象,而且这个prop对象是肯定存在的
        template: '\
            <div class="child"> \
            <slot text="hello from child"></slot> \ //在子组件里直接将数据传递给slot
            </div> \
        '
    });
    new Vue({
        el: '#parent',
    })

渲染结果:

<div class="parent">
  <div class="child">
    <span>hello from parent</span>
    <!--子组件的东西出现在这里了-->
    <span>hello from child</span>
  </div>
</div>

另外一个例子,作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项

<div id="parent">
<!--绑定一个组件的prop ,位置1-->
    <my-awesome-list :items="items">
        <!-- 作用域插槽也可以在这里命名 -->
        <!--这里props只代表确定接受prop对象的东西,不关注prop对象里面有什么,位置2-->
        <template slot="item" scope="props">
            <li class="my-fancy-item">{{ props.text }}</li>
        </template>
    </my-awesome-list>
</div>
    Vue.component('my-awesome-list', {
        props:['items'], //需要声明prop为items,需要是为下面的循环遍历的items的数据源做设定,位置3
        template: '\
            <ul> \
                <slot name="item" v-for="item in items" :text="item.text"> \ //在slot中,循环遍历输出items的text,位置4
                </slot> \
            </ul> \
        '
    });
    new Vue({
        el: '#parent',
        data : {
            items:[ //初始化items数据
                {text:"aa"},
                {text:"bb"}
            ]
        }
    })
  1. 位置1,实现了一个组件的prop绑定,prop需要在组件里面声明,这里绑定的是items,这是要将父组件的items传递到子组件,所以在位置3里面需要声明,在vue实例要初始化

  2. 位置2,这里scope的props是代表作用域插槽接收来自prop对象的数据,props.text是代表每一个li要输出的是prop对象的text属性

  3. 位置3,在组件里声明props,为了接收父组件绑定的items属性,然后将其给位置4的循环使用

  4. 位置4,这里绑定了text属性,就是前呼位置2里面输出的prop对象的text属性

动态组件

多个组件可以使用同一个挂载点,然后动态地在它们之间切换。使用保留的 <component> 元素,动态地绑定到它的 is 特性

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home' //默认值
  },
  components: { //根据不同的值进行不同的组件切换,这里用components写法
    home: { /* ... */ }, 
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
<!--这个is是一个字符串,根据返回值来给组件进行v-bind-->
<component v-bind:is="currentView">
  <!-- 组件在 vm.currentview 变化时改变! -->
</component>

keep-alive

如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数

<keep-alive>
  <component :is="currentView">
    <!-- 非活动组件将被缓存! -->
  </component>
</keep-alive>

杂项

编写可复用组件

在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,但是可复用组件应当定义一个清晰的公开接口。
Vue 组件的 API 来自三部分 - props, events 和 slots :

  • Props 允许外部环境传递数据给组件

  • Events 允许组件触发外部环境的副作用

  • Slots 允许外部环境将额外的内容组合在组件中。

<!--v-bind,缩写:,绑定prop-->
<!--v-on,缩写@,监听事件-->
<!--slot插槽-->
<my-component
  :foo="baz"  
  :bar="qux"
  @event-a="doThis"
  @event-b="doThat"
>
  <img slot="icon" src="...">
  <p slot="main-text">Hello!</p>
</my-component>

子组件索引

尽管有 props 和 events ,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个索引 ID 。

<div id="parent">
  <user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 访问子组件
var child = parent.$refs.profile
  1. 当 ref 和 v-for 一起使用时, ref 是一个数组或对象,包含相应的子组件。

  2. $refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。

  3. ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 ref

组件命名约定

  • 当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不关心这个。

  • 在 HTML 模版中,请使用 kebab-case 形式:

// 在组件定义中
components: {
  // 使用 kebab-case 形式注册--横线写法
  'kebab-cased-component': { /* ... */ },
  // register using camelCase --驼峰写法
  'camelCasedComponent': { /* ... */ },
  // register using TitleCase --标题写法
  'TitleCasedComponent': { /* ... */ }
}
<!-- 在HTML模版中始终使用 kebab-case--横线写法 -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<title-cased-component></title-cased-component>

递归组件

  • 组件在它的模板内可以递归地调用自己,不过,只有当它有 name 选项时才可以

  • 当你利用Vue.component全局注册了一个组件, 全局的ID作为组件的 name 选项,被自动设置.

//组件可以用name来写名字
name: 'unique-name-of-my-component'
//也可以在创建的时候默认添加名字
Vue.component('unique-name-of-my-component', {
  // ...
})
//如果同时使用的话,递归的时候就会不断递归自己,导致溢出
name: 'stack-overflow',
template: '<div><stack-overflow></stack-overflow></div>'

使用-v-once-的低级静态组件-Cheap-Static-Component

尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样:

Vue.component('terms-of-service', {
  template: '\
    <div v-once>\
      <h1>Terms of Service</h1>\
      ... a lot of static content ...\
    </div>\
  '
})

v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。


线上猛如虎
2.2k 声望178 粉丝

你们都有梦想的,是吧.怀抱着梦想并且正朝着梦想努力的人,寻找着梦想的人,我想为这些人加油呐喊!