公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子。后来接到一个基于模板的活动设计系统的需求,便有了下面的内容。借油开车。
组件化
需求一到,接就是怎么实现,技术选型自然成为了第一个问题。鉴于目前web前端mvvm框架以及组件化开发方式的流行,决定技术栈采用:vue + es6 + 组件化。
这里首先简单说下web前端组件化开发方式的历程:
最早的组件化结构,代码结构可能如下:
- lib/components/calendar |- calendar.css |- calendar.js |- calendar.html
将同功能的组件文件放到同一目录下,结构清晰、职责明确,视图、样式、脚本的关系显著,也易于单元测试,是独立展示和交互的最小单元。
后来:
在之前基础上对组件进行了生命周期的加工(初始化、获取资源、渲染、更新、销毁等),理顺了组件的各个阶段,有助于对组件实现(从初始化到销毁)的理解。并且借助于组件各个阶段的钩子可以对组件有更好的利用和扩展。对外暴露接口,数据绑定或者说数据仓库的加入,各种xMD模块加载器的出现,也让这种这种开发方式上升了一个层级。ExtJs、YUI等都是这方面的专家。
再后来:
有了之前发展,进步是很大的,但依然不够。组件的可复用性(基础样式,基础逻辑,基础属性、可复用的稳定业务逻辑等)、组件间通信、全局状态管理、甚至是能否有更好的代码组织方式等依然是问题。Angular、React、Polymer、Vue等mvvm框架和webpack、browserify等构建、预编译工具的出现正试图解决这些问题。
ES6
在正式开始vue之前,因为本项目用到了es6,那么就谈谈大家都关注的EcmaScript6。多余的就不说了,es6经历了多年的苦,终于在2015年下半年定稿,正式名称:EcmaScript2015。每个刚开始接触es6的人应该都有这么一个问题,es6的出现到底是为了什么,或者说它解决了什么。老版本es4/5虽然坑多,就像Brendan Eich评价js一样:"优秀之处并非原创,原创之处并不优秀"。但我们不也是去其槽粕,留其精髓,一路填坑走过了吗?
来直接一点,es6常用的特性有:class类的支持、箭头函数、对象和数组的解构、默认参数、不定参数、对象合并、let与const关键字、for of迭代、字符串模板、对象字面量增强、同名对象字面量缩写、模块化import/export、map、promise、* yeild生成器等。
这里挑出几个常用的简单说下:
首先class:
在没有class的时候,创建类的一种比较标准的方式是将非函数的属性放到构造函数里,函数属性在原型链里添加。类的继承的实现就更为多样:对象冒充、call/apply方式、原型链方式等。es6的class和extends关键字的出现给出了一个统一的规范
class People { constructor (name, age, gender){ this.name = name } sayName (){ return this.name } } class Student extends People { constructor (name, age, gender, skill){ super(name, age, gender) this.skill = skill } saySkill (){ return this.skill } } let tom = new Student('tom', 16, 'male', 'computer') tom.sayName() // => 'tom' tom.saySkill() // => 'computer' tom.__proto__ == Student.prototype // => true Student.__proto__ == People // => true
可以看出虽然是新的规范,但是还是遵守js的原则:对象的__proto__指向它的构造函数(类)的prototype。es6对象字面量的__proto__注入也能快速的实现继承。
接下来是let:
es6之前js只有函数作用域,let的出现有了块级作用域,也就算是if、else、for这类也有了作用域,块内用let声明的变量外面是访问不到的,在js预解析的时候,是不会被提升到当前函数作用域的前面的。基于该特性,在for迭代的时候,每次迭代都会产生一个块级作用域的独立的迭代变量,让最后的结果就是我们期待的结果。
var arr = []; for (let i = 0; i < 10; i ++){ arr[i] = function (){ return i } } arr[6]() // => 6 //如果用var声明i,无论多少次迭代,外层的i始终被每次迭代的函数内部引用着(闭包),不会被当做垃圾回收,最后的结果都指向同一个i,值为10。 //以往为了避免这个问题,通常会这么做: for (var i = 0; i < 10; i ++){ arr[i] = (function (i){ return function (){ return i } })(i) }
最后讲讲箭头函数:
es6之前的function有一个特点:函数内部的上下文并不是由该函数写在那里决定的,而是由谁调用决定的,谁调用函数内部的this就指向谁。然后我们有些时候并不想让他这样,但又没办法,只能通过先保存this,或者call/apply,或者bind来调整上下文。箭头函数的出现解决了这个宁人苦恼的问题,因为箭头函数内的上线文(this)是由函数写在哪决定的,无论被哪个对象调用,上下文都不会改变。