工作经历(六)——美黛拉篇

工作生活  |  3天前

我还在56网工作的那段时间里,创业公司遍地开花,偶尔也会有以前的同事邀请我加入,其中就有美黛拉的产品技术团队。虽然跟公司的老板并不熟,但是团队中有不少以前网易的老同事,所以考虑再三之后还是选择了加入。

架构搭建

入职之初,除了我以外,公司只有一名前端工程师。前端的项目也不多,只有后台管理系统和几个零散的手机端页面。所有前端项目都采用前端渲染的模式,目的是为了避开前端写页面后端套模板这个坑。但这样做的问题在于: SEO不友好;加载数据的延时。而就在此时,公司打算要做PC站,客户端的一些功能也要通过内嵌网页来实现。所以建立起一个良好的前端架构显得非常必要。

毫无疑问,这个架构必须是前后端分离的,而且要做到前端渲染和后端渲染兼备,同时两端的代码要尽可能复用。要同时满足上述条件,最好的选择就是用Node.js在后端和浏览器之间做一个中间层。浏览器的请求会先到达这个中间层,中间层请求数据渲染出HTML后返回到浏览器。

我在加入公司之前,已经把自己的博客用Node.js重写了一遍,所以再去写这个中间层并不吃力,最终做出来的成品被我命名为「Back2Front」。此外,还基于Gulp开发了配套的构建工具。

108次阅读,0条评论

setTimeout/setInterval的最大延时值

前端开发  |  2个月前

先来看这样一段代码:

function update() {
    loadData().then(function(data) {
        $('#content').html(data.content);
        var delay = data.nextUpdateTime - new Date();
        if (delay > 0) {
            setTimeout(update, delay);
        }
    });
}

其流程非常简单:通过AJAX加载数据后更新HTML的内容;如果有指定下次更新时间,则通过计时器在该时间点再执行一次整个流程。

要说这段代码有什么隐患的话,那就是data.nextUpdateTime与当前时间的时间差(即delay变量的值)比较小的时候,会导致内容频繁更新。但这是属于正常的业务逻辑,要优化就只能牺牲内容更新的即时性。然而这里我要说的是,当时间差非常大的时候,也会出现同样的问题。

136次阅读,0条评论

封装你的Gulp代码

Node.js开发  |  7个月前

近年来,前端工程化深入人心,而工程化中必不可少的环节就是构建。所谓构建就是基于既定的流程对项目中的文件进行处理,从而得到最终用于发布的文件。而Gulp正是目前最流行的前端构建工具之一。

Gulp的使用

以下是使用Gulp进行构建的例子,也是本文的示例项目。文件结构为:

/Users/me/project/project-a
- src/
- dist/
- package.json
- gulpfile.js

其中「src」为源代码目录,「dist」为发布代码目录。「gulpfile.js」的代码为:

var gulp = require('gulp'),
	gulpCleanCSS = require('gulp-clean-css'),
	gulpUglify = require('gulp-uglify'),
	gulpMD5 = require('gulp-md5-plus');

// 把.html文件直接copy到发布目录
gulp.task('copy-html', function() {
	return gulp.src('./src/*.html')
		.pipe( gulp.dest('./dist') );
});

gulp.task('compress-css', ['copy-html'], function() {
	return gulp.src('./src/*.css')
		// 压缩CSS代码
		.pipe( gulpCleanCSS() ) 
		// 文件名增加MD5,并替换.html文件中的引用地址
		.pipe( gulpMD5(10, './dist/*.html') ) 
		// 构建到发布目录
		.pipe( gulp.dest('./dist') );
});

gulp.task('compress-js', ['copy-html'], function() {
	return gulp.src('./src/*.js')
		// 压缩JS代码
		.pipe( gulpUglify() )
		// 文件名增加MD5,并替换.html文件中的引用地址
		.pipe( gulpMD5(10, './dist/*.html') )
		// 构建到发布目录
		.pipe( gulp.dest('./dist') );
});

gulp.task('default', ['compress-css', 'compress-js']);
1103次阅读,0条评论

安卓版微信Webview中两个页面互相跳转的BUG

前端开发  |  7个月前

都说微信是移动端的IE6,不管你们信不信,反正我信了。最近在开发过程中就遇到了一个极其容易触发的BUG。

复现这个BUG只需要两个页面「a.html」以及「b.html」,并且两个页面都有跳去另一个页面的链接:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<title>A</title>
</head>

<body>
<h1>I am A.</h1>
<p><a href="b.html">to B</a></p>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<title>B</title>
</head>

<body>
<h1>I am B.</h1>
<p><a href="a.html">to A</a></p>
</body>
</html>

先打开「a.html」,点击链接跳转到「b.html」,再点击「b.html」中的链接跳转到「a.html」。如此往复点击跳转几次,就会出现无法再次跳转的情况(会出现进度条,但是无法跳转)。

616次阅读,0条评论

异步流程控制

Node.js开发  |  11个月前

单线程与异步

Javascript是单线程运行、支持异步机制的语言。进入正题之前,我们有必要先理解这种运行方式。

