我的一個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');
	});
});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM