• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

Vue系列 组件化开发

开发技术 开发技术 2周前 (04-30) 8次浏览

认识组件化

将一个html拆解,分成很多个组件,每个组件实现页面的一个功能块,每个组件里面又可以细分,就是不断抽象的思想,不断把公共的、可以独立拆分出来的抽出来作为一个独立可复用的组件来向上提供调用,这样让我们的代码更加方便组织和管理,并且扩展性也更强。

注册组件

组件的使用有三步:

  1. script标签内,创建组件构造器对象,定义组件模板

    <script>
    	const cpnC = Vue.extend({
            template: `
                <div>
                    <h2>我是标题</h2>
                    <p>我是内容,11111</p>
                    <p>我是内容,22222</p>
                </div>`
        })
        ....
    </script>
    
  2. 注册组件(全局组件),在创建Vue实例前

    <script>
        ...
        Vue.component('mycpn', cpnC)
        const app = new Vue({
            el: "#app",
            data: {
                message: 'hello'
            }
        })
    </script>
    
  3. 在html中使用组件

    <div id="app">
        <!--3.使用组件-->
        <mycpn></mycpn>
        <mycpn></mycpn>
        <mycpn></mycpn>    
    
    </div>
    

    下面引用网上的一个图:

Vue系列   组件化开发

注册组件步骤解析

完整的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Vue Demo</title>
</head>
<body>
<div id="app">
    <!--3.使用组件-->
    <mycpn></mycpn>
    <mycpn></mycpn>
</div>
<mycpn></mycpn>   
<script src="../vue.js"></script>
<script>
    //1.创建组件构造器对象
    const cpnC = Vue.extend({
        template: `
            <div>
                <h2>我是标题</h2>
                <p>我是内容,11111</p>
                <p>我是内容,22222</p>
            </div>`
    })

    //2.注册组件
    Vue.component('mycpn', cpnC)

    const app = new Vue({
        el: "#app",
        data: {
            message: 'hello'
        }
    })
</script>
</body>
</html>
  1. Vue.extend():

调用Vue.extend()创建的是一个组件构造器。

通常在创建组件构造器时,传入template代表我们自定义组件的模板。

该模板就是在使用到组件的地方,要显示的HTML代码。

事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资 料还是会提到这种方式,而且这种方式是学习后面方式的基础。

  1. Vue.component():

调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。

所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

  1. 组件必须挂载在某个Vue实例下,否则它不会生效。(见下页)

我们来看下面我使用了三次<mycpn></mycpn>

而第三次其实并没有生效,因为他没有在new Vue挂载的实例#app下

Vue系列   组件化开发

组件其他补充

全局组件和局部组件

当我们通过调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用

如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件,开发过程常用!

<script>
	const app = new Vue({
        el: "#app",
        data: {
            message: 'hello'
        },
        components: {
            mycpn: cpnC
        }
    })
</script>

父组件和子组件

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Vue Demo</title>
    
</head>
<body>
<div id="app">
    知识点1:
	组件构造器1 放在组件构造器2里面注册,而组件构造器2放在Vue实例里注册
    组件2是组件1的父组件
    <!--3.使用组件-->
    <cpn2></cpn2>
    <cpn1></cpn1>   //知识点3:这里会报错,如果要在这里使用cpn1,可以在Vue实例里再注册下cpn1

</div>
<cpn1></cpn1>   
<script src="../vue.js"></script>
<script>
    //1.创建第一个组件构造器(子组件)
    const cpnC1 = Vue.extend({
        template: `
            <div>
                <h2>我是标题1</h2>
                <p>我是内容,11111</p>
                <p>我是内容,22222</p>
            </div>`
    })
    //2.创建第二个组件构造器(父组件)
    const cpnC2 = Vue.extend({
        template: `
            <div>
                <h2>我是标题2</h2>
                <p>我是内容,11111</p>
                <p>我是内容,22222</p>
                <cpn1></cpn1>
            </div>
            `,
        components: {
            cpn1: cpnC1    //知识点2:cpn1只能在cpnC2组件里使用(作用域只是在当前组件模板使用),无法在外面(全局)使用,如果要在全局用,可以在Vue实例再注册cpn1即可
        }
    })

    // Vue实例也是一个组件, 相当于root组件
    const app = new Vue({
        el: "#app",
        data: {
            message: 'hello'
        },
        components: {
            cpn2: cpnC2
        }
    })

</script>
</body>
</html>

解析过程

