前端项目从0到1的感悟

十度 WEB前端 2017年04月28日 收藏

去年6月份左右,加入了一个创业公司,很幸运做了一个从零开始的项目,前端工程由我一手搭建起来,并不断迭代功能到现在,有许多的感悟心得,在这里写点总结

确定框架、技术点

一个项目的开始,特别是丰富多样的前端工程,首先一定要确定好采用的框架和技术点。2016年vue.js如火如荼,webpack强势崛起,但是是否就可直接拿到项目中搞起呢?答案是否定的,一个新的技术如果自己或团队中成员都还在学习摸索的过程,是肯定不能在生产环境中使用的,更何况这是个创业的团队,没有成熟完整的前端团队。所以我当时还是走老套路,jquery为核心,fastclick辅助,requireJs按需加载,arttemplate做模板渲染,核心UI类库使用jqueryWeUI,加上sass预编译样式文件,gulp打包构建,(后台是微服务架构,maven构建,springMVC+mybatis,此工程为h5前置工程)这样一来就基本上满足条件,可以开工了。

开发工具

不同的开发工具可能在展示格式上有一定的差别,所以一个团队最好还是能统一的开发工具。后台一般使用eclipse,前端我推荐使用sublime,以及相关的插件:

开发规范,命名规则

既然是多个人同时开发,肯定会有一些代码风格上的异同,然而为了方便后期维护,必需制定一些相关的规范。如:

  • 开发规范
    1. 所有页面编码必须是
    2. 尽量使用语义话标签,如头部使用header,内容使用articl,页脚使用footer,模块使用section标签
    3. 所有页面采用下面的通用模板
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    
        <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
        <meta http-equiv="Pragma" content="no-cache" />
        <meta http-equiv="Expires" content="0" />
        <meta name="format-detection" content="telephone=no" >
    
        <title>通用模板页面</title>
        <link rel="stylesheet" type="text/css" href="/static/li/li-1.2.0.css">
    </head>
    <body>
        <header></header>
        <article></article>
        <footer></footer>
    </body>
    <script type="text/html" id="registerMainTpl">
    
    </script>
    <script src="../static/component/requirejs/require.min.js"></script>
    <script type="text/javascript">
    var script = document.createElement("script"),
        head = document.head || document.getElementsByTagName('head')[0];
    
    script.type = "text/javascript";
    script.src = '/config.js?ver=' + (new Date()).getTime();
    
    head.appendChild(script); 
    
    script.onload = script.onreadystatechange = function(){
    require(
        [
            'jquery',
            'fastclick',
            'artTemplate',
            'li',//自己封装的js类库,包括head里面引用的li-1.2.0.css
            'common',//公共js,存放一些公用的方法
        ], 
        function($,FastClick,template){
            FastClick.attach(document.body);
    
            var $body = $('body'),
                $header = $('header'),
                $article = $('article');
            var registMain = {
                h5: function(){
                    this.renderHtml();
                    this.renderComponent();
                    this.watch(); 
                },
                //全局属性
                options: {
    
                },
    
                //渲染模板的数据
                data: {
    
                },
    
                //渲染html
                renderHtml: function(){
    
                },
                //渲染组件
                renderComponent: function(){
    
                },
    
                //事件监听
                watch: function(){
                    var self = this;
    
                }
            }
    
            dn.init(function(){
                registMain.h5();
            })
        })
    }
    </script>
    </html>
  • 命名
    1. id采用驼峰命名法则,如 formName
    2. class中间用-隔开,如 li-col-50
    3. 图片命名也用-隔开,如 zhongan-bananer
    4. 模板以Tpl结尾,如 registerMainTpl
    5. 函数也用驼峰命名,使用 get/set/put/delete等前缀
    6. 文件名也用驼峰,使用跟文件内容相同的英文单词或词组
    7. jquery选择器能用id就不用class

目录结构

一个项目的目录结构就像人的骨架一样重要,对于日渐迭代版本越来越多的前端项目,如果没有一个好的目录结构会显得非常臃肿,难以维护,以下这个截图是我现在正使用的目录,也还有需要改进的地方,做一个参考:

定义readme.md

将规范规则,文档目录、wiki链接等说明放在readme必读文档里

定义config.js

如果你是使用requirejs的项目,肯定有个config.js文件,这个文件每个页面都会引用,并且是在页面加载js的开始同步引用。官方说法是这个页面放js文件的key-value形式配置呢,而我习惯再js配置之前再定一个window对象的项目唯一子对象,再将项目一些常用的常量也定义好,以及请求状态码、url汇总,这样极有利于后续维护,如:

//项目的全局唯一对象
window.li = {
    version: 20170301,//当前版本号,打包时用gulp自动变更
    errorTips: '哎哟,网络好像有点问题了..',//无网络提示
    timeout: 1000*60*60*24*20,//登录失效暂时为20天
    _MOBILE: 'http://h5.liliangel.cn/base/mobile',//跳转手机页面
    _ROOT: 'http://www.liliangel.cn',//测试域名地址
    _XHR: '/rest/',//ajax请求前缀
    _CORS: '/cors/',//跨域请求前缀
    _STATIC: 'http://static.liliangel.cn',//静态资源域名
    _WX: 'http://h5.liliangel.cn/'//微信h5服务器地址
}

//所有请求状态码
li.code = {
    SUCCESS: 600, //成功
    PHONE_EXIST: 603 //手机号码已经存在
}

//所有请求url
li.api = {
    sendMessage: 'wechat/sendMessage' //发送消息url
}

//静态资源配置
require.config({
    urlArgs: "v=" +  li.version,
    baseUrl : "/",
    paths: {
        jquery:'plugin/jquery/jquery-1.9.0.min',
        artTemplate: 'plugin/template/artTemplate-3.0',
        
    },
    shim: {
        bootstrap: {
            deps: ['jquery'],
            exports: '$'
        }
    },
    waitSeconds: 15
});

定义common.js

用来放置所有公共方法,同样也是每个页面都引用,正如上面定义好的一些常量一样,一个项目的开始,还需要一些方法上的准备工作,如:

  1. 所有js方法li.init()主入口,相当于jquery的ready()方法,这样一来就可以控制所有js加载前做的事情了,如前置判断在微信浏览器执行fun1,在原生app中执行fun2,这也是混合式开发中常见的需求。

  2. ajax的li.GET()、li.POST()方法,如果你觉得ajax的封装可能不能满足的特定需求,比如通用的加载中、比如通用的异常、通用的请求超时时间和回调、通用的请求完成回调、是否需要验证token等等,总之你可以根据自己风格重写一下ajax未必不可...

  3. 通用的获取token方法li.getToken(),很多时候请求需要验证token,前端需要把这个证书获取到传给后台,那么这个获取证书的逻辑后续可能会存在一些变动,如加密算法修改、原生端通过交互方法调用app内的然后微信端从缓存或session中获取等等,所以获取token方法也必须封装好

  4. 通用的登录过期提示、回调。当调用后台接口返回登录过期或者非法请求后,需要做些通用的处理,如去登录页、三秒提示后去登录页、去错误页等,总之存在需求变故的可能的地方,能封装的方法尽量封装,免得到时候需求一变化全部都得去改

  5. 通用的去登录、注册页方法。通常一个项目中,去登录页面的会有很多地方调用,同时可能在方法里判断一下手机号是否已经注册、没有注册就去注册页面这样的简单逻辑,提高用户体验..

  6. 跳转页面方法,可能你会觉得用window.location.href足够了,但是实际的开发中,缓存的现象真的很头疼,尽管你各种设置各种加版本号了,但是你通常会忽略在加载页面的时候在url后面也添加一个版本号

  7. 后退
    通常情况下,你可能会觉得window.history.go(-1)就可以了,但是如果你的页面同时也要用在混合app里时,就需要考虑一些和原生端交互的问题,跳转页面也是一样,通常情况下需要保留当前webview重新打开一个webview,所以尽量将后退和跳转页面封装一个通用的方法,方便后期添加修改。

  8. 判断当前浏览器厂家
    h5最多的可能就是判断是否是微信浏览器了

    var ua = navigator.userAgent.toLowerCase(),
        isWechat = ua.indexOf('micromessenger') != -1;
    if(isWechat){
        ...
    }
  9. 获取url参数
    获取当前页面url地址中的参数是很常用的方法,通常我也会将其封装在common里面,如:

    getUrlPar: function(name){
        var _reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"),
            _regNext = window.location.search.substr(1).match(_reg);
        if (_regNext != null) return decodeURI(_regNext[2]) || '';
        else return '';
    },
  10. ajax 全局错误监听

通常情况下,后台会在web.xml会配置一个error-page指向一个错误页面,但是那样都是跳转页面404错误还好,其他错误也跳页面其实并不友好,而其他的错误通常是请求中错误,我们只要设置一个ajax全局监听即可,下面是代码片段,当然还可以做很多你暂时没有想到的事情

$(document).on('ajaxError', function(e, xhr, options){
    if (404 == xhr.status) {
        common.404()
    }else if(500 == xhr.status){
        common.500()
    }
})

