Vue全家桶实践项目总结(推荐)


Posted in Javascript onNovember 04, 2017

从前端的角度看,Vue可以说是目前最理想的前端MVVM框架,一切为界面服务,上手难度低,本文就将记录使用Vue全家桶(Vue+Vue-router+Vuex)重构一个jQuery+template项目的过程,以及期间的收获。

入门

Vue的官方文档就是学习Vue的最佳教程,没有之一,可能因为框架作者是设计出身,没有后端背景,因此各种抽象概念在Vue里都得以用最容易理解的方式被恰到好处的阐述,这里只简单介绍Vue、Vue-router、Vuex的概念,要全面学习建议去官方文档。

Vue

Vue的核心功能是双向绑定,可以自动实现界面驱动数据变化和数据驱动界面变化,这能极大降低前端富交互应用的开发成本。同类框架不止Vue一个,但Vue的实现借助ES5原生特性,在性能方面有一定优势。

Vue-router

Vue-router是官方路由,用来组织url和组件间的映射关系,并自动将url的变化响应到组件中去,使开发者只需将关注点放在组件开发上,路由帮你解决相关的琐碎问题。

Vuex

Vuex提供一种集中式的数据管理模式,用以应对数据流向复杂的情况,比如多个组件共用数据却各自为营,可能导致该同步的数据没有同步,或者由于js中Object对象的钩子在内存里指向同一实例,导致一旦操作原数据就会污染到其他组件,这时就需要一套更有条理的数据操作模式,那就是Vuex。

技术选型

跟jQuery对比

在了解了Vue的基本概念之后,肯定会不自觉的拿他们跟jQuery技术栈做对比,想知道这些东西对我的业务是否真的有必要。

首先MVVM解决的问题能不能用jQuery解决?答案是肯定的,还记得最初做表单提交时用jQuery从一个一个的input里取值吗?这就是界面驱动数据;如果做过任何异步交互功能的话,应该都有过用jQuery将Ajax数据填充到界面中各个元素里的经验,这就是数据驱动界面。虽然能做,但有点繁琐,即便用上表单验证插件和前端模板引擎,也仍然需要在各个运行节点手动调用验证方法和渲染方法,做个网站页面还好,可当需求复杂到一定程度时,这将是很大的负担。

然后是路由,路由的本质是通过操作url实现界面切换和界面保持,单页面应用必备,这个其实跟技术栈没关系,只要产生了路由需求,即使是基于jQuery的项目也不过是再造了一个路由而已,只不过jQuery时代很少做单页面应用。

Vuex完全是基于双向绑定延伸出来的东西,相当于在数据和组件之间加了一个经纪人,组件不能直接操作数据,只能像经纪人提交操作需求,由经纪人去实施操作,以此解决人多手杂造成的各种不可预料的问题,并且数据被从应用中挪了出去,专门建立了一个Store,这样就杜绝了组件之间数据污染的问题。jQuery应该说不太会有这个需求,因为jQuery完全是手动操作数据,根本没有意料之外的情况。

适用场景

经过跟jQuery的对比之后,Vue的适用场景就很明显了,从开发角度讲交互越复杂的项目越适合,单页面最适合,内容类网站最不适合,如果网站中个别页面有需求也可以局部使用,例如购物车页面。

当然,这一切都要建立在不兼容IE8的前提下,对于这一点我有点疑惑,因为见到有些2C的站点都在使用Vue,这些前端er是怎么把老板忽悠瘸的?

项目分析

项目背景

这次重构的项目是为上一家公司开发的前端组件管理系统,选择重构这个项目是因为对需求比较熟悉,这是一个典型的单页面应用,而且复杂度适中,比较适合用作上手练习。

项目背景是外包类建站公司里,设计环节沉淀了大量可复用组件,设计师往往只需要微调组件就拼凑出页面,交付给前端,理论上这些组件在前端也可以复用,但实际上前端每次都要重新实现整个页面,浪费很多人力。

功能需求

这个项目的思路是,将所有组件开发出来,统一录入到一个平台上管理,设计师可以到平台上挑选组件,并实时预览和调整组件,整个过程所见即所得,平台会将调整结果生成一串代码,只要将代码交给前端,就可以用这串代码在平台上重现设计师修改后的组件,并能一键复制组件的html/css/js代码,快速应用到项目中去,使组件部分的前端开发成本接近于零。平台需要实现以下几个功能:

  1. 管理组件,支持分类、搜索、排序
  2. 展示组件,支持组件在线预览/编辑
  3. 组件交接,支持生成组件代码和基于代码重现组件
  4. 使用统计,支持统计组件的使用情况,便于进一步优化组件

功能分析

