4. 前端框架—Vue组件化

前端框架—Vue组件化

在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。在vue里,所有的vue实例都是组件。

  1. 组件(Component)是 Vue.js 最强大的功能之一。
  2. 组件可以扩展 HTML 元素,封装可重用的代码。

1. 全局组件

我们通过Vue的component方法来定义一个全局组件。

  • 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等。
  • 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
  • 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板。
  • 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
  • data必须是一个函数,不再是一个对象。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>全局组件</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
          <!--使用定义好的组件-->
          <!--组件的复用,互相独立,不会影响
           主要原因是data是一个方法而不是一个对象
           -->
        <counter></counter>
        <counter></counter>
        <counter></counter>

    </div>
</body>
    <script>
        /**
         * 第一个参数是组件名
         * 第二个参数是组件的参数
         * */
        Vue.component("counter",{
           template:'<button @click="count++">你点击了我{{count}}次,我记住了.</button>',
           data(){
               return{
                   count:0,
               }
           }
        });

        var vue = new Vue({
           el:"#app",
           data:{
               wd:"",
           },
        });
    </script>
</html>

1.2 组件的复用

定义好的组件,可以任意复用多次:

<div id="app">
    <!--使用定义好的全局组件-->
    <counter></counter>
    <counter></counter>
    <counter></counter>
</div>

效果:

在这里插入图片描述

  1. 你会发现每个组件互不干扰,都有自己的count值。怎么实现的呢?

  2. 组件的data属性必须是函数!

  3. 当我们定义这个 <counter> 组件时,它的data 并不是像之前直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

2. 局部组件

一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。

//我们先在外部定义一个对象,结构与创建组件时传递的第二个参数一致:
const counter = {
    template:'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>',
    data(){
        return {
            count:0
        }
    }
};

然后在Vue中使用它:

var app = new Vue({
    el:"#app",
    components:{
        counter:counter // 将定义的对象注册为组件
    }
})
  • components就是当前vue对象子组件集合。
    • 其key就是子组件名称
    • 其值就是组件对象名
  • 效果与刚才的全局注册是类似的,不同的是,这个counter组件只能在当前的Vue实例中使用。

2.1 定义局部组件的另一种写法

<body>
    <div id="app">
         <!--使用组件-->
        <Navbar></Navbar>
    </div>
    
    <script src="node_modules/vue/dist/vue.js"></script>
    
    <script>
        new Vue({
            el: '#app',
            data: {
                
            },
            // 定义局部组件,这里可以定义多个局部组件
            components: {
                //组件的名字
                'Navbar': {
                    //组件的内容
                    template: '<ul><li>首页</li><li>学员管理</li></ul>'
                }
            }
        })
    </script>
</body>

3. 组件通信

通常一个单页应用会以一棵嵌套的组件树的形式来组织:

在这里插入图片描述

  • 页面首先分成了顶部导航、左侧内容区、右侧边栏三部分
  • 左侧内容区又分为上下两个组件
  • 右侧边栏中又包含了3个子组件

各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。

3.1 props(父向子传递)

  1. 父组件使用子组件时,自定义属性(属性名任意,属性值为要传递的数据)。
  2. 子组件通过props接收父组件数据,通过自定义属性的属性名。
  3. 父组件使用子组件,并自定义了title属性:
    1. 父组件app
    2. 子组件introduce
<div id="app">
    <!--使用子组件,同时传递title属性-->
    <introduce title="大家好我是涛哥"/>
</div>

<script src="./node_modules/vue/dist/vue.js"></script>

<script type="text/javascript">
    
    Vue.component("introduce",{
        // 直接使用props接收到的属性来渲染页面
        template:'<h1>{{title}}</h1>',
        props:['title'] // 通过props来接收一个父组件传递的属性
    })
    var app = new Vue({
        el:"#app"
    })
</script>

3.2 props验证

我们定义一个子组件,并接收复杂数据:

  1. props:定义需要从父组件中接收的属性。
    • items:是要接收的属性名称。
      • type:限定父组件传递来的必须是数组。
      • default:默认值。
      • required:是否必须。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件通信-复杂数据类型</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <!--定义子组件introduce,使用 v-bind:class可以简写为:class -->
   <introduce :users="users"></introduce>
