当前位置: 首页 > Web前端 > HTML5

深入理解Vue.js轻量高效的前端组件化解决方案

时间:2023-04-05 19:37:08 HTML5

Vue.js通过简洁的API提供高效的数据绑定和灵活的组件体系。在复杂的前端生态中,Vue.js有幸受到了一定程度的关注,目前在GitHub上有5000+star。本文将从各个方面对Vue.js进行深入的介绍。Vue.js是我在2014年2月开源的一个前端开发库,通过简洁的API提供高效的数据绑定和灵活的组件体系。在复杂的前端生态中,Vue.js有幸获得了一定程度的关注,目前在GitHub上拥有5000+star。本文将从各个方面对Vue.js进行深入的介绍。开发初衷2013年底,我还在谷歌创意实验室工作。当时在项目中使用了一段时间的Angular。在感叹数据绑定带来的生产力提升的同时,也觉得Angular的API设计过于繁琐,导致学习曲线相当陡峭。出于对Angular数据绑定原理的好奇,我开始“发明轮子”,基于依赖收集实现了一个很粗糙的数据绑定库。这是Vue.js的前身。同时,在实际开发中,我发现用户界面可以用嵌套的组件树来描述,一个组件可以对应MVVM中的ViewModel。所以我决定把我的数据绑定实验改进成一个真正的开源项目,其核心思想是“数据驱动的组件系统”。MVVM数据绑定MVVM的本质是通过数据绑定将View和Model链接起来,让数据的变化自动映射到视图的更新。Vue.js在数据绑定API设计上借鉴了Angular的指令机制:用户可以通过带有特殊前缀的HTML属性实现数据绑定,或者使用普通的花括号模板插值,或者在表单元素上使用双向绑定:{{msg}}插值本质上是一条指令,只是为了模板编写方便。在模板编译过程中,Vue.js会为每个需要动态更新的DOM节点创建一个指令对象。每当一个指令对象观察到的数据发生变化时,它就会对绑定的目标节点执行相应的DOM操作。基于指令的数据绑定使得特定的DOM操作可以合理地封装在指令定义中,业务代码只需要涉及模板和对数据状态的操作,大大提高了应用程序的开发效率和可维护性。与Angular不同,Vue.jsAPI没有模块、控制器、作用域、工厂、服务等复杂概念,一切以“ViewModel实例”为基本单元:{{msg}}

