vue.js进阶之组件

因为之前的项目用了vue,但是是边学边用,很多细节都不熟悉,比如vue-router,之前也写过vue+browserify构建大型应用。这次写一个vue最强大的功能,就是vue的组件。
首先推荐一个博客,真的写得非常好,当然官方API也写得很好。
http://www.cnblogs.com/keepfool/p/5625583.html

vue组件基本步骤

这个图也是盗用这个博客的,所以先声明一下,我算是转载吧,哈哈。
步骤:
1 、创建组件构造器
2、 注册组件
3、在Vue实例中使用组件
这里写图片描述
一个小小的demo来说明一下,这个就是官方的例子:

<div id="example">
  <my-component></my-component>
</div>
// 定义
var MyComponent = Vue.extend({
  template: '<div>A custom component!</div>'
})

// 注册
Vue.component('my-component', MyComponent)

// 创建根实例
new Vue({
  el: '#example'
})
<div id="example">
  <div>A custom component!</div>
</div>

理解

  1. Vue.extend()是Vue构造器的扩展,调用Vue.extend()创建的是一个组件构造器。
  2. Vue.extend()构造器有一个选项对象,选项对象的template属性用于定义组件要渲染的HTML。
  3. 使用Vue.component()注册组件时,需要提供2个参数,第1个参数时组件的标签,第2个参数是组件构造器。
  4. 组件应该挂载到某个Vue实例下,否则它不会生效。

属性值的传递

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

Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 一样,prop 可以用在模板内
  // 同样也可以在 vm 实例中像 “this.message” 这样使用
  template: '<span>{{ message }}</span>'
})
<child message="hello!"></child>

官网的例子。
然而如何绑定到style和id呢?

data和props

1、data

使用组件Components时,大多数选项可以被传入到 Vue 构造器中,有一个例外: data 必须是函数。因为如果不是函数的,声明多个组件的时候,他们共享的就是同一个data,这样就会乱掉。如果通过函数返回,那么每个组件维持自己的data作用域。该data属性只在其component中可见。

<body>
    <div id="app">
        <my-component>
        </my-component>
    </div>
    <template id="myComponent">
        <div>
            <h2>{{msg}}</h2>
            <button @click="showMsg">Show Message</button>
        </div>
    </template>
</body>
<script type="text/javascript" src="../dist/vue.js"></script>
<script type="text/javascript" src="../dist/vue-router.js"></script>
<script type="text/javascript">
new Vue({
    el: '#app',
    components: {
        'my-component': {
            template: '#myComponent',
            data: function() {
                return {
                    msg: 'This is a Component!' //Vue中component的data必须通过function() return
                }
            },
            methods: {
                showMsg: function() {
                    alert(this.msg);
                }
            }
        }
    }
})
</script>

Component其他感觉和之前用的Vue没什么区别。
也可以有methods方法,有data,多了props

2、关于组件的作用域

template不是标准的HTML元素,浏览器是不理解这个元素的。
这里写图片描述
这里还可以看到template

那么Vue是如何让浏览器理解template标签的呢:
感觉必须是挂载在Vue中,通过Vue解析出这个标签成为浏览器可以理解的元素。
就像下面这段代码,必须新new 一个Vue,Vue使用myComponent作为components,然后在Vue绑定的app中,这个component就会被解析成浏览器可以阅读的语言。

new Vue({
    el: '#app',
    components: {
        'my-component': {
            template: '#myComponent',
            data: function() {
                return {
                    msg: 'This is a Component!' //Vue中component的data必须通过function() return
                }
            },
            methods: {
                showMsg: function() {
                    alert(this.msg);
                }
            }
        }
    }
})

并且vue实例和component的作用域是独立的

new Vue({
    el: '#app',
    data:{
        display:true //vue实例的display
    },
    components: {
        'my-component': {
            template: '#myComponent',
            data: function() {
                return {
                    msg: 'This is a Component!',
                    display: false //component中的display
                }
            },
            methods: {
                showMsg: function() {
                    alert(this.msg);
                }
            }
        }
    }
})
 <div id="app">
        <my-component v-show="display">
        </my-component>
    </div>
    <template id="myComponent">
        <div>
            <h2 v-show="display">{{msg}}</h2>
            <button @click="showMsg">Show Message</button>
        </div>
    </template>

运行结果:

这里写图片描述
h2被隐藏了,
但是my-component没有被隐藏。
也就是说:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
那么父子组件如何进行通信呢?
答案是props

3、父组件和子组件通信

官网的一张图
这里写图片描述
父组件通过* props* 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。

a. 父元素向子元素通信:props

还是刚刚的例子,我们在component中添加了props
例子1:静态props

new Vue({
    el: '#app',
    data:{
        display:true
    },
    components: {
        'my-component': {
            template: '#myComponent',
            props:['parentmsg'], //声明props
            data: function() {
                return {
                    msg: 'This is a Component!',
                    display: false //Vue中component的data必须通过function() return
                }
            },
            methods: {
                showMsg: function() {
                    alert(this.msg);
                }
            }
        }
    }
})

