vue2(二)组件化

一、组件化

1.1 简单认识组件化

 

1.2 全局组件与局部组件

全局组件:可以在多个Vue实例中使用。

局部组件:只能在单个Vue实例中使用。

在项目开发中,一般只设置一个vue实例,一般采用局部组件的方式。

<body>
    <div id="app">
        <mycpn></mycpn>
    </div>
    <div id="app2">
        <mycpn></mycpn>
    </div>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        //1.创建组件构造器
        const cpnC = Vue.extend({
            template: `
            <div>
                <h2>你好啊</h2>
                <h1> ahhha</h1>
            </div>
            `
        })
        // // 2.注册全局组件:
        // Vue.component('mycpn',cpnC)
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好'
            },
            // 2.注册局部组件
            components: {
                mycpn: cpnC
            }
        })
        const app2 = new Vue({
            el: '#app2',
            data: {
                message: '你好'
            }
        })
    </script>
</body>

 1.3 父组件与子组件

父组件中使用子组件,父组件中要注册子组件。

<body>
    <div id="app">
        <cpn1></cpn1>
    </div>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        const cpn2 = Vue.extend({
            template: `
            <div>
                <h1>你好,我是标题二</h1>
            </div>
            `  
        })
        const cpn1 = Vue.extend({
            template: `
            <div>
                <h1>你好,我是标题一</h1>
                <cpn2><cpn2>
            </div>
            `,
            //注意要在父组件中注册子组件
            components: {
                cpn2:cpn2
            }       
        })
        const app  =  new Vue({
            el: '#app',
            components: {
                cpn1: cpn1,
                cpn2: cpn2
            }
        })
    </script>
</body>

在运行的时候,会找到对应的组件,将模板内容编译好,编译成render函数,直接渲染Html,因此子组件与vue实例没有任何关系,如果想用子组件,需要在vue实例中注册。

语法糖的写法。省去了Vue.extend(),但是实际上还是调用了Vue.extend()


        Vue.component('mycpn',{
            template: `
            <div>
                <h2>你好啊</h2>
                <h1> ahhha</h1>
            </div>
            `
        })

        const app = new Vue({
            el: '#app',
            components: {
                'mycpn': {
                    template: `
                        <div>
                            <h2>你好啊</h2>
                            <h1> ahhha</h1>
                        </div>
                        `
                    }
            }
        })

 1.4 组件的模板分离

js与html混在一起,显得很乱,因此要使用模板分离:有以下两种方式:

法1:使用script标签,注意类型要设置成 text/x-template 

法2:使用template标签

<body>
    <!-- 写法一:使用script标签,注意类型要设置成 text/x-template -->
    <!-- <script type="text/x-template" id="tmp"> 
        <div>
            <h1>hhahaha</h1>
            <h2>你好</h2>
        </div>
    </script> -->
    <!-- 写法2:使用template标签 -->
    <template id="tmp">
        <div>
            <h1>hhahaha</h1>
            <h2>你好</h2>
        </div>
    </template>
    <div id="app">
        <cpn1></cpn1>
    </div>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        const app = new Vue({
            el: "#app",
            components: {
                "cpn1": {template: '#tmp'}
            }
        })
    </script>
</body>

 1.5 组件能够访问vue中的数据吗。

不能。如果数据都在vue实例中,相当于多个vue组件共享了数据,但其实我们希望,相同的组件是分离的(各自运行自己的,互不影响).因此,组件有自己的数据。并且组件的数据不是一个对象,而是一个函数,并且该函数返回一个对象。

为什么这样设计呢。可以用以下代码来理解。

如果data是一个对象,vue在获取数据的时候,相当于返回data,其中一个组件改变了data中的值,因为data是共享的,所以data中的值相对于其他的组件就会改变。

因为改成了函数的方式,并且返回一个大括号形式的对象。

    <script>
        function fn() {
            return {
                name: 18,
                age: 20
            }
        }
        var obj1 = fn();
        var obj2 = fn();
        var obj3 = fn();
        obj1.age = 'lklkl'
        console.log(obj1);
        console.log(obj2);
        console.log(obj3);
        // 上述三个obj返回三个不同的地址。因此,三个obj互不影响。
        const person = {
            name:18,
            age:20
        }
        function fnn(){
            return person;//返回的其实是地址
        }
        var obj4 = fnn();
        var obj5 = fnn();
        var obj6 = fnn();
        obj4.age = 'lklkl'
        console.log(obj4);
        console.log(obj5);
        console.log(obj6);
        // 上述obj返回的是相同的地址,因此三个obj是相互影响的。一个改变,其余三个也跟着改变,并且person也受影响。

       // 组件的数据写法:
        Vue.extend("cpn1",{
            template:`...`,
            data(){
                return {
                    title: "title"
                }
            }
        })
    </script>

1.6 父子组件的通信

