Vue全家桶之组件化开发

Vue全家桶之组件化开发

    • 一、组件化开发思想
    • 二、组件注册
        • 1. 全局组件注册语法及使用
        • 2. 组件注册注意事项
        • 3. 局部组件注册语法及使用
    • 三、组件间数据交互
      • 3.1 父组件向子组件传值(父→子)
        • 1. 父组件通过属性名将值传递给子组件
        • 2. 组件内部通过`props`接收传递过来的值
        • 3. props属性名规则
        • 4. props属性值类型
      • 2. 子组件向父组件传值(子→父)
      • 3. 非父子组件间(兄弟)传值
    • 四、组件插槽
        • 1. 组件插槽的作用
        • 2. 组件插槽基本用法
        • 3. 具名插槽用法
        • 4. 作用域插槽
    • 五、基于组件的案例:购物车
        • 1. 需求分析
        • 2. 实现步骤

一、组件化开发思想

组件化规范:Web Components

  • 我们希望尽可能多的重用代码。
  • 自定义组件的方式不太容易。
  • 多次使用组件可能导致冲突。
    Vue全家桶之组件化开发_第1张图片

二、组件注册

1. 全局组件注册语法及使用

<div id="app">
    <!-- 子组件之间互相独立 -->
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>

<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
    /* 
        组件注册
        Vue.component(组件名称, {
           data: 组件数据,
           template:组件模板内容
        })
    */
    Vue.component('button-counter', {
        data: function() {
            return {
                count: 0
            }
        },
        template: '',
        methods: {
            handle: function() {
                this.count++;
            }
        }
    });
    var app = new Vue({
        el: "#app ",
        data: {
        }
    });
</script>

2. 组件注册注意事项

(1) data必须是一个函数
(2)组件模板内容必须是单个根元素
(3)组件模板内容可以是模板字符串。
(4)组件命名方式:只能在字符串模板中使用驼峰方式,在普通的标签模板中,必须使用短横线的方式使用组件。

  • 短横线方式
Vue.component('my-component', {
      data: 组件数据,
      template:组件模板内容
})
  • 驼峰方式
Vue.component('MyComponent', {
      data: 组件数据,
      template:组件模板内容
})

3. 局部组件注册语法及使用

<div id="app">
    <!-- 子组件之间互相独立 -->
    <hello-world></hello-world>
    <hello-tom></hello-tom>
    <hello-jerry></hello-jerry>
</div>

<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
    /* 
         局部组件注册
         局部组件只能在注册他的父组件中使用
    */
    var HelloWorld = {
        data: function() {
            return {
                msg: 'HelloWorld'
            }
        },
        template: '
{{msg}}
'
, }; var HelloTom = { data: function() { return { msg: 'HelloTom' } }, template: '
{{msg}}
'
, }; var HelloJerry = { data: function() { return { msg: 'HelloJerry' } }, template: '
{{msg}}
'
, }; var app = new Vue({ el: "#app ", data: {}, components: { 'hello-world': HelloWorld, 'hello-tom': HelloTom, 'hello-jerry': HelloJerry, } }); </script>

三、组件间数据交互

3.1 父组件向子组件传值(父→子)

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

<!-- 静态获取 -->
<menu-item title='来自父组件的值'></menu-item>
<!-- 动态绑定属性 -->
<menu-item :title='ptitle'></menu-item>

var app = new Vue({
	el: "#app ",
	data: {
	    pmsg: '父组件中内容',
	    ptitle: '动态绑定属性'
	}
});

2. 组件内部通过props接收传递过来的值

Vue.component('menu-item', {
    props: ['title'],
    data: function() {
        return {
            msg: '子组件本身的数据'
        }
    },
    template: '
{{msg+"----"+title}}
'
});

结果演示:
在这里插入图片描述

3. props属性名规则

  • props中使用驼峰形式,标签模板中需要使用短横线的形式。
Vue.component('menu-item', {
    // 在js中是驼峰形式
    props: ['menuTitle'],
    template: '
{{menuTitle}}
'
}); <!-- 在html中是短横线形式 --> <menu-item :menu-title='ptitle'></menu-item>
  • 字符串形式的模板有这个限制
Vue.component('third-com', {
    props: ['testTitle'],
    template: '
{{testTitle}}
'
}); Vue.component('menu-item', { props: ['menuTitle'], // 字符串模板中没有一定要短横线的规定 template: '
{{menuTitle}}
'
});

4. props属性值类型

  • 字符串 String
  • 数值 Number
  • 布尔值 Boolean
  • 数组 Array
  • 对象 Object