</div>
</body>
<script>
    Vue.component("introduce",{
       template:' <ul>\n' +
           '        <li v-for="(user,index) in users" :key="index">\n' +
           '            {{index+1}}--{{user.name}}--{{user.age}}--{{user.gender}}\n' +
           '        </li>\n' +
           '    </ul>',
       data(){
           return {};
       },
        props:{
           /*通过props属性拿到父组件传来的属性*/
           users:{
               type:Array,
               default:[],
               required:true
           }
        }
    });

    var vue = new Vue({
        el: "#app",
        data: {
            users:[
                {name:'柳岩', gender:'女', age: 21},
                {name:'晓哥', gender:'男', age: 18},
                {name:'范冰冰', gender:'女', age: 24},
                {name:'刘亦菲', gender:'女', age: 18},
                {name:'古力娜扎', gender:'女', age: 25}
            ]
        },
        methods: {}
    });
</script>
</html>

当 props 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

3.2.1 type类型

在这里插入图片描述

注意:子组件模板有且只有一个根标签

3.3 动态静态传递

给 props 传入一个静态的值:

<introduce title="大家好,我是锋哥"/>

给 prop 传入一个动态的值: (通过v-bind从数据模型中,获取title的值)

<introduce :title="title"/>

静态传递时,我们传入的值都是字符串类型的,但实际上任何类型的值都可以传给一个 props。

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

</head>
<body>
<div id="app">
    <!-- 即便 `1021` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
	<!-- 这是一个JavaScript表达式而不是一个字符串。-->
	<!--  <blog-post v-bind:like="1021"></blog-post>-->
    
    <!-- 用一个变量进行动态赋值。-->
    <blog-post v-bind:like="post.like + post.dislike"></blog-post>
</div>
<script>
    Vue.component("blog-post",{
        template:"<h1>{{like}}</h1>",
        data(){
            // 必不可少
            return {
            };
        },
        props:['like']
    });
    var vue = new Vue({
        el:"#app",
        data:{
            post:{
                like:1021,
                dislike:"haha"
            }
        }
    })
</script>
</body>
</html>

3.4 子向父的通信:$emit

来看这样的一个案例:

<div id="app">
    <h2>num: {{num}}</h2>
    <!--使用子组件的时候,传递num到子组件中-->
    <counter :num="num"></counter>
</div>

<script src="./node_modules/vue/dist/vue.js"></script>

<script type="text/javascript">
    Vue.component("counter", {// 子组件,定义了两个按钮,点击数字num会加或减
        template:'\
            <div>\
                <button @click="num++">加</button>  \
                <button @click="num--">减</button>  \
            </div>',
        props:['num']// num是从父组件获取的。
    })
    var app = new Vue({
        el:"#app",
        data:{
            num:0
        }
    })
</script>
  • 子组件接收父组件的num属性

  • 子组件定义点击按钮,点击后对num进行加或减操作

  • 我们尝试运行,好像没问题,点击按钮试试:

在这里插入图片描述

  1. 子组件接收到父组件属性后,默认是不允许修改的。怎么办?

  2. 既然只有父组件能修改,那么加和减的操作一定是放在父组件:

var app = new Vue({
    el:"#app",
    data:{
        num:0
    },
    methods:{ // 父组件中定义操作num的方法
        increment(){
            this.num++;
        },
        decrement(){
            this.num--;
        }
    }
})
  1. 但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数,怎么做?

  2. 我们可以通过v-on指令将父组件的函数绑定到子组件上:

<div id="app">
    <h2>num: {{num}}</h2>
    <counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>
  • 在子组件中定义函数,函数的具体实现调用父组件的实现,并在子组件中调用这些函数。当子组件中按钮被点击时,调用绑定的函数:
        Vue.component("counter", {
            template:'\
                <div>\
                    <button @click="plus">加</button>  \
                    <button @click="reduce">减</button>  \
                </div>',
            props:['count'],
            methods:{
                plus(){
                    this.$emit("inc");
                },
                reduce(){
                    this.$emit("dec");
                }
            }
        })
  • vue提供了一个内置的this.$emit()函数,用来调用父组件绑定的函数

  • 效果:

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件通信--子到父</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h2>num:{{num}}</h2>
    <!--使用子组件的时候,传递num到子组件中-->
    <counter :num="num" @increment="increment" @decrement="decrement"></counter>
</div>
</body>
<script>
    Vue.component("counter",{
        template:'    <div>\n' +
            '        <button @click="plus">加</button>\n' +
            '        <button @click="reduce">减</button>\n' +
            '    </div>',
        data(){
            return {};
        },
        methods:{
            plus(){
                this.$emit("increment");
            },
            reduce(){
                this.$emit("decrement");
            }
        },
        //是从父组件中获取的
        props:['num']
    });

    var vue = new Vue({
        el: "#app",
        data: {
            num:0
        },
        methods: {
            increment(){
                this.num++;
            },
            decrement(){
                this.num--;
            }
        }
    });
</script>
</html>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值