Vue.js实例学习:组件

什么是组件?

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。

所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。


使用组件

组件应该挂载到某个Vue实例下,否则它不会生效。
因而组件都是先注册,再挂载到Vue实例上。

一、全局注册

全局组件:先注册,再创建根实例。
Vue.component()方法内部会调用组件构造器,创建一个组件实例。
Vue.component(‘模板名字‘,‘template(组件构造器):要渲染的html‘);

例1:

<div id="app">
  <mycon></mycon>
</div>
<script type="text/javascript">
// 注册 (注册必须在 创建根实例之前,否则失效)
 Vue.component('mycon',{
   template:'<div>adadasd</div>'
 });
// 创建根实例
  new Vue({
    el:'#app',
  });
</script>

html部分被渲染为:

<div id="app">
  <div>adadasd</div>
</div>

输出:adadasd

例2:

反撇号()基础知识:
ES6引入了一种新型的字符串字面量语法,我们称之为模板字符串(template strings)。除了使用反撇号字符
代替普通字符串的引号 ’ 或 ” 外,它们看起来与普通字符串并无二致。

<div id="app">
    <checkbox-test></checkbox-test>
</div>
<script type="text/javascript">
  var content="content-test";
  Vue.component('checkbox-test', {
      //下面${content}改为{{content}}也行
      template: `<div  @click="check">
            <span>{{ title }}</span><br>
            <span>${content}</span><br>
            <span>checked:{{checked}}</span>
          </div>`,
      data() {
          return { checked: false, title: 'mytitle' }
      },
      methods: {
          check() { this.checked = !this.checked; }
      }
  });

  window.vm=new Vue({
      el:"#app",
  });
</script>

输出:
这里写图片描述

二、局部注册

用Vue 实例的 components选项,格式:

components:{
    '模板名称':{template(组件构造器):'要渲染的html'}
}

实例:

<div id="app">
   <mycon></mycon>
</div>
<script type="text/javascript">
  //必须在创建实例之前创建template(组件构造器),否则失效
  var Child = {template:'<div>adadasd</div>'};

  var vm = new Vue({
    el:'#app',
    components:{
      //或者'my-component':{template:'<div>局部注册</div>'}
      'mycon':Child,
    }
  });
</script>

输出结果:adadasd

三、DOM 模板解析注意事项

当使用 DOM 作为模板时 (例如,使用 el 选项来把 Vue 实例挂载到一个已有内容的元素上),你会受到 HTML 本身的一些限制,因为 Vue 只有在浏览器解析、规范化模板之后才能获取其内容。

尤其要注意,像 < ul>、< ol>、< table>、< select> 这样的元素里允许包含的元素有限制,而另一些像 < option> 这样的元素只能出现在某些特定元素的内部。变通的方案是使用特殊的 is 特性。

应当注意,如果使用以下来源之一的字符串模板,则没有这些限制:

  • < script type=”text/x-template”>
  • JavaScript 内联模板字符串
  • .vue 组件
    请尽可能使用上面三个。https://sdk.cn/news/7244

意思就是:


这样不可以

    <div id="app">
  <select>
    <option>please choose</option>
    <!--组件<myoption>、<myoption2>会被当作无效的内容,导致错误的渲染-->
    <myoption></myoption>
    <myoption2></myoption2>
  </select>
</div>
<script type="text/javascript">
  var option1 = {template:'<option>math</option>'};
  var option2 = {template:'<option>chinese</option>'};
  var vm = new Vue({
    el:'#app',
    components:{
      'myoption':option1,
      'myoption2':option2,
    }
  });
</script>

但是用is特殊属性可以:

<div id="app">
  <select>
    <option>please choose</option>
    <option is='myoption'></option>   <!-- is='myoption'不加单双引号也行,但是不规范-->
    <option is='myoption2'></option>
  </select>
</div>
<script type="text/javascript">
//若为template:'<option>math</option><option>chinese</option>',则只显示第一个,即math
  var option1 = {template:'<option>math</option>'};
  var option2 = {template:'<option>chinese</option>'};
  var vm = new Vue({
    el:'#app',
    components:{
      'myoption':option1,
      'myoption2':option2,
    }
  });
</script>

执行结果:
这里写图片描述
这里写图片描述


或者< script type=”text/x-template”>也可以

<div id="app">
  <select>
    <option>please choose</option>
    <option is='myoption'></option>
    <option is='myoption2'></option>   
  </select>

  <!--模板内容存放区域,放在#app里是为了方便阅读-->
  <script type="x-template" id="optemp">
    <option>math</option>
  </script>

  <script type="x-template" id="optemp2">
    <option>chinese</option>
  </script>

</div>

<script type="text/javascript">
  var option1 = {template:'#optemp'};
  var option2 = {template:'#optemp2'};
  var vm = new Vue({
    el:'#app',
    components:{
      'myoption':option1,
      'myoption2':option2,
    }
  });
</script>

执行结果:同上


或者 JavaScript 内联模板字符串 也行
即:局部模版 嵌套 全局模版。
注:不能 局部模版 嵌套 局部模版

<div id="app">
  <myselect></myselect>
</div>

<script type="text/javascript">
  var sel = {
    //局部嵌套全局
    template:
      '<select>'+
        '<option>please choose</option>'+
        '<myoption></myoption>'+
        '<myoption2></myoption2>'+
      '</select>'
  };
  //全局模版
  Vue.component('myoption',{template:'<option>math</option>'});
  Vue.component('myoption2',{template:'<option>chinese</option>'});
  var vm = new Vue({
    el:'#app',
    components:{
      'myselect':sel,
    }
  });