1.6.1 父传子:

<body>
    <template id="cpn">
        <div>
            <h1>{{cmessage}}</h1>
            <h2>{{cmovies}}</h2>
            <ul>
                <li v-for="item in cmovies">{{item}}</li>
            </ul>
        </div>
    </template>
    <div id="app">
        <cpn1 :cmovies="movies" :c-message="message"></cpn1>
    </div>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        const cpn1 = Vue.component("cpn1",{
            template: '#cpn',
            // 写法1:数组写法:
            //props: ["cMovies","cmessage"],
            // 写法2:对象写法:类型限制
            // props: {
            //     cmovies: Array,  //[String, Number] 可以写多个类型  Person,也可以自定义类型
            //     cMessage: String
            // },
            // 写法3:对象写法:默认值
            // props: {
            //     cmovies: {
            //         type: Array,
            //         default(){return ["你好啊"]},  //数组和对象,此处必须是函数
            //         required:false
            //     },
            //     cMessage: {
            //         type: String,
            //         default: "你好啊",
            //         required:true   //当为true时,表示该参数必须传入,不传入的话会报错
            //     }
            // },
            // 写法4:自己写验证:还不太懂,初次尝试
            props: {
                cmovies: {
                    validator(value){
                        if(value.length >= 1)   //如果小于1,就会报错。
                            return "success";
                    }
                },
                cMessage: {
                    type: String,
                    default: "你好啊",
                    required:true   //当为true时,表示该参数必须传入,不传入的话会报错
                 }
            },
            data(){
                return {}
            }
        })
        const app = new Vue({
            el: '#app',
            data: {
                movies: ['海贼王','花束般的恋爱','奇迹笨小孩', '你好旧时光'],
                message: "电影系列"
            },
            components: {
                cpn1
            }
        })
    </script>
</body>

一些小的注意点:

(1)template中,必须有一个明确的根元素

(2)v:bind绑定时,不支持驼峰命名法,比如定义的元素叫cMessage,因此使用v:bind绑定时,需要做一个转换::c-message。

1.6.2 子传父

 首先在模板中绑定子组件事件,同时在子组件中的方法中写调用的方法,然后用$emit来向父组件发射点击事件m,同时在父组件模板中写监听事件@m=“f”,然后在父组件methods中写调用的方法f。

<body>
    <template id="cpn">
        <div>
             <!--步骤1:子组件写监听-->
            <button v-for="item in btns" @click="btnclick(item)">{{item.name}}</button>
        </div>
    </template>
    <div id="app">
        <!-- 不写括号时,默认是子组件传过来的参数。但是vue实例中,不写括号时,默认时event参数,注意区分 -->
        <!--步骤3:父组件监听-->
        <cpn @btnclickfather="btnclickfather"></cpn>
    </div>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        const cpn = Vue.component("cpn",{
            template: '#cpn',
            data(){
                return {
                    btns: [
                        {id:'aaa',name: "电器"},
                        {id:'bbb',name: "手机"},
                        {id:'ccc',name: "服饰"},
                        {id:'ddd',name: "化妆品"},
                    ]
                }
            },
            methods: {
                //步骤2:写监听函数
                btnclick(item){
                    this.$emit("btnclickfather", item);
                }
            }
        })
        const app = new Vue({
            el: '#app',
            components: {
                cpn
            },
            methods: {
                //步骤4:父组件写监听函数
                btnclickfather(value) {
                    console.log(value);
                }
            }
        })
    </script>
</body>

案例练习:在子组件中双向绑定从父组件中传递来的数据