在#app模板中使用<cpn2></cpn2>时,cpn2组件已经在组件cpnC2内部编译好了,Vue内部解析编译cpnC2的模板的时候,当解析到<cpn1></cpn1>,会在当前自己注册的组件内查找是否有注册cpn1组件,如果没找到,再去Vue实例中查找是否有注册cpn1组件,如果Vue实例里也没有,再去全局组件里找,最终找到后,会把cpn1标签替换为组件cpn1中定义的模板,替换后,cpn2组件会编译如下:

    const cpnC2 = Vue.extend({
        template: `
            <div>
                <h2>我是标题2</h2>
                	<p>我是内容,11111</p>
                	<p>我是内容,22222</p>
                <h2>我是标题1</h2>
                	<p>我是内容,11111</p>
                <p>我是内容,22222</p>
            </div>
            `,
        components: {
            cpn1: cpnC1    //cpn1只能在cpnC2组件里使用(作用域只是在当前组件模板使用),无法在外面(全局)使用
        }
    })

所以,对于Vue实例来说,Vue实例本身不关心cpn1组件的存在(因为他也不知道),所以当前示例在#app模板中使用cpn1,就会报错

vue.js:634 [Vue warn]: Unknown custom element: <cpn1> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

(found in <Root>)

注册组件语法糖

主要是省去调用Vue.extend()的步骤

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Vue Demo</title>
    
</head>
<body>
<div id="app">
    <cpn1></cpn1>
    <cpn2></cpn2>
</div>  
<script src="../vue.js"></script>
<script>
    //注册全局组件语法糖
    Vue.component('cpn1', {
        template: `
            <div>
                <h2>我是标题cpn1</h2>
                <p>我是内容,cpn1</p>
                <p>我是内容,cpn1</p>
                
            </div>`
    })

    //注册局部组件语法糖
    const app = new Vue({
        el: '#app',
        data: {
            message: 'hello'
        },
        components: {
            'cpn2': {
            template: `
                <div>
                    <h2>我是标题cpn2</h2>
                    <p>我是内容,cpn2</p>
                    <p>我是内容,cpn2</p>
                </div>
                `               
            }
        }
    })

</script>
</body>
</html>

模板的分离写法

这里的分离指的是:模板和组件分离

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn></cpn>

</div>

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

<!--template标签-->
<template id="cpn">
    <div>
        <h2>我是cpn2</h2>
    </div>    
</template>


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

  // 注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn'
  })

  const app = new Vue({
    el: '#app',
    data: {
      message: 'hello'
    }
  })
</script>

</body>
</html>

组件数据存放

模板里面不能直接访问Vue实例data数据, Vue组件的数据存放在组件里

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn></cpn>
</div>

<!--模板里面不能直接访问Vue实例data数据, Vue组件的数据存放在组件里-->
<template id="cpn">
  <div>
    <h2>{{title}}</h2>
    <p>我是模板的数据</p>
  </div>
</template>

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


Vue.component('cpn', {
  template: '#cpn',
  data() {
    return {
      title: '我是组件里的数据'
    }
  }
})

const app = new Vue({
  el: '#app',
  data: {
    message: 'hello',
    // title: '我是标题'
  }
})
</script>
</body>
</html>

为什么组件data必须是函数

主要是为了解决多个组件实例同时调用data的同一个数据的时候,不会相互影响:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--组件实例-->
  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
</div>

<template id="cpn">
  <div>
    <h2>当前计数: {{counter}}</h2>
    <button @click="decrement" :disabled="disabled"> - </button>
    <button @click="increment"> + </button>
  </div>
</template>
<script src="vue.js"></script>
<script>
  //注册组件
  Vue.component('cpn', {
    template: '#cpn',
    data() {
      return {
        counter: 0
      }
    },
    computed: {
      disabled(){
        return this.counter <= 0
      }
    },
    methods: {
      increment () {
        this.counter ++
      },
      decrement () {
        this.counter --
      }
    }
  })

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

  })
</script>
</body>
</html>

假设data这里不是函数,即:

Vue.component('cpn', {
  template: '#cpn',
  data: {
    counter: 0
  }
})

这时候就会有一个问题,比如有三个组件实例1,2,3,实例1把counter+1,实例2再去取counter的时候,就会取到counter=1而不是0,三个实例访问的变量counter在内存中是指向同一个地址。

<div id="app">
  <cpn></cpn>  //实例1
  <cpn></cpn>  //实例2
  <cpn></cpn>  //实例3
</div>

父子组件通信

父组件通过props向子组件传递数据,子组件通过自定义事件emit Event与父组件发送消息.

Vue实例和子组件的通信父组件和子组件的通信过程是一样的.

父级向子级传递

基本使用

最简单的props传递过程

1.Vue实例中data

data: {
message: 'hello world!'
},

2.子组件中的props

props: ['message']

3.通过:message="message"将data中的数据传递给props

<child_cpn :message="message"></child_cpn>

4.将props中的数据显示在子组件中

//子组件的模板
<template id="child_cpn">
  <div>
    <h2>{{message}}</h2>
  </div>