<body>
    <div id="app">
        <div>{{pmsg}}</div>

        <menu-item :pstring='pstr' :pnumber='pnumber' :pbool='pbool' :parr='parr' :pobj='pobj'></menu-item>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
    <script>
        Vue.component('menu-item', {
            props: ['pstring', 'pnumber', 'pbool', 'parr', 'pobj'],
            template: `
            
{{pstring}}
{{12+pnumber}}
{{pbool}}
  • {{item}}
{{pobj.name}} {{pobj.age}}
`
}); var app = new Vue({ el: "#app ", data: { pmsg: '父组件中内容', // 字符串 pstr: 'hello', // 数值 pnumber: 12, //布尔值 pbool: true, //数组 parr: ['apple', 'lemon', 'banana'], //对象 pobj: { name: 'lisi', age: '21' } } }); </script> </body>

结果演示:
Vue全家桶之组件化开发_第2张图片

2. 子组件向父组件传值(子→父)

① 子组件通过自定义事件向父组件传递信息。

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

② 父组件监听子组件的事件。

<menu-item :parr='parr' @enlarge-text='fontsize += 5'></menu-item>

③ 子组件(具体传参)向父组件传递信息。

//子组件通过自定义事件向父组件传递信息
<button @click="$emit('enlarge-text',5)">扩大父组件中的字体大小</button>
//父组件监听子组件的事件
<menu-item :parr='parr' @enlarge-text='fontsize += $event'></menu-item>

3. 非父子组件间(兄弟)传值

① 单独的事件中心管理组件间的通信。
Vue全家桶之组件化开发_第3张图片

var eventHub = new Vue();

② 监听事件与销毁事件。

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

③ 触发事件。

eventHub.$emit('add-todo',id);

④ 具体案例:

<body>
    <div id="app">
        <div>父组件</div>
        <div><button @click="handle">销毁事件</button></div>
        <test-tom></test-tom>
        <test-jerry></test-jerry>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
    <script>
        // 提供事件中心
        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; }); } }); var app = new Vue({ el: "#app ", data: { parr: ['apple', 'lemon', 'banana'], fontsize: 10 }, methods: { handle: function() { hub.$off('tom-event'); hub.$off('jerry-event'); } } }); </script> </body>

四、组件插槽

1. 组件插槽的作用

  • 父组件向子组件传递内容

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

2. 组件插槽基本用法

① 插槽位置

Vue.component('alert-box', {
    template: `
       
ERROR: 默认内容
`
});

② 插槽内容

<div id="app">
    <alert-box>有bug发生</alert-box>
    <alert-box>有一个警告</alert-box>
    <!-- 父组件没有传递子组件内容,那么就默认是slot插槽内的内容 -->
    <alert-box></alert-box>
</div>

③ 结果演示:
在这里插入图片描述

3. 具名插槽用法

① 插槽定义

Vue.component('base-layout', {
   template: `
       
`
});

② 插槽内容

<base-layout>
    <p slot="header">标题信息</p>
    <p>主要内容1</p>
    <p>主要内容2</p>
    <p slot="footer">底部信息</p>
</base-layout>

③ 结果演示
Vue全家桶之组件化开发_第5张图片

4. 作用域插槽

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

① 插槽定义

Vue.component('fruit-list', {
    props: ['list'],
    template: `
       
`
});

② 插槽内容

<fruit-list :list='list'>
    <!-- template填充slot -->
    <!-- slotProps是自定义的名字,获取子模板中的slot -->
    <template v-slot='slotProps'>
         <strong v-if='slotProps.info.id==2' class="current"> {{slotProps.info.name}}</strong>
         <strong v-else> {{slotProps.info.name}}</strong>
    </template>
</fruit-list>

③ 具体视频链接:
https://www.bilibili.com/video/BV1vE411871g?p=66.

五、基于组件的案例:购物车

1. 需求分析

根据业务功能进行组件化划分
① 标题组件(展示文本)。
② 列表组件(列表展示、商品数量变更、商品删除)。
③ 结算组件(计算商品总额)。

2. 实现步骤

  • 实现整体布局和样式效果。
  • 划分独立的功能组件。
  • 组合所有的子组件形成整体结构。
  • 逐个实现各个组件功能。
    1. 标题组件
    2. 列表组件
    3. 结算组件
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>兄弟组件</title>
    <!-- 引入car css -->
    <link rel="stylesheet" href="./car.css">
</head>

<body>
    <div id="app">
        <!-- 购物车模块start -->
        <div class="c-container">
            <my-cart></my-cart>
        </div>
        <!-- 购物车模块end -->
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
    <script>
        // 三个子组件
        var CartTitle = {
            props: ['uname'],
            template: `
             
{{uname}}的商品
`
} var CartList = { props: ['list'], template: `
{{item.name}}
{{item.price}}
- +
{{item.price*item.num}}
`
, methods: { del: function(id) { //把id传递给父组件 this.$emit('cart-del', id); }, //event是传过来的最新的数据 changeNum: function(id, event) { //把id传递给父组件 //console.log(id, event.target.value); this.$emit('change-num', { id: id, type: 'change', num: event.target.value }); }, sub: function(id) { this.$emit('change-num', { id: id, type: 'sub' }) }, add: function(id) { this.$emit('change-num', { id: id, type: 'add' }) } } } 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: '儿童文学', price: 13, num: 1, img: 'upload/p1.jpg' }, { id: 2, name: '贴纸书', price: 25, num: 2, img: 'upload/p2.jpg' }, { id: 3, name: '唐诗三百首', price: 30, num: 3, img: 'upload/p3.jpg' }] } }, template: `
`
, components: { 'cart-title': CartTitle, 'cart-list': CartList, 'cart-total': CartTotal, }, methods: { delCart: function(id) { //根据id删除list中对应的数据 //1. 找到id所对应数据的索引 var index = this.list.findIndex(item => { return item.id == id; }); console.log(index); //2. 根据索引删除对应数据 this.list.splice(index, 1); }, changeNum: function(val) { //分为三种情况:1.输入与变更 2.加号变更 3.减号变更 //1. 根据子组件传递过来的数据更新list中对应的数据 if (val.type == 'change') { 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) { if (item.num > 0) { item.num -= 1; } else { alert('不能在减少了!'); } //终止遍历 return true; } }) } else if (val.type == 'add') { //减一操作 this.list.some(item => { if (item.id == val.id) { item.num += 1; //终止遍历 return true; } }) } } } }); var app = new Vue({ el: "#app ", data: { }, }); </script> </body> </html>

结果演示:
Vue全家桶之组件化开发_第6张图片

你可能感兴趣的:(vue)