Vue全家桶之组件化开发

Vue全家桶之组件化开发

文章目录

  • Vue全家桶之组件化开发
    • 1. 组件化开发思想
    • 2. 组件的注册
      • 2.1 全局组件注册语法
      • 2.2 组件用法
      • 2.3 组件注册注意事项
      • 2.4 局部组件注册
    • 3. 组件间数据交互
      • 3.1 父组件向子组件传值props
      • 3.2 子组件向父组件传值$emit
      • 3.3 非父子组件间传值
    • 4. 组件插槽
      • 4.1 组件插槽的作用
      • 4.2 组件插槽基本用法
      • 4.3 具名插槽用法
      • 4.4 作用域插槽
    • 5. 基于组件的案例:购物车

1. 组件化开发思想

组件是可复用的 Vue 实例, 把一些公共的模块抽取出来,然后写成单独的的工具组件或者页面,在需要的页面中就直接引入即可,那么我们可以将其抽出为一个组件进行复用,提高了代码的复用率

组件化开发思想:如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

Vue全家桶之组件化开发_第1张图片
Vue全家桶之组件化开发_第2张图片

  • 我们将一个完整的页面分成很多个组件。
  • 每个组件都用于实现页面的一个功能块。
  • 而每一个组件又可以进行细分。

组件化思想的应用

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

2. 组件的注册

2.1 全局组件注册语法

​ 使用Vue.component('button-counter', cpnc)方式注册,直接使用调用。button-counter是全局组件的名字,cpnc是定义的组件对象。

     Vue.component("button-counter", {
        data: 组件数据,
        template: 组件模块内容
      });
//注册一个名为button-counter 的新组件
Vue.component("button-counter", {
        data: function () {
          return {
            count: 0,
          };
        },
        template: '',
      });

2.2 组件用法

    <div id="app">
      
      <button-counter>button-counter>
      <button-counter>button-counter>
    div>
//1-组件的基本使用.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title></title>
  </head>
  <body>
    <div id="app">
      <!-- 全局组件的使用 -->
      <button-counter></button-counter>
      <button-counter></button-counter>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      //全局组件的注册
      Vue.component("button-counter", {
        data: function () {
          return {
            count: 0,
          };
        },
       template: `
        
`
, }); const app = new Vue({ el: "#app", data: {}, methods: {}, }); </script> </body> </html>

Vue全家桶之组件化开发_第3张图片

2.3 组件注册注意事项

  1. data必须是一个函数

    保证每个模板的数据是相互独立的

  2. 组件模板内容必须是单个跟元素

    只能有一个父元素,如上面代码中的div

  3. 组件模板内容可以是模板字符串``

    ES6 引入新的声明字符串的方式 ``

  4. 组件命名方式

    短横线方式 my-component (推荐)

    驼峰方式 MyComponent

2.4 局部组件注册

局部组件,只能在当前vue实例挂载的对象中使用,类似于局部变量,有块级作用域。

   //注册方式
   const app = new Vue({
      el:"#app",
      components:{//局部组件创建
        'hello-world': HelloWorld
      }
    })

​ 使用方式与全局变量一样,直接使用调用。 'hello-world’组件命名的名字,HelloWorld定义的组件对象。

//04-局部组件用法.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="app">
    <hello-world></hello-world>
  </div>
  <script type='text/javascript' src='js/vue.js'></script>
  <script type='text/javascript'>
    var HelloWorld = {
      data: function () {
        return {
          msg: 'helloworld'
        }
      },
      template: `
  
{{msg}}
`
} const app = new Vue({ el: "#app", data: {}, components: { 'hello-world': HelloWorld } }) </script> </body> </html>

3. 组件间数据交互

3.1 父组件向子组件传值props

Vue全家桶之组件化开发_第4张图片

1.子组件内部通过props接收父组件传递过来的值

props传递数据原则:单向数据流,只能父传子

v-bind是不支持使用驼峰标识的,例如cUser要改成c-User

Vue.component(‘menu-item',{
    //props接受父组件传递的数据
	props: ['title'],
	template: '
{{ title }}
'
})

2.父组件通过属性将值传递给子组件

//传统方式
<menu-item title="来自父组件的数据"></menu-item>
//动态绑定
<menu-item :title="title"></menu-item>
 //07-父组件向子组件传值-props命名规则.html
