Vue 组件基础

官方教程: https://cn.vuejs.org/v2/guide

声明组件

let myComponent = Vue.extend({
    template : '#my',
    // 此时的 #my 是id="my"的template标签
});

// 可以简化为
let myComponent = {
    template : '#my',
    data(){
        // 此处的data必须是一个函数
    }
};

注册组件(全局组件, 局部组件)

// 全局组件: 一次注册在所有的vue实例中(都能够使用
Vue.component('my-component',{
    template : '#my-component',
    data () {
        // 此处的data必须是一个函数    
    },
});

// 局部组件: 只能在某一个vue实例中使用
const vm = new Vue({
    'el' : '#app',
    components : {
        'my-component' : {
            template : '#my',
            data() { 
                // 此处的data必须是一个函数
            }
        },
    }
});

注: 为了保证各个组件的相互独立性,所有的组件中的data必须是一个函数

组件之间的通信

  • 父组件 => 子组件: props
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue study</title>
    <script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
<!-- templates -->
<template id="my">
    <div>
        <p>这是本组件自己的数据: {{selfData}}</p>
        <p>这是父组件的数据: {{msg}}</p>
    </div>
</template>
<!-- /templates -->


<!-- 使用组件 -->
<div id="app">
    <my-component :msg="message"></my-component>
</div>


<script>
    // 1.声明一个组件
    let myComponent = Vue.extend({
        template: '#my',
        props: ["msg"],
        // props 不仅仅可以是一个数组,也可以是一个对象
        data() {
            return {
                selfData: "hello Vue",
            };
        },
    });
    
    
/** props 支持的类型=========================
 1. 简单语法
  Vue.component('props-demo-simple', {
    props: ['size', 'myMessage']
  })
  2. 对象语法,提供校验
  Vue.component('props-demo-advanced', {
    props: {
      // 检测类型
      height: Number,
      // 检测类型 + 其他验证
      age: {
        type: Number,
        default: 0,
        required: true,
        validator: function (value) {
          return value >= 0
        }
      }
    }
  });
==================================*/

    // 2.在Vue实例中来注册组件
    const vm = new Vue({
        el: '#app',
        data: {
            message: 'hello world',
        },
        components: {
            // 注册一个局部组件
            'my-component': myComponent,
            // 注: 此时必须使用 - 分割单词,或者全部小写,不能使用驼峰式写法
        }
    });
</script>
<!--
    父组件 => 子组件: 在子组件中使用父组件的数据
    1. 声明一个组件
    2. 注册组件(全局或者局部组件都可以通信)
    3. 使用组件(my-component), 并在使用时绑定一个属性 :msg
    4. 在子组件中用 props 来获取绑定的属性   props : ["msg"],
    5. 测试,看是否能够成功传递值
-->
</body>
</html>
  • 子组件 => 父组件: 发布订阅
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue study</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <!-- 使用组件 -->
    <my-component :msg="message" @custom_event="changeData"></my-component>
</div>

<!-- templates -->
<template id="my">
    <div>
        <p>{{msg}}</p>
        <p>{{selfData}}</p>
        <!-- <button @click="$emit('custom_enevt','args')">点我改变父组件的数据</button> -->
        <!-- 如果觉得这种行内的触发满足不了复杂的逻辑需求,你也可以把这个触发写到这个组件的方法中 -->
         <button @click="triggerParent(10)">点我改变父组件的数据</button>
    </div>
</template>

<script>
    // 声明一个组件
    let myComponent = Vue.extend({
        template: '#my',
        props: ["msg"],
        data() {
            return {
                selfData: 'hello Vue',
            };
        },
        methods : {
            triggerParent(val){
                // 触发父组件并传递一个参数,此时的val是click调用时传递的参数
                this.$emit('custom_event', val);
            },
        },
    });

    const vm = new Vue({
        el: '#app',
        data: {
            message: 'hello world',
        },
        methods: {
            changeData (val) {
                // 把message变成一个随机数连接上子组件传递的参数
                this.message = val + Math.ceil(Math.random()*100);
            },
        },
        components: {
            // 注册一个局部组件
            'my-component': myComponent,
        }
    });
