Vue 学习(四、基础 - vue 组件化开发)


一、组件化概述


对组件化的简单理解

组件化开发就是对一个页面功能进行细分的意思,每个细分出来的部分就叫组件,组件可以是一个普通的 DOM
素 ( 比如就是一个单独的输入框 ),也可以是一系列的 DOM 元素 ( 比如一个完整的菜单栏的实现 ),细分的粒度是没有
具体定义的,但我认为一个有意义的前端组件至少要包含 HTMLCSSJavascript 三部分


组件化的优势

下图,以京东 <商品列表页> 为例, 我们设计时可以将其拆分为头部组件、侧边栏组件、底部组件、展示组件四部
分,甚至对每个组件内部仍可以继续细分,这么做的好处是显而易见的:

1) 可复用易组合,组件间可以随意组合,形成一个新的页面
2) 高内聚易维护,拆分合理的组件,在其修改时,只需要考虑组件内的代码就可以
组件

组件化与模块化的不同

组件化更多的是以 UI 界面为维度,针对页面的某一部分进行完整的封装,像一个菜单栏的完整实现,或一个轮播
图的完整实现,而模块化是以代码功能为维度,针对同类功能的 Javascript 进行封装,像发送网络请求相关的 Javascript
可以封装为一个模块,登录相关的 Javascript 也可以封装成一个模块


二、用 Vue 实现组件化


在 Vue 中使用组件化开发,离不开最基础的三个步骤,
1)声明组件
2)注册组件到目标作用域
3)作用域内使用组件

下面我们按照这三个步骤一起学习

1. 组件的声明

Vue 中想声明一个组件,我们要使用Vue.extend 函数,将定义它的源码简化后如下:

Vue.extend = function (extendOptions) {
  return function VueComponent() {
    this._init(extendOptions)
  }
}

可以看到会返回一个 VueComponent 对象,在对象内会调用 this._init(选项)this._init(选项) 这个函数在
new Vue(options) 时也曾被执行,其作用是匹配 Vue 中预定义的 options 属性,既然此处也使用了该函数,则代表
extendOptionsoptions 应该具有相同结构,事实也是如此,唯一不同的是 data 属性在 extendOptions 中要定义成
函数,而不是对象

options 也好,extendOptions 也罢,我要介绍一下它的另一个预定义属性 template, 该属性称为模板属性,其接收一
个字符串,内容可以是 ID ( 涉及到组件模板抽离,后面再提 ),也可以是 html 代码 ( 实现组件的代码 ),
Vue.extend(extendOptions) 中使用该属性,会返回一个组件对象 VueComponent,组件的模板内容为 template 的值


组件声明的基本结构

组件中的 html 模板,一定要有根元素,一般习惯在最外层套一个 <div> 元素

<script>
  const vueComponent = Vue.extend({
    template: '<div>' +
                '<div>{{message}}</div>' +
              '</div>',
    data: function () {
      return {
        message: '这是组件的消息'
      }
    }
  })
</script>

2. 不同作用域下注册组件


组件声明后,可以在不同的作用域内注册,现在分别学习其各种注册方式,至于使用方式都一样,就是在 Vue 对象
的挂载范围内写上 <组件名></组件名> 即可,注意:自定义的组件名尽量不要出现驼峰命名


1) 全局注册

使用 Vue.component('自定义组件名', VueComponent对象) 注册后,就可以在任意 Vue 对象的挂载范围内使用组件

效果:
组件全局注册
代码:

<body>

  <!-- 第一个 Vue 对象的挂载范围 -->
  <div id="first">
    第一个 Vue 对象:
    <my-component></my-component>
  </div>  <br/><br/>

  <!-- 第二个 Vue 对象的挂载范围 -->
  <div id="second">
    第二个 Vue 对象:
    <my-component></my-component>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 组件声明
    const vueComponent = Vue.extend({
      template: '<div>' +
                  '<div>{{message}}</div>' +
                '</div>',
      data: function () {
        return {
          message: '这是组件的消息'
        }
      }
    })
    // 组件全局注册
    Vue.component('my-component', vueComponent)

    // 第一个 Vue 对象
    const first = new Vue({
      el: '#first'
    })
    // 第二个 Vue 对象
    const second = new Vue({
      el: '#second'
    })
  </script>
