Vuejs基础之:组件

2017年8月19日 357点热度 0人点赞 0条评论


1Vuejs之组件

9.1 什么是组件

可以拓展HTML元素,封装常用的代码

是自定义元素

原生HTML的形式,以js特性扩展

每一个应用界面都可以看作是组件构成的,每一个组件都可以看做是一个ViewModel,如图:

图片

9.2 使用组件

全局注册

§new Vue()

§Vue.component(tagName,options)

局部注册

§用实例components注册

DOM模板解析

data必须是函数

构成组件

§props down

§events up

代码如下:

// 扩展 Vue 来自定义一个可复用的组件类

var MyComponent = Vue.extend({

  template: '<p>{{msg}}</p>',

  paramAttributes: ['msg']

})

// 全局注册该组件

Vue.component('my-component', MyComponent)

<my-component msg="Hello!"></my-component>

my-component 组件的模板将会被填充到该元素中,而 msg 则会被作为数据传入该组件实例。渲染结果如下。

<my-component>

  <p>Hello!</p>

</my-component>

9.3 使用组件的基本Demo

<!DOCTYPE html>

<html>

  <head>

  <meta charset="UTF-8">

  <title>组件-组件使用</title>

  </head>

  <body>

  <div id="example">

  <h1>基本用法</h1>

  <my-component></my-component>

  <h1>data必须是函数</h1>

  <my-data></my-data>

  <h1>每个组件返回同一个对象引用是错误的做法</h1>

  <simple-counter></simple-counter>

  <simple-counter></simple-counter>

  <simple-counter></simple-counter>

  <h1>通过每个组件返回新的data来实现独立不共用</h1>

  <simple-counter2></simple-counter2>

  <simple-counter2></simple-counter2>

  <simple-counter2></simple-counter2>

  </div>

  <script src="vue.js" type="text/javascript" charset="utf-8"></script>

  <script type="text/javascript">

  var data = { counter: 0 }

  Vue.component('my-component', {

    template: '<div>A custom component!</div>'

  });

  Vue.component('my-data', {

    template: '<div>{{msg}}</div>',

    data:function(){

      return {

      msg:'hello vue.js'

      }

    }

  });

  Vue.component('simple-counter', {

    template: '<button v-on:click="counter += 1">{{ counter }}</button>',

    // data 是一个函数,因此 Vue 不会警告,

    // 但是我们为每一个组件返回了同一个对象引用

    data: function () {

      return data

    }

  })

  Vue.component('simple-counter2', {

    template: '<button v-on:click="counter += 1">{{ counter }}</button>',

    // data 是一个函数,因此 Vue 不会警告,

    // 但是我们为每一个组件返回了同一个对象引用

    data: function () {

      return { counter: 0 }

    }

  })

  var vm = new Vue({

    el: '#example'

  });

  </script>

  </body>

</html>

9.4 props

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

  prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用  props 选项 声明 “prop”:

Vue.component('child', {

// 声明 props

props: ['message'],

// 就像 data 一样,prop 可以用在模板内

// 同样也可以在 vm 实例中像 “this.message” 这样使用

template: '<span>{{ message }}</span>'

})

<child message="hello!"></child>

camelCase vs kebab-case

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

Vue.component('child', {

// camelCase in JavaScript

props: ['myMessage'],

template: '<span>{{ myMessage }}</span>'

})

<!-- kebab-case in HTML -->

<child my-message="hello!"></child>

动态props

  类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 绑定动态 props 到父组件的数据。每当父组件的数据变化时,也会传导给子组件:

<div>

<input v-model="parentMsg">

<br>

<child v-bind:my-message="parentMsg"></child>

</div>

<child :my-message="parentMsg"></child>

字面量语法vs动态语法

  初学者常犯的一个错误是使用字面量语法传递数值:

<!-- 传递了一个字符串"1" -->

<comp some-prop="1"></comp>

  因为它是一个字面 prop ,它的值以字符串 “1” 而不是以实际的数字传下去。如果想传递一个实际的 JavaScript 数字,需要使用 v-bind ,从而让它的值被当作 JavaScript 表达式计算:

<!-- 传递实际的数字 -->

<comp v-bind:some-prop="1"></comp>

单向数据流

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

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

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

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

prop 作为需要被转变的原始值传入。

更确切的说这两种情况是:

定义一个本地数据,并且将 prop 的初始值设为本地数据的初始值。

定义一个基于 prop 值的计算属性。

Prop验证

  组件可以为 props 指定验证要求。如果未指定验证要求,Vue会发出警告。当组件给其他人使用时这很有用。

  prop 是一个对象而不是字符串数组时,它包含验证要求:

Vue.component('example', {

props: {

// 基础类型检测 (`null` 意思是任何类型都可以)

propA: Number,

// 多种类型

propB: [StringNumber],

// 必须且是字符串

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

}

}

}

})

9.5 props用法Demo

<!DOCTYPE html>

<html>

  <head>

  <meta charset="UTF-8">

  <title>组件-props</title>

  </head>

  <body>

  <div id="example">

  <h1>基本用法</h1>

  <child message="hello!"></child>

  <h1> camelCase 转为 kebab-case</h1>

  <child2 my-message="hello kebab-case!"></child2><!--html中使用kebab-case-->

  <h1>动态props</h1>

  <div>

    <input v-model="parentMsg"><br>

    <child2 :my-message="parentMsg"></child2>

  </div>

  </div>

  <script src="vue.js" type="text/javascript" charset="utf-8"></script>

 

<script type="text/javascript">

  Vue.component('child', {

    props: ['message'],// 声明 props

    template: '<span>{{ message }}</span>'// 就像 data 一样,prop 可以用在模板内,同样也可以在 vm 实例中像 “this.message” 这样使用

  })

  Vue.component('child2', {

    props: ['myMessage'],// camelCase in JavaScript

    template: '<span>{{ myMessage }}</span>'

  })

  Vue.component('example', {

    props: {

      propA: Number,// 基础类型检测 (`null` 意思是任何类型都可以)

      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

        }

      }

    }

  })

  var vm = new Vue({

    el: '#example',

    data:{

      parentMsg:''

    }

  });

  </script>

  </body>

</html>

9.6 自定义事件

使用v-on

§$on(eventName)

§$emit(eventName)

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

非父子组件通信

代码如图:

图片

9.7.1 自定义事件Demo

<!DOCTYPE html>

<html>

  <head>

  <meta charset="UTF-8">

  <title>组件-自定义事件</title>

  </head>

  <body>

  <div id="example">

  <h1>v-on绑定自定义事件</h1>

  <p>{{ total }}</p>

  <button-counter v-on:increment="incrementTotal"></button-counter>

  <button-counter v-on:increment="incrementTotal"></button-counter>

  <h1>自定义表单输入</h1>

  <currency-input v-model="price"></currency-input>

  </div>

  <script src="vue.js" type="text/javascript" charset="utf-8"></script>

  <script type="text/javascript">

  Vue.component('button-counter', {

    template: '<button v-on:click="increment">{{ counter }}</button>',

    data: function () {

      return {

        counter: 0

      }

    },

    methods: {

      increment: function () {

        this.counter += 1

        this.$emit('increment')

      }

    },

  })

Vue.component('currency-input', {

    template: '\

      <span>\

        $\

        <input\

          ref="input"\

          v-bind:value="value"\

          v-on:input="updateValue($event.target.value)"\

        >\

      </span>\

    ',

    props: ['value'],

    methods: {

      // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制

      updateValue: function (value) {

        var formattedValue = value// 删除两侧的空格符

          .trim()// 保留 2 小数位

          .slice(0, value.indexOf('.') + 3)// 如果值不统一,手动覆盖以保持一致

        if (formattedValue !== value) {

          this.$refs.input.value = formattedValue

        }// 通过 input 事件发出数值

        this.$emit('input', Number(formattedValue))

      }

    }

  })

  var vm = new Vue({

    el: '#example',

    data: {

      total: 0,

      price:''

    },

    methods: {

      incrementTotal: function () {

        this.total += 1

      }

    }

  });

  </script>

  </body>

