• 我的一个javascript项目的重构历程


    一个月前,组内的一个内部使用的浏览器比价插件的前端部分交给我来维护,作为一个老司机我是拒绝的,自己的代码都是坑,还要去给别人填坑,搞笑地说。

    呵呵,能拒绝么。。。。

    好好享受吧,骚年。。。。。。

    第一次修改

    看到代码的那一刻我惊呆了,就一个js文件,接近2000行的代码。这个还好,比这个行数多的我见的多了,这个还吓不到我。有哪些问题,一会再说。

    因为从我接手的那一刻算起,几天后就要发新版本,我只要也只能调一些样式。代码重构,想都不要想,有想法我也得掐死。

    我以为很简单,很快就能完成,too young too naive,上代码:

    .hover(function(){
        $("").css("border-top", "1px solid #5589d4");
        $("").css("border-bottom", "1px solid #f4f4f4");
        $("").css("border-left", "1px solid #5589d4");
        $("#").css("border-right", "1px solid #f4f4f4");
        $("#").css("border-left", "1px solid #e3e3e3");
        $("#").css("border-right", "1px solid #5589d4");
    });
    

    文件内大量的这种内联样式写法,是不是很666,而且好多地方的内联样式代码还是重复的,我要是直接在上面改,能累死烦死。

    基于样式表现和行为逻辑分离的原则,慢慢改吧,还好,慢慢调,两个工作日基本完成了。

    ok,提交测试,没问题。

    but,两天后,兄弟组在四级详情页新增的浮层抽奖效果,插件对其有影响,查代码

    //是这样
    $('.nodata').hide();
    //以及这样
    $('.no-text').hide();
    

    插件第一原则,never and never 影响或更改原有页面的结构、行为

    还好没有对外推,要不用户分分钟把插件卸了。所以临时改成了这样

    //是不是区别不大?但是重复率低啊
    $('.chm-x-nodata').hide();
    

    作为一个有轻微代码洁癖的人,当时我就想,有时间我一定把丫重构了。

    不过理智告诉我,先这么着吧,千万不要再来新版本了,这是一个大坑。

    重构

    墨菲定律,该来的还得来。

    这次是大改版,增加对某网站的支持。

    ok,这次时间足够了,重构。

    1. 根据业务将js文件拆开,用gulp构建。

    项目目录结构如下:

    -plugin
    -- build
    ---- chrome     //chrome插件模块
    ---- plug       //内容
    ------ dist
    --------- js    //生成的js文件,包括.min.js
    ------ css      //原来的css文件
    ------ images
    ------ js       //其他js
    
    -- js_src
    ---- 01-x.js    //公共变量、方法
    ---- 02-mod.js  //模块化机制封装
    ---- .....js    //子逻辑
    ---- 90-main.js //主要逻辑
    ---- 99-init.js //入口文件
    
    -- gulpfile.js
    -- package.json
    

    有意思的是,插件对象(x)内部定义了requiredefine对象,我以为是采用了模块化机制,实际上就是使用了一层皮。

    (function(x) {
        x.mod={};
    	//构建模块化机制
    	var defined = function(name, module) {
    		x.modules[name] = module;
    	}
    	var require = function(name) {
    		var m = star.modules[name];
    		if (!m) {
    			return null;
    		} else if(typeof m === 'function'){
                //对象已实例化,直接返回
    			m = m.call(m);
    		}
    		return m;
    	}
    	x.mod.defined=defined;
    	x.mod.require=require;
    })(x);
    

    2.通用变量及方法的定义

    • 上面已经提到了,DOM操作用$,很容易误操作页面DOM。
    //因此在`01-x.js`中,对其进行封装。
    var x=(function(win){
        var _x={
            modules:{}
        };
        _x.$=function(str){
    		if (str === 'body' || typeof str === 'object') return $(str);
            //对根DOM要添加此类
    		return $('.x-root').find(str);        
        }
        
    })(window);
    //调用
    (function(x) {
    	'use strict';
    	var require=x.mod.require,defined=x.mod.defined;
    	var qs=x.$;
        //这样对页面的影响几乎降到了最低。
        var m=qs("xxx");
    })(x)
    
    • 另外locastorage也未做封装,loginIDpassword居然直接存,是不是醉了。
    /**
     * @description 封装localStorage对象,防止对外暴露
     */
    if (!localStorage.getItem('x')) localStorage.setItem('x', JSON.stringify({}));
    x.getItem = function(key) {
        return JSON.parse(localStorage.getItem('x'))[key];
    };
    x.setItem = function(key, val) {
        var o = JSON.parse(localStorage.getItem('x'));
        o[key] = val;
        localStorage.setItem('x', JSON.stringify(o));
    };
    
    • 由于线上和测试的请求链接不同,原代码是用${x-Base-link}占位,上线前,测试时,都要批量替换,累不累啊亲。直接在配置文件中定义好就可以了啊。

    3.其他代码的整理

    • 封装的ajax有同步的也有异步的方法,用的很混乱。居然有三种定义。。。
    //为了代码统一,全部采用异步
    //实际上用$.ajax更好,拼接url很讨厌。
    (function(x) {
    	'use strict';
    	var qs=x.$;
    	var require=x.mod.require,defined=x.mod.defined;
        //工具模块
    	defined('Tool', function() {
    		return {
    			//统一使用异步
    			xHttpRequest: function(url, callback) {
    				return this.xHttpRequest_yb(url,callback);
    			}, //封装ajax操作---异步
    			xHttpRequest_yb: function(url, callback) {
    				var xhr = new XMLHttpRequest();
    				xhr.open("GET", url, true);
    				xhr.onreadystatechange = function() {
    					if (xhr.readyState == 4) {
    						callback(xhr.responseText);
    					}
    				}
    				return xhr.send();
    			},
                xFalseHttpRequest: function(url, callback) {
    				return this.xHttpRequest_yb(url,callback);
    			},
        );
    )(x);
    
    • 代码混乱,如DOM绑定事件居然有写在ajax内或同步的ajax后的。
    • 冗余的代码非常多
    //如判断对象为空的这种写法多次出现
    if(x.userId == "" || x.userId == null || x.userId == 'null')
    //封装一下就好了呀
    function isNullOrEpmty(str) {
    	return !str || str == ' ' || str == 'undefined' || str == 'null';
    }
    //另外同样的事件绑定,只是因为一两处逻辑的不同,居然又重新绑定了两次,代码只是更改了一点点啊
    

    结语

    有了基本的脉络后,实际上重构很快,三天多一点的时间就搞定了。坑填的差不多了,如若以后他人接手,希望坑不多。(逃


    对了,最后附一下gulp.js文件,一些童鞋可能还没有接触过。

    // 引入 gulp及组件
    var gulp = require('gulp'), //基础库
    	jshint = require('gulp-jshint'), //js检查
    	uglify = require('gulp-uglify'), //js压缩
    	rename = require('gulp-rename'), //重命名
    	concat = require('gulp-concat'), //合并文件
    	clean = require('gulp-clean'); //清空文件夹
    var jsDst = './build/plug/dist/js';
    var jsSrc='./js_src/*.js';
    // js处理
    gulp.task('jsTask', function() {
    		gulp.src(jsSrc)
    		.pipe(jshint())
    		.pipe(jshint.reporter('default'))
    		.pipe(concat('dist.js'))
            .pipe(gulp.dest(jsDst))
            .pipe(rename({ suffix: '.min' }))
            .pipe(uglify())
    		//.pipe(livereload(server))
    		.pipe(gulp.dest(jsDst));
    });
    // 清空图片、样式、js
    gulp.task('clean', function() {
    	gulp.src(jsDst, {read: false}).pipe(clean());
    });
    // 默认任务 清空图片、样式、js并重建 运行语句 gulp
    gulp.task('default', ['clean'], function() {
    	gulp.start('jsTask');
    });
    // 监听任务 运行语句 gulp watch
    gulp.task('watch', function() {
    	gulp.watch(jsSrc, function() {
    		gulp.run('jsTask');
    	});
    });
    
  • 相关阅读:
    第一次作业
    第0次作业
    第14、15周作业
    第七周作业
    第六周作业
    第四周作业
    第三周作业
    第4次作业
    第3次作业
    第二次作业
  • 原文地址:https://www.cnblogs.com/phillyx/p/5758361.html
Copyright © 2020-2023  润新知