Vue31_组件化4

 在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。

 但是如果每个页面都独自开发,这无疑增加了开发的成本。所以会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。

 可以将通用或者公用的页面模块抽取成vue组件,在vue实例中引用。

 组建使用场景:在项目需要重用某个模块(头部、尾部、新闻。。。)的时候,可以将模块抽取成组件,其它页面中注册组件并引用。

  • 全局注册:在任何vue实例中都可以引用,如:一般网站的头部导航菜单
  • 局部注册:可以在有需要的页面引入组件,如:商城网站首页页面中各种活动模块

一、定义全局组件

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs测试</title>
</head>
<body>
<div id="app">
    <!--使用定义好的全局组件-->
    <counter></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //定义组件
    const counter = {
        template: "<button @click='num++'>你点击了{{num}}次;我记住了</button>",
        data(){
            return {num: 0}
        }
    };
    //全局注册组件:在所有的vue实例中都可以使用组件
   //参数1:组件名称(使用的组件名),参数2:具体的组件(定义的组件)
   Vue.component("counter", counter);    var app = new Vue({        el:"#app"   }); </script> </body> </html>
  • 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
  • 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
  • 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
  • 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
  • data的定义方式比较特殊,必须是一个函数。

 效果:

  

二、组件的复用

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

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

  

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

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

 当定义这个组件时,它的data 并不是像这样直接提供一个对象: 

data: {
  num: 0
}

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

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

 如果 Vue 没有这条规则,点击一个按钮就会影响到其它所有实例!

三、局部注册

 一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。

 因此,对于一些并不频繁使用的组件,会采用局部注册。

 先在外部定义一个对象,结构与创建组件时传递的第二个参数一致:

 然后在Vue中使用它:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs测试</title>
</head>
<body>
<div id="app">
    <!--使用定义好的全局组件-->
    <counter></counter>
    <counter></counter>
    <counter></counter>
</div>
<hr><br>
<!--以下不能识别出counter组件-->
<div>
  <counter></counter>
</div>
<hr> <script src="node_modules/vue/dist/vue.js"></script> <script>    //定义组件    const counter = {        template: "<button @click='num++'>你点击了{{num}}次;我记住了</button>",        data(){            return {num: 0}       }   };    //全局注册组件;参数1:组件名称,参数2:组件    //Vue.component("counter", counter);    var app = new Vue({        el:"#app",        //局部注册组件        components:{            counter: counter       }   }); </script> </body> </html>
  • components就是当前vue对象子组件集合。
    • 其key就是子组件名称
    • 其值就是组件对象的属性
  • 效果与刚才的全局注册是类似的,不同的是,这个counter组件只能在当前的Vue实例中使用  

   

四、组件通信

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

  

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

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

 1、父向子传递props

  比如有一个子组件:

Vue.component("introduce",{
    // 直接使用props接收到的属性来渲染页面
    template:'<h3>{{title}}</h3>',
    props:[title] // 通过props来接收一个父组件传递的属性
})
    • 这个子组件中要使用title属性渲染页面,但是自己并没有title属性
    • 通过props来接收父组件属性,名为title

  父组件使用子组件,同时传递title属性:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs测试</title>
</head>
<body>
<div id="app">
    <!--使用定义好的全局组件-->
    <introduce :title="msg"></introduce>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //定义子组件
    const introduce = {
        //使用props属性title的值渲染模版
        template: "<h2>{{title}}</h2>",
        //定义接收来自父组件的属性
        props:["title"]
   };
    //全局注册组件;参数1:组件名称,参数2:组件
    Vue.component("introduce", introduce);
    var app = new Vue({
        el:"#app",
        data:{
            msg: "父组件中的msg属性的内容"
       }
   });
</script>
</body>
</html>

      

 2、传递复杂数据

  定义一个子组件:

const myList = {
    template:'\
        <ul>\
       <li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
        </ul>\
        ',
    props:{ // 通过props来接收父组件传递来的属性
        items:{// 这里定义items属性
            type:Array,// 要求必须是Array类型
            default:[] // 如果父组件没有传,那么给定默认值是[]
        }
   }
 }
    • 这个子组件可以对 items 进行迭代,并输出到页面。
    • 但是组件中并未定义items属性。
    • 通过props来定义需要从父组件中接收的属性 
      • items:是要接收的属性名称  
        • type:限定父组件传递来的必须是数组,否则报错;type的值可以是数组Array或者对象Object(传递对象的时候使用)
        • default:默认值,