以「起床上班」的过程为例,假设有以下几个步骤:

  • 起床(10min)
  • 洗刷(10min)
  • 换衣(5min)
  • 叫车(10min)
  • 上班(15min)

最简单最不容易出错的执行方式就是按顺序逐步执行,这样从起床到上班共需45分钟,效率较低。而高效的方式是,在「洗刷」之前先「叫车」,这样就可以节省10分钟的等车时间。

异步叫车

这样一来「叫车」就成了异步操作。但为何只有「叫车」可以异步呢?因为车不需要自己开过来,所以自己处于空闲状态,可以先干点别的。

把上面的过程写成伪代码:

起床();
叫车(function() {
	上班();
});
洗刷();
换衣();
1161次阅读,1条评论

hashchange事件及其妙用

前端开发  |  1年前

hash即URL中“#”字符后面的部分。使用浏览器访问网页时,如果网页URL中带有hash,页面就会定位到id(或name)与hash值一样的元素的位置,故而又称之为锚点。hash还有另一个特点,它的改变不会导致页面重新加载,因此在单页应用流行的当下,它的用处就更多了。

而hashchange事件,顾名思义,就是hash改变时触发的事件。在 caniuse.com 上查一下兼容性可以发现,IE8就已经支持该事件,但一直以来应用甚少。

hashchange事件兼容性

hashchange事件触发时,事件对象会有hash改变前的URL(oldURL)和hash改变后的URL(newURL)两个属性:

window.addEventListener('hashchange', function(e) {
	console.log(e.oldURL);
	console.log(e.newURL);
}, false);
1837次阅读,0条评论

网页版简历制作经验分享

前端开发  |  1年前

2012年中,因为换工作面试的需要,我得更新旧的简历。但是在Word中排版实在是各种不顺手,于是就发挥了作为前端工程师的优势把简历做成了网页;2014年末想换工作时又对其进行了改版。这份简历曾经受到好几位HR和猎头的好评,所以特地分享制作经验,也算是给想换工作的同行参考。

进入正题之前,先想想HTML简历的好处:

  • 无须下载,直接打开。
  • 可以采用更丰富的设计和更灵活的排版。
  • 可以通过超链接访问其他资源。
  • 可以顺带展示自己设计以及前端方面的技能。

而在此基础上,我还给这份简历定下了以下目标:

  • 一个页面中展示所有内容。
  • Write once, run anywhere. 兼容PC设备与移动设备,最好还能直接打印。
  • 简单实用,最好连JS都不用写。那些用上了各种动画做出来的甚至是做成了小游戏的炫酷简历,其实并不利于阅读。

好了,下面具体讲讲如何打造这样一份简历。

2190次阅读,13条评论

multer中间件的安全问题

Node.js开发  |  1年前

昨天备份本博客的时候发现,上传目录下多了几个奇怪的.asp和.php文件。这些文件并非我自己上传的,很可能是通过某个漏洞传到了服务器上。这会有什么危害呢?下面简单介绍一下。

假设某人把一个功能为删除站点下所有文件的evil.php通过漏洞传到了http://abc.com/upload/下,然后访问http://abc.com/upload/evil.php,那么:

  • 如果该站点不支持php脚本,是没什么危害的;
  • 如果该站点支持php脚本,它的全部文件就会被删除。

这里有两个关键点,一是主动访问该文件才会触发脚本执行,二是服务器支持该类型脚本才能执行。而本博客刚好不具备这两个条件(后面再详细说明),所以没有造成任何损失。即便如此,这漏洞还是要修复的,不然被无休止地上传文件迟早会占满硬盘空间。

本博客基于express框架开发,上传功能是通过multer中间件实现的,且只有管理后台存在文件上传的功能。因为后台页面、接口都设有权限验证,是不可能被绕过的。最后发现问题出在multer的调用上,而这问题又要归咎于官方文档的错误引导了:

app.use(multer({
	dest: './uploads/',
	rename: function (fieldname, filename) {
		return filename.replace(/\W+/g, '-').toLowerCase() + Date.now()
	}
}))
709次阅读,2条评论

ECMAScript 6中的面向对象

前端开发  |  1年前

ECMAScript 6(下面简称ES6)增强了对面向对象的支持,引入了class关键字,并且为类的创建、继承提供了简洁清晰的语法。

类声明与类表达式

毋庸置疑,class关键字就是用来定义类的。

定义类的常用方式是类声明,语法如下:

class ClassName {
	// 构造函数
	constructor() {
		// ...
	}

	// 方法
	method() {
		// ...
	}
}
643次阅读,2条评论

深入理解Javascript中的面向对象(三)

前端开发  |  1年前

在Javascript中,虽然借助原型链就可以实现继承,但这里面还是有很多细节问题的要处理的。分析并解决这些问题后,就可以把创建类的过程写成一个通用函数了。

constructor属性

Javascript中的对象都有一个constructor的属性指向其构造函数。例如:

function A() { }
var a = new A();
a.constructor; // A

确切地说,constructor属性是位于构造函数的prototype上。下面的代码可以证实这一规则:

function A() { }

var a = new A();
console.log(a.constructor); // A

delete A.prototype.constructor; // 删除原型上的constructor属性
console.log(a.constructor); // Object
1043次阅读,1条评论