我们在template中加了一个p,这个p引用父元素的msg,另外说一声Vue中属性绑定这个不识别大小写

<my-component v-show="display" parentMsg="ParentMsg">

在浏览器看到就是

<my-component v-show="display" parentmsg="ParentMsg">

所以即使你声明了props:[‘parentMsg’]也显示不出来。我的代码是这样

后来看到官方的解释是这样的:

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

 <div id="app">
        <my-component v-show="display" parentmsg="ParentMsg">
        </my-component>
    </div>
    <template id="myComponent">
        <div>
            <h2 v-show="display">{{msg}}</h2>
            <p>{{parentmsg}}</p> 
            <button @click="showMsg">Show Message</button>
        </div>
    </template>

运行结果
这里写图片描述
这里就是父元素的ParentMsg传递给了component

例子2:动态props
感觉神奇之处就在这里,用v-bind把刚刚的props绑定起来

<input type="" name="" v-model="ParentMsg">
        <my-component v-show="display" v-bind:parentmsg="ParentMsg">
        </my-component>
    </div>

这样ParentMsg就根据输入框中的ParentMsg动态变化了。

new Vue({
    el: '#app',
    data:{
        display:true,
        ParentMsg:"Hello This is Parent"
    },
    components: {
        'my-component': {
            template: '#myComponent',
            props:['parentmsg'],
            data: function() {
                return {
                    msg: 'This is a Component!',
                    display: false //Vue中component的data必须通过function() return
                }
            },
            methods: {
                showMsg: function() {
                    alert(this.msg);
                }
            }
        }
    }
})

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

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

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

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

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

 <div id="app">
    <input type="" name="" v-model="ParentMsg">
        <my-component v-show="display" v-bind:parentmsg="ParentMsg">
        </my-component>
    </div>
    <template id="myComponent">
        <div>
            <h2 v-show="display">{{msg}}</h2>
            <p>{{parentmsg}}</p>
            <p>{{childprops}}</p>
            <button @click="showMsg">Show Message</button>
        </div>
    </template>
new Vue({
    el: '#app',
    data:{
        display:true,
        ParentMsg:"Hello This is Parent"
    },
    components: {
        'my-component': {
            template: '#myComponent',
            props:['parentmsg'],
            data: function() {
                return {
                    msg: 'This is a Component!',
                    childprops:"child:"+this.parentmsg, //可以在data中获取props,并生成新的data
                    display: false //Vue中component的data必须通过function() return
                }
            },
            methods: {
                showMsg: function() {
                    alert(this.msg);
                }
            }
        }
    }
})
a. 子元素向父元素传递信息:自定义事件

我们知道,父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,应该怎样做?那就是自定义事件!

使用 $on(eventName) 监听事件 
使用 $emit(eventName) 触发事件
 <button-counter v-on:increment="incrementTotal"></button-counter>
Vue.component('button-counter', {
    template: '<button v-on:click="increment">{{counter}}</button>',
    data: function() {
        return {
            counter: 0
        }
    },
    methods: {
        increment: function() {
            alert("increment")
            this.counter += 1;
            this.$emit('increment');
        }
    }
})
new Vue({
    el: '#app',
    data: {
        display: true,
        ParentMsg: "Hello This is Parent",
        total: 0
    },
    methods: {
        incrementTotal: function() {
            alert("incrementTotal")
            this.total += 1;
        }
    },
})

先运行increment,再运行incrementTotal
感觉这两句是非常重要的:

v-on:increment="incrementTotal"
this.$emit('increment');

分析,button上面绑定了v-on:click=”increment”,当点击按钮触发increment事件,当increment函数执行完毕,触发incrementTotal函数。全在于 this.$emit(‘increment’); 不然执行完毕increment就完毕了。
官网的例子:

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
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')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

这里写图片描述
这样点击任意一个button都会调用 incrementTotal。

input v-model="something">

其实v-model仅仅是一颗语法糖

<input v-bind:value="something" v-on:input="something = $event.target.value">

所以要让组件的 v-model 生效,它必须:

1.接受一个 value 属性
2. 在有新的 value 时触发 input 事件

<div id="app2">
        <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>
    <template id="myComponent">
        <div>
            <h2 v-show="display">{{msg}}</h2>
            <p>{{parentmsg}}</p>
            <p>{{childprops}}</p>
            <button @click="showMsg">Show Message</button>
        </div>
    </template>
Vue.component('currency-input', {
  template: '\
    <div>\
      <label v-if="label">{{ label }}</label>\
      $\
      <input\
        ref="input"\
        v-bind:value="value"\
        v-on:input="updateValue($event.target.value)"\
        v-on:focus="selectAll"\
      >\
    </div>\
  ',
  props: {
    value: {
      // type: Number,
      default: 0
    },
    label: {
      // type: String,
      default: ''
    }
  },
  methods: {
    updateValue: function (value) {
      this.$emit('input', value) //触发input事件
    },
    selectAll: function (event) {
      setTimeout(function () {
        event.target.select()
      }, 0)
    }
  }
})

new Vue({
  el: '#app2',
  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)
    }
  }
})

使用slot

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值