Vue组件详解


**前言:**第一次接触vue组件的时候,迷迷糊糊的,什么组件传值,组件嵌套。。。。应生硬把我饶进去了,现在想想,最大的一个问题就是,开始选的网课不对,虽然评论区中那么多说不行的,但是我还是硬着头皮把视频看完了,结果就是-半吊子,这过了一个多月了,从新找个视频看,老师讲的非常细,之前的很多疑惑点都解开了,下面是这次学习的一点记录

什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

vue实例属性

var vm2 = new Vue({
  el: '#app2',
  data: {},
  methods: {},
  filters: {},
  directives: {},
  components: { // 定义实例内部私有组件的
    login: {
      template: '#tmpl2'
    }
  },

  beforeCreate() { },
  created() { },
  beforeMount() { },
  mounted() { },
  beforeUpdate() { },
  updated() { },
  beforeDestroy() { },
  destroyed() { }
})

下面开始记录

导入vue.js

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
一、创建组件的方式
  1. 创建组件的基本方式

    <script>
    //使用Vue.extend创建一个全局的组件
    var com1 = Vue.extend({
            template: '<h3>这是使用最笨的方法创建的vue组件</h3>'
        })
        //给创建的组件定义一个名字
    Vue.component('mycom1', com1)
    </script>
    
    //在页面中使用创建的组件(注意,要在挂载点标签内使用)
    <div id="app">
        <mycom1></mycom1>
    </div>
    
  2. 处理定义组件时的驼峰命名法

    //定义组件的时候使用驼峰命名法
    //使用驼峰命名法创建的组件,使用时需要用-进行隔开
    var com = Vue.extend({
        template: '<h3>使用驼峰命名法进行创建的vue组件</h3>'
    })
    Vue.component('myCom', com)
    
    
    //在页面中使用创建的组件
    <div id="app">
    	<my-com></my-com>
    </div>
    
  3. 将创建组件和命名组件整合在一起

    //将创建组件与命名组件整合在一起
    Vue.component('mycom3', Vue.extend({
        template: '<h3>整合创建组件和命名组件</h3>'
    }))
    
    //在页面中使用创建的组件
    <div id="app">
        <mycom3></mycom3>
    </div>
    
  4. 再一次简化组件的创建方式,省略Vue.extend()

    //再度简化创建组件的方式,采用匿名创建
    Vue.component('mycom4', {
        template: '<h3>匿名创建组件并命名</h3>'
    })
    
    //在页面中使用创建的组件
    <div id="app">
        <mycom4></mycom4>
    </div>
    
  5. 使用template标签创建组件(常用)

    <!-- 使用template标签来创建一个组件 -->
    <template id="mycom5">
        <div>
            使用template标签来创建一个组件
        </div>
    </template>
    
    //给使用template标签创建的组件绑定id并命名
    Vue.component('mycom5', {
        template: '#mycom5'
    })
    
    //在页面中使用创建的组件
    <div id="app">
        <mycom5></mycom5>
    </div>
    
  6. 使用template标签创建私有组件

    <!-- 使用template标签来创建一个私有的组件 -->
    <template id="mycom6">
        <div>
            使用template标签来创建一个组件
        </div>
    </template>
    
    // 在vue实例中挂载组件
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        components: { //在vue实例内部创建一个私有的组件
            mycom6: {
                template: '<div>我是被定义在vue实例内部的一个组件</div>'
            }
        }
    });
    
    //在页面中使用创建的组件
    <div id="app">
        <mycom5></mycom5>
    </div>
    
二、组件中的data和methods
  1. 组件可以有自己的 data 数据

  2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法

  3. 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;

  4. 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!

  5. 在组件中,data需要被定义为一个方法,例如:

    Vue.component('account', {
      template: '#tmpl',
      data() {
        return {
          msg: '大家好!'
        }
      },
      methods:{
        login(){
          alert('点击了登录按钮');
        }
      }
    });
    
  6. 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问;

