欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 你想学 Vue?那还不来看看什么是 Vue 组件化开发

程序员文章站 2022-03-13 20:23:25
为什么需要组件化开发,许多答案都是复用,而我却认为分而治之或许是更好的答案...



荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发


1.1 组件化简介

1.1.1 概述

  将实现页面某一部分功能的结构、样式和逻辑封装成为一个整体,使其高内聚,低耦合,达到分治与复用的目的。在前端范畴,我们可以用下面的这张图来简单地理解组件化:

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发

  这样看起来,组件化前端开发就像造一辆车,我们将*、发动机、悬挂、车身车门等等各部分组装成一辆车,*、发动机就是组件,车就是最终产品。我们将页头、侧边栏、页脚、内容区等等组件拼装起来组成了我们的页面。

1.1.2 意义

  在谈到组件化的意义时,很多人的看法都是组件化的目的是复用。在某一篇博客看到另一种想法,分而治之,我比较这一种想法,良好地组件化以后的组件,会表现出高内聚低耦合的特征,这会给我们带来好处;组件之间不会相互影响,能有效减少出现问题时定位和解决问题的时间;组件化程度高的页面,具有清晰的页面组织和高可读性的 HTML 结构代码,组件之间的关系一目了然;组件化会强迫开发人员划清各个组件的功能边界,使得开发出的功能更加健壮;所以分而治之才是组件化的意义所在,复用只是它的副作用。同时我们还有很多其他方式都可以做到复用,这并不是组件化的专利。

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发


1.1.3 组件化与模块化

  模块化是一种处理复杂系统分解成为更好的可管理模块的方式。它可以通过在不同组件设定不同的功能,把一个问题分解成多个小的独立、互相作用的组件,来处理复杂、大型的软件。似乎在后端领域,组件化和模块化说的是同一件事。但在我的理解中,前端领域的组件化和模块化是两个概念。先说结论:组件化是从产品功能角度进行分割,模块化是从代码实现角度进行分割,模块化是组件化的前提和基础。
  当我们将一段代码写成一个模块的时候,它有可能是一个函数、一个对象或者其他什么做了一件单一事情的东西,我们将它做成模块是因为它完成了一个单一的功能,并且这个功能很多地方都可能用得到。而当一个组件被从产品中抽象出来,它有时候就只是一个模块,但有时候却有相对复杂的实现,它就可能会有多个模块。我们说一个日期选择器是一个组件,但实现它的时候,我们分成了计算模块、渲染模块、用户输入响应模块等等模块来实现。一个单一产品功能的实现,可能是由多个模块来实现的。这样理解起来,其实可以说组件化是更粗粒度的模块化,它是在产品功能上的模块化。

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发




1.2 组件注册

1.2.1 全局组件

  使用 Vue.component("组件名称", { }) 进行组件注册,全局组件注册后,任何 Vue 实例都可以使用;组件其实也是一个 Vue 实例,因此它在定义时也有:data、methods、生命周期函数等。不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有 el 属性。但是组件渲染需要 html 模板,所以增加了 template 属性,值就是 HTML 模板,模板中只能有一个根标签。data 必须是一个函数,不再是一个对象。因此组件复用时各个组件之间互补影响。

<div id="app">
    <first></first>
</div>
    
<script>
    Vue.component("first",{
    	// 使用 ` 将 HTML 代码包裹起来,可以不用加换行符,使用 " 需要加换行符
        template: `
            <div>
                <h1>China---{{year}}---{{msg}}</h1>
            </div>
        `,
        data() {
            return {
                msg: "大好河山",
                year: 2019
            }
        }
    });

    var app = new Vue({
        el:"#app"
    });
</script>

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发

1.2.3 局部组件

局部组件只有在当前注册它的 Vue 实例中使用

语法

// 注册组件
var component_name = { 
		template:'',
        data(){
            return {}
        }
};

// 引用组件
var app = new Vue({
    el:"#app",
    components:{
        component_name: first // 将定义的对象注册为组件
        component_name 		// 组件名与对象名一致时可以简写
    }
})  

示例

<div id="app">
    <!-- 组件复用 -->
    <first></first>
    <first></first>
    <first></first>
</div>
    
<script>
    var first = {
        template:'<button v-on:click="count++">{{ count }} 次</button>',
        data(){
            return {
                count:0
            }
        }
    }

    var app = new Vue({
        el:"#app",
        components: {
            first
        }
    });
</script>

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发




1.3 单文件组件

1.3.1 概述

  显然,组件注册的两种写法都是在同一个页面完成的,而且没有 CSS 样式等,这样的组件是没有意义的,因此我们需要将“组件”抽离开来。Vue 单文件组件每个单文件组件的后缀名都是 .vue 优化了传统全局组件的一些弊端(模板缺乏高亮、没有 css 等) 每一个 Vue 单文件组件都由 template、script、style 三部分组成。