</template>

完整案例代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Title</title>
</head>
<body>

<div id="app">
  <child_cpn :message="message"></child_cpn>
</div>
//子组件的模板
<template id="child_cpn">
  <div>
    <h2>{{message}}</h2>
  </div>
</template>

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

  const app = new Vue({
    el: '#app',
    data: {
      message: 'hello world!'
    },
    // 子组件
    components: {
      'child_cpn': {
        template: '#child_cpn',
        props: ['message']
      }
    }
  })
</script>

</body>
</html>

案例2:通过props传递数组

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--通过:cmovies="movies"将父组件的数据movies传递给子组件cpn的变量cmovies-->
  <cpn :cmovies="movies" :message="message"></cpn>  
</div>

<template id="cpn">
  <div>
    <h2>{{message}}</h2>
    <h2>{{cmovies}}</h2>
    <ul>
      <li v-for="item in cmovies">{{item}}</li>
    </ul>
  </div>
</template>

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

  // 父传子
  const cpn = {
    template: '#cpn',  //挂载cpn
    props: ['cmovies', 'message'],  //props属性里面放变量cmovies,在模板里用
    data() {  //data必须是一个函数,函数就必须要有返回值,所以需要return一个对象
      return {

      }
    },
    methods: {

    }
  }

  //父组件
  const app = new Vue({
    el: '#app',  //挂载app
    data: {
      message: 'hello world!',
      movies: ['海王','海贼王','海尔兄弟']

    },
    // 子组件
    components: {
      cpn
    }
  })
</script>

</body>
</html>

props数据验证

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--通过:cmovies="movies"将父组件的数据movies传递给子组件cpn的变量cmovies-->
  <cpn :cmovies="movies" ></cpn>  
</div>

<template id="cpn">
  <div>
    <h2>{{cmessage}}</h2>
    <h2>{{cmovies}}</h2>
    <ul>
      <li v-for="item in cmovies">{{item}}</li>
    </ul>
  </div>
</template>

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

  // 父传子
  const cpn = {
    template: '#cpn',  //挂载cpn
    props: {  //props属性里面放变量cmovies,在模板里用
      //1.类型限制, 变量名: 类型
      // cmovies: Array,  //定义了一个变量cmovies,且数据类型必须是array
      // cmessage: String,
      

      //2.定义类型,且提供一些默认值,以及必传值
      cmessage: {
        type: String,
        default: 'halo',  //演示默认值的时候要把父组件的数据注释掉或者在div里面不绑定(:cmessage="message"),父组件如果没有数据,在调用的时候会直接拿子组件定义的默认值
        required: true  //required为true,意味着div里面必须绑定:cmessage="message",也就是说在使用的时候必须要传message,否则报错
      },
      cmovies: {
        type: Array,
        // default: ['死亡笔记','扫毒','湄公河行动']  //在2.5.17往上这样写就会报错
        default() {
          return ['死亡笔记','扫毒','湄公河行动']  // 上面的写法Vue会有警告(提示必须是工厂函数),因为对象和数组默认值必须从一个工厂函数获取
        }
      }

    },  
    data() {  //data必须是一个函数,函数就必须要有返回值,所以需要return一个对象
      return {

      }
    },
    methods: {

    }
  }

  //父组件
  const app = new Vue({
    el: '#app',  //挂载app
    data: {
      message: 'hello world!',
      // movies: []

    },
    // 子组件
    components: {
      cpn
    }
  })
</script>

</body>
</html>

上面演示了props选项使用字符串、数组、对象类型的数据验证,props支持的数据类型还有:

  • String

  • Number

  • Boolean

  • Array

  • Object

  • Date

  • Function

  • Symbol

Vue.component('my-component', {
	props: {
		// 基础的类型检查(‘null’ )
		propA: Number
		// 多个可能的类型
		propB: [String, Number],
		//必填的字符串
		propC: {
			type: String,
			required: true
		},
		//带有默认值的数字
		propD: {
			type: Number,
			default: 100
		},
		//带有默认值的对象
		propE: {
			type: object,
			//对象或数组默认值必须从一个工厂函数获取
			default: function(){
				return {message: 'hello'}
			}
		},
		//自定义验证函数
		propF: {
			validator: function (value) {
			//这个值必须匹配下列字符串中的一个
			return ['success', 'warning', 'danger'].indexOf(value) !== -1
			}
		}
	}
})

当我们有自定义构造函数时,验证也支持自定义的类型

function Person(first_name,last_name){
	this.first_name = first_name
	this.last_name = last_name
}

Vue.component('blog-post',{
	props: {
		author: Person
	}
})

子级向父级传递

后面内容明天再更! 加油!

插槽slot


程序员灯塔
转载请注明原文链接:Vue系列 组件化开发
喜欢 (0)