10-组件化

组件化开发

在实际开发中,一个页面的功能可能及其复杂,我们需要将其拆分成各个功能模块组件,以此达到复用和方便管理的目的。
在这里插入图片描述
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

示例1.1:不使用组件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>不使用模板</title>
</head>
<body>
<div id="app">
  <h2>标题</h2>
  <p>内容一</p>
  <p>内容二</p>
  <p>内容三</p>

  <h2>标题</h2>
  <p>内容一</p>
  <p>内容二</p>
  <p>内容三</p>

  <h2>标题</h2>
  <p>内容一</p>
  <p>内容二</p>
  <p>内容三</p>

  <h2>标题</h2>
  <p>内容一</p>
  <p>内容二</p>
  <p>内容三</p>
</div>
</body>
</html>

示例1.2:使用组件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>组件化的基本使用</title>
</head>
<body>
<div id="app">
  <!--  3.使用组件-->
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
  // 1.创建组件
  const cpnC = Vue.extend({
    template: `
  <div>
      <h2>标题</h2>
      <p>内容一</p>
      <p>内容二</p>
      <p>内容三</p>
    </div>`
  })

  // 2.注册组件
  Vue.component('my-cpn', cpnC)


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

全局组件和局部组件

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>全局组件和局部组件</title>
</head>
<body>
<div id="app">
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
  <mycpn2></mycpn2>
</div>

<br>

<div id="app2">
  <mycpn2></mycpn2>
  <mycpn2></mycpn2>
</div>

</body>
<script src="../js/vue.js"></script>
<script>
  const cpnC = Vue.extend({
    template: `
  <div>
      <h2>标题1</h2>
    </div>`
  })

  const cpnC2 = Vue.extend({
    template: `
  <div>
      <h2>标题2</h2>
    </div>`
  })


  //全局组件
  Vue.component('my-cpn', cpnC)

  const app = new Vue({
    el: '#app',
  })

  const app2 = new Vue({
    el: '#app2',
    components: {
      // 局部组件
      mycpn2: cpnC2
    }
  })
</script>
</html>

父组件和子组件

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父组件和子组件</title>
</head>
<body>
<div id="app">
  <parentc></parentc>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
  // 1.子组件
  const sonC = Vue.extend({
    template: `<div>
                <h2>子组件</h2>
              </div>`
  })

  // 2.父组件
  const parentC = Vue.extend({
    template: `
      <div>
        <h2>父组件</h2>
        <sonc></sonc>
      </div>`,
    components: {
      // 3.将子组件注册到父组件中
      sonc: sonC
    }
  })

  // vue实例对象可以看做最顶层的组件,root
  const app = new Vue({
    el: '#app',
    components: {
      // 4.将父组件注册到实例对象
      parentc: parentC
    }
  })
</script>
</html>

组件注册语法糖写法

示例1:语法糖写法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>组件注册语法糖</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    components: {
      'cpn': {
        template: `
              <div>
                <h2>标题</h2>
              </div>`
      }
    }
  })
</script>
</html>

示例2:将模板抽离

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>分离写法</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn2></cpn2>
</div>

<!--1.使用script标签,注意:类型必须是text/x-template-->
<script type="text/x-template" id="cpn">
  <div>
    <h2>标题1</h2>
  </div>
</script>

<!--2.使用template标签-->
<template id="cpn2">
  <div>
    <h2>标题2</h2>
  </div>
</template>

</body>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    components: {
      cpn: {
        template: cpn
      },
      cpn2: {
        template: cpn2
      }
    }
  })
</script>
</html>

组件中的data问题

示例1:组件中的data存放

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>组件中的data存放</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>{{title}}</h2>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册一个全局组件
Vue.component('cpn', {
  template: '#cpn',
  data() {
    return {
      title: '我是标题'
    }
  }
})

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

示例2:为什么组件中的data需要是一个函数

当data是一个函数时:
此时,返回一个对象,由于地址值不同,所以组件之间的数据互不干扰

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>data为什么是个函数</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>当前计数:{{counter}}</h2>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
// 修改计数器案例,注册组件
Vue.component('cpn', {
  template: '#cpn',
  data() {
    return {
      counter: 0
    }
  },
  methods: {
    increment() {
      this.counter++
    },
    decrement() {
      this.counter--
    }
  }
})

const app = new Vue({
  el: '#app'
})
</script>
</html>

当data是一个普通对象时:
对象的地址值不会变化,组件之间公用同一套数据,实际过程中产生问题

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>data为什么是个函数</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>当前计数:{{counter}}</h2>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const obj = {
  counter: 0
}