</body>

2) 局部注册

使用 Vue 构造参数 options 的可选属性 components ,就可以在 Vue 对象中实现组件的局部注册,注册后,只有该
Vue 对象的挂载范围内可以使用组件

new Vue({
  el: '#vm',
  components: {
    自定义组件名1 : 组件对象1,
	自定义组件名2 : 组件对象2,
  }
})

效果:
局部注册
代码:

<body>

  <!-- 第一个 Vue 对象的挂载范围 -->
  <div id="first">
    第一个 Vue 对象:
    <my-component></my-component>
  </div> <br /><br />

  <!-- 第二个 Vue 对象的挂载范围 -->
  <div id="second">
    第二个 Vue 对象:
    <my-component></my-component>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 组件声明
    const vueComponent = Vue.extend({
      template: '<div>' +
                  '<div>{{message}}</div>' +
                '</div>',
      data: function () {
        return {
          message: '这是组件的消息'
        }
      }
    })

    // 第一个 Vue 对象
    const first = new Vue({
      el: '#first'
    })
    // 第二个 Vue 对象
    const second = new Vue({
      el: '#second',
      components: {
        'my-component': vueComponent
      }
    })
  </script>
</body>

3) 父子注册

虽然 optionsextendOptions 为同一结构,但是为了区分,我们先来统一下叫法,说 options 时,就代表 Vue 对象
的构造参数,说 extendOptions 就代表 Vue.extend 的参数

我们在 options 中通过可选属性 components 来实现局部注册,现在可以在 extendOptions 中通过 components
实现父子组件的注册,其实在 options 中通过 components 注册的组件也可以视为父子注册,因为 Vue 对象本身也
可以被看成一个组件,通常做为根组件使用

写在 components 中的组件,称为子组件, 定义后,就可以在父组件内使用

效果:
父子注册
代码:

<body>
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 子组件声明
    const son = Vue.extend({
      template: '<div>' +
                  '<div>{{message}}</div>' +
                '</div>',
      data: function () {
        return {
          message: '这是子组件的内容'
        }
      }
    })
    // 父组件声明
    const father = Vue.extend({
      template: '<div>' +
                  '<div>{{message}}</div>' +
                  '<son></son>' +
                '</div>',
      data: function () {
        return {
          message: '这是父组件的内容'
        }
      },
      // 父子组件注册
      components: {
        son: son
      }
    })
    // 局部注册
    new Vue({
      el: "#vm",
      components: {
        father: father
      }
    })
  </script>
</body>

3. 使用语法糖简化组件的声明与注册

上面展示的是最完整的组件使用方式,其实我们在使用时,还可以通过语法糖省略组件声明部分

组件作用域合并前合并后
全局注册Vue.component(‘自定义组件名’, 组件对象)Vue.component(‘自定义组件名’, extendOptions)
局部注册options.compontents.自定义组件名 : 组件对象options.compontents.自定义组件名 : extendOptions
父子注册extendOptions.compontents.自定义组件名 : 组件对象extendOptions.compontents.自定义组件名 : extendOptions

全局注册对比

<body>
  <div id="vm">
    <my-component></my-component>
    <simple></simple>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 完整写法
    const myComponent = Vue.extend({
      template: '<div>组件全局注册 - 完整写法</div>'
    })
    Vue.component('my-component', myComponent);

    // 语法糖写法
    Vue.component('simple', {
      template: '<div>组件全局注册 - 语法糖写法</div>'
    })
    new Vue({
      el: "#vm"
    })
  </script>
</body>

局部注册对比