<!DOCTYPE html>
 <html lang="en">

 <head>
   <meta charset="UTF-8">
   <title></title>
 </head>

 <body>
   <div id="app">
     <div>{{pmsg}}</div>
	//传统方式
     <menu-item title='来自父组件的值'></menu-item>
	//动态绑定
     <menu-item :title='ptitle' content='hello'></menu-item>
   </div>
   <script type='text/javascript' src='js/vue.js'></script>
   <script type='text/javascript'>
     //相对于Vue实例为子组件 
     Vue.component('menu-item', {
       //props接受父组件传递的数据
       props: ['title', 'content'],
       data: function () {
         return {
           msg: '子组件本身的数据'
         }
       },
       template: `
     
{{msg + '-----' +title + '-----' +content}}
`
}) //Vue实例为根组件 const app = new Vue({ el: "#app", data: { pmsg: "父组件中内容", ptitle: '动态绑定的值' } }) </script> </body> </html>

Vue全家桶之组件化开发_第5张图片

3.props属性名规则

  • 在props中使用驼峰形式,在html中需要使用短横线的形式

  • 字符串形式的模板中没有这个限制

    Vue.component(‘menu-item', {
    	// 在JavaScript中是驼峰式的
    	props: ['menuTitle'],
    	template: '
    {{ menuTitle }}
    '
    }) //在html中是短横线方式的 <menu-item menu-title=“nihao"></menu-item>

4.props属性值类型

  • 字符串String

  • 数值Number

  • 布尔值Boolean

  • 数组Array

  • 对象Object

    
    
    
      
      Document
    
    
      
    {{pmsg}}
    //字符串 数字 布尔类型 数组 对象

Vue全家桶之组件化开发_第6张图片

3.2 子组件向父组件传值$emit

1.子组件通过自定义事件向父组件传递信息

子组件向父组件传值,使用自定义事件$emit$emit(自定义事件名称)

<button @click='$emit("enlarge-text")'>扩大父组件字体</button>

2.在父组件中监听子组件的事件

<menu-item @enlarge-text='handle'></menu-item>
//09-子组件向父组件传值.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <!-- 整个app为父组件 -->
  <div id="app">
    <div :style='{fontSize:fontsize+"px"}'>{{msg}}</div>
    <!-- 在父组件中监听子组件事件 -->
    <menu-item @enlarge-text='handle'></menu-item>
  </div>
  <script type='text/javascript' src='js/vue.js'></script>
  <script type='text/javascript'>
    //子组件点击按钮控制父组件字体大小
    //创建子组件
    Vue.component('menu-item', {
      template: `

`
    })
    const app = new Vue({
      el: "#app",
      data: {
        msg: '父组件中的内容',
        fontsize: 10
      },
      methods: {
        handle: function () {
          this.fontsize += 5;
        }
      }
    })
  </script>
</body>

</html>

Vue全家桶之组件化开发_第7张图片

3.子组件通过自定义事件向父组件传递信息,携带额外的值

5为另外额外的值

<button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>

4.父组件监听子组件的事件

通过$event接受额外的值

<menu-item  @enlarge-text='handle($event)'></menu-item>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="app">
    <div :style='{fontSize:fontsize+"px"}'>{{msg}}</div>
    <menu-item @enlarge-text='handle($event)'></menu-item>
  </div>
  <script type='text/javascript' src='js/vue.js'></script>
  <script type='text/javascript'>
    Vue.component('menu-item', {
      template: `

`
    })
    const app = new Vue({
      el: "#app",
      data: {
        msg: '父组件中的内容',
        fontsize: 10
      },
      methods: {
        handle: function (val) {
          this.fontsize += val;
        }
      }
    })
  </script>
</body>

</html>

3.3 非父子组件间传值

Vue全家桶之组件化开发_第8张图片

1.单独的事件中心管理组件间的通信

var eventHub = new Vue()

2.监听事件与销毁事件

$on用于监听事件,add-todo为事件名称,addTodo为事件函数

$off用于销毁事件

eventHub.$on('add-todo', addTodo)  
eventHub.$off('add-todo')          

3.触发事件

$emit用于触发事件,add-todo为事件名称, id为携带参数