<template>
   <!-- 组件代码区域 -->
</template>

<style scoped>
   /* 样式代码区域 */
</style>

<script>
   // js 代码区域
</script>

1.3.2 编写一个组件

  由于每个组件都是独立的,要想被引用,需要将其暴露出来,使用 export default { } 导出默认成员,使用 export A 可以按需导出。引用组件的那一方需要使用 import 接收名称 from "模块路径" 将其到导入本页面。使用 import { A, B } from "模块路径" 可以按需导入。

<template>
    <div>
        <span class="font">{{msg}}</span>
        <button @click="fun">组件中的按钮</button>
    </div>
</template>

<style scoped>
    .font {
        font-size: 24px;
        color: #42b983;
    }
</style>

<script>
	// 只能存在一个默认暴露,内容与 Vue 示例类似
    export default {
    	// 组件的名称
        name: 'myComponent',
        // 数据,与 Vue 实例类似
        data() {
            return {
                msg: "Hello World",
            }
        },
        // 方法,与 Vue 实例类似
        methods: {
            fun() {
                alert("我是组件的方法")
            }
        }
    }
	
	// 单独暴露方法
    export function funC() {
        alert("这是组件中 funC() 方法")
    }
</script>

  如上所示,一个简单的组件就写好了,export default {} 中可以有 Vue 实例中除 el 属性的所有类容。如果我们只想要抽取方法,不想要页面,此时我们虽然可以写在 Vue 单文件组件中,但是非常不方便,我们可以将其写到 JS 文件中。

export function funA() {
    alert("这是 js 中 funA() 方法")
}

export function funB() {
    alert("这是 js 中 funB() 方法")
}

  一般我们使用 Vue 进行开发需要抽取的东西就只有组件和方法了,下面我们就可以将 MyComponent.vue 组件和 Fun.js 引入到页面中。页面也不再是熟知的 HTML,而是之前用到的 Vue 单文件组件。因此我们称引用组件的那一方为父组件,被引用的那一方为子组件。下面我们在 views 新建 vue 单文件组件编写我们的代码。Vue 中路径有个新的写法 @/ 表示 src 目录下。

<template>
    <div>
        <my-component></my-component>
        <button @click="onA">按钮A</button>
        <button @click="onB">按钮B</button>
        <button @click="onC">按钮C</button>
    </div>
</template>

<style scoped>
</style>

<script>
	// 引入文件
    import MyComponent from "@/components/MyComponent";
    import {funC} from "../components/MyComponent";
    import {funA, funB} from "@/components/MyFun";

    export default {
    	// 局部注册组件
        components: {
            myComponent: MyComponent,
        },
        data() {
            return {}
        },
        methods: {
            onA() {
                funA();
            },
            onB() {
                funB();
            },
            onC() {
                funC();
            }
        }
    }
</script>

  启动项目,打开 http://localhost:8080/#/test,哎?怎么啥也没有,难道是哪里写错了。发现在 views 文件夹中还有两个文件,我们来访问试试,发现http://localhost:8080/#/home 也没有东西而 http://localhost:8080/#/about 有东西。
荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发
荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发
荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发
荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发
  产生上述问题的原因是 Vue 通过路由来找页面,而我们没有配置路由,所以找不到页面,那为什么自带的 Home.vue 也找不到呢?这是因为该页面配置的路径并不是 /home,查看路由可以发现 Home.vue 配置的路径是 /,所以直接使用 http://localhost:8080/#/ 就可以找到了。路由这里后面后详细说,这里只需要在 /route/index.js 中配好就可以了。参考 About、Home 的配置,我们配置好 Test 的路由,就可以访问到页面了。

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

  const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {
    path: '/test',
    name: 'Test',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '@/views/Test')
  }
]

const router = new VueRouter({
  routes
})

export default router

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发





1.4 组件之间的通信

1.4.1 父向子通信

  父组件发送信息的形式是以属性的形式绑定值到子组件身上。 然后子组件用属性 props 接收,使用 props 向子组件传递数据,首先要在子组件中定义子组件能接受的 props,然后在父组件中子组件的自定义元素上将数据传递给它。

子组件(MyComponent.vue)

<template>
    <div>
        <span class="font">{{msg}}</span>
        <h1 class="font">{{title}}</h1>
        <span class="font">{{text}}</span>
    </div>
</template>

<style scoped>
    .font {
        font-size: 24px;
        color: #42b983;
    }
</style>

<script>
    export default {	// 一个 vue 中只能存在一个
        name: 'myComponent',
        props: ['title', 'text'],
        data() {
            return {
                msg: "Hello World",
            }
        },
        methods: {

        }
    }