<body>
  <div id="vm">
    <my-component></my-component>
  </div>
  <div id="simpleVm">
    <simple></simple>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 完整写法
    const myComponent = Vue.extend({
      template: '<div>组件局部注册 - 完整写法</div>'
    })
    new Vue({
      el: "#vm",
      components: {
        'my-component': myComponent
      }
    })

    // 语法糖写法
    new Vue({
      el: "#simpleVm",
      components: {
        'simple': {
          template: '<div>组件局部注册 - 语法糖写法</div>'
        }
      }
    })
  </script>
</body>

父子注册对比

  <div id="vm">
    <father></father>
  </div>
  <div id="simpleVm">
    <simple-father></simple-father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 完整写法
    const son = Vue.extend({
      template: '<div>组件父子注册 - 完整写法 - 子组件内容</div>'
    })
    const father = Vue.extend({
      template: '<div><son-component></son-component>组件父子注册 - 完整写法 - 父组件内容</div>',
      components: {
        'son-component': son
      }
    })
    new Vue({
      el: "#vm",
      components: {
        'father': father
      }
    })

    // 语法糖写法
    new Vue({
      el: "#simpleVm",
      components: {
        'simple-father': {
          template: '<div><simple-son></simple-son>组件父子注册 -  语法糖写法 - 父组件内容</div>',
          components: {
            'simple-son': {
              template: '<div>组件父子注册 - 语法糖写法 - 子组件内容</div>'
            }
          }
        }
      }
    })
  </script>
</body>

三、组件模板抽离

我们在设置 template 属性时,都是直接把模板内容写在字符串内,这样做的缺点就是,字符串内编写时 IDE 不会做
语法提示、而且也不能使用 Emmet 语法 或 快捷键,还有就是将 html 的布局代码写在 javascript 中,分离性不是很好,
而且当模板内容很复杂时,阅读性也不好

前面提到过,template 属性除了可以赋值布局用的字符串,还可以使用 模板 ID,现在就要用 模板 ID 的这种方式
来实现模板抽离,其步骤为定义模板和通过 模板 ID 引用,现在学习两种常见的方式

1) script 定义模板,自定义 ID,并指定 type 为 text/x-template

<body>
  <div id="vm">
    <my-component></my-component>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 定义模板 -->
  <script id="myTemplate" type="text/x-template">
    <div>
      我是定义的模板
    </div>
  </script>
  <script>
    new Vue({
      el: "#vm",
      components: {
        'my-component': {
          template: '#myTemplate'
        }
      }
    })
  </script>
</body>

2) Vue 的 template 标签定义模板,只需要指定自定义 ID

<body>
  <div id="vm">
    <my-component></my-component>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 定义模板 -->
  <template id="myTemplate">
    <div>
      我是定义的模板
    </div>
  </template>
  <script>
    new Vue({
      el: "#vm",
      components: {
        'my-component': {
          template: '#myTemplate'
        }
      }
    })
  </script>
</body>

四、父子组件间通信

不管是 options ,还是 extendOptions,它们定义的可选项都有自己的作用域,都只能在自己的组件内访问,当组
件关系为父子时,有时需要互相访问,那么则需要一些特殊的方式来达到目的


1. 父子传值

父子组件间最常见的访问目的,无非就是传值了,子组件的内容依赖父组件的数据,或者父组件的内容依赖子组件
操作后产生的数据,这都是典型的传值问题,现在分别对这两种情况进行学习


父子传值方式概览图:
父子传值
1)父传子

实现父传子,主要分两步, 首先在声明子组件时,在 extendOptions 中添加可选属性 props,然后父组件中使用
子组件时,通过 ( v-bind:传值变量 = 欲传值 ) 的方式向子组件传值,注意:如果传值变量是用 - 分割的形式,那么
v-bind 使用时要写成驼峰,如子组件:props:['my-message'],父组件:v-bind:myMessage


① props 的值可以是数组

只需要把子组件中要用到的传值变量,罗列在数组中就可以,注意,每个传值变量要用引号包裹

