Vue学习记录day05:Vue组件化开发

组件

  1. 组件化的基本使用

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <div id="app">
      <!--  3.组件的基本使用-->
      <my-cpn></my-cpn>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
    
      //1.创建组件构造器对象
      const cpnConstructor = Vue.extend({
        template: `<div>
                    <h2>我是标题</h2>
                    <p>我是内容,哈哈哈哈</p>
                   </div>`
      });
    
      //2.注册组件
      Vue.component('my-cpn',cpnConstructor);
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好'
        }
      })
    </script>
    </body>
    </html>
    这里的步骤都代表什么含义呢?
    1.Vue.extend()
    调用 Vue.extend () 创建的是一个组件构造器。
    通常在创建组件构造器时,传入 template 代表我们自定义组件的模板。
    该模板就是在使用到组件的地方,要显示的 HTML 代码。
    事实上,这种写法在 Vue2.x 的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
    2.Vue.component()
    调用 Vue.component () 是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
    所以需要传递两个参数: 1 、注册组件的标签名 2 、组件构造器
    3. 组件必须挂载在某个 Vue 实例下,否则它不会生效。
    我们来看下面我使用了三次 <my- cpn ></my- cpn >
    而第三次其实并没有生效:
     
  2. 全局组件和局部组件
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <div id="app">
      <dada></dada>
    </div>
    <div id="app2">
      <cpn></cpn>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      //1.创建组件构造器
      const cpn = Vue.extend({
        template: `<div>
                   <h2>你好呀!H2</h2>
                   <div>我是div</div>
                    </div>`
      })
      //2.注册组件(全局组件)
      //Vue.component('cpn',cpn);
    
      //疑问:怎么注册的组件才是局部组件?
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好'
        },
        //局部组件
        components: {
          // 前者cpn:使用组件时的标签名
          cpn: cpn
        }
      })
    
    
      const app2 = new Vue({
        el: '#app2',
        data: {
          message: '你好'
        }
      })
    </script>
    </body>
    </html>
  3. 父组件和子组件

     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <div id="app">
      {
         
         {message}}
      <cpn2></cpn2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      //1.创建第一个组件构造器(子组件)
      const cpnC1 = Vue.extend({
        template: `
        <div>
        <h2>我是标题11</h2>
        <div>我是内容呵呵呵呵</div>
    </div>
        `
      })
      //2.创建第二个组件构造器(父组件)
      const cpnC2 = Vue.extend({
        template: `
          <div>
            <h2>我是标题22</h2>
            <div>我是内容呵呵呵呵22222</div>
            <cpn1></cpn1>
          </div>
        `,
        //在父组件中注册子组件
        components: {
          cpn1: cpnC1
        }
      })
    
      //root组件
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好'
        },
        components: {
          cpn2: cpnC2
        }
      })
    </script>
    </body>
    </html>


     

  4. 组件的语法糖注册方式
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>注册组件语法糖</title>
    </head>
    <body>
    <div id="app">
      <my-cpn></my-cpn>
    
      <my-cpn-ju></my-cpn-ju>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      //全局注册组件
      Vue.component("my-cpn", {
        template: `<div>hello,my-cpn</div>`
      })
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好'
        },
        //局部注册组件
        components: {
          "my-cpn-ju": {
            template: `<div>hello,my-cpn-ju</div>`
          }
        }
      })
    </script>
    </body>
    </html>
  5. 组件模板分离写法
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>注册组件语法糖</title>
    </head>
    <body>
    <div id="app">
      <cpn></cpn>
    </div>
    
    <!--1.script标签,注意:类型必须是text/x-template-->
    <!--<script type="text/x-template" id="cpn">
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈</p>
      </div>
    </script>-->
    
    <!--2.template标签-->
    <template id="cpn">
      <div>
        <h2>我是标题</h2>
        <p>我是内容,template标签</p>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
      //全局注册组件
      Vue.component("cpn", {
        template: `#cpn`
      })
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好'
        }
      })
    </script>
    </body>
    </html>
  6. 组件中数据存放问题
    组件不可以访问Vue实例数据!
    组件自己的数据存放在哪里呢?
           组件对象也有一个data
    属性(也可以有methods等属性,下面我们有用到),只是这个data属性必须是一个函数,而且这个函数返回一个对象,对象内部保存着数据
    为什么data
    在组件中必须是一个函数呢?
           首先,如果不是一个函数,Vue
    直接就会报错。其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>注册组件语法糖</title>
    </head>
    <body>
    <div id="app">
      <cpn></cpn>
    </div>
    
    <!--1.script标签,注意:类型必须是text/x-template-->
    <!--<script type="text/x-template" id="cpn">
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈</p>
      </div>
    </script>-->
    
    <!--2.template标签-->
    <template id="cpn">
      <div>
        <h2>{
         
         {title}}</h2>
        <p>我是内容,template标签</p>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
      //全局注册组件
      Vue.component("cpn", {
        template: `#cpn`,
        data() {
          return {
            title: 'hello'
          }
        }
      })
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好',
          title: '我是标题'
        }
      })
    </script>
    </body>
    </html>
  7. 组件中的data为什么是函数
    首先,如果不是一个函数,Vue直接就会报错。
    其次,原因是在于Vue
    让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。


     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <!--组件实例对象-->
    <div id="app">
        <cpn></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <h2>当前计数:{
         
         {counter}}</h2>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    
      //1.注册组件
      Vue.component(`cpn`,{
        template: `#cpn`,
        data() {
          return {
            counter: 0
          }
        },
        methods: {
          increment(){
            this.counter++;
          },
          decrement(){
            this.counter--;
          }
        }
      })
    
      const app = new Vue({
          el: '#app',
          data: {
              message : '你好'
          }
      })
    </script>
    
    <script>
      // const obj = {
      //   name: 'why',
      //   age: 18
      // }
      //
      // function abc() {
      //   return obj;
      // }
      //
      // let obj1 = abc();
      // let obj2 = abc();
      // let obj3 = abc();
      //
      // obj1.name = 'kobe';
      // console.log(obj2);
      // console.log(obj3);
    </script>
    </body>
    </html>
  8. 父子组件的通信

      注意:子组件是不能引用父组件或者Vue实例的数据的

      

