加班加点连续一个多月,总算是快把一个开始时心里完全没有底的项目收工了。新项目基于旧系统开发,在保留原有老架构jade + knockout + jquery + gulp的同时,新页面完全采用vue + vue-route + vuex + webpack。两套框架都没正儿八经做过,jade和knockout第一次接触,vue和webpack以前只是写写demo,vue-route和vuex也没用过,又恰巧另外一个前端同事离职,所以当时决定新页面完全用新框架的时候,还是鼓了不少勇气的。
趁热打铁,梳理下这段时间学习和使用vue以后,给自己最大的收获:开发思维转变。
事件驱动
先说说以前前端发开的思维方式。
比如说,要实现ajax方式提交一个表单,那么就要监听一个提交按钮的事件。在触发点击事件后,从页面的DOM元素中一个个收集数据,然后在做数据处理,最后调用接口提交。收集数据是个特别繁琐的事情,各种选择器各种变量。
再比如说,在固定区域内,事件触发展示不同组件,那就要监听事件传递来的条件,来判断哪个组件要display:block,其他组件要display:none,甚至可能大量用到DOM操作,各种appendChild,insertBefore,removeChild。
其他类似根据条件addClass, removeClass调整组件样式;通过getAttribute、dataset等等获取DOM绑定的数据;通过setAttribute、innerHtml等等直接修改DOM的操作也是数不胜数。
个人感觉这种方式的好处就是直观,页面做任何改变全交由js处理,先干什么后干什么都由自己一手代码操控全场,有种掌控一切的感觉。
但是,大型项目里,代码编写的成本和维护的成本都很高。对代码规范的要求高,对抽象的要求高,尤其是抽象程度,全赖个人编程水平,抽象程度低的话,冗长的代码根本不想多看一眼。这大概就是为啥项目里有一个代码写的烂的人在,代码就会越来越烂的原因。
数据驱动
网上已经很多关于数据驱动的文章了,但光看文章,体会远不如自己实践一遍来的印象深刻。在基于vue开发的新页面中,用js直接处理DOM的代码极少(刚开始上手的时候一般都会不太习惯数据驱动编程,写些操作DOM的代码,后来慢慢会替换掉的)。
数据驱动的前提是事先将DOM与数据绑定,像vue这种就是依赖defineProperties, setter, getter将一个vue对象与一个DOM节点模板关联起来,这个DOM节点里所有子节点、所有节点属性,全都可以和vue对象中的data绑定,做到data中某个属性值变化时,相应DOM节点中对应的某个属性就变化。
如此一来,关注点完全分离。分开设计页面DOM结构和数据结构,然后将DOM与数据结构做关联,之后所有的事件触发的都只是数据的变化,DOM会自动根据数据的变化做相应改变。
前面例子中的表单提交,交由v-model属性将input双向绑定到data中去,在触发表单提交时,直接把data传给后端完事儿了;不同组件之间的切换,vue官网就有个动态组件的例子:
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home,
posts,
archive
}
})
<component v-bind:is="currentView">
<!-- 组件在 vm.currentview 变化时改变! -->
</component>
只需关心currentView的指向便可轻易切换组件,或者通过v-if或v-show来实现根据value的真假值来控制相应DOM节点是否显示;其他DOM属性变化都可以用v-bind与data做绑定,比如v-bind:class=”dataClass”,只要dataClass一变化,DOM的class就会改变。
vue的这些动态绑定的功能很容易上手,相对上手比较慢的是vue提供的组件功能,涉及父子组件通讯,以及用vuex实现全局state管理等等,先掌握数据驱动的思想,再接触这些会容易得多。
当然即使vue也会遇到不得不直接操作DOM的情况。记录一个目前遇到实际问题:
有一个table组件,展示从后台传过来的数据。数据中会有”http://xxx.xxx/xxx.png”需要渲染成<img src=”” target=”_blank”>http://xxx.xxx/xxx.png”>标签。一般表格组件会提供formatter方法来处理单元格特殊处理逻辑,这里我就不得不直接生成DOM节点
formatter: function({picUrl}){
this.$createElement('img', {
attrs: {
src: picUrl
},
}
}