default,如果是对象则需要写成方法的方式返回默认值。如:
default(){
  return {"xxx":"默认值"};
}

  页面内容: 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父向子传递数组数据</title>
</head>
<body>
<div id="app">    <!-- 接受来自父组件的属性值,使用v-bind指向父组件的属性lessons;myList注意使用my-list -->
    <my-list :items="lessons"></my-list>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //定义子组件
    const myList = {
        //可以使用双引号、单引号或者如下使用的 ` 飘号
        template: `
            <ul>
              <li v-for="item in items" :key="item.id">{{item.id}}--{{item.name}}</li>
            </ul>
        `,
        //定义接收来自父组件的属性
        props: {
            //定义模版中使用的属性
            items: {
                //必须为数组类型
                type: Array,
                //默认为空数组
                default: []
            }
        }
    };
    var app = new Vue({
        el: "#app",
        data: {
            msg: "父组件中的msg属性的内容",
            lessons:[
               {"id":1, "name":"Java"},
               {"id":2, "name":"PHP"},
               {"id":3, "name":"前端"}
           ]
       },
        //注册组件
       components: {
            //如果组件key和value一致可以简写如下
            myList
        }
   });
</script>
</body>
</html>  

    

 3、子向父的通信

  来看这样的一个案例: 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs测试</title>
</head>
<body>
<div id="app">
    <h2>num = {{num}}</h2>
    <!--使用定义好的全局组件-->
    <counter :snum="num"></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //定义组件
    const counter = {
        //组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
        template: `
            <div>
                <button @click="snum++">+</button>
                <button @click="snum--">-</button>
            </div>
        `,
        props:["snum"]
   };
    //全局注册组件;参数1:组件名称,参数2:组件
    Vue.component("counter", counter);
    var app = new Vue({
        el:"#app",
        data:{
            num:0
       }
   });
</script>
</body>
</html>
    • 子组件接收父组件的num属性
    • 子组件定义点击按钮,点击后对num进行加或减操作

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

   

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

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

    var app = new Vue({
        el:"#app",
        data:{
            num:0
       },
        methods:{
            //父组件中定义操作num的方法
            numPlus(){
                this.num++;
           },
            numReduce(){
                this.num--;
           }
       }
   });

  但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数,怎么做?

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

<div id="app">
    <h2>num = {{num}}</h2>
    <!--使用定义好的全局组件-->
    <counter @plus="numPlus" @reduce="numReduce" :snum="num"></counter>
</div>

  然后,当子组件中按钮被点击时,调用绑定的函数: 

    //定义组件
    const counter = {
        //组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
        template: `
            <div>
                <button @click="incrNum">+</button>
                <button @click="decrNum">-</button>
            </div>
        `,
        props:["snum"],
        methods: {
            //点击模板中使用的方法
            incrNum(){
                return this.$emit("plus");
           },
            decrNum(){
                return this.$emit("reduce");
           }
       }
   };

  完成页面如下: 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs测试</title>
</head>
<body>
<div id="app">
    <h2>num = {{num}}</h2>
    <!--使用定义好的全局组件-->
    <counter @plus="numPlus" @reduce="numReduce" :snum="num"></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //定义组件
    const counter = {
        //组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
        template: `
            <div>
                <button @click="incrNum">+</button>
                <button @click="decrNum">-</button>
            </div>
        `,
        props:["snum"],
        methods: {
            //点击模板中使用的方法
            incrNum(){
                return this.$emit("plus");
           },
            decrNum(){
                return this.$emit("reduce");
           }
       }
   };
    //全局注册组件;参数1:组件名称,参数2:组件
    Vue.component("counter", counter);
    var app = new Vue({
        el:"#app",
        data:{
            num:0
       },
        methods:{
            //父组件中定义操作num的方法
            numPlus(){
                this.num++;
           },
            numReduce(){
                this.num--;
           }
       }
   });
</script>
</body>
</html>
  • vue提供了一个内置的this.$emit函数,用来调用父组件绑定的函数 

    

   子组件不能直接修改父组件传递参数的引用或者基本类型参数值。

 

posted on 2024-06-21 15:42  花溪月影  阅读(2)  评论(0编辑  收藏  举报