如何进行父子组件间的通信呢?Vue官方提到

1.通过props向子组件传递数据

2.通过事件向父组件发送消息

 

  • 父级向子级传递(通过props传递)
     
    props的值有两种方式:
    方式一:字符串数组,数组中的字符串就是传递时的名称。
         方式二:对象,对象可以设置传递时的类型,也可以设置默认值等

    示例:

     
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <div id="app">
      <cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <ul>
          <li v-for="(item,index) in cmovies">
            {
         
         {index+1}}.{
         
         {item}}
          </li>
        </ul>
        <h2>{
         
         {cmessage}}</h2>
      </div>
    
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    //父传子:props
      const cpn = {
        template: `#cpn`,
        //props: ['cmovies','cmessage'],
        props: {
          //验证所支持的数据类型:String Number Boolean Array Object Date Function Symbol
    
          //1.类型限制
          // cmovies: Array,
          // cmessage: String
    
          //2.提供一些默认值,以及必传值
          cmessage: {
            type: String, //类型
            default: 'aaaaaa', //默认值
            required: true  //必传
          },
          //类型是对象或者数组时,默认值必须是一个函数
          cmovies: {
            type: Array,
            default() {
              return ['黑马','传智']
            }
          }
        },
        data() {
          return {
          }
        },
        methods: {
    
        }
      }
    
      //root组件
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好',
          movies: ['海王','哪吒']
        },
        components: {
          //增强写法
          cpn
        }
      })
    </script>
    </body>
    </html>


     
  • 子级向父级传递

  自定义事件的流程:

在子组件中,通过$emit()来触发事件。

                在父组件中,通过v-on来监听子组件事件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<!--父组件模板-->
<!--
事件对象参数 event 的处理。不加括号时,函数第一个参数为 event,加了括号后,需要手动传入 $event 才能获得事件对象。
-->
<div id="app">
  <cpn @item-click="cpnClick" ></cpn>
  <!--<cpn @item-click="cpnClick($event)" ></cpn>-->
</div>

<!--子组件模板-->
<template id="cpn">
  <div>
    <button v-for="item in categories"
            @click="btnClick(item)">
      {
   
   {item.name}}
    </button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>

  //子组件
  const cpn = {
    template: `#cpn`,
    data() {
      return {
        categories: [
          {id: 'aaa', name: '热门推荐'},
          {id: 'bbb', name: '手机数码'},
          {id: 'ccc', name: '家用家电'},
          {id: 'ddd', name: '电脑办公'}
        ]
      }
    },
    methods: {
      btnClick(item) {
        // 发射事件
        this.$emit('item-click',item);
      }
    }
  }


  //父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello,Panghl',
    },
    components: {
      cpn
    },
    methods: {
      cpnClick(item) {
        console.log('itemClick',item);
        console.log(item.id);
      }
    }
  })
</script>
</body>
</html>

9.父子组件的访问方式

    父组件访问子组件 : 使用$children  $refs preference(引用)

    子组件访问父组件:使用$parent

