Vue 组件化开发——组件详解

147 阅读4分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

什么是组件化?

  1. 将一个复杂的问题拆分成很多个可以处理的小问题,再将其放入整体当中,很大的问题也会迎刃而解。
  2. 组件是构成页面中独立结构单元,主要以页面结构的形式存在。
  3. 不同组件也具有基本交互功能,根据业务逻辑实现复杂的项目功能。
  4. 组件化也是类似的思想:
    • 如果将一个页面中的所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
    • 但是如果将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那之后的整个页面的管理和维护就变得非常容易。
  5. 组件可以实现复杂的页面结构,提高代码的可复用性。

Vue 组件化思想

  1. 组件化是 Vuejs 中的重要思想。

    • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。

    • 任何的应用都可以被抽象成一颗组件树。

    在这里插入图片描述

  2. 组件化思想的应用:

    • 有了组件化的思想,我们在之后的开发中就要充分利用它。
    • 尽可能将页面拆分成一个个小的、可复用的组件。
    • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

注册组件的基本步骤

  1. 组件的使用分为三个步骤:

    • 创建组件构造器。
    • 注册组件。
    • 使用组件。
  2. 注册组件的步骤:

    • 调用 Vue.extend() 方法创建组件构造器
    • 调用 Vue.component() 方法注册组件
    • 在 Vue 实例中的作用范围内使用组件
    <div id="app">
        <!-- 3. 使用组件 -->
        <my-cpn></my-cpn>
    </div>
    
    <script src="../js/vue.js"></script>
    
    <script>
    	// 1. 创建组件构造器
        const myComponent = Vue.extend({
            template: `
    			<div>
        			<h2>组件标题</h2>
    				<p>我是组件中的一个段落内容</p>
        		</div>
    		`
        })
        
        // 2. 注册组件,并且定义组件标签的名称
        Vue.component('my-cpn',myComponent)
        
        let app = new Vue({
            el: 'app'
        })
    </script>
    
  3. 查看运行结果:

    • 和直接使用一个 div 看起来并没有什么区别。
    • 但是我们可以设想,如果很多地方都要显示这样的信息,我们是不是就可以直接使用<my-cpn></my-cpn>来完成呢?

注册组件步骤解析

  1. Vue.extend() :

    • 调用 Vue.exted() 创建的是一个组件构造器。
    • 通常在创建组件构造器时,传入 template 代表自定义组件的模板。
    • 该模板就是在使用到组件的地方,要显示的 HTML 代码。
    • 事实上,这种写法在 Vue2.x 的文档中几乎已经看不到了,会直接使用语法糖,但是很多资料话是会提到,而且这种方式也是后面的基础。
  2. Vue.component() :

    • 调用 Vue.component() 是将刚才注册的组件构造器注册为一个组件,并且给他起一个组件的标签名称。
    • 所以需要传递两个参数:
      • 注册组件的标签名。
      • 组件构造器。
  3. 组件必须挂载在某个 Vue 实例下,否则它不会生效。

    • 下面使用三次,第三次不会生效。
    <div id="app">
    	<!-- 3.使用组件 -->
        <my-cpn></my-cpn>
        <div>
        	<my-cpn></my-cpn>
        </div>
    </div>
    <!-- 这个并不会渲染出来,因为它没有在new Vue挂载的实例 #app 之下 -->
    <my-cpn></my-cpn>
    

全局组件和局部组件

全局组件

  1. 当通过调用 Vue.component() 注册组件时,组件的注册是全局的。

    • 这就意味着该组件可以在任意 Vue 实例下使用。
    <div id="app1">
      <my-cpn></my-cpn>
    </div>
    <div id="app2">
      <my-cpn></my-cpn>
    </div>
    <script>
    // 创建组件构造器
    const myComponent = Vue.extend({
      template: `
      <div>
        <h2>组件标题</h2>
        <p>组件正文的内容</p>
      </div>
      `
    })
    
    // 注册组件
    Vue.component('my-cpn', myComponent)
    const app1 = new Vue({
      el: '#app1',
    })
    const app2 = new Vue({
      el: '#app2',
    })
    </script>
    

