Blogger Information
Blog 128
fans 9
comment 5
visits 241315
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
08-Vue_组件交互
 一纸荒凉* Armani
Original
753 people have browsed it

一、创建组件

什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

1.1 创建全局组件

  • 注意:模板中有且只有一个根元素,就像一个网页只有一个根元素为<html>一样

1.1.1 第一种方式

  • 使用 Vue.extend 配合 Vue.component 方法:
  1. var com1 = Vue.extend({
  2. template: '<h2>第一种方式</h2>',
  3. data(){
  4. return{
  5. }
  6. }
  7. });
  8. Vue.component('mycom1', com1);
  • 直接使用 Vue.component 方法:
  1. Vue.component('mycom2', {
  2. template: '<h4>第一种方式的最终简写</h4>',
  3. data(){
  4. return {
  5. }
  6. }
  7. });

1.1.2 第二种创建方式

  • 将模板字符串,定义到script标签中:
  1. <script id="tmpl" type="template">
  2. <div>
  3. <a href="#">{{msg.login}}</a> | <a href="#">{{msg.register}}</a>
  4. </div>
  5. </script>
  • 同时,需要使用 Vue.component 来定义组件:
  1. Vue.component('account', {
  2. template: '#tmpl',
  3. data(){
  4. return{
  5. msg: {login: '登录',register: '注册'}
  6. }
  7. }
  8. });

1.1.2 第三种创建方式

  • <template>中定义 HTML 结构
  1. <template id="tmp">
  2. <div>
  3. <h2>第二种方式</h2>
  4. <h3 v-text="msg"></h3>
  5. </div>
  6. </template>

使用 Vue.component 中 template 属性 进行调用

  1. Vue.component('myCom3', {
  2. template: '#tmp',
  3. data(){
  4. return{
  5. msg: '好耶~'
  6. }
  7. }
  8. });

1.2 创建私有组件

  1. new Vue({
  2. el: '#app',
  3. data: {},
  4. methods:{},
  5. filters:{},
  6. directives:{},
  7. components: {
  8. myLogin: {
  9. template: `<div>
  10. <h1>login 组件</h1>
  11. <p>私有组件</p>
  12. </div>`,
  13. data(){
  14. return{
  15. }
  16. }
  17. }
  18. },
  19. beforeCreate(){},
  20. created(){},
  21. beforeMount(){},
  22. Mounted(){},
  23. beforeUpdate(){},
  24. updated(){},
  25. beforeDestroy(){},
  26. destroyed(){}
  27. });

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

分离私有组件的定义,将组件模板对象分开定义

  1. const myLogin = {
  2. template: `<div>
  3. <h1>login 组件</h1>
  4. <p>私有组件</p>
  5. </div>`,
  6. data(){
  7. return{
  8. }
  9. }
  10. }
  11. new Vue({
  12. el: '#app',
  13. data: {},
  14. methods:{},
  15. components: {
  16. // 组件名称:组件的模板对象
  17. // myLogin: myLogin
  18. myLogin,
  19. }
  20. });

1.3 组件的嵌套

我们的子组件是可以多级嵌套的,子组件中还可以在声明子组件。

  1. <div id="app">
  2. <account></account>
  3. </div>
  4. <script>
  5. // 创建 Vue 实例,得到 ViewModel
  6. var vm = new Vue({
  7. el: '#app',
  8. data: {},
  9. methods: {},
  10. components: {
  11. account: {
  12. template: `<div>
  13. <h1>这是Account组件{{name}}</h1>
  14. <login></login>
  15. <register></register>
  16. </div>`,
  17. data(){
  18. return {
  19. name: 'hello world!'
  20. }
  21. },
  22. components: {
  23. login: {
  24. template: "<h3>这是登录组件</h3>"
  25. },
  26. register: {
  27. template: '<h3>这是注册组件</h3>'
  28. }
  29. }
  30. }
  31. }
  32. });
  33. </script>

1.4 组件使用方式

  • 在HTML中直接用标签应用即可
  1. <div id="app">
  2. <mycom1></mycom1>
  3. <mycom2></mycom2>
  4. <my-com3></my-com3>
  5. <my-login></my-login>
  6. </div>
  • 非驼峰命名法,则直接照着写就好了
  • 驼峰命名法:定义时使用驼峰,使用时需要以 “ - ” 分隔

二、组件中展示数据和响应事件

  • data 定义方式不太一样,但是使用方式和实例中的 data 没什么太大区别
  • 因为组件需要被重用,为了让每个组件有自己的私有作用域,data需要为函数并且返回一个对象

  • 在组件中,data需要被定义为一个方法

通过计数器案例演示

  1. <div id="app">
  2. <myCount></myCount><hr>
  3. <myCount></myCount><hr>
  4. <myCount></myCount><hr>
  5. </div>
  6. <template id="tmp">
  7. <input type="button" value="+1" @click="increment">
  8. <h3 v-text="count"></h3>
  9. </template>
  10. <script>
  11. Vue.component('myCount',{
  12. template: '#tmp',
  13. data: function (){
  14. return {
  15. count: 0
  16. }
  17. },
  18. methods:{
  19. increment(){
  20. this.count++;
  21. }
  22. }
  23. });
  24. new Vue({
  25. el:'#app',
  26. data:{ },
  27. methods:{ }
  28. })
  29. </script>

三、组件切换

3.1 使用 v-if 和 v-else 配合进行切换

  1. <div id="app">
  2. <a href="" @click.prevent="flag = true">登录</a>
  3. <a href="" @click.prevent="flag = false">注册</a>
  4. <transition mode="out-in">
  5. <login v-if="flag"></login>
  6. <register v-else="flag"></register>
  7. </transition>
  8. </div>
  9. <script>
  10. Vue.component('login',{
  11. template: '<h3>登录组件</h3>'
  12. })
  13. Vue.component('register',{
  14. template: '<h3>注册组件</h3>'
  15. })
  16. new Vue({
  17. el: '#app',
  18. data:{
  19. flag: true,
  20. },
  21. methods:{
  22. }
  23. })
  24. </script>

3.2 使用<component>进行切换

  • :is 指定要显示的组件名称
  1. <style>
  2. .v-enter,
  3. .v-leave-to {
  4. opacity: 0;
  5. transform: translateX(150px);
  6. }
  7. .v-enter-active,
  8. .v-leave-active {
  9. transition: all .5s ease;
  10. }
  11. </style>
  12. <div id="app">
  13. <a href="" @click.prevent="comName = 'login'">登录</a>
  14. <a href="" @click.prevent="comName = 'register'">注册</a>
  15. <a href="" @click.prevent="comName = 'forget'">忘记密码</a>
  16. <!-- 通过mode属性,设置组件切换时候的模式 -->
  17. <transition mode="out-in">
  18. <component :is="comName"></component>
  19. </transition>
  20. </div>
  21. <script>
  22. Vue.component('login',{
  23. template: '<h3>登录组件</h3>'
  24. })
  25. Vue.component('register',{
  26. template: '<h3>注册组件</h3>'
  27. })
  28. Vue.component('forget',{
  29. template: '<h3>忘记密码组件</h3>'
  30. })
  31. new Vue({
  32. el: '#app',
  33. data:{
  34. comName: 'login'
  35. },
  36. methods:{
  37. }
  38. })
  39. </script>

四、父组件向子组件传值(属性)

  • 注意:一定要使用props属性来定义父组件传递过来的数据,由于 props 默认是单向绑定,即父组件的属性发生改变,子组件也会改变。反之则不会。props中数据是只读的

  • 使用v-bind或简化指令:,将数据传递到子组件中

  1. <div id="app">
  2. <!-- 父组件可以在引用子组件的时候,通过属性绑定的形式 ,把需要传递给子组件的数据,以属性绑定的形式传递到子组件内部供使用-->
  3. <my-component v-bind:parentmsg="msg" :msg="message"></my-component>
  4. </div>
  5. <script>
  6. Vue.component('myComponent', {
  7. template: '<span>{{ parentmsg }}--{{msg}}</span>',
  8. // props:声明待接收的父组件数据
  9. // props中数据都是只读的,无法重新赋值
  10. props: ['parentmsg', 'msg'],
  11. // 把父组件传递过来的parentmsg和msg属性,现在props数组中定义一下,这样才能使用父组件传递过来的数据
  12. data() {
  13. return {
  14. // 子组件中也可以有自己的数据在data中
  15. }
  16. }
  17. })
  18. var vm = new Vue({
  19. el: '#app',
  20. data: {
  21. msg: 'hello 子组件',
  22. message: 'hello world'
  23. }
  24. })
  25. </script>

绑定修饰符

  1. <div id="app">
  2. <!-- 默认 单向绑定 -->
  3. <my-component :msg="message"></my-component>
  4. <!-- 双向绑定 -->
  5. <my-component :msg.sync="message"></my-component>
  6. <!-- 单向绑定 -->
  7. <my-component :msg.once="message"></my-component>
  8. </div>
  • 注意:若传递引用类型,则无论哪种绑定类型,在子组件修改 props 都会影响父组件的状态。

props 类型验证

  1. Vue.component('my-component', {
  2. props: {
  3. // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
  4. propA: Number,
  5. // 多个可能的类型
  6. propB: [String, Number],
  7. // 必填的字符串
  8. propC: {
  9. type: String,
  10. required: true
  11. },
  12. // 带有默认值的数字
  13. propD: {
  14. type: Number,
  15. default: 100
  16. },
  17. // 带有默认值的对象
  18. propE: {
  19. type: Object,
  20. // 对象或数组默认值必须从一个工厂函数获取
  21. default: function () {
  22. return { message: 'hello' }
  23. }
  24. },
  25. // 自定义验证函数
  26. propF: {
  27. validator: function (value) {
  28. // 这个值必须匹配下列字符串中的一个
  29. return ['success', 'warning', 'danger'].indexOf(value) !== -1
  30. }
  31. }
  32. }
  33. })

五、父子组件间的事件通信(方法)

  • 使用事件绑定机制,父组件向子组件传递方法,绑定后,子组件可以通过某些方式来调用传递过来的这个方法了
  • 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  • 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
  1. <son v-on:func="show"></son>
  • 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
  1. <div id="app">
  2. <h1>{{msg}}</h1>
  3. <!-- 父组件向子组件传递方法,使用的是事件绑定机制 -->
  4. <!-- v-on,当我们自定义了一个事件属性之后,那么子组件就能够通过某种方式来调用 -->
  5. <son @func="getMsg"></son>
  6. </div>
  7. <!-- 组件模板定义 -->
  8. <template id="son">
  9. <div>
  10. <h1>这是子组件</h1>
  11. <input type="button" value="向父组件传值" @click="sendMsg" />
  12. </div>
  13. </template>
  14. <script>
  15. const son = {
  16. template: '#son', // 组件模板Id
  17. data(){
  18. return {
  19. msg: "我是子组件传递过来的数据"
  20. }
  21. },
  22. methods: {
  23. sendMsg() { // 按钮的点击事件
  24. // 我们可以通过调用父组件传递过来的方法,将子组件数据通过参数形式传递给父组件中
  25. this.$emit('func', this.msg); // 调用父组件传递过来的方法,同时把数据传递出去
  26. }
  27. }
  28. }
  29. // 子组件的定义方式
  30. Vue.component('son',son);
  31. // 创建 Vue 实例,得到 ViewModel
  32. var vm = new Vue({
  33. el: '#app',
  34. data: {
  35. msg: "父组件"
  36. },
  37. methods: {
  38. getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
  39. this.msg = val;
  40. console.log("调用了父组件身上的getMsg方法");
  41. alert(val);
  42. }
  43. }
  44. });
  45. </script>

5.1 $emit() 与 $on() 函数

  • vm.$emit( event, arg ) // 触发当前实例上的事件
  • vm.$on( event, fn ) // 监听 event 事件后运行 fn;
  • 用法:
    1. 父组件可以使用 props 把数据传给子组件。
    2. 子组件可以使用 $emit 触发父组件的自定义事件。

六、非父子组件事件通信

6.1 父子组件访问

  • $parent:访问当前组件的父组件
  • $root:访问当前主键的根组件

6.2 ref 获取DOM元素和组件

  • 我们可以在元素上使用ref属性,然后赋予一个优雅的名字
  1. <h3 ref="myh3">我是H3</h3>
  2. <!-- login组件 -->
  3. <login ref="login"></login>
  • 在实例中,我们通过$refs来获取DOM
  1. getElement() { console.log(this.$refs.myh3); console.log(this.$refs.login.$el.innerText);}

七、组件的应用案例

7.1 分页组件

  1. <style>
  2. .pagination {
  3. display: flex;
  4. list-style: none;
  5. }
  6. .page-item {
  7. width: 25px;
  8. height: 25px;
  9. line-height: 25px;
  10. text-align: center;
  11. border: 1px solid #ccc;
  12. }
  13. .page-item.active {
  14. background-color: skyblue;
  15. }
  16. </style>
  17. <div id="app">
  18. <page-component :total="total"></page-component>
  19. </div>
  20. <template id="page-component">
  21. <ul class="pagination">
  22. <li :class="p == page ? 'page-item active' : 'page-item'" v-for="p in pagecount">
  23. <a href="#" class="page-link" @click.prevent="page = p">{{ p }}</a>
  24. </li>
  25. </ul>
  26. </template>
  27. <script>
  28. const pageComponent = {
  29. template: '#page-component',
  30. name: 'PageComponent',
  31. props: ['total'],
  32. data: function() {
  33. return {
  34. page: 1, // 当前页码
  35. pagesize: 10 // 每页显示条数
  36. }
  37. },
  38. computed: {
  39. pagecount: function() {
  40. // 总页码数
  41. return Math.ceil(this.total / this.pagesize)
  42. }
  43. }
  44. }
  45. var vm = new Vue({
  46. el: '#app',
  47. data: {
  48. total: 35
  49. },
  50. components: {
  51. pageComponent
  52. }
  53. })
  54. </script>

7.2 购物车组件

  1. <div id="app">
  2. <div v-for="goods in goodslist">
  3. <p>商品名称:{{ goods.name }}</p>
  4. <p>单价:{{ goods.price }}</p>
  5. <cart-component v-model="goods.count"></cart-component>
  6. <hr>
  7. </div>
  8. <div>
  9. 订单总金额:{{ amount }} 元
  10. </div>
  11. </div>
  12. <template id="cart-component">
  13. <div class="cart">
  14. <button @click="count--; updateCount();">-</button>
  15. <input type="text" v-model="count" style="width: 50%;" @input="updateCount()">
  16. <button @click="count++; updateCount();">+</button>
  17. </div>
  18. </template>
  19. <script>
  20. const cartComponent = {
  21. name: 'Cart',
  22. template: '#cart-component',
  23. // 在组件中不允许直接修改 props 中的数据
  24. props: ['value'],
  25. data: function() {
  26. return {
  27. count: this.value
  28. }
  29. },
  30. methods: {
  31. // v-model 指令双向数据绑定,修改父组件内容
  32. updateCount: function() {
  33. // 触发 input 事件
  34. this.$emit('input', this.count)
  35. }
  36. }
  37. }
  38. const app = new Vue({
  39. el: '#app',
  40. data: {
  41. goodslist: [
  42. {
  43. name: 'iphone 8 plus',
  44. price: 5888,
  45. count: 0
  46. },
  47. {
  48. name: 'iphone x',
  49. price: 7222,
  50. count: 0
  51. }
  52. ]
  53. },
  54. computed: {
  55. // 当前订单总金额
  56. amount: function() {
  57. var money = 0;
  58. this.goodslist.forEach(goods => {
  59. money += parseInt(goods.count) * parseInt(goods.price)
  60. })
  61. return money;
  62. }
  63. },
  64. components: {
  65. cartComponent
  66. }
  67. })
  68. </script>

7.3 评论列表案例

  1. <div id="app">
  2. <cmt-box @loadcomments="loadComments"></cmt-box>
  3. <ul class="list-group">
  4. <li class="list-group-item" v-for="item in list" :key="item.id">
  5. <span class="badge">评论人:{{item.user}}</span> {{item.content}}
  6. </li>
  7. </ul>
  8. </div>
  9. <template id="temp">
  10. <div>
  11. <div class="form-group">
  12. <label for="">评论人:</label>
  13. <input type="text" class="form-control" v-model="user">
  14. </div>
  15. <div class="form-group">
  16. <label for="">评论内容:</label>
  17. <textarea class="form-control" v-model="content"></textarea>
  18. </div>
  19. <div class="form-group">
  20. <input type="button" value="发表评论" @click="postComment">
  21. </div>
  22. </div>
  23. </template>
  24. <script>
  25. const vm = new Vue({
  26. el: '#app',
  27. data: {
  28. list: []
  29. },
  30. methods: {
  31. loadComments() {
  32. const list = JSON.parse(localStorage.getItem('cmts') || '[]');
  33. this.list = list;
  34. console.log(6666);
  35. }
  36. },
  37. created() {
  38. this.loadComments();
  39. },
  40. components: {
  41. 'cmt-box': {
  42. template: '#temp',
  43. data() {
  44. return {
  45. user: '',
  46. content: ''
  47. }
  48. },
  49. methods: {
  50. postComment() {
  51. const comment = {
  52. id: Date.now(),
  53. user: this.user,
  54. content: this.content
  55. };
  56. // 从localStorage中获取所有的评论
  57. const list = JSON.parse(localStorage.getItem('cmts') || '[]');
  58. list.unshift(comment);
  59. // 保存最新的评论数据
  60. localStorage.setItem('cmts', JSON.stringify(list));
  61. this.user = this.content = '';
  62. this.$emit('loadcomments');
  63. }
  64. },
  65. }
  66. }
  67. });
  68. </script>
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post