示例代码:

<body>
  <!-- 根组件 -->
  <div id="vm">
    <my-component></my-component>
  </div>

  <!-- 父组件模板 -->
  <template id="father">
    <div>
      <son v-bind:message="'这是父组件传过来的消息'"></son>
      <son v-bind:message="send"></son>
    </div>
  </template>

  <!-- 子组件模板 -->
  <template id="son">
    <div>
      {{message}}
    </div>
  </template>
  
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script>
    // 子组件
    const son = {
      template: '#son',
      'props': ['message']
    }
    // 父组件
    const father = {
      template: '#father',
      data: function () {
        return {
          send: '这也是父组件传过来的消息'
        }
      },
      components: {
        'son': son
      }
    }
    // 根组件
    new Vue({
      el: "#vm",
      components: {
        'my-component': father
      }
    })
  </script>
</body>

② props 的值可以是对象

这种使用方式,可以对传值变量进行更好的控制,比如值的类型、是否必传、默认值、校验等,具体使用方法,通
过示例代码应该琢磨一下就能懂,不再详细解释

示例代码:

<body>
  <!-- 根组件 -->
  <div id="vm">
    <my-component></my-component>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 父组件模板 -->
  <template id="father">
    <div>
      <son v-bind:message="'这是父组件传过来的消息'" v-bind:price="101"></son>
    </div>
  </template>

  <!-- 子组件模板 -->
  <template id="son">
    <div>
      {{message}}
      {{price}}
    </div>
  </template>

  <script>
    // 子组件
    const son = {
      template: '#son',
      'props': {
        message: {
          type: String,
          default: '父组件未传值,这是默认值',
        },
        price: {
          type: [Number, String],
          required: true,
          validator: function (value) {
            return value > 100
          }
        }
      }
    }
    // 父组件
    const father = {
      template: '#father',
      components: {
        'son': son
      }
    }
    // 根组件
    new Vue({
      el: "#vm",
      components: {
        'my-component': father
      }
    })
  </script>
</body>

2)子传父

实现子传父,也分两步,首先在子组件中使用 this.$emit(自定义事件名,...传给父组件的参数列表) 给父组件发送一个
自定义事件,然后父组件中使用子组件时,通过 v-on:自定义事件名 的方式,监听子组件发送过来的自定义事件,最后
做出相应事件处理,注意:自定义组件名不要使用驼峰的方式

示例代码

<!-- 根组件 -->
<body>
  <div id="vm">
    <my-component></my-component>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 父组件模板 -->
  <template id="father">
    <div>
      <!-- 此处方法的默认参数,不再是浏览器事件对象,而是子组件传过来的参数列表 -->
      <son @sub-click="pass"></son>
    </div>
  </template>
  <!-- 子组件模板 -->
  <template id="son">
    <div>
      <button v-on:click="btnClick">点击按钮给父组件传值</button>
    </div>
  </template>

  <script>
    // 子组件
    const son = {
      template: '#son',
      methods: {
        btnClick: function () {
          this.$emit('sub-click', '这是子组件传给父组件的内容')
        }
      }
    }
    // 父组件
    const father = {
      methods: {
        // 此处方法的默认参数,不再是浏览器事件对象,而是子组件传过来的参数列表
        pass: function (value) {
          alert(value)
        }
      },
      template: '#father',
      components: {
        'son': son
      }
    }
    // 根组件
    new Vue({
      el: "#vm",
      components: {
        'my-component': father
      }
    })
  </script>
</body>

2. 访问子组件/父组件对象

有时父子通信并不仅仅为了传值,比如想调用子组件的方法,或者调用父组件的方法,这种时候我们就需要在当
前组件下,拿到另一组件的组件对象,既 VueComponent、甚至是根组件对象 Vue


子组件访问父组件对象

在子组件中可以使用 this.$parent 拿到父组件,或使用 this.$root 拿到根组件,虽然子组件中可以拿到父组件对象,
但是强烈不建议这么用,一旦子组件依赖父组件后,就会变成强耦合,不利于子组件的复用

