初学vue2.0-组件

DanFossey 8年前
   <h2>组件</h2>    <ul>     <li> <p>组件可以扩展 HTML 元素,封装可重用的代码</p> </li>     <li> <p>在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能</p> </li>     <li> <p>在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。</p> </li>    </ul>    <h2>使用组件</h2>    <h3>注册一个全局组件</h3>    <pre>  <code class="language-javascript"><div id="example">  <!--web组件的定义脱离了一般的dom元素的写法,相当于自定义了元素-->    <my-component></my-component>  </div></code></pre>    <pre>  <code class="language-javascript">// 注册全局组件,指定之前设定的元素名,然后传入对象  Vue.component('my-component', {    template: '<div>A custom component!</div>'  })  // 创建根实例  new Vue({    el: '#example'  })</code></pre>    <h3>局部注册组件</h3>    <p>不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用</p>    <pre>  <code class="language-javascript">//将传入给组件的对象单独写  var Child = {    template: '<div>A custom component!</div>'  }  new Vue({    //通过components语法创建局部组件    //将组件仅仅放在这个vue实例里面使用    components: {      // <my-component> 将只在父模板可用      'my-component': Child    }  })</code></pre>    <h3>DOM模板解析说明</h3>    <p>当使用 DOM 作为模版时(例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,</p>    <p>因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。</p>    <p>尤其像这些元素 <ul> , <ol>, <table> , <select> 限制了能被它包裹的元素, <option> 只能出现在其它元素内部。</p>    <pre>  <code class="language-javascript"><!--这种是不行的,会报错-->  <table>    <my-row>...</my-row>  </table>  <!--要通过is属性来处理-->  <table>    <tr is="my-row"></tr>  </table></code></pre>    <h3>data必须是函数</h3>    <p>使用组件时,大多数可以传入到 Vue 构造器中的选项可以在注册组件时使用,有一个例外: data 必须是函数。 实际上</p>    <pre>  <code class="language-javascript">//这样会报错,提示data必须是一个函数  Vue.component('my-component', {    template: '<span>{{ message }}</span>',    data: {      message: 'hello'    }  })</code></pre>    <pre>  <code class="language-javascript"><div id="example-2">    <simple-counter></simple-counter>    <simple-counter></simple-counter>    <simple-counter></simple-counter>  </div></code></pre>    <pre>  <code class="language-javascript">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'  })</code></pre>    <p>避免出现同时改变数据的情况</p>    <pre>  <code class="language-javascript">//返回一个新的对象,而不是返回同一个data对象引用  data: function () {    return { //字面量写法会创建新对象      counter: 0    }  }</code></pre>    <h2>构成组件</h2>    <p>组件意味着协同工作,通常父子组件会是这样的关系:</p>    <ul>     <li> <p>组件 A 在它的模版中使用了组件 B 。它们之间必然需要相互通信</p> </li>     <li> <p>父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件</p> </li>    </ul>    <p>然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。</p>    <p>在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。</p>    <p>父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。</p>    <p><img src="https://simg.open-open.com/show/27584e95845e262286d25c47d44e0979.png"></p>    <h3>prop</h3>    <p>使用prop传递数据</p>    <ul>     <li> <p>组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。</p> </li>     <li> <p>使用 props 把数据传给子组件。</p> </li>     <li> <p>prop 是父组件用来传递数据的一个自定义属性</p> </li>     <li> <p>子组件需要显式地用 props 选项声明 “prop”</p> </li>    </ul>    <pre>  <code class="language-javascript"><div id="example-2">      <!--向这个组件传入一个字符串-->      <child message="hello!"></child>  </div></code></pre>    <pre>  <code class="language-javascript">Vue.component('child', {          // 声明 props,用数组形式的对象          props: ['message'],          // 就像 data 一样,prop 可以用在模板内          // 同样也可以在 vm 实例中像 “this.message” 这样使用          template: '<span>{{ message }}</span>'      });      new Vue({          el: '#example-2'      })</code></pre>    <h3>动态prop</h3>    <p>用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件</p>    <pre>  <code class="language-javascript"><div id="example-2">  <!--使用v-modal实现双向绑定-->      <input v-model="parentMsg">      <br>      <!--需要注意这里使用短横线的变量,因为在html下是使用短横线变量的,但是在vue下使用驼峰变量-->      <!--将父组件的parentMsg和子组件的my-message进行绑定-->      <child v-bind:my-message="parentMsg"></child>  </div></code></pre>    <pre>  <code class="language-javascript">Vue.component('child', {          // 声明 props          props: ['my-message'],           template: '<span>{{ myMessage }}</span>' //如果写my-message会报错,需要转换为驼峰写法      });      new Vue({          el: '#example-2',          data: {              parentMsg: ''          }      })</code></pre>    <h3>短横线和驼峰写法</h3>    <p>HTML 特性不区分大小写。当使用非字符串模版时,prop的名字形式会从 camelCase 转为 kebab-case(短横线隔开)</p>    <ul>     <li> <p>在javascript里面使用驼峰写法,但是在html里面需要转成短横线写法</p> </li>     <li> <p>反之亦然,vue会自动处理来自html的短横线写法转为驼峰写法</p> </li>    </ul>    <h3>字面量语法和动态语法</h3>    <pre>  <code class="language-javascript"><!-- 默认只传递了一个字符串"1" -->  <comp some-prop="1"></comp>    <!-- 用v-bind实现传递实际的数字 -->  <comp v-bind:some-prop="1"></comp></code></pre>    <h3>单向数据流</h3>    <ul>     <li> <p>prop 是单向绑定的</p> </li>     <li> <p>当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。</p> </li>     <li> <p>每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop 。如果你这么做了,Vue 会在控制台给出警告。</p> </li>    </ul>    <p>通常有两种改变 prop 的情况:</p>    <ol>     <li> <p>prop 作为初始值传入,子组件之后只是将它的初始值作为本地数据的初始值使用</p> </li>    </ol>    <p>定义一个局部 data 属性,并将 prop 的初始值作为局部数据的初始值。</p>    <pre>  <code class="language-javascript"><div id="example-2">  <!--这里用短横线写法-->      <child initial-counter="10"></child>  </div></code></pre>    <pre>  <code class="language-javascript">Vue.component('child', {          props: ['initialCounter'],//这里用驼峰写法          data: function () { //转为一个局部变量,写一个data对象给组件使用              return {counter: this.initialCounter}          },          template: '<span>{{ counter }}</span>'      });      new Vue({          el: '#example-2'      })</code></pre>    <ol>     <li> <p>prop 作为需要被转变的原始值传入。</p> </li>    </ol>    <p>定义一个 computed 属性,此属性从 prop 的值计算得出。</p>    <pre>  <code class="language-javascript">//例子没有写完,但是根据第一个例子可以知道利用computed的手法原理其实跟写一个data差不多  props: ['size'],  computed: {    normalizedSize: function () {      return this.size.trim().toLowerCase()    }  }</code></pre>    <p>注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。</p>    <h3>prop验证</h3>    <p>组件可以为 props 指定验证要求,当组件给其他人使用时这很有用。</p>    <pre>  <code class="language-javascript">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        }      }    }  })</code></pre>    <h2>自定义事件</h2>    <p>每个 Vue 实例都实现了事件接口(Events interface)</p>    <ul>     <li> <p>使用 $on(eventName) 监听事件</p> </li>     <li> <p>使用 $emit(eventName) 触发事件</p> </li>     <li> <p>父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。</p> </li>    </ul>    <pre>  <code class="language-javascript"><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></code></pre>    <pre>  <code class="language-javascript">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              }          }      })</code></pre>    <p>1.组件之间因为作用域不同的关系,所以相互独立,所以子组件想要使用父组件的方法的话需要做一个新的监听映射</p>    <h3>给组件绑定原生事件</h3>    <pre>  <code class="language-javascript"><!--代替.on,这么就能够绑定原生js的事件了-->  <my-component v-on:click.native="doTheThing"></my-component></code></pre>    <h3>使用自定义事件的表单输入组件</h3>    <p>自定义事件也可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。</p>    <p>所以要让组件的 v-model 生效,它必须:</p>    <ul>     <li> <p>接受一个 value 属性</p> </li>     <li> <p>在有新的 value 时触发 input 事件</p> </li>    </ul>    <pre>  <code class="language-javascript"><!--直接使用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"></code></pre>    <p>一个非常简单的货币输入:</p>    <pre>  <code class="language-javascript"><!--绑定一个v-model为price,其实是绑定了一个value-->  <currency-input v-model="price"></currency-input></code></pre>    <pre>  <code class="language-javascript">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要有数据源          }      })</code></pre>    <p>ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 <a href="/misc/goto?guid=4959735944528701496" rel="nofollow,noindex">ref</a></p>    <p>这是一个比较完整的例子:</p>    <pre>  <code class="language-javascript"><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></code></pre>    <pre>  <code class="language-javascript">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)      }    }  })</code></pre>    <h3>非父子组件通信</h3>    <p>在简单的场景下,使用一个空的 Vue 实例作为中央事件总线:</p>    <pre>  <code class="language-javascript">var bus = new Vue()    // 触发组件 A 中的事件  bus.$emit('id-selected', 1)    /*  通过on来监听子组件的事件来实现传递  */    // 在组件 B 创建的钩子中监听事件  bus.$on('id-selected', function (id) {    // ...  })</code></pre>    <h2>使用Slot分发内容</h2>    <p>为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为 内容分发 (或 “transclusion” 如果你熟悉 Angular)</p>    <h3>编译作用域</h3>    <p>组件作用域简单地说是:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。</p>    <p>假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。</p>    <pre>  <code class="language-javascript"><!-- 无效 -->  <child-component v-show="someChildProperty"></child-component></code></pre>    <p>如果要绑定子组件内的指令到一个组件的根节点,应当在它的模板内这么做:</p>    <pre>  <code class="language-javascript">Vue.component('child-component', {    // 有效,因为是在正确的作用域内    template: '<div v-show="someChildProperty">Child</div>',    data: function () {      return { //因为这个属性在当前组件内编译(创建了)        someChildProperty: true      }    }  })</code></pre>    <p>类似地,分发内容是在父组件作用域内编译。</p>    <h3>单个Slot</h3>    <ul>     <li> <p>除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃。</p> </li>     <li> <p>当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。</p> </li>     <li> <p>备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。</p> </li>    </ul>    <pre>  <code class="language-javascript"><!--父组件模版:-->  <div id="aa">      <h1>我是父组件的标题</h1>      <!--子组件的作用域内编译,宿主元素为空,且没有要插入的内容-->      <my-component></my-component>      <my-component>          <p>这是一些初始内容</p>          <p>这是更多的初始内容</p>      </my-component>  </div></code></pre>    <pre>  <code class="language-javascript">Vue.component('my-component', {       //my-component 组件有下面模板          template: '\              <div>\                  <h2>我是子组件的标题</h2> \                  <slot> \  //有slot插口,所以没有被父组件丢弃                  只有在没有要分发的内容时才会显示。\                  </slot> \              </div> \          '      })      new Vue({          el: '#aa',      })</code></pre>    <p>渲染结果:</p>    <pre>  <code class="language-javascript"><div id="aa"><h1>我是父组件的标题</h1>      <div>          <h2>我是子组件的标题</h2>          <!--这里是直接插入,没有使用DOM元素-->          只有在没有要分发的内容时才会显示。      </div>      <div>          <h2>我是子组件的标题</h2>          <p>这是一些初始内容</p>          <p>这是更多的初始内容</p>      </div>  </div></code></pre>    <h3>有名字的Slot</h3>    <ul>     <li> <p><slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。</p> </li>     <li> <p>仍然可以有一个匿名 slot ,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。</p> </li>    </ul>    <pre>  <code class="language-javascript"><div id="aa">      <app-layout>          <!--这是header-->          <h1 slot="header">这里可能是一个页面标题</h1>          <p>主要内容的一个段落。</p>          <p>另一个主要段落。</p>          <!--这是footer-->          <p slot="footer">这里有一些联系信息</p>      </app-layout>  </div></code></pre>    <pre>  <code class="language-javascript">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',      })</code></pre>    <p>渲染结果为:</p>    <pre>  <code class="language-javascript"><div class="container">    <header>      <h1>这里可能是一个页面标题</h1>    </header>    <main>      <p>主要内容的一个段落。</p>      <p>另一个主要段落。</p>    </main>    <footer>      <p>这里有一些联系信息</p>    </footer>  </div></code></pre>    <h3>作用域插槽(vue2.1)</h3>    <ul>     <li> <p>作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。</p> </li>     <li> <p>在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样</p> </li>     <li> <p>在父级中,具有特殊属性 scope 的 <template> 元素,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 prop 对象</p> </li>    </ul>    <pre>  <code class="language-javascript"><div id="parent" class="parent">      <child>      <!--接收从子组件中传递的prop对象(这个就是作用域插槽)-->          <template scope="props">              <span>hello from parent</span>              <!--使用这个prop对象-->              <span>{{ props.text }}</span>          </template>      </child>  </div></code></pre>    <pre>  <code class="language-javascript">Vue.component('child', {          props: ['props'], //这个写不写都可以,作用域插槽固定会接收prop对象,而且这个prop对象是肯定存在的          template: '\              <div class="child"> \              <slot text="hello from child"></slot> \ //在子组件里直接将数据传递给slot              </div> \          '      });      new Vue({          el: '#parent',      })</code></pre>    <p>渲染结果:</p>    <pre>  <code class="language-javascript"><div class="parent">    <div class="child">      <span>hello from parent</span>      <!--子组件的东西出现在这里了-->      <span>hello from child</span>    </div>  </div></code></pre>    <p>另外一个例子,作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项</p>    <pre>  <code class="language-javascript"><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></code></pre>    <pre>  <code class="language-javascript">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"}              ]          }      })</code></pre>    <ol>     <li> <p>位置1,实现了一个组件的prop绑定,prop需要在组件里面声明,这里绑定的是items,这是要将父组件的items传递到子组件,所以在位置3里面需要声明,在vue实例要初始化</p> </li>     <li> <p>位置2,这里scope的props是代表作用域插槽接收来自prop对象的数据,props.text是代表每一个li要输出的是prop对象的text属性</p> </li>     <li> <p>位置3,在组件里声明props,为了接收父组件绑定的items属性,然后将其给位置4的循环使用</p> </li>     <li> <p>位置4,这里绑定了text属性,就是前呼位置2里面输出的prop对象的text属性</p> </li>    </ol>    <h2>动态组件</h2>    <p>多个组件可以使用同一个挂载点,然后动态地在它们之间切换。使用保留的 <component> 元素,动态地绑定到它的 is 特性</p>    <pre>  <code class="language-javascript">var vm = new Vue({    el: '#example',    data: {      currentView: 'home' //默认值    },    components: { //根据不同的值进行不同的组件切换,这里用components写法      home: { /* ... */ },       posts: { /* ... */ },      archive: { /* ... */ }    }  })</code></pre>    <pre>  <code class="language-javascript"><!--这个is是一个字符串,根据返回值来给组件进行v-bind-->  <component v-bind:is="currentView">    <!-- 组件在 vm.currentview 变化时改变! -->  </component></code></pre>    <h3>keep-alive</h3>    <p>如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数</p>    <pre>  <code class="language-javascript"><keep-alive>    <component :is="currentView">      <!-- 非活动组件将被缓存! -->    </component>  </keep-alive></code></pre>    <h2>杂项</h2>    <h3>编写可复用组件</h3>    <p>在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,但是可复用组件应当定义一个清晰的公开接口。</p>    <p>Vue 组件的 API 来自三部分 - props, events 和 slots :</p>    <ul>     <li> <p>Props 允许外部环境传递数据给组件</p> </li>     <li> <p>Events 允许组件触发外部环境的副作用</p> </li>     <li> <p>Slots 允许外部环境将额外的内容组合在组件中。</p> </li>    </ul>    <pre>  <code class="language-javascript"><!--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></code></pre>    <h3>子组件索引</h3>    <p>尽管有 props 和 events ,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个索引 ID 。</p>    <pre>  <code class="language-javascript"><div id="parent">    <user-profile ref="profile"></user-profile>  </div></code></pre>    <pre>  <code class="language-javascript">var parent = new Vue({ el: '#parent' })  // 访问子组件  var child = parent.$refs.profile</code></pre>    <ol>     <li> <p>当 ref 和 v-for 一起使用时, ref 是一个数组或对象,包含相应的子组件。</p> </li>     <li> <p>$refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。</p> </li>     <li> <p>ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 <a href="/misc/goto?guid=4959735944528701496" rel="nofollow,noindex">ref</a></p> </li>    </ol>    <h3>组件命名约定</h3>    <ul>     <li> <p>当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不关心这个。</p> </li>     <li> <p>在 HTML 模版中,请使用 kebab-case 形式:</p> </li>    </ul>    <pre>  <code class="language-javascript">// 在组件定义中  components: {    // 使用 kebab-case 形式注册--横线写法    'kebab-cased-component': { /* ... */ },    // register using camelCase --驼峰写法    'camelCasedComponent': { /* ... */ },    // register using TitleCase --标题写法    'TitleCasedComponent': { /* ... */ }  }</code></pre>    <pre>  <code class="language-javascript"><!-- 在HTML模版中始终使用 kebab-case--横线写法 -->  <kebab-cased-component></kebab-cased-component>  <camel-cased-component></camel-cased-component>  <title-cased-component></title-cased-component></code></pre>    <h3>递归组件</h3>    <ul>     <li> <p>组件在它的模板内可以递归地调用自己,不过,只有当它有 name 选项时才可以</p> </li>     <li> <p>当你利用Vue.component全局注册了一个组件, 全局的ID作为组件的 name 选项,被自动设置.</p> </li>    </ul>    <pre>  <code class="language-javascript">//组件可以用name来写名字  name: 'unique-name-of-my-component'  //也可以在创建的时候默认添加名字  Vue.component('unique-name-of-my-component', {    // ...  })  //如果同时使用的话,递归的时候就会不断递归自己,导致溢出  name: 'stack-overflow',  template: '<div><stack-overflow></stack-overflow></div>'</code></pre>    <h3>使用-v-once-的低级静态组件-Cheap-Static-Component</h3>    <p>尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样:</p>    <pre>  <code class="language-javascript">Vue.component('terms-of-service', {    template: '\      <div v-once>\        <h1>Terms of Service</h1>\        ... a lot of static content ...\      </div>\    '  })</code></pre>    <p>v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。</p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000008251348</p>    <p> </p>