</script>
<!--
由于vue为了保证各个组件的独立性,所以将各个组件的数据流向做成那单向数据流,
而这样,就只能父组件改变子组件的内容,而不能子组件改变父组件的内容
所以为了能够在子组件中改变父组件的数据,只有使用自定义事件系统

1. 声明一个组件  let myComponent = Vue.extend({...});
2. 注册一个组件(全局,局部都行)
3. 使用组件
    3.1) 绑定一个属性     :msg="message"  子组件可以用 props 来获取绑定的属性
    3.2) 绑定一个自定义事件 @custom_event="changeData" 此时的custom_event是事件名称,changeData是methods中的方法
    3.3) 子类想改变父类的数据,可以用$emit('事件名称')来触发父组件的事件,然后由父组件决定改变的规则,从而达到单向数据流的目的
    3.4) 触发父类自定义事件
        3.4.1) 行内触发
            <button @click="$emit('custom_enevt','args')">点我改变父组件的数据</button> 此时的args是参数
        3.4.2) 使用方法触发
            <button @click="triggerParent(10)">点我改变父组件的数据</button>
            methods : {
                 triggerParent(val){
                     // 触发父组件并传递一个参数,此时的val是click调用时传递的参数
                     this.$emit('custom_event', val);
                  },
            }
-->
</body>
</html>
  • 组件嵌套的情况
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue组件嵌套</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <style>
        .cmpt1, .cmpt2 {
            color: #ffffff;
            padding: 15px;
        }

        .cmpt1 {
            background: #272822;
        }

        .cmpt2 {
            background: #265a88;
        }
    </style>
</head>
<body>
<div id="app">
    <my-component @custom_event="changeData" :msg="message"></my-component>
</div>

<!-- templates -->
<template id="my">
    <div>
        <one :msg="msg" :name="name"></one>
        <two :msg="msg" @test_event="changeMess"></two>
    </div>
</template>

<!--one-->
<template id="one">
    <div class="cmpt1">
        <p>这是组件1</p>
        <p>这是二级传递: hello {{msg}}!!! </p>
        <p>这是三级传递: what's your name? my name is {{name}}</p>
    </div>
</template>

<!--two-->
<template id="two">
    <div class="cmpt2">
        <p>这是组件2</p>
        <p> hello {{msg}} </p>
        <p>
            <button @click="changePrentData(10)">点我改变父组件数据</button>
        </p>
    </div>
</template>


<script>
    // 声明一个子组件
    let one = Vue.extend({
        template: '#one',
        props: ["msg", "name"],
    });

    // 声明一个子组件
    let two = Vue.extend({
        template: '#two',
        props: ["msg"],
        methods: {
            changePrentData(val) {
                let m = Math.ceil(Math.random() * val);
                // this.$emit('custom_event', m);
                this.$emit('test_event', m);
            }
        },
    });

    // 声明一个父组件
    let myComponent = Vue.extend({
        template: '#my',
        props: ["msg"],
        methods: {
            changeMess(val){
                this.$emit('custom_event',val);
            }
        },
        components: {
            'one': one,
            'two': two
        },
        data() {
            return {
                name: 'Tom',
            };
        }
    });

    const vm = new Vue({
        el: '#app',
        data: {
            message: 'world',
        },
        methods: {
            changeData(val) {
                this.message = "这是改变后的message,喜欢吗? " + val + " 是点击是传递的参数";
            }
        },
        components: {
            // 注册一个局部组件
            'my-component': myComponent,
        }
    });
</script>
<!--
    组件嵌套时,最底层的组件想要与最顶层的组件通信,必须一层一层向上传递
