Vue 一套构建用户界面的渐进式的框架(组件篇★~)

32 篇文章 0 订阅
12 篇文章 0 订阅

概述

将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易。

组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树

语法

'Vue2.x 版本之前的写法:'



组件使用三个步骤:

创建组件构造器(调用Vue.extend())
注册组件(Vue.component())
使用组件(在Vue实例的作用范围使用组件,使用自定义的组件名)

例如:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件化</title>
</head>
<body>
    <div class="app">
        <mycpv></mycpv>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 创建组件构造器
        const cpvC = Vue.extend({
            template : `
                <div>
                    <h2>我是标题</h2>
                    <p>我是内容</p>
                </div>
            `
        })

        //注册组件
        Vue.component('mycpv',cpvC)
        const app = new Vue({
            el : '.app'
        })
        
    </script>
</body>
</html>
        
'注意:创建组件构造器和注册组件的过程必须放在new Vue 实例前,而且使用组件时,需要放在el选中的元素标签内'


======================================================================
    
    
'Vue2.x 版本之后写法,简化了Vue.extend()步骤,直接用对象来替代'

Vue.component('组件名',{
    data : () => {
        return {
            // 存放组件中的数据,data必须是个函数并返回对象
        }
    },
    template : '模板',
    methods : '方法'
})


例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="app">
        <ccc></ccc>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const cpn = Vue.component('ccc',{
            data : () => {
                return {
                    count : 0
                }
            },
            template : `<button @click='add'>点击:{{count}}</button>`,
            methods : {
                add(){
                    this.count ++
                }
            }
        })
        const app = new Vue({
            el : '.app'
        })
    </script>
</body>
</html>

组件注意事项

1'注意:创建组件构造器和注册组件的过程必须放在new Vue 实例前,而且使用组件时,需要放在el选中的元素标签内'

2、Vue.component()中的data,必须是个函数并返回一个对象。(一个组件使用多次,并不会共享一个data,而是不同的实例)

3、组件模板内容必须是单个元素(也就是说只能有一个root元素)

例如:
template : `<div><h2></h2></div>`

这样是可以的,但在给div加兄弟元素便报错(此时div便是组件模板的根元素)


4、组件命名规则:
(1)在普通标签中,可以使用短横线命名(<test-one></test-one>)
(2)若使用驼峰命名组件,只能在template中的字符串模板使用,不能当做普通标签使用

全局组件和局部组件

全局组件:可以在多Vue实例中使用
局部组件:只可以在当前Vue实例的组件中使用



例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="app1">
        <!-- 全局组件 -->
        <gol></gol>
        <!-- Vue实例app2的局部组件,在这里不可用 -->
        <loc></loc>
    </div>

    <div class="app2">
        <loc></loc>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const cpvG = Vue.extend({
            template : `
                <h2>我是全局</h2>
            `
        })

        const cpvL = Vue.extend({
            template :  `
                <h2>我是局部</h2>
            `
        })

        // 全局(可以在多个Vue实例下使用)
        Vue.component('gol',cpvG)

        const app1 = new Vue({
            el : '.app1'
        })
        const app2 = new Vue({
            el : '.app2',
            components : {
                // 局部(只可以在这个Vue实例下使用)
                loc : cpvL
            }
        })
    </script>
</body>
</html>

模板的分离写法

可以Vue注册组件时的HTML分离出来写

有如下俩种方法:

1、使用<script type="text/x-template" id='myTemp'></script>


2<template id='myTemp'></template>
(推荐使用第二种)


例如:

Vue.component('cpnC',{
    template : '#myTemp'
})

父子组件

父级组件中调用子级组件,这样简便很多

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="app">
        <par></par>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 子组件构造器
        const chire = Vue.extend({
            template : '<h2>我是子组件</h2>'
        })

        // 父组件构造器
        const pare = Vue.extend({
            template :`
                <div>
                    <h2>我是父组件</h2>
                    <chi></chi>    
                </div>
            
            ` ,
            components : {
                chi : chire
            }
        })

        let chire = {}

        // 注册父组件
        Vue.component('par',pare)

        const app = new Vue({
            el : '.app'
        })
    </script>
</body>
</html>

父子组件通信

开发中,往往一些数据需要从上层传递到下层,比如一个页面中,我们从服务器请求到了很多数据,但有些数据并非整个页面的大组件来去展示,而是需要下面的子组件进行展示。这个时候并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件),将数据传递给小组件(子组件)。