第一版是用jQuery+template实现的,这个技术栈太灵活了,优点是什么需求都好做,缺点是怎么做都行,不利于理清思路,往往伴随着边做边改。

组件被统一放在一个widgets/文件夹里,被称作组件库,因为是纯前端项目没有文件操作能力,因此组件的读取依赖一个静态json文件,这个文件充当组件库的目录,其中包括组件分类、标签、名称、日期等所有信息,数据结构大概是这样:

[{
 "title": "导航类",
 "list": [{
 "widget": "bread-1",
 "title": "图标面包屑",
 "tag": "面包屑/图标",
 "author": "UI",
 "date": "2015-06-04"
 }, 
 ...]
},
...]

组件在组件库中以栏目/编号二级文件夹的形式存放,同时约定用存放目录作为组件代号,例如组件bread-1意味着该组件存放地址是widgets/bread/1/文件夹。

当然组件的内部文件结构也必须约定下来,如下:

widgets
  |-bread
    |-1
      |-album.jpg   //缩略图
      |-config.json  //配置文件
      |-script.js   //脚本模板
      |-style.css   //样式模板
      `-temp.htm   //界面模板

有了这些约定,程序就可以通过目录文件得到所有组件的信息,组件的获取、展示、检索也就可以实现了。

组件里最关键的是config.json文件,这里面包含该组件的可配置项及其默认值,平台在展示组件时会读取这个配置文件,根据配置信息生成配置面板,这里面可以定义组件界面、样式、脚本中的所有变量,配置文件大概长这样:

{
 "cssConfig": {
 "fontSize": {
  "name": "字号",
  "value": "12px",
  "type": "text"
 },
 ...
 },
 "jsConfig": {
    ...
 },
 "showConfig": {
 "viewWidth": {
  "name": "栅格宽度",
  "value": 12,
  "type": "number"
 },
 ...
 }
}

配置文件里的cssConfig、showConfig、jsConfig三个分支,就是组件中所有可以修改的变量集合,想将这些变量应用到组件上,需要借助前端模板引擎,所以组件的三大件在开发的时候是用模板语法写的,经过模板引擎的解析,就能得到配置后的实际html/css/js内容,例如样式模板大概是这样的:

.widget-bread-1 {
  font-size: ${fontSize.value}; 
  color: ${textColor.value}; 
}
.widget-bread-1 a { 
  color: ${textColor.value};
}
.widget-bread-1 a:hover{
  color:${hoverColor.value};
}
.widget-bread-1 .ion { 
  font-size: ${iconSize.value}; 
  margin: 0 ${iconMargin.value};
}

在得到组件实际代码后,只要将结果插入页面中并适时更新就行了,其中HTML和css可以直接替换文本内容,js因为是模块化引入的,只替换模块内容不会重载模块,必须将整个模块重命名后进行整体替换,因此js模块的名称是随机的。

这里会有一个问题,有的组件需要在页面里多次使用,那么这个组件的js选择器就会发生冲突,这个问题的解决正好可以借助js模块的那个随机名称,我们约定在组件开发中id作为保留变量,由平台自动赋值一个随机字符串,这个字符串在组件实例内部相同,多次调用则不同,这样只要将${id}作为组件父节点的id或class,就解决了选择器冲突问题,同时也可以作为组件的css命名空间,使可能发生的css命名冲突也解决于无形。

以上是项目核心功能。

此外还用localStorage作为存储方式实现了单机版的数据统计,可以收集当前浏览器的组件使用记录,以及每个使用时的配置情况,这里主要是对本地存储的操作,但是项目自身的开发也用到了前端模板,加上组件的模板,都会在第一次加载后用localStorage缓存起来,这些内容的缓存策略不同,用户数据应该是永久存储,项目模板应该可以手动更新,组件模板需要视情况而定,存储的内容多了就需要清理,清理的时候一条一条的去删除就不现实了,全部删除可能误伤其他应用的存储,这里的做法是将localStorage操作封装,存储方法会在在key前加上一个特殊前缀,删除时只要遍历本地存储的key并且判断是否匹配前缀就知道是否是应用内的存储了,对应的取值方法也要逆向的先给key加上前缀再去取值。

另外localStorage是只支持字符串存取的,为了方便我们存取对象类型,封装方法还支持自动转换,但这个转换还不能是盲目的遇到对象就转字符,取值的时候匹配到可以转对象就自动转了对象,因为有时候用户可能真的就存了一个对象字符串进去,取出的时候也希望原样拿回来,要解决这个问题需要做一个小hack,当存储方法检测到值为对象时,会转成字符串然后在前面拼上一个标识字符串,取值方法只有在检测到这个标识后才会将后面的字符串还原成对象,这种方式虽然可以满足需求,但不是很保险,因为这个前缀是固定的,理论上总是有可能遇到中奖的,不知道这个问题还有没有其他的更优解。

项目的主要功能点就是这些。

重构

一次重构

第一次重构只用了Vue,重构过程中首先体会到的是各种便捷,本来要调用模板引擎做的事框架顺便就做了,本来要在js里绑定事件现在模板里直接可以绑定,还有其他各种语法糖。

当然,最重要的还是双向绑定,基于双向绑定可以让界面和数据自动的关联起来,让人感觉程序具有了一定的自主能动性,但为了让这种自主性正常运转,开发者必须事先规划好每一步路,这相对jQuery来说就会显得不那么自由。拿搬砖头举例,jQuery好比一个特别灵活的起重机,可以举重若轻,可以花式搬砖;Vue则像一个万能遥控器,你告诉他你要把砖头从某地搬到某地,期间发生什么情况要如何处理,按动按钮就可以自动搬砖了。

两种方式各有优劣,起重机开的好可以很灵活,路上遇到坑容易躲避,缺点是你要一趟一趟的开它;按钮可以一次编程自动运行,缺点是你必须事先把路上的坑实地考察好,把别的车全部调度好,所有的情况说清楚,否则就会翻车或撞车,从jQuery转到Vue一定会感觉到这种束缚感,逼的你必须”谋而后动”,不能先上车再说。

重构期间很大一部分工作就是建立Vue实例,将散布在js各个角落的数据收集到data中去,将操作数据的过程一点一点的集中到methods中去,将数据的筛选过程集中到computed中去,这整个过程可以清晰的回顾每一个实现细节,反思每一个实现方式是不是合理,其实也就是将原来开起重机的过程归纳成一个一个的遥控器按钮,当全部归纳完成后,Vue示例也就变成了最终我们的项目,能够自动运行。

经过重构,依托Vue的各种功能使逻辑部分的代码量减少了,除此之外对项目本身来说并没有什么改进,因为没有路由所以刷新页面状态丢失问题仍然存在;因为没有使用Vuex还遇到一个数据污染的坑,只能用深拷贝解决;并且基于组件的开发模式,让代码组织更零碎了,这些问题都需要二次重构。

二次重构

二次重构目标是完善路由、Vuex、代码组织、野狗云后端。

虽然有了第一次重构的经验,但二次重构一开始还是有点茫然,路由和Vuex应该先上哪一个?想了想,路由做的事是”拆”,Vuex做的事是”改”,感觉改完再拆的工作量会小一点,所以先上Vuex。

Vuex的概念凭空理解有点抽象,一旦用上却觉得的得心应手,而且这个东西不像路由,几乎不需要区分场景都可以用,引入Vuex后数据污染的问题自然就解决了,而且Vuex带来的 action => mutation => store 流程一旦接受了真的会让事情变简单,引入Vuex的过程基本就是将data转移到store,将数据操作分散到actions,getters,mutations中去,同时很多同步数据操作都不需要了,从而使代码量又减少了一些。

之后开始引入路由,一开始拿不准应该怎么划分视图,大的视图肯定是登录、注册、主界面,问题是主界面需不需要再细分,理论上可以分的很细,但结合应用实际使用场景发现,界面的切换相对频繁,组件频繁载入和卸载的开销会很大,而且将耦合紧密的组件拆到不同的视图,需要记录很多状态信息,有点得不偿失,最终作罢,没有将主视图继续分下去。考虑到三个视图的访问重叠性不高,自然就需要将组件做成异步加载,只在访问到的时候才加载组件,Vue自身支持异步组件,所以这件事变得非常简单,只要能返回一个Promise,你可以使用任意方式获取组件。

接下来要接入野狗云,实现真正的用户管理和数据统计,野狗云可以提供用户鉴权和数据存储等一系列功能,通过它只需要用js就可以开发一个完整的WEB应用。这样之前所有对localStorage的操作都要改成对野狗云的操作,数据到了云端也变得更可靠了。

至此二次重构就完成了,业务代码总体感觉减少了很多,但总代码量估计没少多少,毕竟还增加了三个框架文件,不过经过重重拆分,文件数量从当初的三两个js变成了十来个js,模块化方面用的seajs而不是webpack,因为个人对 webpack仍然持观望态度,目前还感觉不到用它的必要性,关键是基于webpack开发的代码会夹杂很多私货,让你的代码变得不原生,离了他就运行不起来,这个我不太能接受,而且在多页面场景seajs配合本地缓存比webpack更有优势,当然了,他们的的区别就跟jQuery和Vue的区别一样,本质不是一个东西,关键在于使用场景,适合的就是最好的。

后记

经过两次重构的实践和踩坑,对Vue框架有了更深刻的认识,Vue想要用的灵活自如,对开发者的项目架构能力有一个最低要求,如果要将Vue引入底层,对基础设施建设者的规划能力也有一个最低要求,这些都是jQuery技术栈所不存在的,使用Vue的过程也是接受这些约束的过程,他们能引导开发者建立自己的规则体系,这是好事也是大势所趋,毕竟真正的自由只存在于规则中。

本文的完整代码见Github。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
非常不错的功能强大代码简单的管理菜单美化版
Jul 09 Javascript
jQuery使用动态渲染表单功能完成ajax文件下载
Jan 15 Javascript
使用jQuery解决IE与FireFox下createElement方法的差异
Nov 14 Javascript
Egret引擎开发指南之视觉编程
Sep 03 Javascript
node.js中的emitter.emit方法使用说明
Dec 10 Javascript
基于bootstrap实现bootstrap中文网巨幕效果
May 02 Javascript
Node.js连接mongodb实例代码
Jun 06 Javascript
Angular通过angular-cli来搭建web前端项目的方法
Jul 27 Javascript
js HTML5 canvas绘制图片的方法
Sep 08 Javascript
vue 1.0 结合animate.css定义动画效果
Jul 11 Javascript
vue实现点击隐藏与显示实例分享
Feb 13 Javascript
VUE动态生成word的实现
Jul 26 Javascript
浅谈Vuex的状态管理(全家桶)
Nov 04 #Javascript
简述Angular 5 快速入门
Nov 04 #Javascript
vue2.0在table中实现全选和反选的示例代码
Nov 04 #Javascript
vue中使用localstorage来存储页面信息
Nov 04 #Javascript
javascript+jQuery实现360开机时间显示效果
Nov 03 #jQuery
原生JavaScrpit中异步请求Ajax实现方法
Nov 03 #Javascript
使用 Javascript 实现浏览器推送提醒功能的示例
Nov 03 #Javascript
You might like
php生成随机数或者字符串的代码
2008/09/05 PHP
php中记录用户访问过的产品,在cookie记录产品id,id取得产品信息
2011/05/04 PHP
给初学者的30条PHP最佳实践(荒野无灯)
2011/08/02 PHP
MacOS 安装 PHP的图片裁剪扩展Tclip
2015/03/25 PHP
CodeIgniter框架常见用法工作总结
2017/03/16 PHP
php如何修改SESSION的生存存储时间的实例代码
2017/07/05 PHP
详解PHP如何更好的利用PHPstorm的自动提示
2017/08/18 PHP
PHP实现的多维数组排序算法分析
2018/02/10 PHP
PHP实现的简单留言板功能示例【基于thinkPHP框架】
2018/12/07 PHP
[对联广告] JS脚本类
2006/08/27 Javascript
常用js字符串判断方法整理
2013/10/18 Javascript
js数值和和字符串进行转换时可以对不同进制进行操作
2014/03/05 Javascript
jfinal与bootstrap的登录跳转实战演习
2015/09/22 Javascript
老生常谈JQuery data方法的使用
2016/09/09 Javascript
jQuery 检查某个元素在页面上是否存在实例代码
2016/10/27 Javascript
有关JS中的0,null,undefined,[],{},'''''''',false之间的关系
2017/02/14 Javascript
JS使用面向对象技术实现的tab选项卡效果示例
2017/02/28 Javascript
vue父组件通过props如何向子组件传递方法详解
2017/08/16 Javascript
一个简易时钟效果js实现代码
2020/03/25 Javascript
Vue父子组建的简单通信之控制开关Switch的实现
2018/06/04 Javascript
React 无状态组件(Stateless Component) 与高阶组件
2018/08/14 Javascript
微信小程序实现基于三元运算验证手机号/姓名功能示例
2019/01/19 Javascript
详解Element 指令clickoutside源码分析
2019/02/15 Javascript
vue中v-show和v-if的异同及v-show用法
2019/06/06 Javascript
bootstrap datepicker的基本使用教程
2019/07/09 Javascript
微信小程序实现图片压缩
2019/12/03 Javascript
Python 的 Socket 编程
2015/03/24 Python
pandas按若干个列的组合条件筛选数据的方法
2018/04/11 Python
Python实现的插入排序,冒泡排序,快速排序,选择排序算法示例
2019/05/04 Python
python3.x+pyqt5实现主窗口状态栏里(嵌入)显示进度条功能
2019/07/04 Python
使用tensorboard可视化loss和acc的实例
2020/01/21 Python
给 TensorFlow 变量进行赋值的方式
2020/02/10 Python
迟到检讨书400字
2014/01/13 职场文书
广告语设计及教案
2014/03/21 职场文书
react如何快速设置文件路径别名
2021/04/28 Javascript
SpringCloud之@FeignClient()注解的使用方式
2021/09/25 Java/Android