为什么组件中的data属性必须定义为一个方法并返回一个对象

  1. 通过计数器案例演示

  2. 对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;

  3. 而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题,样组件间不会互相干扰

    指向一致的data数据

    <div id="app">
      <num1></num1>
      <num1></num1>
      <num1></num1>
    </div>
    
    <!-- 定义组件 -->
    <template id="tmpl">
      <div>
        <input type="button" value="加1" @click="add">
        <h1>{{ count }}</h1>
      </div>
    </template>
    <script>
    
        //如果使用全全局的定义,
        var dataObj = { count:0 }
    
        Vue.component('num1', {
          template: '#tmpl',
          data(){
                return dataObj
          },
          methods:{
            add(){
              this.count++
            }
          }
        });
    
        var vm = new Vue({
            el:'#app',
            data:{},
            methods:{}
        })
    </script>
    
    在这里插入图片描述

    指向不一致的data数据

    <div id="app">
        <count></count>
        <count></count>
        <count></count>
    </div>
    
    <template id="count">
        <div>
            <input type="button" value="添加" @click="add()">
            <h3>数字:{{count}}</h3>
        </div>
    </template>
    
    //在组件中创建一个私有函数,一般情况下只能在组件的内部使用
    Vue.component('count', {
        template: '#count',
        data: function() {
            return {
                count: 0
            }
        },
        methods: {
            add() {
                this.count++
            }
        }
    })
    
    在这里插入图片描述
三、组件之间的切换
//先创建两个组件,然后在两个组件之间进行切换
    Vue.component("login", {
        template: ' <h3>登录</h3>'
    })

    Vue.component('register', {
        template: ' <h3>注册</h3>'
    })

// 创建 Vue 实例,定义两个数据用于进行组件切换
    var vm = new Vue({
        el: '#app',
        data: {
            flag: true,
            comName: 'login',
        },
        methods: {}
    });
  1. 使用v-if和v-else进行切换

    <div id="app">
        <!-- 使用v-if和v-else实现两个组件之间的切换
             缺点就是,如果需要切换的组件数量在两个之上,则不能使用这种方法 -->
        <a href="" @click.prevent="flag =true">登录</a>
        <a href="" @click.prevent="flag = false">注册</a>
        <login v-if="flag"></login>
        <register v-else="flag"></register>
    </div>
    
  2. 使用component标签中的属性绑定进行组件切换

    <component :is="comName">
    </component>
    
  3. 给组件切换添加动画

    <a href="" @click.prevent="comName ='login'">登录</a>
    <a href="" @click.prevent="comName = 'register'">注册</a>
    <transition mode="out-in">
        <component :is="comName">
        </component>
    </transition>
    
    <style>
        .v-enter,
        .v-leave-to {
            opacity: 0;
            /* 设置透明度 */
            transform: translateX(150px);
        }
    
        .v-enter-active,
        .v-leave-to {
            position: absolute;
            transition: all 0.5s ease;
        }
    </style>
    
四、父子组件传值
  1. 父组件向子组件传值

    <div id="app">
        <!-- 直接在子组件中调用父组件里面的值,发现失败 -->
        <!-- // 结论:经过演示,发现,子组件中,默认无法访问到 父组件中的 data 上的数据 和 methods 中的方法 -->
        <com>{{msg}}</com>
        <!-- 父组件,可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
        <com :son_msg="msg"></com>
    </div>
    
    //在子组件中使用父组件的数据
    Vue.component('com', {
        props: ['son_msg'],
        data: function() {
            return {
                son_msg: "0000"
            }
        }, // data 上的数据,都是可读可写的;
        template: '<div>我是子组件---{{son_msg}}---{{msg}}</div>'
    
    })
    
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {
            msg: '我是父组件里面的数据'
        },
        methods: {}
    });
    
  2. 父组件向子组件传递方法

    <div id="app">
        尝试在子组件中直接使用父组件里面的方法
        <son></son>
        通过绑定方法向子组件传递父组件的方法
        <son @son='father'></son>
        <!-- 在这一步中有一个小细节,绑定的father方法不能在后面加()
            在后面加()的含义是将funther方法的执行结果绑定
            不加括号的含义是直接绑定的是father方法 -->
    </div>
    
    <script>
    //创建一个组件,用于获取父组件里面的方法
    Vue.component('son', {
        template: "<div><h3>我是子组件</h3> <input type='button' value='111' @click='son()'></div>",
        methods: {
            son() {
                this.$emit('son') /* 接收父组件传来的方法并调用 */
            }
        }
    })
        
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            father() {
                console.log("我是爸爸")
            }
        }
    });
    </script>
    
  3. 子组件向父组件传值

    思路:

    1、父组件有个带参数的函数

    2、父组件将函数传递给子组件

    3、子组件调用函数并将数据当参数传递给父组件

    <div id="app">
    	<son @son='father'></son>
        <!-- 在这一步中有一个小细节,绑定的father方法不能在后面加()
            在后面加()的含义是将funther方法的执行结果绑定
            不加括号的含义是直接绑定的是father方法 -->
    </div>
    
    <script>
    //创建一个组件,用于获取父组件里面的方法
    Vue.component('son', {
        template: "<div><h3>我是子组件</h3> <input type='button' value='111' @click='son()'></div>",
        data: function() {
            return {
                msg: '我是儿子'
            }
        },
        methods: {
            son() {
                this.$emit('son', this.msg) /* 接收父组件传来的方法并调用 */
            }
        }
    })
    
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            father(msg) {
                console.log("我是爸爸----" + msg)
            }
        }
    });
    </script>
    