// 修改计数器案例,注册组件
Vue.component('cpn', {
  template: '#cpn',
  data() {
    return obj
  },
  methods: {
    increment() {
      this.counter++
    },
    decrement() {
      this.counter--
    }
  }
})

const app = new Vue({
  el: '#app'
})
</script>
</html>

父子组件通信问题

父传子

示例:通过props
props传值
深入了解props

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父传子</title>
</head>
<body>
<div id="app">
  <!--  v-bind不支持驼峰,要使用驼峰时用“-”连接,例如cMessage => c-message -->
  <cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>cmovies: {{cmovies}}</h2>
    <h2>cmessage: {{cmessage}}</h2>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const cpn = {
  template: '#cpn',
  props: ['cmovies', 'cmessage'],
}

const app = new Vue({
  el: '#app',
  data: {
    message: 'hello  Vue',
    movies: ['海贼王', '海尔兄弟', '海王']
  },
  components: {
    cpn
  }
})
</script>

</html>

子传父

示例:通过$emit自定义事件传值
自定义事件传值

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>子传父</title>
</head>
<body>
<div id="app">
  <cpn @item-click="cpnClick"></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <button v-for="item in movies" @click="btnClick(item)">{{item.name}}</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const cpn = {
  template: '#cpn',
  data() {
    return {
      movies: [
        {id: 0, name: '海贼王'},
        {id: 1, name: '海王'},
        {id: 2, name: '海尔兄弟'},
        {id: 3, name: '海下两万米'},
      ]
    }
  },
  methods: {
    btnClick(item) {
      this.$emit('item-click', item)
    }
  }
}

const app = new Vue({
  el: '#app',
  components: {
    cpn
  },
  methods: {
    cpnClick(item) {
      console.log(item)
    }
  }
})
</script>
</html>

父组件调用子组件方法-$children

$children返回的是一个数组,返回的是当前组件的全部子组件的信息,可以通过$children[i].mthod的方法调用,也就是数组中的array[i].method的形式,不过开发中由于需求变动较大,这种方法不够灵活,因此使用的不多。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父访子-children</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn2></cpn2>
  <button @click="btnClick">子组件信息</button>
</div>
</body>

<template id="cpn">
  <div>
    <h2>子组件1</h2>
  </div>
</template>

<template id="cpn2">
  <div>
    <h2>子组件2</h2>
  </div>
</template>


<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  methods: {
    btnClick() {
      console.log(this.$children)
      for (const child of this.$children) {
        child.showMessage()
      }
    }
  },
  components: {
    cpn: {
      template: "#cpn",
      data() {
        return {
          name: '子组件1'
        }
      },
      methods: {
        showMessage() {
          console.log('我是' + this.name)
        }
      }
    },
    cpn2: {
      template: "#cpn2",
      data() {
        return {
          name: '子组件2'
        }
      },
      methods: {
        showMessage() {
          console.log('我是' + this.name)
        }
      }
    }
  }
})
</script>
</html>

父组件调用子组件-$refs

$refs返回的是一个对象,因此可以通过调用对象内部的方法使用。如下列的例子,在使用控件的时候在控件加上"ref = abc",本质上类似于div标签中的id,因此可以通过this.$refs.abc找到在当前组件中使用的命名为abc的子组件。此时使用this.$refs.adc.method调用子组件方法。该方法类似于对象中的通过名称绑定值,因此即便开发过程中有变动也没啥影响,用的比较频繁。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父访问子-ref</title>
</head>
<body>
<div id="app">
  <cpn2></cpn2>
  <cpn ref="abc"></cpn>
  <cpn2 ref="bcd"></cpn2>
  <button @click="btnClick">按钮</button>
</div>
</body>

<template id="cpn">
  <div>
    <h2>子组件1</h2>
  </div>
</template>
<template id="cpn2">
  <div>
    <h2>子组件2</h2>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  methods: {
    btnClick() {
      console.log(this.$refs)
      console.log(this.$refs.abc.name)
      this.$refs.abc.showMessage()
    }
  },
  components: {
    cpn: {
      template: '#cpn',
      data() {
        return {
          name: '子组件1'
        }
      },
      methods: {
        showMessage() {
          console.log('我是' + this.name)
        }
      }
    },
    cpn2: {
      template: '#cpn2',
      data() {
        return {
          name: '子组件2'
        }
      },
      methods: {
        showMessage() {
          console.log('我是' + this.name)
        }
      }
    }
  }
})
</script>
</html>

子组件访问父组件和根组件-$parent && $root

$parent可以用来访问该组件的父组件,不过会加强和父组件之间的耦合性,一般不推荐使用
$root可以用来直接访问根组件

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>子访问父-parent与root</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>{{name}}</h2>
    <cpn2></cpn2>
  </div>
</template>
<template id="cpn2">
  <div>
    <h2>{{name}}</h2>
    <button @click="showParent">访问父组件</button>
    <button @click="showRoot">访问根组件</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
let cpn2 = {
  template: '#cpn2',
  data() {
    return {
      name: '子组件'
    }
  },
  methods: {
    showParent() {
      console.log(this.$parent)
      console.log(this.$parent.name)
    },
    showRoot() {
      console.log(this.$root)
      console.log(this.$root.name)
    }
  }
}


let cpn = {
  template: '#cpn',
  data() {
    return {
      name: '父组件'
    }
  },
  components: {
    cpn2
  }
}


const app = new Vue({
  el: '#app',
  data: {
    name: '根组件'
  },
  components: {
    cpn
  }
})
</script>
</html>

slot插槽

原始组件的可扩展性太差,vue中提供了一种方法。slot插槽可以用于对组件的扩展
插槽的详细使用

slot的基本使用

例如有以下需求:

  • 第一个子组件需要加一个按钮
  • 第二个子组件需要加一个span标签
  • 第三个子组件不需要改动

此时可以在组件中添加slot用于可能产生的替换

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>slot的基本使用</title>
</head>
<body>
<div id="app">
  <cpn>
    <button>按钮</button>
  </cpn>
  <cpn>
    <span>span</span>
  </cpn>
  <cpn></cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>子组件</h2>
    <slot></slot>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  components: {
    cpn: {
      template: '#cpn',
    }
  }
})
</script>
</html>

slot的默认值

slot中可以设置默认值

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>slot的基本使用</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn>
    <span>span</span>
  </cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>子组件</h2>
    <slot>
      <button>按钮</button>
    </slot>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  components: {
    cpn: {
      template: '#cpn',
    }
  }
})
</script>
</html>

具名插槽

有一个需求:

  • 将一个div分成三块
  • 中间改为标题
  • 左边改为返回按钮
  • 右边改为前进按钮

示例1:不使用具名

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>具名插槽</title>
</head>
<body>
<div id="app">
  <cpn>
    <span>标题</span>
  </cpn>
</div>
</body>

<template id="cpn">
  <div>
    <slot><span>左边</span></slot>
    <slot><span>中间</span></slot>
    <slot><span>右边</span></slot>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  components: {
    cpn: {
      template: '#cpn'
    }
  }
})
</script>
</html>

此时使用会将所用的未使用具名的slot都替换

示例2:使用具名完成需求

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>具名插槽</title>
</head>
<body>
<div id="app">
  <cpn>
    <button slot="left">返回</button>
    <span slot="center">标题</span>
    <button slot="right">前进</button>
  </cpn>
</div>
</body>

<template id="cpn">
  <div>
    <slot name="left"><span>左边</span></slot>
    <slot name="center"><span>中间</span></slot>
    <slot name="right"><span>右边</span></slot>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  components: {
    cpn: {
      template: '#cpn'
    }
  }
})
</script>
</html>

编译作用域

简单来说就是这个组件在哪使用就只能访问那个实例对象中的内容,例如下面的例子中,cpn组件在app中使用,cpn只能访问app.data中的message,而cpn.data的name无法访问。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>编译作用域</title>
</head>
<body>
<div id="app">
  <cpn>
    <p>{{name}}</p>
    <p>{{message}}</p>

  </cpn>
</div>
</body>

<template id="cpn">
  <div>
    <h2>子组件</h2>
    <slot></slot>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  data: {
    message: 'vue'
  },
  components: {
    cpn: {
      template: '#cpn',
      data() {
        return {
          name: '子组件'
        }
      }
    }
  }
})
</script>
</html>

作用域插槽

简单来讲就是通过插槽获取子组件中的数据

一个例子:

  • 在父组件中将以下组件中的标签修改为h2

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>作用域插槽</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>
</body>

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

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  components: {
    cpn: {
      template: '#cpn',
      data() {
        return {
          Planguages: ['Java', 'JavaScript', 'C++', 'C#', 'Go']
        }
      }
    }
  }
})
</script>
</html>

示例2:修改后

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>作用域插槽</title>
</head>
<body>
<div id="app">
  <cpn>
    <!--    <template v-slot="slot"> 2.6以上推荐使用-->
    <template slot-scope="slot">
      <h2 v-for="item in slot.planguages">{{item}}</h2>
    </template>
  </cpn>
</div>
</body>

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

<script src="../js/vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  components: {
    cpn: {
      template: '#cpn',
      data() {
        return {
          Planguages: ['Java', 'JavaScript', 'C++', 'C#', 'Go']
        }
      }
    }
  }
})
</script>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值