局部组件

  1. 如果注册的组件是挂载在某个实例中,那么就是一个局部组件

    • 通过 Vue 实例的 components 属性在实例中注册组件。

    • 在下面这个例子中,app2 中的组件并没有渲染出来,而且会报错。

    <div id="app1">
      <my-cpn></my-cpn>
    </div>
    <div id="app2">
      <my-cpn></my-cpn>
    </div>
    <script>
    // 创建组件构造器
    const myComponent = Vue.extend({
      template: `
      <div>
        <h2>组件标题</h2>
        <p>组件正文的内容</p>
      </div>
      `
    })
    const app1 = new Vue({
      el: '#app1',
      // 在实例内部注册组件
      components: {
        'my-cpn': myComponent
        // 另外一种写法:myCpn: myComponent,这种写法效果和上面的相同。
        // 如果在注册组件的时候使用了驼峰命名,那么在使用的时候需要转换成短横线分隔命名。 
      }
    })
    const app2 = new Vue({
      el: '#app2',
      // components: {
      //   'my-cpn': myComponent
      // }
    })
    

父组件和子组件

  1. 在前面见到了组件树:
    • 组件和组件之间存在层级关系。
    • 而其中最重要的关系就是父子组件的关系。
  2. 父子组件关系的代码组成:
    <div id="app">
       <parent-cpn></parent-cpn>
     </div>
     <script>
       // 创建一个子组件构造器
       const childComponent = Vue.extend({
         template: `
         <div>
           <p>我是子组件的内容</p>
         </div>
         `
       })
       // 创建一个父组件构造器
       const parentComponent = Vue.extend({
         template: `
           <div>
             我是父组件的内容
             <h2>我是父组件的一个标题</h2>
             <child-cpn></child-cpn>
           </div>
           `,
         components: {
           // 在父组件中注册子组件
           'child-cpn': childComponent
         }
       })
       const app = new Vue({
         el: '#app',
         // 在实例中注册父组件
         components: {
           'parent-cpn': parentComponent
         }
       })
    
  3. 父子组件错误用法:
    • 当子组件注册到父组件的 components 时,Vue 会编译好父组件的模块。
    • 该模板的内容已经决定了父组件将要渲染的 HTML (相当于父组件中已经有了子组件中的内容了。)
    • 是只能在父组件中被识别的。
    • 类似这种用法,是会被浏览器忽略的。

注册组件语法糖

  1. 在上面注册组件的方式会有些繁琐。
    • 为了简化这个过程,提供了注册的语法糖。
    • 主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。

全局组件注册语法糖

Vue.component('my-cpn',{
  template: `
		<div>
			<h2>组件标题</h2>
			<p>组件正文的内容</p>
		</div>
	`
})

局部组件注册语法糖

// 注册局部组件
let app = new Vue({
  el: 'app',
  components: {
    'my-cpn': {
  		template: `
				<div>
					<h2>组件标题</h2>
					<p>组件正文的内容</p>
				</div>
			`
	}
})

模板的分离写法

  1. 除了 Vue 组件的注册过程比较复杂,还有 template 模板的写法也比较麻烦。

  2. 如果将其中的 HTML 分离出来,然后挂载到对应的组件上,必然结构会变得清晰。

  3. Vue 提供了两种方案来定义 HTML 模块内容。

    • 使用标签。

      <div id="app">
        <my-cpn></my-cpn>
      </div>
      <script type="text/x-template" id="myCpn">
        <div>
          <h2>组件标题</h2>
          <p>组件内容</p>
        </div>
      </script>
      <script>
        const app = new Vue({
          el: '#app',
          // 注册组件
          components:{
            myCpn:{
              template:'#myCpn'
            }
          }
        })
      </script>
      
    • 使用标签。

      <div id="app">
        <my-cpn></my-cpn>
      </div>
      <template id="myCpn">
        <div>
          <h2>组件标题</h2>
          <p>组件内容</p>
        </div>
      </template>
      <script>
        const app = new Vue({
          el: '#app',
          // 注册组件
          components: {
            myCpn: {
              template: '#myCpn'
            }
          }
        })
      </script>
      

组件可以访问 Vue 实例数据吗?

  1. 组件是一个单独功能模块的封装。
    • 这个模板有属于自己的 HTML 模板,也应该有自己的数据 data。
  2. 组件不能访问 Vue 实例中的数据,而且即使可以访问,如果把所有的数据都放在 Vue 实例中,Vue 实例就会变得非常臃肿。
  3. 结论:Vue 组件应该有自己保存数据的地方。

组件数据的存放

  1. 组件对象也有一个 data 属性(也可以有 methods 等属性)。
  2. 只是这个 data 必须是一个函数。
  3. 而且这个函数返回一个对象,对象内部保存着数据。

组件的 data 为什么必须是一个函数?

  • 因为函数在每一次调用的时候都会形成一个新的对象,这样就不会对同时使用多次组件的数据产生影响。