Vue组件化开发

上期回顾

  • 条件判断(v-if、v-show)的基本使用、开发时选择
  • 条件渲染案例(input添加key的区别)
  • 循环遍历(v-for)
  • 表单绑定(v-model)
  • lazy修饰符
  • number修饰符
  • trim修饰符
  • 拓展:数组的响应式方法

什么是组件化:

独立的,可复用的,整体化的一个功能模块

为什么要组件化:

1.实现功能模块的复用
2.开发单页面复杂应用
3.高效执行


组件化开发

注册组件

三大步骤
1.创建组件构造器( Vue.extend() )
2.注册组件( Vue.component() )
3.使用组件

  • 代码演练
  <div id="app">
    <!-- 第三步:使用组件 -->
    <my-cpn></my-cpn>
    <!-- 错误的使用方式 -->
    <myCpn></myCpn>
  </div>

  <script src="../js/vue.js"></script>
  <script>
    //第一步:创建组件构造器
    let cpn = Vue.extend({
      template: `
      <div>
        <h2>我是组件</h2>
      </div>
      `
    })

    //第二步:注册组件
    Vue.component("myCpn", cpn)

    const app = new Vue({
      el: "#app",
      data: {

      }
    })
  </script>
组件的使用

注意:
第三步不能使用小驼峰命名,大写字母变为小写字母,前面加 "-"

Vue.extend({template})
Vue.extend():返回一个组件构造器
template:代表自定义组件的html模板
Vue.vomponent("组件命名",组件构造器)
Vue.vomponent():注册组件


全局组件和局部组件

  • 代码演示
  <div id="app1">
    <!-- 全局 -->
    <global-cpn></global-cpn>
    <!-- 局部 -->
    <part-cpn></part-cpn>
  </div>
  <div id="app2">
    <!-- 全局 -->
    <global-cpn></global-cpn>
    <!-- 局部 -->
    <part-cpn></part-cpn>
  </div>

  <script src="./js/vue.js"></script>
  <script>
    //1.创建全局组件构造器
    let cpn1 = Vue.extend({
      template: `
          <div>
            <h2>我是全局组件</h2>  
          </div>
        `
    })
    //1.创建局部组件构造器
    let cpn2 = Vue.extend({
      template: `
          <div>
            <h2>我是局部组件</h2>  
          </div>
        `
    })

    //2.注册全局组件
    Vue.component("globalCpn", cpn1)
    const app1 = new Vue({
      el: "#app1",
      //第二步:注册局部组件
      components: {
        "partCpn": cpn2
      }
    })
    const app2 = new Vue({
      el: "#app2"
    })
  </script>
全局组件和局部组件

父组件和子组件

  • 代码演示
  <div id="app">
    <!-- 使用父组件 -->
    <father-cpn></father-cpn>
  </div>

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

    //1.创建子组件构造器
    let cpn2 = Vue.extend({
      template: `
          <div>
            <h2>我是子组件</h2>  
          </div>
        `
    })

    //1.创建父组件构造器
    let cpn1 = Vue.extend({
      template: `
          <div>
            <h2>我是父组件</h2>
            <!-- 使用子组件 -->
            <son-cpn></son-cpn>
          </div>
        `,
      //在父组件中挂载子组件
      components: {
        "sonCpn": cpn2
      }
    })

    const app1 = new Vue({
      el: "#app",
      //在全局组件中挂载父组件
      components: {
        "fatherCpn": cpn1
      }
    })
  </script>
父组件和子组件

组件的简写方式

  • 代码演示
  <div id="app">
    <!-- 使用全局组件 -->
    <global-cpn></global-cpn>
    <!-- 使用局部组件 -->
    <part-cpn></part-cpn>

  </div>

  <script src="./js/vue.js"></script>
  <script>
    // 创建全局组件
    Vue.component("globalCpn", {
      template: `
        <div>
          <h2>我是全局组件</h2>
        </div>
      `
    })

    const app1 = new Vue({
      el: "#app",
      // 创建局部组件
      components: {
        "partCpn": {
          template: `
            <div>
              <h2>我是局部组件</h2>
            </div>
          `
        }
      }
    })
  </script>
组件简写

省去了Vue.extend()的步骤,直接用一个对象代替


模板的两种分离写法