</html>

9.8 使用Slots分发内容

在使用组件时,常常要像这样组合它们:

<app>

<app-header></app-header>

<app-footer></app-footer>

</app>

注意两点:

<app> 组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>的父组件决定的。

<app> 组件很可能有它自己的模版。

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

编译作用域

  组件作用域简单地说是:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。

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

Vue.component('child-component', {

 // 有效,因为是在正确的作用域内

template: '<div v-show="someChildProperty">Child</div>',

data: function () {

return {

someChildProperty: true

}

}

})

单个slot 

  除非子组件模板中包含<slot>,否则父组件的内容将会被抛弃。如果子组件模板只有一个没有特性的 slot,父组件整个内容片段将插到 slot 所在的 DOM 位置并替换掉 slot 标签。

  <slot> 标签的内容视为回退内容。回退内容在子组件的作用域内编译,当宿主元素为空并且没有内容供插入时显示这个回退内容。

  假定 my-component 组件有下面模板:

<div>

<h2>I'm the child title</h2>

<slot>

如果没有分发内容则显示我。

</slot>

</div>

单个slot 

    父组件模版:

<div>

<h1>I'm the parent title</h1>

<my-component>

<p>This is some original content</p>

<p>This is some more original content</p>

</my-component>

</div>

     渲染结果:

<div>

<h1>I'm the parent title</h1>

<div>

<h2>I'm the child title</h2>

<p>This is some original content</p>

<p>This is some more original content</p>

</div>

</div>

具名slots

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

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

  假定我们有一个 app-layout 组件,它的模板为:

<div class="container">

<header>

<slot name="header"></slot>

</header>

<main>

<slot></slot>

</main>

<footer>

<slot name="footer"></slot>

</footer>

</div>

父组件模板

<app-layout>

<h1 slot="header">Here might be a page title</h1>

<p>A paragraph for the main content.</p>

<p>And another one.</p>

<p slot="footer">Here's some contact info</p>

</app-layout>

渲染结果为:

<div class="container">

<header>

<h1>Here might be a page title</h1>

</header>

<main>

<p>A paragraph for the main content.</p>

<p>And another one.</p>

</main>

<footer>

<p>Here's some contact info</p>

</footer>

</div>

9.9 使用Slots分发内容Demo

<!DOCTYPE html>

<html>

  <head>

  <meta charset="UTF-8">

  <title>组件-使用solts分发</title>

  </head>

  <body>

  <div id="example">

  <h1>单个solt</h1>

  <div>

    <h3>我是父组件的标题</h3>

    <my-component>

      <p>这是一些初始内容</p>

      <p>这是更多的初始内容</p>

    </my-component>

  </div>

  <h1>具名solt</h1>

  <app-layout>

    <h3 slot="header">这里可能是一个页面标题</h3>

    <p>主要内容的一个段落。</p>

    <p>另一个主要段落。</p>

    <p slot="footer">这里有一些联系信息</p>

  </app-layout>

  </div>

  <script src="vue.js" type="text/javascript" charset="utf-8"></script>

  <script type="text/javascript">

  Vue.component('app-layout', {

    template: '\

      <div class="container">\

    <header>\

      <slot name="header"></slot>\

    </header>\

    <main>\

      <slot></slot>\

    </main>\

    <footer>\

      <slot name="footer"></slot>\

    </footer>\

  </div>\

    '

  });

  Vue.component('my-component', {

    template: '\

      <div>\

    <h5>我是子组件的标题</h5>\

    <slot>\

      只有在没有要分发的内容时才会显示。\

    </slot>\

  </div>\

    '

  });

  var vm = new Vue({

    el: '#example'

  });

  </script>

  </body>

</html>

9.6 动态组件

Keep-alive

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

代码如图:

图片

本站代码下载方法:

    关注公众号,在后台回复“代码下载”,如图:

图片

图片

8880Vuejs基础之:组件

root

这个人很懒,什么都没留下

文章评论