//原始对象是数据vardata={msg:'hello!'}//创建一个ViewModel实例varvm=newVue({//Select目标元素el:'#app',//提供初始数据data:data})渲染结果:hello!
在渲染的同时,Vue.js也完成了动态绑定dataFixed:如果此时改变data.msg的值,DOM会自动更新。是不是很简单易懂?此外,Vue.js还大大简化了自定义指令和过滤器的API。如果你有Angular开发经验,你会很快上手。数据观察的实现Vue.js的数据观察实现原理与Angular有本质区别。了解Angular的读者可能知道,Angular的数据观察使用了脏检查机制。每条指令都会有一个对应的观察数据的对象,称为watcher;范围内会有很多观察者。每当界面需要更新时,Angular会遍历当前作用域内的所有观察者,一一求值,并与之前保存的旧值进行比较。如果评估结果发生变化,则触发相应的更新。这个过程称为摘要循环。Dirtychecking有两个问题:1.任何数据变化都意味着当前作用域内的每一个watcher都需要重新评估,所以当watcher数量很多时,应用的性能必然会受到影响,而且很难重新评估优化。2、当数据发生变化时,框架无法主动检测到变化,需要手动触发digestcycle来触发相应的DOM更新。Angular通过在DOM事件处理程序中自动触发摘要循环来部分规避这个问题,但仍然有很多情况需要用户手动触发。Vue.js使用了一种基于依赖收集的观察机制。原理上和旧的MVVM框架Knockout是一样的。依赖收集的基本原则是:1.将原生数据转化为“可观察对象”。可以访问可观察对象并为其分配值。2.在watcher的求值过程中,每一个被赋值的observable对象都会将当前watcher注册为自己的订阅者,成为当前watcher的依赖。3.当一个依赖的observable对象被赋值时,会通知所有订阅的watcher重新评估并触发相应的更新。4.依赖集合的好处是可以准确主动的跟踪数据变化,不存在上面提到的脏检查的两个问题。然而,传统的依赖收集实现,如Knockout,通常需要将原始数据进行包装来创建可观察对象,并且在取值和赋值时需要使用函数调用的形式。进行数据操作时,书写方式繁琐,不够直观;同时,对于复杂的embeddingObject,对嵌套结构的支持也不理想。Vue.js利用ES5的Object.defineProperty方法,将原生数据对象的属性直接转化为getter和setter,在这两个函数内部实现依赖收集和触发,完美支持嵌套对象结构。对于数组,通过包装数组的变量方法(如push)来监听数组的变化。这使得操作Vue.js数据和操作原生对象几乎没有区别【注意:添加/删除属性,或者修改数组中特定位置的元素时,需要调用特定的函数,比如obj.$add(键,值)触发更新。这是受限于ES5的语言特性。】,数据操作逻辑更清晰流畅,与第三方数据同步方案的集成也更方便。组件系统在一个大型应用中,为了分工、复用和可维护性,我们不可避免地需要将应用抽象成多个相对独立的模块。在比较传统的开发模式中,我们在考虑复用的时候,只会把某个部分做成一个组件;但实际上,应用程序的UI可以看作是完全由组件树组成的:因此,在Vue.js中,组件是设计中的一个核心概念。可以说每一个Vue.js应用都是围绕组件开发的。注册一个Vue.js组件非常简单:Vue.component('my-component',{//templatetemplate:'
{{msg}}{{privateMsg}}
',//接受参数props:{msg:String
},//私有数据,需要在函数中返回,避免多个实例共享一个对象data:function(){return{privateMsg:'component!'}}})注册后在父组件模板中调用一个子组件作为自定义元素:渲染结果:
hellocomponent!
Vue.jscomponent可以是理解为具有预定义行为的ViewModel类。一个组件可以预定义很多选项,但最重要的有以下几种:?Template(模板):模板声明了数据与最终显示给用户的DOM之间的映射关系。?初始数据(data):组件的初始数据状态。对于可重用组件,这通常是私有状态。?接受的外部参数(props):参数用于在组件之间传递和共享数据。参数默认为单向绑定(从上到下),但也可以显式声明为双向绑定。?Methods(方法):数据的修改一般在组件的方法内部进行。用户输入事件可以通过v-on指令绑定到组件方法。?生命周期钩子(lifecyclehooks):一个组件会触发多个生命周期钩子,如created、attached、destroyed等。在这些钩子函数中,我们可以封装一些自定义的逻辑。相对于传统的MVC,可以理解为Controller的逻辑被分散到了这些钩子函数中。?私有资源(assets):在Vue.js中,用户自定义指令、过滤器、组件等统称为资源。由于全局注册的资源容易出现命名冲突,组件可以声明自己的私有资源。私有资源只能被该组件及其子组件调用。此外,同一组件树内的组件也可以通过内置的事件API进行通信。Vue.js提供了完整的定义、复用和嵌套组件的API,允许开发者像搭积木一样使用组件构建整个应用程序的界面。这个想法的可行性在Facebook开源的React中也得到了证实。基于构建工具的单文件组件格式,Vue.js的核心库只提供了基本的API,对于如何组织应用程序的文件结构没有太多限制。但是在构建大型应用时,推荐使用Webpack+vue-loader的组合,让组件化的开发更加高效。Webpack是由TobiasKoppers开发的开源前端模块构建工具。它的基本功能是将多个以模块格式编写的JavaScript文件打包成一个文件,同时支持CommonJS和AMD两种格式。但它的不同之处在于,它提供了强大的加载器API来定义不同文件格式的预处理逻辑,这样我们就可以将CSS、模板,甚至自定义文件格式作为JavaScript模块来使用。Webpack在loader的基础上还可以实现大量的高级功能,比如自动分块打包按需加载、图片资源引用自动定位、根据图片大小判断是否使用base64inline、开发时模块热替换、等等,可以说是目前前端搭建领域最具竞争力的解决方案之一。我基于Webpack的loaderAPI开发了vue-loader插件,这样我们就可以使用这种单一的文件格式(*.vue)来编写Vue组件:div.my-componenth2Hellofrom{{msg}}这样的组件格式将一个组件的模板、样式、逻辑都集成到同一个文件中,方便开发,也便于复用和维护。另外,Vue.js本身就支持组件的异步加载。借助Webpack的分块打包功能,实现组件的异步按需加载变得异常轻松。其他特性Vue.js还有几个值得一提的特性:1.异步批量DOM更新:当大量数据变化时,所有受影响的watcher会被推入一个队列,每个watcher只会推入队列一次。该队列将在进程的下一个“滴答”时异步执行。这种机制可以避免同一个数据的多次变化带来的DOM冗余操作,也可以保证所有的DOM写操作一起执行,避免DOM读写切换可能导致的布局。2.动画系统:Vue.js提供了一个简单但功能强大的动画系统。当一个元素的可见性发生变化时,用户不仅可以方便地定义相应的CSSTransition或Animation效果,还可以使用丰富的JavaScript钩子函数来实现更底层的动画处理。3.可扩展性:除了自定义指令、过滤器和组件之外,Vue.js还提供了灵活的mixin机制,允许用户在多个组件中复用通用功能。与WebComponents的异同熟悉WebComponents的读者看到这里可能会有疑惑:Vue.js组件和WebComponents有什么区别?这里简单分析一下。Web组件是一组低级规范。它没有数据绑定和动画系统等上层功能。因此,更合适的对比对象可能是Polymer。Polymer在API和功能上类似于Vue.js,但其对WebComponents的刚性依赖使其在浏览器支持上存在一定的问题——在不支持WebComponents规范的浏览器中,需要加载庞大的polyfill,而不是只是会对性能有影响,但是有些功能,比如ShadowDOM,polyfills是不能完美支持的。同时,WebComponents规范本身还没有定稿,在一些具体的设计上还存在很多差异。相比之下,Vue.js在支持的浏览器(IE9+)中没有任何依赖。另外,在支持WebComponents的环境下,我们也可以简单的使用WebComponents的底层API,将一个Vue.js组件封装在一个真正的自定义元素中,从而实现Vue.js组件与其他框架的集成。无缝集成。总结在发布之初,Vue.js最初专注于轻量级的嵌入式使用场景。今天,Vue.js仍然适用于此类场景。由于其重量轻(22kbmin+gzip)和高性能,它也非常适合移动场景。更重要的是,精心设计的组件体系和配套的构建工具和插件,使得Vue.js在保留其简单API的同时,完全有能力开发复杂的大型应用。