eventHub.$emit(‘add-todo', id)
//11-兄弟组件之间数据交互.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="app">
    <div>父组件</div>
    <div>
      <button @click='handle'>销毁事件</button>
    </div>
    <test-tom></test-tom>
    <test-jerry></test-jerry>
  </div>
  <script type='text/javascript' src='js/vue.js'></script>
  <script type='text/javascript'>
    //提供事件中心
    var hub = new Vue()
    Vue.component('test-tom', {
      data: function () {
        return {
          num: 0
        }
      },
      template: `
    
Tom:{{num}}
`
, methods: { handle: function () { //触发兄弟组件的事件 hub.$emit('jerry-event', 2) } }, mounted: function () { //监听事件 hub.$on('tom-event', val => { this.num += val; }) } }) Vue.component('test-jerry', { data: function () { return { num: 0 } }, template: `
Jerry:{{num}}
`
, methods: { handle: function () { //触发兄弟组件的事件 hub.$emit('tom-event', 1) } }, mounted: function () { //监听事件 hub.$on('jerry-event', val => { this.num += val; }) } }) const app = new Vue({ el: "#app", data: {}, methods: { handle: function () { //销毁事件 hub.$off('tom-event'); hub.$off('jerry-event') } } }) </script> </body> </html>

Vue全家桶之组件化开发_第9张图片

4. 组件插槽

4.1 组件插槽的作用

编译作用域:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。

插槽的目的是让我们原来的设备具备更多的扩展性。比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等。

组件的插槽是为了让我们封装的组件更加具有扩展性,让使用者可以决定组件内部的一些内容到底展示什么。

组件插槽的作用:父组件向子组件传递内容

Vue全家桶之组件化开发_第10张图片

4.2 组件插槽基本用法

在子组件中,使用特殊的元素就可以为子组件开启一个插槽。

该插槽插入什么内容取决于父组件如何使用。

如何封装合适呢?

抽取共性,保留不同。最好的封装方式就是将共性抽取到组件中,将不同预留为插槽。

1.插槽位置

Vue.component('alert-box', {
	template: `
		
Error!
` })

2.插槽内容

Something had happened.
//12-插槽基本用法.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <alert-box>有bug发生</alert-box>
    <alert-box>有一个警告</alert-box>
    <alert-box></alert-box>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">

    Vue.component('alert-box', {
      template: `
        
ERROR: 默认内容
`
}); var vm = new Vue({ el: '#app', data: { } }); </script> </body> </html>

Vue全家桶之组件化开发_第11张图片

4.3 具名插槽用法

1. 插槽定义

2.插槽内容

//根据名称进行匹配,没有匹配到的放在默认插槽中
<base-layout>
	<h1 slot="header">标题内容</h1>
	<p>主要内容1</p>
	<p>主要内容2</p>
	<p slot="footer">底部内容</p>
</base-layout>
//13-具名插槽用法.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <base-layout>
      <template slot='header'>
        <p>标题信息1</p>
        <p>标题信息2</p>
      </template>
      <p>主要内容1</p>
      <p>主要内容2</p>
      <template slot='footer'>
        <p>底部信息信息1</p>
        <p>底部信息信息2</p>
      </template>
    </base-layout>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    Vue.component('base-layout', {
      template: `
        
`
}); var vm = new Vue({ el: '#app', data: { } }); </script> </body> </html>

Vue全家桶之组件化开发_第12张图片

4.4 作用域插槽

应用场景:父组件对子组件的内容进行加工处理

1.插槽定义

<ul>
	<li v-for= "item in list" v-bind:key= "item.id" >
		<slot v-bind:item="item">
			{{item.name}}
		</slot>
	</li>
</ul>

2.插槽内容

<fruit-list v-bind:list= "list">
	<template slot-scope="slotProps">
		<strong v-if="slotProps.item.current">
			{{ slotProps.item.text }}
		</strong>
	</template>
</fruit-list>
//14-作用域插槽用法.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<style type="text/css">
  .current {
    color: orange;
  }
</style>
<body>
  <div id="app">
    <fruit-list :list='list'>
      <template slot-scope='slotProps'>
        <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
        <span v-else>{{slotProps.info.name}}</span>
      </template>
    </fruit-list>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    Vue.component('fruit-list', {
      props: ['list'],
      template: `
        
  • {{item.name}}
  • `
    }); var vm = new Vue({ el: '#app', data: { list: [{ id: 1, name: 'apple' },{ id: 2, name: 'orange' },{ id: 3, name: 'banana' }] } }); </script> </body> </html>

    在这里插入图片描述

    5. 基于组件的案例:购物车

    Vue全家桶之组件化开发_第13张图片

    案例需求分析:按照组件化方式实现业务需求

    根据业务功能进行组件化划分

    ①标题组件(展示文本)

    ②列表组件(列表展示、商品数量变更、商品删除)

    ③结算组件(计算商品总额)

    功能实现步骤:

    • 实现整体布局和样式效果
    • 划分独立的功能组件
    • 组合所有的子组件形成整体结构
    • 逐个实现各个组件功
      • 标题组件
      • 列表组件
      • 结算组件
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Document</title>
      <!-- 引入css样式 -->
      <link rel="stylesheet" type="text/css" href="购物车案例.css" />
    </head>
    
    <body>
      <div id="app">
        <div class="container">
          <my-cart></my-cart>
        </div>
      </div>
      <script type="text/javascript" src="js/vue.js"></script>
      <script type="text/javascript">
        //标题组件
        var CartTitle = {
          //传递用户名数据
          props: ['uname'],
          template: `
            
    {{uname}}的商品
    `
    } //列表组件 var CartList = { props: ['list'], //列表展示 //列表名 //商品数量变更 //商品数量减 //呈现数据 失去焦点呈现数据 //商品数量加 //商品删除 template: `
    {{item.name}}
    ×
    `
    , methods: { //商品数量输入域修改 changeNum: function (id, event) { this.$emit('change-num', { id: id, type: 'change', num: event.target.value //当前输入域最新值 }); }, //商品数量减 add: function (id) { this.$emit('change-num', { id: id, type: 'add' }); }, //商品数量加 sub: function (id) { this.$emit('change-num', { id: id, type: 'sub' }); }, //商品删除 del: function (id) { // 把id传递给父组件 this.$emit('cart-del', id); } } } //结算组件 var CartTotal = { props: ['list'], template: `
    总价:{{total}}
    `
    , computed: { total: function () { // 计算商品的总价 var t = 0; this.list.forEach(item => { t += item.price * item.num; }); return t; } } } Vue.component('my-cart', { data: function () { return { uname: '张三', list: [{ id: 1, name: 'TCL彩电', price: 1000, num: 1, img: 'img/a.jpg' }, { id: 2, name: '机顶盒', price: 1000, num: 1, img: 'img/b.jpg' }, { id: 3, name: '海尔冰箱', price: 1000, num: 1, img: 'img/c.jpg' }, { id: 4, name: '小米手机', price: 1000, num: 1, img: 'img/d.jpg' }, { id: 5, name: 'PPTV电视', price: 1000, num: 1, img: 'img/e.jpg' }] } }, template: `
    `
    , components: { 'cart-title': CartTitle, 'cart-list': CartList, 'cart-total': CartTotal }, methods: { changeNum: function (val) { // 商品数量变更 //分为三种情况:商品数量输入域变更、商品数量加、商品数量减 if (val.type == 'change') { // 根据子组件传递过来的数据,跟新list中对应的数据 this.list.some(item => { if (item.id == val.id) { item.num = val.num; // 终止遍历 return true; } }); } else if (val.type == 'sub') { // 减一操作 this.list.some(item => { if (item.id == val.id) { item.num -= 1; // 终止遍历 return true; } }); } else if (val.type == 'add') { // 加一操作 this.list.some(item => { if (item.id == val.id) { item.num += 1; // 终止遍历 return true; } }); } }, //列表删除功能 delCart: function (id) { // 根据id删除list中对应的数据 // 1、找到id所对应数据的索引 var index = this.list.findIndex(item => { return item.id == id; }); // 2、根据索引删除对应数据 this.list.splice(index, 1); } } }); var vm = new Vue({ el: '#app', }); </script> </body> </html>

    Vue全家桶之组件化开发_第14张图片

    你可能感兴趣的:(Vue全家桶基础知识,vue)