示例代码

<body>
  <!-- 根组件 -->
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 子组件 -->
  <template id="son">
    <button @click="get">点击获取父组件和根组件对象</button>
  </template>
  
  <!-- 父组件 -->
  <template id="father">
    <son></son>
  </template>
  
  <script>
    // 子组件
    let son = {
      template: '#son',
      methods: {
        get: function () {
          console.log(this.$parent)
          console.log(this.$root)
        }
      }
    }
    // 父组件
    let father = {
      template: '#father',
      components: {
        son
      }
    }
    //根组件
    new Vue({
      el: "#vm",
      components: {
        father
      }
    })
  </script>
</body>

父组件访问子组件对象

父组件访问子组件也有两种常见的方式,this.$childrenthis.$refsthis.$children 会取出所有的子组件,并以数组
形式返回,所以想使用某一个组件时,要通过下标访问,有点不利于扩展,this.$refs 需要在使用子组件时指定一个 ref 属性,
然后通过 this.$refs.ref属性 的方式访问

示例代码

<body>
  <!-- 根组件 -->
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 子组件 -->
  <template id="son"></template>
  <!-- 父组件 -->
  <template id="father">
    <div>
      <button @click="get">点击获取子组件对象</button>
      <son ref="sonComponent"></son>
    </div>
  </template>
  <script>
    // 子组件
    let son = {
      template: '#son'
    }
    // 父组件
    let father = {
      template: '#father',
      methods: {
        get: function () {
          console.log(this.$children[0])
          console.log(this.$refs.sonComponent)
        }
      },
      components: {
        son
      }
    }
    //根组件
    new Vue({
      el: "#vm",
      components: {
        father
      }
    })
  </script>
</body>

五、插槽

有时为了让子组件灵活性更高,子组件内部只会定义一个大框,某些具体内容让父组件自行填充,如下图:插槽
上图中子组件已将大框布局定义好,只有中间部分空出来,让父组件任意填充,这么做的好处就是,该子组件更灵
活,可复用的场景会更多

Vue 中想实现这种插拔的效果,就需要学习插槽的用法, Vue 中提供了 普通插槽、默认值插槽、具名插槽、
作用域插槽 4 种用法来应对不同场景


1. 普通插槽

比较适合子组件中预留一个空间,父组件使用时必须填充的场景,比如上图中的场景就可以用普通插槽来应对,
实现分两步,先在子组件的预留空间处写上 <slot></slot>,然后父组件使用子组件时,直接填充就可以, 按如下
方式填充<子组件>填充内容</子组件>

<body>
  <!-- 根组件 -->
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 子组件 -->
  <template id="son">
    <div>
      <p>元素1</p>
      <slot></slot>
      <p>元素3</p>
    </div>
  </template>
  
  <!-- 父组件 -->
  <template id="father">
    <div>
      <!-- 插槽填充 -->
      <son>
        <p>父组件填充的元素</p>
      </son>
    </div>
  </template>
  
  <script>
    // 子组件
    let son = {
      template: '#son'
    }
    // 父组件
    let father = {
      template: '#father',
      components: {
        son
      }
    }
    //根组件
    new Vue({
      el: "#vm",
      components: {
        father
      }
    })
  </script>
</body>

2. 默认值插槽

见名知意,子组件中仍会预留一个空间,但是如果父组件不主动填充时,会有一个默认填充,实现分两步,先在子组件的预留空间处写上 <slot>默认填充内容</slot>,然后父组件使用子组件时,按实际需求决定是否主动填充