-->
</body>
</html>
  • 实例 模态框
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>蒙版效果</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        html, body {
            width: 100%;
            height: 3000px;
            /* 为了测试滚动 */
        }

        #alert-panel {
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            position: absolute;
            top: 0;
            left: 0;
            /*display: none;*/
        }

        #alert-panel #login {
            width: 500px;
            height: 300px;
            background: #000000;
            position: fixed;
            top: 50%;
            left: 50%;
            margin-left: -250px;
            margin-top: -180px;
        }

        #close-alert-panel {
            background: #000;
            color: #ffffff;
            border: none;
            width: 30px;
            height: 30px;
            float: right;
        }

        #close-alert-panel:hover {
            background: #f00;
        }
    </style>

    <!--javascript-->
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>

<div id="app">
    <!-- 当点击button的时候,显示模态框,在模态框中点击关闭按钮时,关闭模态框 -->
    <button id="btn" @click="showDialog">点击登陆</button>

    <modal :is-hide-dialog="!isHide" @close="closeModal"></modal>
</div>

<template id="dialog">
    <div id="alert-panel" v-show="isHideDialog">
        <div id="login">
            <button id="close-alert-panel" @click="$emit('close')">&times;</button>
        </div>
    </div>
</template>

<script>
    // 声明一个组件
    let modal = Vue.extend({
        template: '#dialog',
        props: ["isHideDialog"],
    });

    const vm = new Vue({
        el: '#app',
        data: {
            isHide: true,
        },
        methods: {
            showDialog() {
                this.isHide = false;
            },
            closeModal() {
                this.isHide = true;
            }
        },
        components: {
            'modal': modal
        }
    });
</script>
</body>
</html>
  • 父组件调用子组件的方法

注: 如果ref放在组件上,那么通过$refs拿到的不是组件的dom而是这个组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue study</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>

<div id="app">
    <!-- 注: 如果ref放在组件上,那么通过$refs拿到的不是组件的dom而是这个组件 -->
    <load ref="load"></load>
</div>

<!--templates-->
<template id="load">
    <div v-if="isShow">
        <p>玩命加载中...</p>
    </div>
</template>

<!-- JavaScript -->
<script>
    let load = Vue.extend({
        template: '#load',
        data () {
            return {
                isShow: true,
            }
        },
        methods: {
            hide(){
                this.isShow = false;
                console.log("我被调用了,这个组件已经被隐藏了");
            }
        },
    });

    const vm = new Vue({
        el: '#app',
        components: {
            "load": load,
        },
        mounted () {
            // 当vm实例挂载的时候,就调用子组件的方法,隐藏加载组件
            this.$refs.load.hide();
        }
    });

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

  • 在子组件中加工父组件传递的值

默认在子组件中不能修改父组件传递的值,如果非要
加工父组件传递过来的数据,可以使用 computed 属性或者 data()
来加工父组件传递的值,此种方式虽然能够加工了父组件
传递的数据,但是不会改变父组件的数据,保证了各个组件的独立性(单向数据流)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue study</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>


<div id="app">
    <my-comp :name="message"></my-comp>
</div>

<!--templates-->
<template id="my">
    <div>
        <p>父组件原数据: {{name}}</p>
        <p>改变后的数据: {{myName}}</p>
        <p>改变后的数据: {{myName2}}</p>
    </div>
</template>

<!-- JavaScript -->
<script>
    // make a component
    let myComponent = Vue.extend({
        template: '#my',
        props: ["name"],
        // 要求: 在子组件中,将父组件传递的值拼接一个随机数
        // 方式一:使用计算属性来改变父组件传递的值
        computed:{
            myName(){
                return this.name + Math.random();
            }
        },
        // 方式二:使用data()方法来改变父组件传递的值(局部变量)
        data(){
            return {
                "myName2" :this.name + Math.random(),
            }
        },
    });

    const vm = new Vue({
        el: '#app',
        data: {
            message: 'hello world',
        },
        components: {
            // register a local component
            "my-comp": myComponent,
        },
    });

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容