前端组件化开发,已经有多年的历史了,不论是服务端渲染,仍是前端SPA,都有了比较成熟的组件化开发的方案。 随着组件化开发的普及,前端社区中贡献了不少不错的前端组件,都提供开箱即用的方案,使得更好的发挥组件化的优点。 前端团队内,若是有人对前端组件化的理解不够深刻,就不能开发出好的组件,会给项目的维护带来更大的成本。css
这几年,从陷入 “React、Vue 和 Angular 哪一个性能好?”的争论,到如今的各个框架(库)的生态愈来愈完善,讨论性能差距已经没有价值了。而国内的前端娱乐圈,最火的就是 React 和 Vue 了,而 Angular 因为历史缘由,在国内的占有率确实不高。前端
随着前端生态 jade、less、scss、typeScript 和 webpack 等工具的完善,前端的组件化开发效率已经有了很大的提高。vue
特别是像 Ant Design、Element UI、iView 这些优秀的前端组件库的流行,更是将组件化开发发挥到了极致。开发一个前端页面已经变得很是的高效,特别是在作管理系统的页面,脚手架搭建、添加依赖包、配置路由、建立页面、引入组件,很快的就能够构建一个系统。react
若是你须要 SEO,React 和 Vue 的 SSR 框架 Next.js 和 Nuxt.js 更是提供了开箱即用的集成方案,也使开发“同构页面系统“(Google It)变得更加简单。webpack
下面切入正题,深刻探讨下前端组件。web
首先,咱们要搞明白什么是前端组件化开发?vue-router
你应该遇到过,将一个页面的几百行,甚至上千行的代码逻辑写在一个 js 文件中的状况。一般这种代码都很难读下去,更别说要维护起来,添加新功能,移除一些老功能了,由于你不知道改动一个地方,会不会出现意想不到的 bug。vuex
这个时候,你就须要利用组件化开发,拆分功能,封装组件,单独维护。 //在此我向你们推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提高思惟能力 现代化前端框架一般都是实现 MVVM 的方案,数据层(M)和 视图层(V)相互链接,同时变动,使得页面交互保持高度的一致性。redux
若是你熟悉 Java,Python,Go 等后端开发语言,你应该对 package (包)的概念很熟悉,前端的组件化在概念上与后端的 package 很类似,只不过前端的组件涉及到更多的是展现和交互方面的逻辑。固然,前端组件与后端架构的微服务概念相似,能够理解成一个组件就是一个服务组件,只提供某个服务。后端
前端组件化开发,就是将页面的某一部分独立出来,将这一部分的 数据层(M)、视图层(V)和 控制层(C)用黑盒的形式所有封装到一个组件内,暴露出一些开箱即用的函数和属性供外部组件调用。
一个前端组件,包含了 HTML、CSS、JavaScript,包含了组件的模板、样式和交互等内容,基本上涵盖了组件的全部的内容,外部只要按照组件设定的属性、函数及事件处理等进行调用便可,彻底不用考虑组件的内部实现逻辑,对外部来讲,组件是一个彻底的黑盒。
组件能够多层封装,经过调用多个小组件,最后封装成一个大组件,供外部调用。好比:一个 Input 输入框 是一个组件,一个 Select下拉选择框 也是一个组件,能够用 form 在这两个组件上包装一层,就是一个 Form 的组件。
有一些比较经常使用的前端组件,像 vue-router,vuex,react-router,redux,mobx 等,都是基于 Vue 和 React 的组件,它们只专一于 路由、状态存储 的工做,而且把这些事情作好。
只要利用好组件化开发,开发一个页面,就像是搭积木同样,将各个组件拼接到一块儿,最后融合到一块儿,就是一个完整的系统。
说到底,前端的组件化开发,能够很大程度上下降系统各个功能的耦合性,而且提升了功能内部的聚合性。这对前端工程化及下降代码的维护来讲,是有很大的好处的。
耦合性的下降,提升了系统的伸展性,下降了开发的复杂度,提高开发效率,下降开发成本。 //在此我向你们推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提高思惟能力 组件封装的好,加班也少了,bug 也少了,就有更多时间喝喝咖啡、打打农药了。:)
既然前端组件化开发这么好,那要怎么设计一个好的组件呢?
通过屡次实践,总结了一些组件设计时的要点。
要想设计一个好的组件,组件也须要专注。
设计组件要遵循一个原则:一个组件只专一作一件事,且把这件事作好。
一个功能若是能够拆分红多个功能点,那就能够将每一个功能点封装成一个组件,固然也不是组件的颗粒度越小越好,只要将一个组件内的功能和逻辑控制在一个可控的范围内便可。
举个例子。页面上有一个 Table 列表和一个分页控件,就能够将 Table 封装为一个组件,分页控件 封装成一个组件,最后再把 Table组件 和 分页组件 封装成一个组件。Table 组件还能够再拆分红多个 table-column 组件,及展现逻辑等。
一个组件,要明确它的输入和输出分别是什么。
组件除了要展现默认的内容,还须要作一些动态的适配,好比:一个组件内有一段文本,一个图片和一个按钮。那么字体的颜色、图片的规则、按钮的位置、按钮点击事件的处理逻辑等,都是能够作成可配置的。
要作可配置性,最基本的方式是经过属性向组件传递配置的值,而在组件初始化的声明周期内,经过读取属性的值作出对应的显示修改。还有一些方法,经过调用组件暴露出来的函数,向函数传递有效的值;修改全局 CSS样式;向组件传递特定事件,并在组件内监听该事件来执行函数等。
在作可配置性时,为了让组件更加健壮,保证组件接收到的是有效的属性、函数接收到的是有效的参数,须要作一些校验。
一. 属性的值的校验
对属性的值进行校验,通常要考虑如下几个方面。
1.属性值的类型是不是有效的。若是某个属性要求传递一个数组,那么传递过来的值不是数组时,就要抛出异常,并给出对应的提示。
2.属性是不是必填的。有的属性的值,是组件内不可缺乏的时,就要是必填的,在组件初始化时要作是否传递的检查,若是没有传递,则须要抛出异常,并给出相应的提示。若是属性不是必填的,能够设定一个默认值,当属性没有被设置时,就使用默认值。
得益于 React、Vue 内部实现的属性检查,且这些属性检查会在组件初始化阶段默认执行,你能够很容易的给组件设置属性的检查。React 中可使用 React.PropTypes 进行类型检查设置,Vue 中只须要给组件设置 props 便可。 //在此我向你们推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提高思惟能力 在 React 中进行类型检查:
// title.jsx (Title组件) import React, { Component, PropTypes } from 'react'; export default class Title extends Component { constructor(props) { super(props); } static propTypes = { title: PropTypes.string.isRequired } render() { const { title } = this.props; return ( <p>{ title }</p> ) } }
在 Vue 中进行类型检查:
// title.vue (Title组件) <template> <p>{{ title }}</p> </template> <script> export default { props: { title: { type: String, required: true } } } </script>
二. 函数的参数的校验
函数的参数校验,只要按照传统的方法进行校验便可。在函数内部顶部判断参数的值和类型,若是不知足要求,则抛出异常,并给出相应的提示。
判断一个函数的第一个必填,且为 String 格式
// ES6 语法 changeTitle(title) { if (typeof title !== 'string') { throw new Error('必须传入一个 title,才能修改 title。') } // 知足条件,能够进行修改 this.title = title // vue 语法 this.setState({ // react 语法,修改state的值 title }) } //在此我向你们推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提高思惟能力
##生命周期
一个组件,须要明确知道在生命周期的不一样阶段作该作的事。
初始化阶段,读取属性的值,若是须要作数据和逻辑处理的话,在这个阶段进行。
属性值变化时,若是属性发生变化,且须要对变化后的数据进行处理的话,在这个阶段进行处理。
组件销毁阶段,若是组件已经建立了一些可能会对系统产生一些反作用的东西,能够在这个阶段进行清除。好比 timeInterval、timeout 等。
若是组件在渲染的时候报错,须要展现错误信息。React v16 中提供了 componentDidCatch 生命周期函数,Vue v2.5 中提供了 errorCaptured 的钩子函数。
React 中提供了一些生命周期函数:componentWillMount,componentDidMount,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,componentDidUpdate,render,componentWillUnmount,componentDidCatch(React v16)。
Vue 中提供了一些生命周期函数:beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed,errorCapture,errorCaptured(Vue v2.5)。
每一个生命周期的具体用法,请参考官方详细文档。
##事件传递
Vue 中传递事件很简单,只须要在子组件内使用 this.$emit(‘event1’) 便可向外传递一个事件 event1,在父组件调用该子组件时,只须要监听 event1 事件,并给出对应事件处理逻辑便可。
Vue中事件传递
Vue子组件定义 child-component.vue
Vue.component('child-component', { methods: { emitEvent() { this.$emit('event1', 'This is a event.') } }, mounted() { this.emitEvent() } }) //在此我向你们推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提高思惟能力
Vue 父组件调用子组件
<template> <div> <child-component @event1="eventHandler" /> </div> </template> <script> import childComponent from './child-component' export default { components: { childComponent }, methods: { eventHandler(event) { console.log(event) } } } </script>
而在 React 中,官方没有给出组件间的事件传递解决方案,这也是 React 中比较坑的一点。不过,仍是可使用其余方式来实现。
React 中,父组件可使用 props 向子组件传值,而子组件向父组件传值,须要在父组件内定义函数并经过属性传递给子组件,在子组件内经过调用该属性对应的函数,传入参数,传递给父组件内的函数,并在父组件的该函数中作逻辑的处理。
React 子组件定义 child-component.js
class ChildComponent extends React.Components { render() { return ( <button onClick={ () => { this.props.clickHandler('This is a click') } }></button> ) } }
React 父组件调用子组件
import ChildComponent from './child-component' //在此我向你们推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提高思惟能力 class ParentComponent extends React.Components { clickHandler(message) { console.log(message) } render() { return ( <child-component clickHandler={ this.clickHandler.bind(this) } /> ) } }
前端组件化开发的实践,是一个很长的过程,坚持并持续优化,带动系统总体的优化。
结语
感谢您的观看,若有不足之处,欢迎批评指正。