<body>
  <!-- 根组件 -->
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 子组件 -->
  <template id="son">
    <div>
      <p>元素1</p>
      <slot>这是默认值填充</slot>
      <p>元素3</p>
    </div>
  </template>

  <!-- 父组件 -->
  <template id="father">
    <div>
      <!-- 主动填充 -->
      <son>
        <p>父组件填充的元素</p>
      </son>
      <!-- 默认值填充 -->
      <son> </son>
    </div>
  </template>

  <script>
    // 子组件
    let son = {
      template: '#son'
    }
    // 父组件
    let father = {
      template: '#father',
      components: {
        son
      }
    }
    //根组件
    new Vue({
      el: "#vm",
      components: {
        father
      }
    })
  </script>
</body>

3. 具名插槽

我们之前都是在子元素中定义一个插槽,当子元素中定义有多个插槽时,就可以使用具名插槽,其能指定具体填充
哪一个预留空间,实现分两步,先在子组件的预留空间处写上 <slot name="自定义插槽名"></slot>,然后父组件使用
子组件时,指名填充就可以,填充方式: <子组件><div slot="自定义插槽名">填充内容</div></子组件>

<body>
  <!-- 根组件 -->
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 子组件 -->
  <template id="son">
    <div>
      <slot name="top">默认填充-元素1</slot>
      <slot name="middle">默认填充-元素2</slot>
      <slot name="bottom">默认填充-元素3</slot>
    </div>
  </template>

  <!-- 父组件 -->
  <template id="father">
    <div>
      <!-- 具名填充 -->
      <son>
        <p slot="top">父组件填充的元素1</p>
        <p slot="bottom">父组件填充的元素3</p>
      </son>
    </div>
  </template>

  <script>
    // 子组件
    let son = {
      template: '#son'
    }
    // 父组件
    let father = {
      template: '#father',
      components: {
        son
      }
    }
    //根组件
    new Vue({
      el: "#vm",
      components: {
        father
      }
    })
  </script>
</body>

4. 作用域插槽

当父组件进行插槽填充布局时,如果需要使用子组件中定义的数据,就可以使用作用域插槽

至于适用场景,举个例子吧,子组件插槽中,默认用 <p> 对数据进行展示,现在父组件想替换成 <div> 展示数
据,我们用前面学的几种插槽方式确实可以将 <div> 填充进去,但是 <div> 中展示的数据是子组件中定义的,我们
在父组件中怎么拿到子组件的数据呢?因为子组件还没渲染完成,所以this.$children 也无法获取,那么这种场景只
能使用作用域插槽

语法:

相比其他插槽的使用方式会更麻烦一些,先说子组件,在定义插槽时使用该语法
<slot :导出给父组件使用的自定义变量名="导出的数据"></slot>,再说父组件,在使用子组件时要使用如下语法:

<子组件>
  <template slot-scope="自定义接收变量名-用来接收子元素数据对象">
    {{父组件中自定义的接收变量名.子组件中自定义的导出变量名}}
  </template>
</子组件>

确实有点绕,现在把刚才说的 div 替换 p 的场景写一个示例代码,耐心研究研究,再动手试一试就懂了

<body>
  <!-- 根组件 -->
  <div id="vm">
    <father></father>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

  <!-- 子组件 -->
  <template id="son">
    <div>
      <!-- :自定义导出变量名 = 导出变量 -->
      <slot :data="message">
        <p>{{message}}</p>
      </slot>
    </div>
  </template>

  <!-- 父组件 -->
  <template id="father">
    <son>
      <!-- slot-scope="自定义接收变量名-用来接收子元素数据对象" -->
      <template slot-scope="childrenData">
        <div style="border:1px solid black; width: 200px; height: 200px;">
          <!-- 父组件中自定义的接收变量名.子组件中自定义的导出变量名 -->
          {{childrenData.data}}
        </div>
      </template>
    </son>
  </template>

  <script>
 
    // 子组件
    let son = {
      template: '#son',
      data: function () {
        return {
          message: '这是子组件的数据'
        }
      }
    }

    // 父组件
    let father = {
      template: '#father',
      components: {
        son
      }
    }
    
    //根组件
    new Vue({
      el: "#vm",
      components: {
        father
      }
    })
  </script>
</body>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值