五、案例-实现评论功能(Ajax的效果)

分析:发表评论的业务逻辑

  1. 评论数据存到哪里去???

    存放到了 localStorage 中 localStorage.setItem(‘cmts’, ‘’)

  2. 先组织出一个最新的评论数据对象

  3. 想办法,把 第二步中,得到的评论对象,保存到 localStorage 中:

  4. localStorage 只支持存放字符串数据, 要先调用 JSON.stringify

  5. 在保存 最新的 评论数据之前,要先从 localStorage获取到之前的评论数据(string), 转换为 一个 数组对象, 然后,把最新的评论, push 到这个数组

  6. 如果获取到的 localStorage 中的 评论字符串,为空不存在, 则 可以 返回一个 ‘[]’ 让 JSON.parse 去转换

  7. 把 最新的 评论列表数组,再次调用 JSON.stringify 转为 数组字符串,然后调用 localStorage.setItem()

  8. 初始化数据的方法在父组件里面,想要在子组件保存完数据之后调用父组件的方法进行刷新数据, 则需要在使用组件时绑定事件,然后在子组件的方法中使用this.$emit(“方法名”),进行使用, 属性绑定时有一个小细节:方法名()表示的是绑定一个方法执行后的结果,不加()表明绑定的是一个方法

<div id="app">
	<!-- 评论区 -->
    <div class="main">
        <!-- 使用发表评论的组件 -->
        <tmpl @load="loadComments"></tmpl>
        <div class="content" v-for="(item,index) in list" :key="index">
        <p>{{item.content}}</p>
        留言人:<span>{{item.name}}</span>
        </div>
    </div>
</div>

<!--发表评论的模板-->
<template id="tmpl">
<div>
<label for="name" >
姓名:<input type="text" id="name" v-model="name">
        </label>
<label for="content">
            评论内容:
            <textarea cols="30" rows="10" id="content" v-model="content"></textarea>
        </label>
<label>
            <input type="button" value="发表" @click="add" >
        </label>
</div>
</template>

<script>
//创建一个发表评论的组件对象
var tmpl = {
    template: '#tmpl',
    data() {
        return {
            name: "",
            content: "",
        }
    },
    methods: {
        //将数据保存至localStorage中
        add() {
            //创建一个列表对象,用于生成一条新的评论
            let listObj = {
                id: Date.now(),
                name: this.name,
                content: this.content
            }

            //从localStorage中获取全部评论,并使用JSON.parse()转换成json对象
            let list = JSON.parse(localStorage.getItem('cmts') || '[]')

            //将最新的评论添加至列表中,这里添加在列表的最前面
            list.unshift(listObj)

            //保存数据
            localStorage.setItem('cmts', JSON.stringify(list))

            //将用户和内容初始化
            this.name = this.content = ""

            //刷新页面数据
            this.$emit('load')

        }
    }
}


// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
    el: '#app',
    data: {
        name: '',
        content: '',
        list: [{
            name: 'daidai',
            content: '大家好,我是呆呆'
        }, {
            name: '栋梁',
            content: '大家好,我是栋梁'
        }]
    },
    methods: {
        //初始化评论列表
        loadComments() {
            //获取本地存储的数据
            let list = JSON.parse(localStorage.getItem('cmts') || '[]')
                //将数据赋值给列表
            this.list = list
        }
    },
    components: {
        tmpl: tmpl,
    },
    created() {
        //在页面数据和方法创建完之后执行该函数
        this.loadComments()
    },
});
</script>

<style>
.main {
    width: 600px;
    margin: 100px auto;
}

.content {
    border: 1px solid #efeeef;
    margin-top: 10px;
}

span {
    color: seagreen;
}
</style>
六、使用 this.$refs 来获取元素和组件
<div id="app">
    <h3 ref="com">hahahhahaha</h3>
    {{msg}}
    <button @click="ceshi">...</button>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
    el: '#app',
    data: {
        msg: ""
    },
    methods: {
        ceshi() {//获取标签中的值
            this.msg = this.$refs.com.innerText
        }
    },
});
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值