@yacent
2016-10-20T15:29:11.000000Z
字数 4857
阅读 934
面试
实习相关:
目前正在网易的LOFTER部门进行实习,主要是参与福利市集开发工作,持续迭代开发供应商管理后台运费设定板块的开发。同时参与部门七夕活动等相关运营活动页面的开发。在公司当中,主要是使用jst、freemarker、dwr、nej进行相关的开发。开发过程当中,采取模块化的开发,并且利用面向对象的编程方式开发前端页面的相关组件,对组件化有了初步的理解。
借鉴与其他语言,比如python,把功能写成一个个的模块,如果要使用某个模块的功能,只需要import进来,然后就可以开始使用,但是由于js一开始设计的原因,是没有实现类似于这样的功能的。
模块化的进程,也从函数封装开始。在以前,我写代码的时候,是每个功能写成一个函数,然后需要使用到这些函数的其他文件,只需要在引入文件前,对该文件进行引入。但是这样会有一个问题,就是会污染全局变量,因为做一个任务的过程当中,不可能只有自己在开发,而是很多人同时开发,这样就很难避免到,每个人命名的异同,有可能两个人命名了同一个函数名字,这样就会造成变量名冲突。
后来发展为对象的形式来进行模块的封装,但是也有一个缺点,就是外部可以随意修改对象当中的属性,会不安全。再后来,就是借鉴于单例模式,使用立即执行函数,在模块内部,只返回接口,而内部实现细节是没有办法直接访问到的,只有通过特权函数进行访问内部的函数。这也是模块化的基础,之后继续发展就是添加了依赖注入,这基本上就是现在模块化的基础。
模块化,简单来说就是分治,好处就是每个人独立开发一个模块,只提供必须的接口,这样做的好处是,作用域是单独的,也就解决了上面所说的命名冲突等问题,而且外部无法直接访问到细节,这也保证了安全性。这就是我认为的模块化。
在实习的过程当中,我也是使用模块化的方法进行代码的编写。
在目前前端模块化标准当中,主要是两种标准,
一种是从requireJS延伸出来的AMD规范,
另外一种是从seaJS延伸出来的CMD规范,
二者主要的不同点在于引入模块和执行模块的时机,AMD是推崇依赖前置,提前执行,就是在定义模块的时候,就引入了其他的模块,在模块当中,就已经是出于可用状态了。而CMD的话,提倡依赖就近,在需要使用到某个模块的时候,才进行引入。
MVC模式,在我的理解当中,首先他是一种设计典范,用一种业务逻辑、数据、界面显示、分离的方法来组织代码,将业务逻辑都聚在一个部件里面,在改进和修改界面以及用户交互的时候,不需要重新写业务逻辑代码,这也可以让开发的人在一个时间内,只关注一个方面,
也可以使开发人员进行分组开发。
我认为MVC和组件化是不等价,组件化的目的主要是为了将web页面按照组件的方式来组织,比如头部是一个组件,footer是一个组件,组件化使得页面开发非常便利。但是组件化是一种思想,MVC是落实到每一个组件的,就是指在每一个组件内部,都应该单独划分成MVC三个部分,这样就更加细分了每一个组件内部的功能。
对于组件化的理解,比如一个电视机的构成,是由显示器、音响、电路板等各个部件构成,每一个组件之间相互连接,而最终构成一台完整的电视机,组件化,我认为就是将网页的各个部件都拆分成一个个的组件,比如将页面拆分成header、nav、container等的组件,组件与组件之间,只有是组合与包含,各自的逻辑、样式都保持独立,相互不影响,每个组件单独开发,在组件化开发过程中,要构成一个完整的页面,只需要将各个组件都拼接在一起,就完成了一个页面。这是我认为的组件化。
在实习的过程当中,也是运用组件的思想,在做七夕活动表白日活动的时候,使用freemarker的宏定义,将页面当中的header、奖赏功能等都分离为一个个的组件,其他页面由共用到这一部分的,只需要使用freemarker的模块引入,将组件加入到页面当中。
组件化的好处,在我看来,像刚才所说的,因为每一个部分都划分一个个的组件,首先
第一,就是降低了整个系统的耦合度,比如,某天,需求突然改动,要将原来某个地方的输入框换为日历或者其他东西,那么只需要更换一个组件就OK了
第二,便于单元测试和调试,因为当出现问题时,可以根据报错或者其他方式快速定位到出问题的地方,由于组件化而形成的低耦合的,每个组件的职责更为单一。
第三,提高了可维护性,首先是出错后的后期维护,只需要对一个组件进行修改,另外,如果需要对组件进行相对应的优化,那么只需要修改单单一个组件,这个系统相对应的部分都会得到相应的提升。
都说,代码应该要有可维护性、可拓展性,我认为组件化都做到了这些。
组件之间拼接方式主要有两种,组合和嵌套,就是平行关系和父子关系。组件之间拼接方式是目前很多框架的重要功能。
在我看来,组件之间的通信方式主要是有以下的几种模式
【父组件】→【子组件】
【子组件】→【父组件】
【子组件】→【子组件】
父组件 → 子组件的通信,比如在react当中,是父组件通过管理自己的state属性,然后向子组件的props属性传递相关的值,进行通信。
子组件 → 父组件的通信,子组件通过控制自己的state,然后告诉父组件点击的状态,在父组件当中进行相对应的显示。即使用回调函数来触发父组件当中状态的变化。
子组件 → 子组件的通信,在实习开发工程当中,有这样一个组件,就是文章与赞赏组件之间有相互关系,点击表白并且赞赏了一定数量的值,表白按钮下方的已表白数量会进行相对应的改变,一开始我的做法是用一个main组件来负责统筹整个页面组件的通信,对赞赏组件进行了相应的操作之后,触发main当中的回调函数,由main来对表白按钮及其已赞赏数值进行相对应的改变,子组件与子组件之间的通信也算完成了。
在这里面,main组件所引用的模式,应该是类似于 观察者模式的。
在做七夕活动表白日活动的时候,因为这个活动主要就是通过对用户对自己喜欢的文章进行表白的形式来进行,所以活动页面当中会存在很多的图片。需求要求使用瀑布流进行实现。现在很多的网站,比如在花瓣等网站或者是各大论坛当中,使用的比较多的都是瀑布流结合触底加载的形式。因为之前没有写过,只是大概知道要如何实现。罗列了一下实现的要点,主要有两个
第一,判断是否触底了
第二,实现瀑布流布局。
问题1:
在这里面的话,比如这个活动页面,采取两列瀑布流,初始化瀑布流,会请求前20个数据,获取到数据之后,使用jst前端模板引擎生成dom节点,并且判断两列的高矮,将产生的节点插入到矮的那一列当中。一开始判断高矮主要是每次插入之前,实时地去判断两列的高度,这里涉及了dom操作获取元素高度,性能有点不太好,插入过程当中,有时候会出现卡顿的现象。后来改用数组存储两列的初始高度,插入之后,实时改变数组当中的值,实践表明,插入的速度也更快了。
后来查询了一下瀑布流的实现方式,主要是那么以下几种
问题2:
触底加载的实现,监听scroll事件,判断 (scrollTop + clientHeight) 与 (scrollHeight)的比较,当前者的和>后者的时候,则触发,进行新的请求,一开始做完之后,在pc上显示是没有什么问题的,很流畅,但是在一些mobile上,当用户进行连续滑动的时候,页面会出现卡顿的现象,因为,给window监听了scroll对象,只要用户滑动了,就会调用监听事件的回调函数,进行判断是否到达底部。
解决的思路,就是不要每次滑动就触发判断,而应该在滑动静止了一段时间后,再进行判断,即函数节流的思想。就用函数节流,通过throttle函数,作为scroll对象的回调函数,在进行了scroll事件的300ms内,如果又进行了滚动,则清除setTimeout,重新进行监听,只有当滚动停止300ms内,无操作,才触发真正判断高度的函数,进行新的列表的加载。
问题3:
这个是属于一点点小优化,因为一开始获取文章列表之后,比如20篇文章,则一次性塞进去,并且图片也是一次性塞完,然后进行加载,这样会出现的一个问题就是页面加载时间会过长,因为要进行图片的加载。之前在segamentfault阅读文章的时候,会有这样一个体验,就是图片懒加载,即在图片到达用户可视区内或者说是即将到达可视区内,才对图片进行加载。后来自己查阅资料研究,和判断触底加载的思想类似,主要就是判断图片所在位置距离屏幕可视区高度,来判断是否要进行加载。通过getBoundingClientRect().top 和 自身高度 + 屏幕可视区高度 的值来判断是否应该进行加载。
提升1
因为在开发当中,是规定了列数的,就是两列,所以判断还比较好,后来想了以下,还是把他拓展为通用性更好的一个功能,所以把其中的列数,每列宽度,每个元素之间的距离等的都抽离出来,作为可供用户自定的一个插件,拓展性更好。实现内部是一样,只不过参数是可以由用户自定义的。并且做了一些兼容性的措施,因为当中使用到了indexOf的方法,在IE6-8当中,是没有这个方法的,所以就自己实现了一下indexOf的方法
实现方式:
1. 设置最外层的宽度
2. 初始化数组的高度
3. 循环flowItem,计算最矮的高度以及index,进行操作,根据前四个与后面的来进行判断
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(item) {
var len = this.length;
if (len <= 0) {
return -1;
}
// since indexOf can search from the specific position, default(0)
var start = Number(arguments[1]) > 0 ? Number(arguments[1]) : 0 || 0;
// loop the array and matching the item
// i in this -> whether the i(index) in arr or overflow
for (var i = start; i < len; i++) {
if (i in this && this[i] == item) {
return i;
}
}
return -1;
}
}
实习问过程当中,因为需要遵循公司的NEC规范,即将css代码的风格转换为每条规则都在一行内进行显示,但是这不符合我一开始的书写习惯,因为一开始书写css的习惯就是花括号内的属性,一条就是一行,所以想着能不能有个方法转换一下css的格式,提高工作效率,所以后来就想着自己写一个gulp的插件,参考了网上的gulp-template插件的书写方式以及看了gulp官网当中插件开发的相关知识,着手进行自己的gulp插件的开发。
开发这个css代码转换的插件的思路主要有以下几个
css
去将css文件转换成css抽象语法树 AST
var gutil = require('gulp-util');
var through = require('through2');
var _ = require('lodash');
var template = _.template;
function compile() {
return through.obj(function(file, enc, cb) {
template(file.contenes.toString(), options); //对流数据进行处理,先转string,方便操作
file.contents = new Buffer(); // 转换回stream类型
this.push(file);
cb();
})
}