</script>

父组件(Test.vue)

<template>
    <div>
        <my-component :title="title" :text="text"></my-component>
    </div>
</template>

<style scoped>
</style>

<script>
    import MyComponent from "@/components/MyComponent";

    export default {
        components: {
            myComponent: MyComponent,
        },
        data() {
            return {
                title: "中国",
                text: "Java 开发者",
            }
        },
        methods: {
        }
    }
</script>

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发


1.4.2 子向父通信

  但是如果子组件需要向父组件传递数据,则不能通过 props,Vue 2 中强调“单项数据流”,所谓“单向数据流”,即是数据的变动只能由外向内传递,而不能由内向外传递。组件只能将从 props 传递进来的数据进行使用,不能对其进行修改。我们唯一能做的,就是在子组件想要修改数据时,发送事件通知父组件修改。父组件通过监听子组件发送的这个事件,来决定需要做什么。 即:子组件不能直接向父组件传值,我们可以通过操作父组件的方法来实现通信。子组件用 $emit() 触发事件,父组件使用 v-on 来监听事件。

子组件(MyComponent.vue)

<template>
    <div>
        子组件的数值:<span class="font">{{num}}</span>
        <br/>
        <button @click="fun"></button>
    </div>
</template>

<style scoped>
    .font {
        font-size: 24px;
        color: #42b983;
    }
</style>

<script>
    export default {
        name: 'myComponent',
        props: ['num'],
        data() {
            return {
            }
        },
        methods: {
            fun() {
                this.$emit("add-num");
            }
        }
    }
</script>

父组件(Test.vue)

<template>
    <div>
        父组件的数值:{{num}}
        <br/>
        <my-component :num="num" @add-num="add"></my-component>
    </div>
</template>

<style scoped>
</style>

<script>
    import MyComponent from "@/components/MyComponent";

    export default {
        components: {
            myComponent: MyComponent,
        },
        data() {
            return {
                num: 10,
            }
        },
        methods: {
            add() {
                this.num++;
            }
        }
    }
</script>

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发


1.4.3 兄弟组件通信

  兄弟组件之间传递数据需要借助于中间桥梁,通过中间桥梁传递数据,中间桥梁一般是空的 Vue 实例。 传递数据方,通过 hub.$emit(方法名,参数) 触发事件。接收数据方,通过在 mounted 钩子中用 hub.$on(方法名,(参数) => {}) 监听事件。销毁事件通过 hub.$off(方法名) 进行销毁,销毁之后无法进行传递数据。

中间桥梁(hub.js)

import Vue from "vue";

export default new Vue();

组件1(MyComponent1.vue)

<template>
    <div>
        子组件1的数值:<span class="font">{{numa}}</span>
        &emsp;&emsp;
        <button @click="fun">2 号组件 + 1</button>
    </div>
</template>

<style scoped>
    .font {
        font-size: 24px;
        color: #42b983;
    }
</style>

<script>
    import hub from "@/components/hub";
    export default {
        name: 'myComponent1',
        data() {
            return {
                numa: 0,
            }
        },
        mounted () {
            // 此处需要使用箭头函数,否侧 this 不代表组件
            hub.$on("myComponent1",(value) => {
                this.numa += value;
            })
        },
        methods: {
            fun() {
                hub.$emit("myComponent2",1);
            }
        },
    }
</script>

组件2(MyComponent2.vue)

<template>
    <div>
        子组件2的数值:<span class="font">{{numb}}</span>
        &emsp;&emsp;
        <button @click="fun">1 号组件 + 2</button>
    </div>
</template>

<style scoped>
    .font {
        font-size: 24px;
        color: #42b983;
    }
</style>

<script>
    import hub from "@/components/hub";

    export default {
        name: 'myComponent2',
        data() {
            return {
                numb: 0,
            }
        },
        mounted() {
            hub.$on("myComponent2",(value) => {
                this.numb += value;
            })
        },
        methods: {
            fun() {
                debugger
                hub.$emit("myComponent1",2);
            }
        },
    }
</script>

父组件(Test.vue)

<template>
    <div>
        <my-component1 ></my-component1>
        <my-component2 ></my-component2>
    </div>
</template>

<style scoped>
</style>

<script>
    import MyComponent1 from "@/components/MyComponent1";
    import MyComponent2 from "@/components/MyComponent2";

    export default {
        components: {
            myComponent1: MyComponent1,
            myComponent2: MyComponent2,
        },
        data() {
            return {
            }
        },
        methods: {
        }
    }
</script>

荐
                                                        你想学 Vue?那还不来看看什么是 Vue 组件化开发

本文地址:https://blog.csdn.net/Demo_Null/article/details/107368175

相关标签: VueJS vue