VUE-组件化

https://cloud.tencent.com/developer/article/1581072

6.组件化

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

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

6.1.全局组件

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

<div id="app">
    <!--使用定义好的全局组件-->
    <counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
    // 定义全局组件,两个参数:1,组件名称。2,组件参数
    Vue.component("counter",{
        template:'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>',
        data(){
            return {
                count:0
            }
        }
    })
    var app = new Vue({
        el:"#app"
    })
</script>
  • 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
  • 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
  • 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
  • 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
  • data必须是一个函数,不再是一个对象。

 

 

 

6.2.组件的复用

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

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

 

 

 

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

data: {
  count: 0
}

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

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

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

6.3.局部注册

一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着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实例中使用

整体完整性代码如下所示:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

    <div id="app">
     

        <counter></counter>
        <counter></counter>
        <counter></counter>
        <counter></counter>
        <counter></counter>

        <button-counter2></button-counter2>
    </div>
     <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- 官网提供的 axios 在线地址 -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>


    <script>
        //1、全局声明注册一个组件
        Vue.component("counter", {
            template: `<button v-on:click="count++">我被点击了 {{count}} 次</button>`,
            data() {
                return {
                    count: 1
                }
            }
        });

        //2、局部声明一个组件
        const buttonCounter = {
            template: `<button v-on:click="count++">我被点击了 {{count}} 次~~~</button>`,
            data() {
                return {
                    count: 1
                }
            }
        };

        new Vue({
            el: "#app",
            data: {
                count: 1
            },
            components: {
                'button-counter2': buttonCounter
            }
        })
    </script>
</body>

</html>

 

6.4.组件通信

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

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

   
     <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- 官网提供的 axios 在线地址 -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>


  <div id="app">
    <h1>打个招呼:</h1>
    <!--使用子组件,同时传递title属性-->
    <introduce title="大家好,我是锋哥"/>
</div>
<script type="text/javascript">
    Vue.component("introduce",{
        // 直接使用props接收到的属性来渲染页面
        template:'<h1>{{title}}</h1>',
        props:['title'] // 通过props来接收一个父组件传递的属性
    })
    var app = new Vue({
        el:"#app"
    })
</script>
</body>

</html>

 

 

 

6.4.1.props(父向子传递)

  1. 父组件使用子组件时,自定义属性(属性名任意,属性值为要传递的数据)
  2. 子组件通过props接收父组件属性

父组件使用子组件,并自定义了title属性:

 

6.4.2.props验证

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

    const myList = {
        template: '\
        <ul>\
            <li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
        </ul>\
        ',
        props: {
            items: {
                type: Array,
                default: [],
                required: true
            }
        }
    };
  • 这个子组件可以对 items 进行迭代,并输出到页面。
  • props:定义需要从父组件中接收的属性
    • items:是要接收的属性名称
      • type:限定父组件传递来的必须是数组
      • default:默认值
      • required:是否必须

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

我们在父组件中使用它:

<div id="app">
    <h2>传智播客已开设如下课程:</h2>
    <!-- 使用子组件的同时,传递属性,这里使用了v-bind,指向了父组件自己的属性lessons -->
    <my-list :items="lessons"/>
</div>
var app = new Vue({
    el:"#app",
    components:{
        myList // 当key和value一样时,可以只写一个
    },
    data:{
        lessons:[
            {id:1, name: 'java'},
            {id:2, name: 'php'},
            {id:3, name: 'ios'},
        ]
    }
})

效果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

    <div id="app">
        <h2>传智播客已开设如下课程:</h2>
        <button-counter2 v-bind:items="lessons"/>
    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 官网提供的 axios 在线地址 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>


    <script>



        //2、局部声明一个组件
        const myList = {
            template: '\
        <ul>\
            <li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
        </ul>\
        ',
            props: {
                items: {
                    type: Array,
                    default: [],
                    required: true
                }
            }
        };

        new Vue({
            el: "#app",
            data: {
                lessons: [
                    { id: 1, name: 'java' },
                    { id: 2, name: 'php' },
                    { id: 3, name: 'ios' },
                ]
            },
            components: {
                'button-counter2': myList
            }
        })
    </script>
</body>

</html>

整个效果如下

 

 

 

 

type类型,可以有:

<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue --> <!-- 这是一个JavaScript表达式而不是一个字符串。--> <blog-post v-bind:likes="42"></blog-post> <!-- 用一个变量进行动态赋值。--> <blog-post v-bind:likes="post.likes"></blog-post>
 

6.4.4.子向父的通信

来看这样的一个案例:

 

6.4.4.子向父的通信

来看这样的一个案例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

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

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 官网提供的 axios 在线地址 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script type="text/javascript">
        Vue.component("counter", {// 子组件,定义了两个按钮,点击数字num会加或减
            template:'\
                <div>\
                    <button @click="num++">加</button>  \
                    <button @click="num--">减</button>  \
                </div>',
            props:['num']// count是从父组件获取的。
        })
        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的方法
        increment(){
            this.num++;
        },
        decrement(){
            this.num--;
        }
    }
})

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

我们可以通过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()函数,用来调用父组件绑定的函数
  • vue提供了一个内置的this.$emit()函数,用来调用父组件绑定的函数

 

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

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

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 官网提供的 axios 在线地址 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script type="text/javascript">
        Vue.component("counter", {
            template: '\
                <div>\
                    <button @click="plus">加</button>  \
                    <button @click="reduce">减</button>  \
                </div>',
            props: ['count'],
            methods: {
                plus() {
                    this.$emit("inc1");
                },
                reduce() {
                    this.$emit("dec1");
                }
            }
        })
        var app = new Vue({
            el: "#app",
            data: {
                num: 0
            },
            methods: { // 父组件中定义操作num的方法
                increment() {
                    this.num++;
                },
                decrement() {
                    this.num--;
                }
            }
        })
    </script>


</body>

</html>

 

posted on 2022-07-05 13:40  luzhouxiaoshuai  阅读(148)  评论(0编辑  收藏  举报

导航