</script>

执行结果:同上


最后 .vue 组件也可以,这里先省略。


当然直接这么完也行,(直接把select放模版里,规避掉上面掉问题)
但相比于上面的js内联模板字符串,JavaScript 内联模板字符串更便于维护。

<div id="app">
  <myselect></myselect>
</div>

<script type="text/javascript">
  var child = {
    template:
     //在编译器中,一个字符串若不在一行会报错,所以用"+"连接。
      '<select>'+
          '<option>please choose</option>'+
          '<option>math</option>'+
          '<option>chinese</option>'+
      '</select>'
  };
  var vm = new Vue({
    el:'#app',
    components:{
      'myselect':child,
    }
  });
</script>

执行结果:同上

四、data 必须是函数

构造 Vue 实例时传入的各种选项大多数都可以在组件里使用。只有一个例外:data 必须是函数。


意思就是:这样不行

Vue.component('my-component', {
  template: '<span>{{ message }}</span>',
  data: {
    message: 'hello'
  }
})

但这样可以

Vue.component('my-component', {
  template: '<span>{{ message }}</span>',
  data: function(){
    return XXXX;
  }
})

一个错误的例子:

<div id="app">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>
<script>
  var mydata = { counter: 0 };
  Vue.component('simple-counter', {
    template: '<button v-on:click="counter += 1">{{ counter }}</button>',
    // 技术上 data 的确是一个函数了,因此 Vue 不会警告,
    // 但是我们却给每个组件实例返回了同一个对象的引用
    data: function () {
      return mydata;
    }
  })

  vm=new Vue({
      el:"#app",
  });
</script>

执行结果:
这里写图片描述
点击任意一个,你会发现所有的都加1 这个bug.
这里写图片描述
这是由于这三个组件实例共享了同一个 data 对象,因此递增一个 counter 会影响所有组件。


我们可以通过为每个组件返回全新的数据对象来修复上面的bug

# 组件全局注册使用data写法:

<div id="app">
  <mycount></mycount>
  <mycount></mycount>
  <mycount></mycount>
</div>

<script type="text/javascript"> 
  Vue.component('mycount',{
    template:'<button @click=counter++>{{counter}}</button>',
    data:function(){
      return {
        counter:0,
      };
    }
  });

  var vm = new Vue({
    el:'#app',
  });
</script>

执行结果:
这里写图片描述
点击后:
这里写图片描述

将上面代码改成局部注册

# 组件局部注册使用data写法:

<div id="app">
  <mycount></mycount>
  <mycount></mycount>
  <mycount></mycount>
</div>

<script type="text/javascript">

  var clkcount = {
    template:'<button @click=counter++>{{counter}}</button>',
    data:function(){
      return {
        counter:0,
      };
    }
  };
  var vm = new Vue({
    el:'#app',
    components:{
      'mycount':clkcount,
    }
  });
</script>

执行结果功能:同上

五、组件组合

组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。然而,通过一个良好定义的接口来尽可能将父子组件解耦也是很重要的。这保证了每个组件的代码可以在相对隔离的环境中书写和理解,从而提高了其可维护性和复用性。

在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。看看它们是怎么工作的。
这里写图片描述

Prop

一、用 Prop 传递数据 ( 父 -> 子 )

组件实例的作用域是孤立的。这意味着不能在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中 ( 父组件 -> 子组件 )。

子组件要显式地用 props 选项声明它预期的数据:

父组件通过props向子组件传递数据。

<!--子组件-->
<child id="app" message="hello boy"></child>

<script type="text/javascript">
//父组件:全局注册
Vue.component('child',{
  // 声明 props
  props:['message'],
  // 就像 data 一样,prop 也可以在模板中使用
  // 同样也可以在 vm 实例中通过 this.message 来使用
  template:'<span>{{message}}</span>'
});
var vm = new Vue({
  el:'#app',
});
</script>

二、命名:驼峰式 vs. 短横线分隔式

驼峰式命名:camelCase
短横线分隔式命名:kebab-case

HTML 特性是不区分大小写的。所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名):

记住:
1、在注册组件时要用camelCase(用kebab-case会出错)。
2、在html中将camelCase 转换为 kebab-case
(因为HTML不区分大小写的)
3、如果你使用字符串模板,则没有这些限制。

<!-- 在 HTML 中使用 kebab-case -->
<child id="app" my-message="hello!"></child>

<script type="text/javascript">
//js变量名 用kebab-case,不能使用camelCase命名
Vue.component('child',{
  props:['myMessage'],
  template:'<span>{{myMessage}}</span>'
});

var vm = new Vue({
  el:'#app',
});
</script>

三、动态 Prop

与绑定到任何普通的 HTML 特性相类似,我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件:

<div id="app">
  <input v-model="parentMsg"></input><br>
  <child :my-message="parentMsg"></child>
</div>

<script type="text/javascript">
Vue.component('child',{
  props:['myMessage'],
  template:'<span>{{myMessage}}</span>'
});

var vm = new Vue({
  el:'#app',
  data:{
    parentMsg:''
  }
});
</script>

输入前:
这里写图片描述
输入后:
这里写图片描述

最近事比较多,晚点写完,
待续·····

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值