如这两年所见,当街边水果店都叫嚣开发app,走向O2O、融资、上市走向人生巅峰之时,创业的泡沫已经吹的很大了。最近外卖行业以饿了吗认怂,宣告外卖O2O进入了快速发展的拐点。繁荣之后,越来越多的人进行这场全民创业盛宴的反思。乐帝最近参加了一场VC风投对关于泡沫看法的活动,VC不是政府,大部分时间并没有谈泡沫,而是谈VC从业人员的工作方法与思想。
王斌的一篇《APP已死,服务永生》,引起了昨日朋友圈疯狂转载。泡沫真的来了,这个创业的黄金时代也进入了调整期。正如秦朝统一六国是历史进步一样,创业泡沫破灭也是一种去伪存真的过程。那么在这场创业狂欢中,我们可以得到什么教训呢?
这是互联网第二次泡沫,正如第一次泡沫当时会个HTML都可以月薪上万,这次泡沫最大的受益人群莫过于IT技术人。而从长远看,这并不能持续。这次泡沫给我们的教育莫过于某个VC所言:“泡沫不可怕,怕的是没有逻辑和常识”。这里的逻辑和常识可以理解为专业化,要能够区分成功的偶然与必然,只有更加专业化才能在企业或个人竞争中真正屹立不倒。
说到个人,这周同事跟我说“咱们是专业程序员”,对我触动很大,我思考了一下作为专业前端开发工程师需要具备的素质或者如何实现专业化,拥有更好的职业感觉,见下图:
由乐帝上图总结可看出,各行各业想做的出色,都必须做到以下几点:
- 具有基本的从业技能素质
- 具有钻研问题的习惯
- 具有反思与总结提升的习惯
- 具有对工作责任感
乐帝这周实习工作内容的重点主要在于重构、优化原有项目上。可以分为两块:
- 招聘后台投递设置项目中采用MVC思想优化项目
- 微信招聘主页重构与优化
一、招聘后台投递设置采用MVC思想优化
这周领导找乐帝谈话提到了两点:代码需要可读性高与高性能、其次技术提升需要深入钻研底层原理。上周乐帝对投递设置联动按钮进行了迭代开发。算是实现了功能,而从性能上分析及MVC架构思想上分析,代码中还有很多可改进的部分,总结如下:
![](https://img-my.csdn.net/uploads/201411/27/1417085781_8369.png)
上述重构可以从以下方向理解:
- 前端架构中,model中数据会默认渲染到模板中,这里体现了模型与视图分离的思想。
- DOM操作设置默认值比较耗性能,完全可以重构model数据的结构,模板渲染中,进行初始化界面。
- 同样道理,最后统一DOM操作,存取数据,相比时刻监听DOM状态,实时修改DOM数据,最后存取model数据。前者更耗费性能。
按照上述思想,构建了具体的实施目标:
![](https://img-my.csdn.net/uploads/201411/27/1417085845_8371.png)
具体构建上,实际上主要涉及到对model数据结构的再造与模板中逻辑的修改。以系统字段为例:
![](https://img-my.csdn.net/uploads/201411/27/1417085780_7994.png)
显示系统字段view:
showSystemList: function() {
var self = this;
if (this.from == "edit") {
var ExtendModel = Talent.Model.extend({
defaults: {
Description: "",
Label: "",
IsRequired: false
}
});
var systemFieldView = new SystemFieldView({
model: new ExtendModel(self.model.toJSON())
});
} else {
var systemFieldView = new SystemFieldView({
model: new Talent.Model({
systemFieldList: self.model.get("systemFieldList"),
from: self.model.get("from")
})
});
}
self.fieldWrap.show(systemFieldView); //将系统数据传入系统视图并渲染
//强制使弹窗居中
var pageHeight = $(window).height();
var top = (pageHeight - 269) / 2; //窗体高度距上下相同
self.$el.find(".simplemodal-container").css("top", top);
}
实时监听输入框变化,修改model:
events: {
"change .fieldList": "changeFieldList",
"change .controlList": "changeControlList",
"change input[name=IsRequired]": "changeIsRequired",
"change .description": "changeDescription",
"change .fieldAlias": "changeFieldAlias",
"click .btn_canc": "closePop",
"click .ico_clse": "closePop",
"click .btn_save": "saveEdit"
}
changeFieldList: function() {
var FieldId;
if (this.model.get("IsSystem")) {
FieldId = $(this.$el.find(".fieldList")).val();
this.model.set("FieldId", FieldId);
} else {
var Label = $(this.$el.find(".fieldList")).find("option:selected").text();
FieldId = $(this.$el.find(".fieldList")).val();
this.model.set("Label", Label);
this.model.set("FieldId", FieldId);
}
},
changeControlList: function() {
var ControlName = $(this.$el.find(".controlList")).val();
this.model.set("ControlName", ControlName);
},
changeIsRequired: function() {
var IsRequired = this.$el.find("input[name=IsRequired]").attr("checked") == "checked" ? true : false;
this.model.set("IsRequired", IsRequired);
},
changeDescription: function() {
var Description = $(this.$el.find(".description")).val();
this.model.set('Description', Description);
},
changeFieldAlias: function() {
var Label;
if (this.$el.find(".fieldAlias").val()) {
Label = this.$el.find(".fieldAlias").val();
}
this.model.set("Label", Label);
},
统一在model存取系统字段数据:
getSystemData: function() {
// 系统字段数据
var FieldId = this.model.get("FieldId");
var data;
// 遍历字段名列表找到选中项拷贝到data数据
_.each(this.systemFieldList, function(item) {
if (item.FieldId == FieldId) {
data = _.clone(item);
}
});
// 必填、别名、描述
data.IsRequired = this.model.get("IsRequired");
data.Label = this.model.get("Label");
data.Description = this.model.get("Description");
return data;
}
二、微信招聘主页重构与优化
微信端采用jquery mobile 页面内设置多个内容块采用的单页面多data-role='page'的方法切换,但jquery mobile构建思想上,更多偏向单页网站架构,page间切换会在URL上添加参数,这样用户点击浏览器返回按钮时,就会造成不按预期功能返回的问题。这里乐帝研究了jquery mobile组件中tabs组件,并打算将page换成tabs组件构建页面。同时原有脚本代码可读性上和性能上都有提高空间,于是又重构了代码。
具体重构目标与流程目标如下:
![](https://img-my.csdn.net/uploads/201411/27/1417085845_3598.png)
乐帝首先研究了tabs组件,从tabs API提供的功能来看,对其属性和方法进行了分类:
![](https://img-my.csdn.net/uploads/201411/27/1417085781_9939.png)
通过通读tabs API乐帝对tabs能够实现的功能有了深入的了解,特别是脚本控制方面。读API的好处,就在于这是除了读源码之外,最深刻了解一门技术的方法。
重新梳理了交互代码逻辑:
![](https://img-my.csdn.net/uploads/201411/27/1417085780_8354.png)
重写整个界面的逻辑:
![](https://img-my.csdn.net/uploads/201411/27/1417085845_7323.png)
采用tabs组件重构后的html代码:
<div class="main-wrap" style="display:none;">
<div data-role="page" id='content-page'>
<div data-role="content">
<div data-role="tabs" id="tabs">
<div data-role="navbar">
<ul class="task-nav clearfix">
<li><a href="#page1"><span id="btn1" class='task-nav-btn task-btn-active'>处理中</span></a>
</li>
<li><a href="#page0"><span id="btn2" class='task-nav-btn '>完成</span></a>
</li>
</ul>
</div>
<div id="page1">
<p class="no-task-1" style="display:none;">暂无任务</p>
<div class="task-list" id="list1">
<ul>
</ul>
</div>
<div class="slide-down" id="slide-down1">
<p class="load-tip">加载更多</p>
</div>
</div>
<div id="page0">
<p class="no-task-2" style="display:none;">暂无任务</p>
<div class="task-list" id="list2">
<ul>
</ul>
</div>
<div class="slide-down" id="slide-down2">
<p>加载更多</p>
</div>
</div>
</div>
</div>
<form id="sublist" action="Detail" method="post">
<input type="text" name="taskId" id="taskId" />
<input type="text" name="interviewId" id="interviewId" />
<input type="text" name="personId" id="personId" />
<input type="text" name="elink" id="elink" />
</form>
<div class="hiddenOpenId">
<input id="taskType" type="text" value="@ViewData["TaskType"]" />
</div>
</div>
</div>
重构后的交互代码:
$(document).ready(function() {
$("#tabs").tabs({
activate: function(event, ui) {
var active = $("#tabs").tabs("option", "active");
// alert(active);
if (active == 1) {
$("#btn2").addClass("task-btn-active");
$("#btn1").removeClass("task-btn-active");
} else {
$("#btn1").addClass("task-btn-active");
$("#btn2").removeClass("task-btn-active");
}
}
});
var page1 = 0,
page2 = 0,
page1Num = 1,
page2Num = 1,
tempId,flag=0;
setIframe();
//一开始显示加载中
$(".waiting-tip").css("display", "block");
loadData(1, page1Num++,true); //未处理
loadData(2, page2Num++,true); //已完成
$("#slide-down1").on("tap", function() {
loadData(1, page1Num++,false);
});
$("#slide-down2").on("tap", function() {
loadData(2, page2Num++,false);
});
function setIframe() {
//设置iframe尺寸
var height = $(window).height();
var width = $(window).width();
$("#mainFrame").height(height);
$("#mainFrame").width(width - 2);
}
function loadData(id, pageNum,initial) {
// 实验部分
var Description ;
if (id == 1) {
Description = "gotodo ";
} else {
Description= "havedone";
}
setTimeout(function() {
data = {};
for (var i = 0; i < 10; i++) {
data[i] = {
"Description":Description,
"TaskName": "heej",
"StartDate": "2014/02/04",
"DutyName": "ssdsf",
"CreateDate": "2014/02/04",
"InterviewId": 'eeeeege',
"TaskId": "sdsfre",
"ApplicationId": "sdsfsf",
"Elink": "sfsfsfs"
}
}
data.length = 10;
if(flag==5){
data.length=4;
}
flag++;
if (data == undefined) {
toLogin();
return;
}
showContent(data, id,initial);
}, 5000);
// 实验部分
// $.ajax({
// url: "../api/OfficerTask/GetOfficerTaskByOpenId",
// type:"POST",
// data: {
// "type": id,
// "pageNum": pageNum,
// "pageSize": 11,
// "taskType": $('#taskType').val()
// },
// async: false,
// success: function (data) {
// if (data == undefined) {
// toLogin();
// return;
// }
// showContent(data, id);
// },
// error: function () {
// },
// dataType: 'json'
// });
}
function toLogin() {
document.title = "用户验证";
$(".user-check-wrap").css({
"display": "block"
});
$(".waiting-tip").css("display", "none"); //数据未定义,进入登录页
}
function showContent(data, id,initial) {
if(initial&&(data.length == 0)){
$(".no-task-" + id).show(); //如果没有列表,显示没有任务
}
if (data.length < 10) {
$("#slide-down" + id).hide();
}
$(".main-wrap").css("display", "block"); //主页面显示出来
$(".waiting-tip").css("display", "none"); //隐藏提示部分
addContent(data, id);
}
function addContent(data, id) {
var taskName, des, sTime, dutyName, cTime, interviewId, taskId, appId, elink, temp = "";
for (var i = 0; i < data.length; i++) {
taskName = data[i]["TaskName"];
des = data[i]["Description"];
sTime = changeTime(data[i]["StartDate"]);
dutyName = data[i]["DutyName"];
cTime = changeTime(data[i]["CreateDate"]);
// data-*数据
interviewId = data[i]["InterviewId"];
taskId = data[i]["TaskId"];
appId = data[i]["ApplicationId"];
elink = data[i]["Elink"];
temp += "<li data-elink=" + elink + " data-appId=" + appId + " data-taskId=" + taskId + " data-interviewId='" + interviewId + "'><div class='task-content'><p class='clearfix task-title-wrap' ><span class='task-title'>" + taskName + "</span></p><p class='task-content-des task-des-clr'>" + des + "</p><p class='task-content-des task-des-clr1 clearfix'><span class='task-creator'>创建人:" + dutyName + "</span><span class='task-crtime'>" + sTime + "</span></p></div></li>";
}
// 循环完统一添加到列表
$("#list" + id + " ul").append($(temp));
bindItemList(id);
}
function bindItemList(id) {
tempId = id;
$("ul li").click(clickBind);
}
function clickBind() {
var interviewId, taskId, appId, elink;
var taskType = $("#taskType").val();
// 取出数据
interviewId = $(this).data("interviewid");
taskId = $(this).data("taskid");
appId = $(this).data("appid");
elink = $(this).data("elink");
if (taskType == 7) {
window.location = "OfferOrJobDetail?elink=" + elink.replace("?", "&") + "&OfficerOrderId=" + appId + "&OfferApplyId=" + interviewId + "&Id=" + tempId + "&type=" + taskType + "&taskId=" + taskId;
} else if (taskType == 8) {
window.location = "OfferOrJobDetail?elink=" + elink.replace("?", "&") + "&actId=" + appId + "&mpid=" + interviewId + "&Id=" + tempId + "&type=" + taskType + "&taskId=" + taskId;
} else if (taskType == 6) {
window.location = "Detail?elink=" + elink.replace("?", "&") + "&personId=" + appId + "&interviewId=" + interviewId + "&Id=" + tempId + "&type=" + taskType + "&taskId=" + taskId;
} else if (taskType == 16) {
window.location = "FilterList?filterId=" + interviewId + "&Id=" + tempId + "&taskId=" + taskId;
}
}
//转换时间格式的函数
function changeTime(date) {
var d = date.split(" ");
var rDate = d[0].split("/");
var str = rDate[2] + "-" + rDate[0] + "-" + rDate[1];
return str;
}
});
上述交互代码在性能和可读性上进行了重构。
乐帝最近最大的改变在于做项目的同时,更多的思考改进方法与做事逻辑上,离优秀的同事还有很长距离,但是在路上。