1.使用<script>标签

  • 代码演示
  <div id="app">
    <my-cpn></my-cpn>
  </div>
  <!-- 使用<script>标签,注意加上type属性和id属性 -->
  <script type="text/x-template" id="myCpn">
    <div>
      <h2>我是组件</h2>
    </div>
  </script>
  <script src="./js/vue.js"></script>
  <script>
    const app1 = new Vue({
      el: "#app",
      // 创建局部组件
      components: {
        "myCpn": {
          template: "#myCpn"
        }
      }
    })
  </script>
使用<script>标签

2.使用<template>标签(推荐)

  • 代码演示
  <div id="app">
    <my-cpn></my-cpn>
  </div>
  <!-- 使用<template>标签,注意加上id属性 -->
  <template id="myCpn">
    <div>
      <h2>我是组件</h2>
    </div>
  </template>
  <script src="./js/vue.js"></script>
  <script>
    const app1 = new Vue({
      el: "#app",
      // 创建局部组件
      components: {
        "myCpn": {
          template: "#myCpn"
        }
      }
    })
  </script>
使用<template>标签(推荐)

注意:
使用两种标签,主要是将template中的html模板抽离出去,所以,两种标签一定要加上id属性,用于template的绑定


组件数据的处理

  • 代码演示
  <div id="app">
    {{message1}} - {{message2}}
    <my-cpn></my-cpn>
  </div>
  <template id="myCpn">
    <div>
      <h2>{{message1}}</h2>
      <h2>{{message2}}</h2>
    </div>
  </template>
  <script src="./js/vue.js"></script>
  <script>
    cpn = {
      "myCpn": {
        template: "#myCpn",
        // 数据存放在组件内部,组件可以访问,全局不能
        data() {
          return {
            message2: "组件内的数据"
          }
        },
      }
    }

    const app1 = new Vue({
      el: "#app",
      components: cpn,
      // 数据存放在实例的data内,全局可以访问在组件中无法访问
      data() {
        return {
          message1: "实例内的数据"
        }
      },
    })
  </script>
组件数据的处理
注意:

组件中data必须是函数:这样可以保证组件每次使用的数据不会被别的组件更改,保证各组件间的数据互不干扰,data不是函数时,会报错!!!


组件间的数据传递(父传子)

props基本用法
  • 代码演示
  <div id="app">
    <!-- 1.通过v-bind绑定数据message -->
    <my-cpn :info="message"></my-cpn>
  </div>
  <template id="myCpn">
    <div>
      <!-- 3.展示组件内props的数据 info -->
      <h2>{{info}}</h2>
    </div>
  </template>
  <script src="./js/vue.js"></script>
  <script>
    cpn = {
      "myCpn": {
        template: "#myCpn",
        // 2.接收组件标签绑定的数据
        props: ["info"]
      }
    }

    const app1 = new Vue({
      el: "#app",
      components: cpn,
      data: {
        message: "hello Vue"
      }
    })
  </script>
props基本用法

注意数据传递过程:
1.通过v-bind绑定数据messag
2.接收组件标签绑定的数据
3.展示组件内props的数据 info

props数据验证
  • 代码演示
  <div id="app">
    <my-cpn :info="message" :addNum="add()" :num="num3"></my-cpn>
  </div>

  <template id="myCpn">
    <div>
      <h2>{{info}}</h2>
      <h2>{{addNum}}</h2>
      <h2>{{num}}</h2>
      <h2>{{str}}</h2>
    </div>
  </template>

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

    const app = new Vue({
      el: "#app",
      data: {
        message: "hello Vue",
        num1: 3,
        num2: 5,
        num3: 10,
        str: "test"
      },
      methods: {
        add() {
          console.log(this.num1 + this.num2)
        }
      },
      components: {
        "my-cpn": {
          template: "#myCpn",
          props: {
            // key:类型
            info: String,
            addNum: Function,
            num: {
              type: Number,
              // 设为必填值
              required: true,
            },
            str: {
              type: String,
              // 设置默认值
              default: "我是默认值"
            }
          }
        }
      }
    })
  </script>
props数据验证
  • props支持验证类型
    String
    Number
    Array
    Boolean
    Function
    Object
    Date
    Symbol

