组件详解

注意点

1.关于组件名
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:
第二种写法:
备注:不用使用脚手架时,会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options

关于VueComponent:

1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写,Vue解析时会帮我们创建school组件的实例对象,
即Vue帮我们执行的:new VueComponent(options)。
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:
(1).组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
(2).new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。

vue实例的render方法

组件直接替换掉控制区域(#app)

  1. <body>
  2. <div id="app">
  3. </div>
  4. <template id="father">
  5. <div>
  6. <h1>111</h1>
  7. </div>
  8. </template>
  9. <script type="text/javascript">
  10. Vue.component("father", {
  11. template: "#father"});
  12. let app = new Vue({
  13. el: '#app',
  14. render: function(creatElement) {
  15. //获取模板father
  16. let html = creatElement("father");
  17. return html
  18. }
  19. });
  20. </script>
  21. </body>

组件嵌套

<!DOCTYPE html>
<html>
    <body>
        <!-- 准备好一个容器-->
        <div id="root"></div>
    </body>

    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

        //定义student组件
        const student = Vue.extend({
            name:'student',
            template:`
                <div>
                    <h2>学生姓名:{{name}}</h2>    
                    <h2>学生年龄:{{age}}</h2>    
                </div>
            `,
            data(){
                return {
                    name:'尚硅谷',
                    age:18
                }
            }
        })

        //定义school组件
        const school = Vue.extend({
            name:'school',
            template:`
                <div>
                    <h2>学校名称:{{name}}</h2>    
                    <h2>学校地址:{{address}}</h2>    
                    <student></student>
                </div>
            `,
            data(){
                return {
                    name:'尚硅谷',
                    address:'北京'
                }
            },
            //注册组件(局部)
            components:{
                student
            }
        })

        //定义app组件
        const app = Vue.extend({
            template:`
                <div>    
                    <school></school>
                </div>
            `,
            components:{
                school
            }
        })

        //创建vm
        new Vue({
            template:'<app></app>',
            el:'#root',
            //注册组件(局部)
            components:{app}
        })
    </script>
</html>

全局组件

main.js

Vue.component('Title',Title)

组件数据传递

父→子(数据): 通过props向子组件传数据(实例一)

<body>
    <div id="app">
        <father></father>
    </div>

    <!-- 父组件 -->
    <template id="father">
        <div>
            <h1>{{name}}</h1>
            <!-- son子组件 -->
            <son :parentname="name"></son>
        </div>
    </template>

    <!-- 子组件 -->
    <template id="son">
        <div>父级数据:{{parentname}}</div>
    </template>

    <script type="text/javascript">
        Vue.component("father", {
            template: "#father",
            data: function() {
                return {
                    "name": "张三"
                }
            },
            components: {
                "son": {
                    props: ["parentname"],
                    template: "#son"
                }
            }
        });

        let app = new Vue({
            el: '#app',
        });
    </script>
</body>

父→子(数据): 通过props向子组件传数据(实例二)

<body>
    <!-- Vue实例是父级,school组件是子级,通过props静态或动态的给"school-name"赋值(此处驼峰命名规范改写) -->
    <div id="app">
        <!-- 静态属性 -->
        <school school-name="清华大学"></school>
        <!-- 动态属性 -->
        <school :school-name="schoolName"></school>
        <!-- 动态属性 -->
        <school :school-name="schoolList[0]"></school>
        <!-- 循环传值组件 -->
        <school v-for="item,index in schoolList" :index="index" :school-name="item" :key="'abc'+index"></school>
    </div>

    <script type="text/javascript">
        let app = new Vue({
            el: '#app',
            data: {
                schoolName: "清远职业技术学",
                schoolList: ['清大', '浙江大学', '北大']
            },
            components: {
                "school": {
                    props: ['schoolName', 'index'],
                    template: ` <h3>{{index}}学校名称: {{schoolName}}</h3>`,
                }
            }
        });
    </script>
</body>

image.png

父→子(方法): 通过$emit向子组件传递方法

<body>
    <div id="app">
        <father></father>
    </div>

    <!-- 父组件 -->
    <template id="father">
        <div>
            <button @click="say">点击</button>
            <!-- son子组件 -->
            <son @parentsay="say"></son>
        </div>
    </template>

    <!-- 子组件 -->
    <template id="son">
        <div><button @click="sonfn">点击</button></div>
    </template>

    <script type="text/javascript">
        Vue.component("father", {
            template: "#father",
            data: function() {
                return {
                    "name": "张三"
                }
            },
            methods: {
                "say" () {
                    alert(123)
                }
            },
            components: {
                "son": {
                    methods: {
                        "sonfn" () {
                            this.$emit("parentsay")
                        }
                    },
                    template: "#son"
                }
            }
        });

        let app = new Vue({
            el: '#app',
        });
    </script>
</body>

父→子(数据): 通过$children(推荐使用ref)调用子组件方法或数据

<body>
    <div id="app">
        <button @click="changeEvent">点击</button>
        <school></school>
    </div>
    <script type="text/javascript">
        Vue.component("school", {
            props: ['action'],
            data: function() {
                return {
                    name: "路飞"
                }
            },
            template: ` <div>
                            <span>子组件-{{name}}</span>
                        </div>`,
            methods: {
                chooseEvent: function(schoolName) {
                    console.log(schoolName)
                }
            }
        })

        let app = new Vue({
            el: '#app',
            methods: {
                changeEvent: function() {
                    //$children 可以获取多个子组件
                    //[0]是因为可以有多个
                    this.$children[0].name = "索隆";
                    this.$children[0].chooseEvent("海贼")
                }
            }
        });
    </script>
</body>

子 →父组件的自定义事件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件
  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>
    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){
      this.$refs.xxx.$on('atguigu',回调)
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
      this.$refs.xxx.$once('atguigu',回调)

  1. 子组件触发自定义事件:this.$emit('atguigu',数据)
  2. 子组件解绑自定义事件(被绑的vc):
    1. this.$off('atguigu') 解绑一个自定义事件
    2. this.$off(['atguigu','demo']) 解绑多个自定义事件
    3. this.$off() 解绑所有的自定义事件*
  1. 组件上也可以绑定原生DOM事件,需要使用native修饰符(不然vue不认为是原生事件)。
    <Student @click.native="show"/>
  2. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数(箭头函数没有this,于是vue会往上一层找 找到vc实例当做this),否则this指向会出问题!

    子→父(方法-参数-数据): 通过$emit向父组件传数据

  3. 父传递【方法】给子

  4. 子调用父【方法】并传递数据
  5. 父【方法】接收数据

    <body>
     <div id="app">
         <!--@cschool事件接收this.$emit('cschool', schoolName)-->
         <school v-for="item,index in schoolList" 
         @cschool="changeEvent" 
         :index="index" 
         :school-name="item" 
         :key="'abc'+index"></school>
         <p>{{chooseSchool}}</p>
     </div>
    
     <script type="text/javascript">
         Vue.component("school", {
             props: ['schoolName', 'index'],
             template: ` <div>
                     <h3>学校名称: {{schoolName}}</h3>
                             <button @click="chooseEvent(schoolName)">传值</button>
                     </div>`,
             methods: {
                 //点击按钮触发事件
                 chooseEvent: function(schoolName) {
                     //触发一个叫 cschool 的事件来调用父组件的changeEvent方法,实现数据传值
                     this.$emit('cschool', schoolName)
                 }
             }
    
         })
    
         let app = new Vue({
             el: '#app',
             data: {
                 schoolList: ['清大', '浙江大学', '北大'],
                 chooseSchool: ""
             },
             methods: {
                 changeEvent: function(data) {
                     this.chooseSchool = data;
                 }
             }
    
         });
     </script>
    </body>
    

    子→父(数据): 通过props调用父组件方法 向父组件传递数据

  6. 父传递【方法】以v-bind绑定数据形式给子

  7. 子调用父【方法】以this.XX(参数)形式 并传递数据
  8. 父【方法】接收数据

    <body>
     <div id="app">
         <!-- 因为父元素的方法可以直接修改父元素的数据
           所以将父元素的方法传递给子元素
           由子元素进行调用,从而修改父元素 -->
         <school :action="changeEvent"></school>
         <p>{{chooseSchool}}</p>
     </div>
    
     <script type="text/javascript">
         Vue.component("school", {
             props: ['action'],
             template: `<button @click="chooseEvent(schoolName)">传值</button>`,
             data: function() {
                 return {
                     schoolName: "清职院"
                 }
             },
             methods: {
                 //点击按钮触发事件
                 chooseEvent: function(schoolName) {
                     //调用定义的属性,以此调用父组件方法changeEvent
                     this.action(schoolName)
                 }
             }
         })
    
         let app = new Vue({
             el: '#app',
             data: {
                 chooseSchool: ""
             },
             methods: {
                 changeEvent: function(data) {
                     this.chooseSchool = data;
                 }
             }
         });
     </script>
    </body>
    

    子→父(数据): 通过$parent调用父组件方法 向父组件传递数据

    <body>
     <div id="app">
         <!-- 因为父元素的方法可以直接修改父元素的数据
           所以将父元素的方法传递给子元素
           由子元素进行调用,从而修改父元素 -->
         <school :action="changeEvent"></school>
         {{chooseSchool}}
     </div>
     <script type="text/javascript">
         Vue.component("school", {
             props: ['action'],
             //1.间接在事件中调用父组件方(this.$parent.)
             //2.在视图直接调用父元素方法($parent)
             //3.直接修改父元素数据chooseSchool
             template: ` <div>
                             <button @click="chooseEvent(schoolName)">传值</button>
                             <button @click="$parent.changeEvent(schoolName)">传值</button>
                             <button @click="$parent.chooseSchool=schoolName">传值</button>
                         </div>`,
             data: function() {
                 return {
                     schoolName: "清职院"
                 }
             },
             methods: {
                 chooseEvent: function(schoolName) {
                     //$parent父组件
                     this.$parent.changeEvent(schoolName);
                     //$root最上层组件
                     this.$root.changeEvent(schoolName)
                 }
             }
         })
    
         let app = new Vue({
             el: '#app',
             data: {
                 chooseSchool: ""
             },
             methods: {
                 changeEvent: function(data) {
                     this.chooseSchool = data;
                 }
             }
         });
     </script>
    </body><body>
     <div id="app">
           <!-- 因为父元素的方法可以直接修改父元素的数据
           所以将父元素的方法传递给子元素
           由子元素进行调用,从而修改父元素 -->
         <school :action="changeEvent"></school>
     </div>
     <script type="text/javascript">
         Vue.component("school", {
             props: ['action'],
             //1.间接在事件中调用父组件方(this.$parent.)
             //2.在视图直接调用父元素方法($parent)
             //3.直接修改父元素数据chooseSchool
             template: ` <div>
                             <button @click="chooseEvent(schoolName)">传值</button>
                             <button @click="$parent.changeEvent(schoolName)">传值</button>
                             <button @click="$parent.chooseSchool=schoolName">传值</button>
                         </div>`,
             methods: {
                 chooseEvent: function(schoolName) {
                     //$parent父组件
                     this.$parent.changeEvent(schoolName)
                     //$root最上层组件
                     this.$root.changeEvent(schoolName)
                 }
             }
         })
    
         let app = new Vue({
             el: '#app',
             data: {
                 chooseSchool: ""
             },
             methods: {
                 changeEvent: function(data) {
                     this.chooseSchool = data;
                 }
             }
         });
     </script>
    </body>