定义common.css

所有公共样式,同样的每个页面都要引用,其中在整个项目样式通用控制起决定性作用,如:

  1. 全局字体样式

    * {
      font-family: 'Microsoft YaHei'; 
    }
  2. 标签样式

    body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td {
      margin: 0;
      padding: 0; }
    
    /** 标签样式 start */
    html {
      font-size: 20px; }
    
    body {
      font-size: 16px;
      background-color: #e5e6e7; }
    
    h1 {
      font-size: 2.25rem; }
    
    h2 {
      font-size: 1.975rem; }
    
    h3 {
      font-size: 1.50rem; }
    
    h4 {
      font-size: 1.125rem; }
    
    h5 {
      font-size: 0.875rem; }
    
    h6 {
      font-size: 0.750rem; }
    
    a {
      text-decoration: none;
      -webkit-tap-highlight-color: transparent; }
    
    em {
      font-style: normal; }
    
    label > * {
      pointer-events: none; }
    
    ul {
      list-style: none; }
    
    button {
      -webkit-appearance: none;
      border: 0;
      background: 0 0; }
  3. 媒体查询控制字体大小

    /** 媒体查询 start */
    @media only screen and (min-width: 400px) {
      html {
        font-size: 21.33333333px !important; } }
    
    @media only screen and (min-width: 414px) {
      html {
        font-size: 22.08px !important; } }
    
    @media only screen and (min-width: 480px) {
      html {
        font-size: 25.6px !important; } } 

    这是所有页面的基础样式控制,主要是在字体方面,用rem来解决移动开发中多屏适配,如果有UI框架引入的时候就要根据实际情况考虑是覆盖基础样式还是被覆盖来决定引入的先后顺序了

打包(gulp)

  1. 压缩css、图片,压缩、加密js
  2. 去掉console.log()等调试信息
  3. 给css自动添加兼容性前缀
  4. 给js、css、img、font、json等静态资源引用处添加版本号(当前时间戳)

    E:
    cd \h5\weixin\target
    gulp build
    cd \h5\weixin\target\weixin\
    del \h5\weixin\target\weixin\weixin.war
    cd \h5\weixin\target\weixin\
    jar cvf weixin.war ./
    del \last\code\2017\dnzd\weixin.war
    move weixin.war \last\code\2017\dnzd\

    实际开发中可能有实时刷新和实时编译sass这些任务。上面是生产构建的脚本,build这个任务里面包含了上面4点,当然还可以添加requireJs优化等等这些...

意识和协作

团队协作开发中,成员写代码的意识很重要,一个再完善的开发规范不如有个良好的代码意识的程序员,一个技术再好团队不如一个团队意识强的的团队,当然,改变一个人的开发习惯是比较痛苦的,但是如果你发现这是一个好的规范,那么你需要刮骨疗伤去成长,如果你不能忍受一些条条框框,那么团队也需要更好的程序员。在项目开发协作中,特别是团队组建初期,特别是前后台交互、UI跟前端对接、产品经理跟开发沟通中,可能会存在很大的意见分歧,可可能有人会说你代码哪里哪里不好、没有注释、不可维护.. 求同存异,尽可能的少去指责他人,因为你看上去乱遭遭的代码,永远相信也是写的人当时深思熟虑后的作品。同时面对别人的说法,你需要更成熟的选择一笑而过,你也应该去采纳别人的建议,沉默中把自己的弱项提高,那就是成长。

多说几句

做前端的这几年,我从一个css、js都不懂的小白也算是历练成了一个老司机,其中的辛酸苦辣只有自己明白。学习的过程的确是痛苦的,但却也是有乐趣在其中的,深有体会,一件事情坚持一直做下去,总会有许多的收获,时间长了,慢慢的你会发现,在很多人心中你已经很厉害了。就像写博客的这一年多,除了我自己收获许多积累许多之外,我还意外收获了35个粉丝!最后想说的是,前端是丰富多彩的,你可以融入其中,但不能只停留在这一块领域,你想要更好的发展,你必须多元化发展,比如做一个会JAVA后台的前端,做一个偏UI的前端,做一个很懂技术的产品经理,这些都将是你的优势,很多企业其实并不需要你会很高深的技术,很多项目其实也用不上多少高深的技术,很多时候,你能实现需求,能做出用户体验好的产品,能再特殊的阶段兼任某个岗位,能做一些别人做不了的事情,你的存在就更有价值!总之在技术上要抓住一个重点,也要发散自己的技术点,走更宽的路,同时技术人也要注重情商的提高..

共勉

程序员最重要的是完美实现需求,技术有时候只是工具。