组件间的数据传递(子传父)

  • 代码演示
  <div id="app">
    <h2>{{num}}</h2>
    <!-- 第四步:  将子组件传出的函数转为自定义函数,并绑定自定义事件的实现函数-->
    <son-cpn :amount="num" @add="change" @cut="change"></son-cpn>
  </div>

  <template id="myCpn">
    <div>
      <!-- 第一步: 绑定事件 -->
      <button @click="add">加10</button>
      <button @click="cut">减10</button>
    </div>
  </template>

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

    const app = new Vue({
      el: "#app",
      data: {
        num: 1
      },
      methods: {
        // 第五步: 实现自定义事件的函数
        // count 是第三步传入的参数
        change(count) {
          this.num = count
        }
      },
      components: {
        "sonCpn": {
          template: "#myCpn",
          props: {
            amount: {
              type: Number,
              required: true,
            }
          },
          data() {
            return {
              count: this.amount
            }
          },
          methods: {
            // 第二步: 事件函数的实现
            add() {
              this.count += 10
              // this.amount += 10  避免直接改变属性,因为当父组件重新呈现时,该值将被覆盖。

              // 第三步: 使用 emit("函数名",参数) 通知父组件,并传递参数
              this.$emit("add", this.count)
            },
            cut() {
              this.count -= 10
              // this.amount += 10 避免直接改变属性,因为当父组件重新呈现时,该值将被覆盖。
              
              // 第三步: 使用 emit("函数名",参数) 通知父组件,并传递参数
              this.$emit("cut", this.count)
            }
          }
        }
      }
    })
组件间的数据传递(子传父)

注意一

注意二

数据传递(子传父)过程
1.在子组件中,通过$emit("事件名",参数)来触发事件。
2.在父组件中,通过v-on来监听子组件事件。

父组件直接访问子组件的数据($children、$refs)

  • 代码演示
  <div id="app">
    <f-cpn></f-cpn>
  </div>

  <!-- 子组件模板 -->
  <template id="zCpn">
    <div>
      <h2>我是子组件</h2>
      <h2>{{info}}</h2>
      <h2>调用方法:{{mult}}</h2>
    </div>
  </template>

  <!-- 父组件模板 -->
  <template id="fCpn">
    <div>
      <h2>我是父组件</h2>
      <!-- 通过ref给某一个子组件绑定一个特定的ID -->
      <z-cpn ref="child"></z-cpn>
      <button @click="access$children">通过$children访问子组件</button>
      <button @click="access$refs">通过$refs访问子组件</button>
    </div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    //子组件实例
    let zCpn = {
      template: "#zCpn",
      data() {
        return {
          info: "我是子组件数据",
          num: 2
        }
      },
      computed: {
        mult() {
          return this.num * 3
        }
      }
    }

    const app = new Vue({
      el: "#app",
      data: {
      },
      components: {
        // 注册父组件
        "f-cpn": {
          template: "#fCpn",
          components: {
            // 注册子组件
            "z-cpn": zCpn
          },
          methods: {
            access$children() {
              // 通过$children访问子组件
              console.log(this.$children)
              console.log(this.$children[0].info)
              console.log(this.$children[0].mult)
              // console.log(this.$children[0].mult())    ❌错误写法,mult是计算属性,不是函数
            },
            access$refs(){
              // 通过$refs访问子组件
              console.log(this.$refs)
              console.log(this.$refs.child)
              console.log(this.$refs.child.info)
              console.log(this.$refs.child.mult)
              // console.log(this.$refs.child.mult())       ❌错误写法,mult是计算属性,不是函数
            }
          }
        }
      }
    })
  </script>
$children的使用

$refs的使用

总结:

  • $children需要通过子组件的下标($children[index]),才能访问该子组件,易出错,
  • $refs是通过ref绑定的ID访问子组件($refs.ID),更加精确到位,推荐使用$refs

子组件直接访问父组件的数据($parent)

  • 代码演示
  <div id="app">
    <f-cpn></f-cpn>
  </div>

  <!-- 子组件模板 -->
  <template id="zCpn">
    <div>
      <h2>我是子组件</h2>
      <button @click="getFInfo">点击获取父组件的信息</button>
    </div>
  </template>

  <!-- 父组件模板 -->
  <template id="fCpn">
    <div>
      <h2>我是父组件</h2>
      <z-cpn></z-cpn>
    </div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    //子组件实例
    let zCpn = {
      template: "#zCpn",
      methods: {
        getFInfo() {
          console.log(this.$parent)
          console.log(this.$parent.info)
          this.$parent.show()
        }
      }
    }

    const app = new Vue({
      el: "#app",
      data: {
      },
      components: {
        // 注册父组件
        "f-cpn": {
          template: "#fCpn",
          components: {
            // 注册子组件
            "z-cpn": zCpn
          },
          data() {
            return {
              info: "我是父组件信息"
            }
          },
          methods: {
            show(){
              console.log("通过$parent访问父组件的方法")
            }
          }
        }
      }
    })
  </script>
$parent的使用