父子组件通信,俩种方式:

通过props向子组件传递数据

通过事件向父组件发送信息

如下图,父子组件通信:

在这里插入图片描述

props(父传子)
props的值有俩种方式:

(1)字符串数组,数组中的字符串就是传递的名称


写法:
props:['data1','data2']2)对象,对象可以设置传递时的类型,也可以设置默认值等。还可以自定义类型

'推荐第二种写法'

对象写法可进行类型等验证,验证支持的类型例如:

String
Number
Boolean
Array
Object
Date
Function
Symbol


写法: 
props:{
	data1 : String,
    data2 : Number,
    data3 : {
        type : String ,
        default : '默认值',
        required : true
        /*必须传值,否则报错*/
    }
}


对象写法父传子,例如: 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="app">
        <cpn-c :data='message'></cpn-c>
    </div>

    <template id="temp">
        <h2>{{data}}</h2>
    </template>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>
        let cpn = {
            template : '#temp' ,
            props : {
                data : String
            } 
        }

        const app = new Vue({
            el : '.app' ,
            data : {
                message : '数据...'
            },
            components : {
                'cpn-c' : cpn
            }
        })
    </script>
</body>
</html>
自定义事件(子传父)
子级向父级传送数据。

举个应用场景,例如:
商品分类:有热门推荐、手机数码,家用家电、电脑办公等,若将商品分类看作子组件,那么当点击了其中的家用电器,那么它会向父级组件传入点击事件,并把当前点击的那个元素内容告诉父组件,父组件在将对应的应该展示的数据刷新渲染前端

配合下图:

在这里插入图片描述

例如,以下代码,通过点击按钮子级商品分类组件,传向父级组件,并打印出当前是点击的那个分类按钮:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 父组件模板 -->
    <div class="app" >
        <cpn-c @custom_click='printf'></cpn-c>
    </div>

    <!-- 子组件模板 -->
    <template id="temp" >
        <div>
            <button v-for='iteam in category' @click='send(iteam)'>{{iteam.name}}</button>
        </div>
    </template>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const cpn = {
            template : '#temp' ,
            data() {
                return {
                    category : [
                        {id : 1, name :'热门推荐'},
                        {id : 2, name : '家用电器'},
                        {id : 3, name : '手机数码'}
                    ]
                }
            },
            methods : {
                send(iteam){
                    // 发射(自定义事件,当前点击的按钮)
                    this.$emit('custom_click',iteam)
                }
            }
        }


        const app = new Vue({
            el : '.app' ,
            data : {
                message: ''
            },
            components : {
                'cpn-c' : cpn
            },
            methods : {
                printf(iteam){
                    console.log(iteam,'aaa')
                }
            }
        })
    </script>
</body>
</html>

结果如下图:

在这里插入图片描述

父子组件访问

有时候需要父组件直接访问子组件,或子组件直接访问父组件,或者子组件访问根组件


父组件访问子组件: 使用 $children 或 $refs
'实际开发推荐使用:$refs'


因为在开发中,$refs 最常用,所以举个例子:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Demo</title>
</head>
<body>
    <div class="app">
        <cpn ref='obj'></cpn>
        <button @click='printf'>按钮获取子级</button>
    </div>

    <template id="temp">
        <div>
            <h2>我是子级组件</h2>
        </div>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el : '.app' ,
            methods : {
                printf(){
                    console.log(this.$refs.obj.name)
                }
            },
            components : {
                cpn : {
                    template : '#temp' ,
                    data(){
                        return {
                            name : 'jine'
                        }
                    }
                }
            }
        })

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

子组件访问父组件: 使用 $parent

语法: this.$parent.name

-----------------------------------------
访问root

语法: this.$root.name


'实际开发不常用,因为这样会降低这个组件的耦合性,所以不举例说明'

插槽

组件的插槽是为了让我们封装的组件更加具有扩展性

让使用者决定组件内部的一些内容展示什么

有区别但又有相同共性(可以封装成一个组件,提供插槽)

抽取共性,保留不同(将不同的作成插槽)
1、插槽的基本使用: <solt> </solt>

2、插槽的默认值 : <solt><button>按钮</button></solt>