$children的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
$refs的使用:
$refs和ref指令通常是一起使用的。
首先,我们通过ref给某一个子组件绑定一个特定的ID。
其次,通过this.$refs.ID就可以访问到该组件了。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>$children $refs</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
  <cpn ref="aaa"></cpn>
  <button @click="btnClick">按钮</button>
</div>

<template id="cpn">
  <div>我是子组件</div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好'
    },
    methods: {
      btnClick() {
        //1.$children
        // console.log(this.$children);
        // for (let c of this.$children){
        //   console.log(c.name);
        //   c.showMessage()
        // }

        //2.$refs => 对象类型,默认时一个空的对象 ref='bbb'
        console.log(this.$refs.aaa.name);
        this.$refs.aaa.showMessage();
      }
    },
    components: {
      cpn: {
        template: `#cpn`,
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage() {
            console.log('showMessage');
          }
        }
      }
    }
  })
</script>
</body>
</html>

子组件访问父组件的访问方式: $parent

如果我们想在子组件中直接访问父组件,可以通过 $parent
注意事项:
尽管在 Vue 开发中,我们允许通过 $parent 来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过 $parent 直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>$children $refs</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>

<template id="cpn">
<div>
  <ccpn></ccpn>
</div>
</template>


<template id="ccpn">
  <div>
    <h2>我是孙子组件</h2>
    <button @click="btnClick">按钮</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好'
    },
    components: {
      cpn: {
        template: `#cpn`,
        data(){
          return{
            name: '我是子组件的name'
          }
        },
        components: {
          ccpn: {
            template: `#ccpn`,
            methods: {
              btnClick() {
                //1.访问父组件$parent
                console.log(this.$parent.$parent.message);
                console.log(this.$parent.name);

                //2.访问根组件 $root
                console.log(this.$root);
              }
            },
          }
        }
      }
    }
  })
</script>
</body>
</html>

组件化高级

  1. slot-插槽的基本使用:
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    
    <!--
    1.插槽的基本使用 <slot></slot>
    2.插槽的默认值 <slot>button</slot>
    3.如果有多个值,同时放入到组件进行替换时,一起作为替换元素
    -->
    
    <div id="app">
      <cpn></cpn>
      <cpn><div>你好啊
      <p>nnihao</p></div></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <h2>我是组件</h2>
        <p>我是组件,哈哈哈</p>
        <slot><button>按钮</button></slot>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
          el: '#app',
          data: {
              message : '你好'
          },
        components: {
          cpn: {
            template: `#cpn`
          }
        }
      })
    </script>
    
    
    </body>
    </html>
  2. slot-具名插槽的使用
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    
    <div id="app">
      <cpn><span slot="mid">标题</span></cpn>
      <cpn><button slot="left">左边按钮</button></cpn>
      <cpn><button slot="right">返回</button></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <slot name="left">左边</slot>
        <slot name="mid">中间</slot>
        <slot name="right">右边</slot>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
          el: '#app',
          data: {
              message : '你好'
          },
        components: {
          cpn: {
            template: `#cpn`
          }
        }
      })
    </script>
    
    
    </body>
    </html>
  3. 什么是编译的作用域
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <div id="app">
      <cpn v-show="isShow"></cpn>
      <button v-show="isShow">按钮</button>
    </div>
    
    <template id="cpn">
      <div>
        <h2>hello,p</h2>
        <p>我是内容,哈哈哈</p>
        <button v-show="isShow">组件按钮</button>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好',
          isShow: true
        },
        components: {
          cpn: {
            template: `#cpn`,
            data(){
              return {
                isShow: false
              }
            }
          }
        }
      })
    </script>
    </body>
    </html>
  4. 作用域插槽的案例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>

  <cpn>
    <!--目的是获取子组件中的pLanguage-->
    <template slot-scope="slot">
<!--      <span v-for="item in slot.data">{
   
   {item}}-</span>-->
      <span>{
   
   {slot.data.join(' - ')}}</span>
    </template>
  </cpn>

  <cpn>
    <template slot-scope="slot">
<!--      <span v-for="item in slot.data">{
   
   {item}} * </span>-->
      <span>{
   
   {slot.data.join(' * ')}}</span>
    </template>
  </cpn>

</div>

<template id="cpn">
  <div>
  <slot :data="pLanguage">
    <ul>
      <li v-for="item in pLanguage">{
   
   {item}}</li>
    </ul>
  </slot>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好'
    },
    components: {
      cpn: {
        template: `#cpn`,
        data() {
          return {
            pLanguage: ['JAVA', 'C++', 'C#', 'C', 'Javascript', 'go']
          }
        }
      }
    }
  })
</script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_45441466/article/details/110925541
今日推荐