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 相关文章推荐
JavaScript打印iframe内容示例代码
Aug 20 Javascript
open 动态修改img的onclick事件示例代码
Nov 13 Javascript
JavaScript中的setMilliseconds()方法使用详解
Jun 11 Javascript
jQuery实现可高亮显示的二级CSS菜单效果
Sep 01 Javascript
this,this,再次讨论javascript中的this,超全面(经典)
Jan 05 Javascript
怎么引入(调用)一个JS文件
May 26 Javascript
用js读写cookie的简单方法(推荐)
Aug 08 Javascript
js模糊查询实例分享
Dec 26 Javascript
微信小程序 radio单选框组件详解及实例代码
Jan 10 Javascript
javaScript和jQuery自动加载简单代码实现方法
Nov 24 jQuery
vue中实现左右联动的效果
Jun 22 Javascript
微信小程序 获取手机号 JavaScript解密示例代码详解
May 14 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 数组遍历方法大全(foreach,list,each)
2010/06/30 PHP
PHP中usort在值相同时改变原始位置问题的解决方法
2011/11/27 PHP
PHP flush()与ob_flush()的区别详解
2013/06/03 PHP
解析php利用正则表达式解决采集内容排版的问题
2013/06/20 PHP
dvwa+xampp搭建显示乱码的问题及解决方案
2015/08/23 PHP
javascript 放大镜效果js组件 qsoft.PopBigImage.v0.35 加入了chrome支持
2009/04/07 Javascript
html数组字符串拼接的最快方法
2009/09/16 Javascript
EasyUI中实现form表单提交的示例分享
2015/03/01 Javascript
JavaScript基本语法讲解
2015/06/03 Javascript
基于js实现微信发送好友如何分享到朋友圈、微博
2015/11/30 Javascript
解析Javascript单例模式概念与实例
2016/12/05 Javascript
详解使用nvm管理多版本node的方法
2017/08/30 Javascript
微信小程序自定义组件实现tabs选项卡功能
2018/07/14 Javascript
element-ui上传一张图片后隐藏上传按钮功能
2019/05/22 Javascript
wepy--用vantUI 实现上弹列表并选择相应的值操作
2020/11/03 Javascript
python基础教程之python消息摘要算法使用示例
2014/02/10 Python
Python上传package到Pypi(代码简单)
2016/02/06 Python
Python文本特征抽取与向量化算法学习
2017/12/22 Python
Python 普通最小二乘法(OLS)进行多项式拟合的方法
2018/12/29 Python
PyQt5实现暗黑风格的计时器
2019/07/29 Python
检测tensorflow是否使用gpu进行计算的方式
2020/02/03 Python
Pyecharts地图显示不完成问题解决方案
2020/05/11 Python
Python 解决相对路径问题:"No such file or directory"
2020/06/05 Python
全球知名的婚恋交友网站:Match.com
2017/01/05 全球购物
JBL美国官方商店:扬声器、耳机等
2019/12/01 全球购物
杭州联环马网络笔试题面试题
2013/08/04 面试题
数控加工专业毕业生自荐信
2013/09/27 职场文书
优秀毕业生求职信范文
2014/01/02 职场文书
心理健康心得体会
2014/01/02 职场文书
简易版租房协议书范本
2014/10/13 职场文书
汽车转让协议书
2015/01/29 职场文书
2015年调度员工作总结
2015/04/30 职场文书
幼儿教师师德培训心得体会
2016/01/09 职场文书
PyQt5爬取12306车票信息程序的实现
2021/05/14 Python
PHP中strval()函数实例用法
2021/06/07 PHP
Java 多线程并发FutureTask
2022/06/28 Java/Android