'若不传值,则有默认值'
'若传一个值,则替换默认值'
'若有多个值,同时放入,那么会这多个值将默认值替换掉

/*
	上面说的值是其他元素,例如:span、p 元素标签等,具体看下面代码
*/


例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>插槽</title>
</head>
<body>
    <div class="app">
        <!-- 无传值,使用slot提供的默认值 -->
        <cpn></cpn>

        <cpn>
            <span>单值:我传了一个值,是span元素</span>
        </cpn>

        <cpn>
            <h2>多值:以下是传了多个值,我是h1元素</h2>
            <p>多值:我是p元素</p>
            <input type="text" value="多值:我是input元素">
        </cpn>
    </div>

    <template id="temp">
        <div>
            <h1>我是cnp组件提供的共同元素h1</h1>
            <slot>
                <button>按钮</button>
            </slot>
            <br></br>
        </div>
    </template>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const cpn = {
            template : '#temp' ,
            
        }

        const app = new Vue({
            el : '.app' ,
            components : {
                cpn 
            }
        })
    </script>
</body>
</html>
具名插槽
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。


可以替换指定名字的插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>插槽</title>
</head>
<body>
    <div class="app">   
        <cpn>
            <span>我替换了默认slot</span>
            <button slot="left">我替换了左边</button>
        </cpn>
    </div>

    <template id="temp">
        <div>
            <slot name='left'>slot左边</slot>
            <slot>slot中间</slot>
            <slot name=right>slot右边</slot>
            <br></br>
        </div>
    </template>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const cpn = {
            template : '#temp' ,
            
        }

        const app = new Vue({
            el : '.app' ,
            components : {
                cpn 
            }
        })
    </script>
</body>
</html>
'Vue的版本官方一直在更新变化,其中上面使用的 slot="xx" 以及scope-slot="xx" 在2.6.0+ 中已弃用'

'现版本使用了v-slot 或语法糖 # '

--------------------------------------------------------
语法格式

不简写,不带参数:

v-slot:slotName  //slotName不能加双引号“”
匿名插槽在新语法下,默认指向default(可以省略 v-slot:default)



不简写,带参数:
<template v-slot:tit>aa</template>




简写方式:
如果你想使用简写语法,必需指定插值的名字

v-slot:header 简写成 #header
v-slot:default 简写成 #default

简写带参数:
#header='{arg}'



-------------------------------------------------------
'注意事项:'

1、在旧版本中slot="xxx"的情况下,可以挂载在非 template标签上

2、但是在v-slot:tit的情况下,必须使用template标签

-----------------------------------------------------------------
    
此语法的代码练习,参考下面作用域插槽中的示例代码
作用域插槽
总结一句话:父组件替换插槽的标签,但是内容由子组件来提供


新语法格式为

v-slot:tit="slotProps" 
//slotProps为自定义的变量名,指向子组件中的data函数返回值


//将具名插槽赋值,很简洁;
//不仅完成插槽指向,还完成了数据挂载


例如,下面这个代码,综合了具名插槽的新语法和作用域插槽的应用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>v-slot</title>
</head>
<body>
    <!-- 父模板 -->
    <div class="app">
        <cpn>
            <!-- v-slot 不带参数 -->
            <template v-slot:title><h1>我是新标题</h1></template>
            
            <!-- # 语法糖 -->
            <template #test></template>


            <!-- # 语法糖 +带参数,此时的user是子模板提供的变量,这个变量指向的子组件的data数据 -->
            <template #content='{user}'><h3>{{user}}</h3></template>

            <!-- 匿名插槽在新语法下,默认指向default(可以省略 v-slot:default-->
            <button>我替代了默认插槽</button>
        </cpn>
    </div>

    <!-- 子模板 -->
    <template id='temp'>
        <div>
            <h2>组件默认提供h2元素</h2>
            <slot name='title'><h3>我是标题</h3></slot>
            <slot name='test'><input type="text"></slot>
            <slot>默认插槽</slot>
            <!-- 带参数绑定,user是自定义变量(供父模板调用),name是子组件的data数据的值 -->
            <slot name='content'  :user='name'><button>按钮</button></slot>
        </div>
    </template>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        const cpn = {
            template : '#temp' ,
            data () {
                return {
                    name : 'jine'
                }
            }
        }

        const app = new Vue({
            el : '.app' ,
            components : {
                cpn
            }
        })
    </script>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值