<body>
    <!-- 实现功能:子组件中数据cnum1和cnum2是从父组件传递过来的。在子组件中有一个input输入框,想要和数据实现双向绑定。 -->
    <!-- 但是vue中不支持直接和props中数据做双向绑定。因为毕竟数据是父组件的。 -->
    <!-- 因此,在子组件的data中,定义dnum1和dnunm2,使其等于prop中的cnum1和cnum2 -->
    <!-- 因此子组件中,input可以和dnum1和dnum2实现双向绑定。-->
    <!-- 法1:为了实现额外的功能,可以利用v-model的原理,绑定value和事件@input,在@input中做一些别的事 -->
    <!-- 如何使父组件可以绑定呢:在子组件中绑定input事件函数,当数据发生改变时,在该事件中利用emit事件向父组件发射监听事件-->
    <!-- 然后在父组件中,实现该事件的监听,动态改变num1和num2的值。 -->
    <!-- 额外功能,num2=num1*100 -->
    <!-- 法2: 利用wathch,监听数据的变化,然后在watch中,做一些其他的事-->
    <div id="app">
        <cpn :cnum1="num1" :cnum2="num2" @num1change="num1change" @num2change="num2change"></cpn>
    </div>
    <template id="cpn">
        <div>
            <h1>cnum1:{{cnum1}}</h1>
            <h1>dnum1:{{dnum1}}</h1>
            <!-- 法1:使用组件的双向绑定原理来实现:利用v:bind value和@input -->
            <input type="text" :value="dnum1" @input="num1change">
            <input type="text" name="" id="" :value="dnum2" @input="num2change">
            <!-- 法2:使用model和watch实现 -->
            <!-- <input type="text" v-model="dnum1">
            <input type="text" name="" id="" v-model="dnum2"> -->
            <h2>cnum2:{{cnum2}}</h2>
            <h2>dnum2:{{dnum2}}</h2>
        </div>
    </template>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        const cpn = {
            template: '#cpn',
            props: {
                cnum1: {
                    type: Number,
                    default: -1
                },
                cnum2: {
                    type: Number,
                    default: -2
                }
            },
            data(){
                return {
                    dnum1: this.cnum1,
                    dnum2: this.cnum2
                }
            },
            methods: {
                num1change(event){
                    this.dnum1 = parseInt(event.target.value);
                    this.dnum2 = parseInt(this.dnum1)*100;
                    this.$emit('num1change',parseInt(this.dnum1));
                },
                num2change(event){
                    this.dnum2 = parseInt(event.target.value);
                    this.dnum1 = parseInt(this.dnum1) / 100;
                    this.$emit('num2change',parseInt(this.dnum2));
                }
            },
            watch: {
                dnum1(){
                    this.dnum2 = parseInt(this.dnum1)*100;
                    this.$emit('num1change',parseInt(this.dnum1));
                },
                dnum2(){
                    this.$emit('num2change',parseInt(this.dnum2));
                }
            }
        }
        const app = new Vue({
            el: '#app',
            data: {
                num1: 0,
                num2: 1
            },
            components: {
                cpn
            },
            methods: {
                num1change(value){
                    this.num1 = value;
                    this.num2 = value * 100;
                },
                num2change(value){
                    this.num2 = value;
                }
            }
        })
    </script>
</body>

1.6.3 父访问子组件的属性。

(1)使用$children,获得的是一个数组

this.$children[0].name

(2)使用$refs,获得的是一个对象.

要先在子组件中,加一个ref属性。然后就可以通过ref属性获得子组件

//设置ref属性
<cpn ref='aaa'></cpn>
//获取组件
this.$ref.aaa

1.6.4 子组件访问父组件属性

使用$parent访问父组件,只用$root访问vue实例

二、slot插槽

2.1 插槽的基本使用

 (1)slot的基本使用:<slot></slot>

(2)插槽的默认值:<slot>button</slot>

(3)如果有多个值同时放入到组建中进行替换时,一起作为替换元素。

(4)具名插槽:有多个插槽时,要给插槽起个名字(加个属性name),替换时,在元素前加个属性slot='name'。(新的版本使用的是v-slot,自己去了解)

2.2 变量的编译作用域

变量会在自己的作用域中寻找变量

2.3  作用域插槽的使用

父组件替换插槽的标签,但是内容由子组件来提供。

实现内容:在子组件的的插槽中,按照父组件的意愿显示子组件的数据。

<body>
    <div id="app">
        <cpn></cpn>
        <cpn>
            <template slot-scope="slot">
                <div>
                    <ul>
                        <li v-for="item in slot.data">{{item}}</li>
                    </ul>
                    <ul>
                        <li>{{slot.data.join(' - ')}}</li>
                    </ul>
                </div>
            </template>
        </cpn>
    </div>
    <template id="cpn">
        <slot :data="movies">
            <ul>
                <li v-for="item in movies">{{item}}</li>
            </ul>
        </slot>
    </template>
    <script src="../../vue学习/js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            components: {
                "cpn": {
                    template: '#cpn',
                    data(){
                        return {
                            movies: ['美人鱼', '送你一朵小红花', '穿过寒冬拥抱你', '奇迹笨小孩']
                        }
                    }
                }
            }
        })
    </script>

三、模块化:

3.1 什么是模块化

由于多人在编写代码时,会存在变量名重复的问题。因此引入了模块化,一个js文件就是一个模块。

(1)匿名函数解决变量名冲突的问题。但是不能在另一个文件中引用变量

为了不能引用变量的问题,使用模块作为出口。返回一个对象。

(2)CommonJS的导入和导出

 (3)es6的模块化的思想

①使用exports导出:

 

②使用import导入

案例: 练习commonJS和es6的导入和导出

CommonJS导出

const sum = function(a,b) {
    return a + b;
}
const mult = function(a, b) {
    return a*b;
}
module.exports = {
    sum,
    mult
}

es6导出

export const name  = 'why'
export const age = 18

导入:

const {sum, mult} = require('./fn.js');
console.log(sum(3,4));
console.log(mult(3,4));

import {name, age } from './info'
console.log(name)
console.log(age)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值