注意:
在开发中尽量避免子组件通过$parent直接访问父组件的数据,因为这样耦合度太高了。而且这样会使父组件中的状态将变得飘忽不定,很不利于我的调试和维护。

插槽(slot)的使用

插值基本用法
  • 代码演示
  <div id="app">
    <div>==========单个slot使用==========</div>
    <my-cpn1></my-cpn1>
    <my-cpn1>
      <!-- 填入内容替换插槽 -->
      <h2>hello vue</h2>
    </my-cpn1>
    <div>==========具名solt使用==========</div>
    <!-- 如果my-cpn2内部没有内容,具名插槽不会显示,不具名插槽会显示,如my-cpn1 -->
    <my-cpn2></my-cpn2>
    <!-- 如果my-cpn2内部所有的标签都绑定了具名插槽,则不具名的插槽不会显示 -->
    <my-cpn2>
      <div name="content1"><h2>替换文字1</h2></div>
      <div name="content2"><h2>替换文字2</h2></div>
      <div name="content3"><h2>替换文字3</h2></div>
    </my-cpn2>
  </div>
  <template id="mySlot1">
    <div>
      <!-- 设置一个插槽 -->
      <slot>默认内容</slot>
    </div>
  </template>
  <template id="mySlot2">
    <div>
      <!-- 设置一个插槽 -->
      <slot><h2>默认内容3</h2></slot>
      <!-- 设置三个具名插槽 -->
      <slot slot="content1"><h2>默认内容1</h2></slot>
      <slot slot="content2"><h2>默认内容2</h2></slot>
      <slot slot="content3"><h2>默认内容3</h2></slot>
    </div>
  </template>
  <script src="../js/vue.js"></script>
  <script>

    const app = new Vue({
      el: "#app",
      components: {
        myCpn1: {
          template: "#mySlot1"
        },
        myCpn2: {
          template: "#mySlot2"
        }
      }
    })
  </script>
插槽(slot)的使用
slot数据传递
  • 代码演示
  <div id="app">
    <my-cpn>
      <!-- 第二步: 接收数据 -->
      <template slot-scope="props">
        <ul>
          <!-- 第三步: 使用数据 -->
          <li v-for="(value,index) in props.info">{{index}} - {{value}}</li>
        </ul>
      </template>
    </my-cpn>
  </div>
  <template id="mySlot">
    <div>
      <!-- 第一步: 绑定数据 -->
      <slot :info="course"></slot>
    </div>
  </template>
  <script src="../js/vue.js"></script>
  <script>

    const cpn = {
      template: "#mySlot",
      data() {
        return {
          course: {
            1001: "Python入门",
            1002: "数据结构与算法",
            1003: "离散数学",
            1004: "概率论",
            1005: "形式与政策",
          }
        }
      }
    }

    const app = new Vue({
      el: "#app",
      components: {
        myCpn: cpn
      }
    })
  </script>
slot数据传递

注意:

注意

编译作用域(补充)

  • 代码演示
  <div id="app">
    <my-cpn v-if="isShow">
    </my-cpn>
  </div>
  <template id="mySlot">
    <div>
      <slot>默认数据</slot>
    </div>
  </template>
  <script src="../js/vue.js"></script>
  <script>

    const cpn = {
      template: "#mySlot",
      data() {
        return {
          isShow: false
        }
      }
    }

    const app = new Vue({
      el: "#app",
      components: {
        myCpn: cpn
      },
      data: {
        isShow: true
      }
    })
  </script>
编译作用域一

编译作用域二

注意:任意组件模板的所有东西都会在该模板作用域内编译,即变量会从该实例中寻找

END
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • 组件化开发步骤:1.创建组件构造器 Vue.extend({})2.注册组件 Vue.component()3.使...
    似朝朝我心阅读 420评论 0 7
  • 一、什么是组件化开发 人面对复杂问题的处理方式:任何一个人处理信息的逻辑能力都是有限的所以,当面对一个非常复杂的问...
    c_gentle阅读 2,114评论 0 1
  • 认识组件化 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展...
    独调1997阅读 791评论 0 0
  • 组件化开发思想 现实中的组件化思想体现(1)标准(2)分治(3)重用(4)组合 编程中的组件化思想体现 组件化规范...
    辽A丶孙悟空阅读 1,873评论 1 19
  • 组件其实就是一个拥有样式、动画、js逻辑、HTML结构的综合块。前端组件化确实让大的前端团队更高效的开发前端项目。...
    IT老马阅读 797评论 0 1