单页Web应用 4 添加功能模块

    功能模块向单页应用提供了精心定义和有作用域限制的功能。除了聊天滑块之外,还有其他功能模块的例子,包括图片查看器、账户管理面板或者是用户集中放置图形对象的工作台。

    和第三方模块的做法很像:精心定义的API和强隔离性。可在多个项目之间很容易地重用模块。

4.1 功能模块策略

     模块有自己的视图、控制器和Shell在它们之间共享的部分模型。

     功能模块的例子包括在工作台上处理草图的spa.wb.js、管理账户功能的spa.acct.js(像登入或登出)和用于聊天界面的spa.chat.js。


     与第三方模块的比较:参考《Third-Party JavaScript》

          包括博客评论姓(DisQus或者LiveFyre)、广告型的(DoubleClick或者ValueClick)、分析型的(Google或者Overture)、分享型的(AddThis或ShareThis)和社交服务型的(赞)。它们都非常流行,因为网站管理员可以把这些高质量的功能添加到他们的网站里面,和自己来开发这些功能相比。

          精心编写的第三方模块具有以下共同特征:

  • 在自己的容器内渲染
  • 提供了精心定义的API,以便控制它们的行为
  • 通过将自己的JavaScript、数据和CSS精心地隔离,避免污染主页面

           它的缺点也有多。

          我们的功能模块没有使用第三方模块,向Shell提供一致的配置、初始化和调用的API。通过使用唯一的和协调的JavaScript和CSS名字空间,功能之间相互隔离,除了共享的工具方法外,不允许任何外部调用。

         像第三方模块一样来开发自己的模块,还有一个巨大的优势:我们处于一种有利的情况,Web应用的非核心功能使用第三方模块,然后在时间和资源允许时,有选择性地使用自己的功能模块来替换它们,这样就能更好地集成、运行更快、侵入性更小,或者是以上全部的好处。

      功能模块和分型MVC模式:分形是一种模式,它在所有层级上显示为自相似性。我们的单页应用架构在多个层级上采用重复的MVC模式,所以我们把它叫做”分形模型-视图-控制器“,或者是FMVC

      应用被分割为两部分:服务器采用MVC模式向客户端提供数据;采用MVC的单页应用允许用户查看浏览器的模型,并与之交互。服务器的模型是从数据库获取的数据,而视图是要发送给浏览器的数据表现,控制器是协调数据管理和同浏览器通信的代码。在客户端,模型包括从服务器接收到的数据,视图是用户界面,控制器是协调客户端数据和界面的逻辑。


      几乎所有的现代网站都适用这种模式,即便开发人员没有意识到这一点。比如,一旦开发人员把DisQus或者LiveFyre的评论模块添加到他们的博客中,他们就添加了另外一个MVC模式。

4.2 创建功能模块文件

      规划文件结构

  • 为Chat模块创建一个有名字空间的样式表
  • 为Chat模块创建一个有名字空间的JavaScript模块js/spa.chat.js,js/spa.model.js
  • 为浏览器端的模型创建一个桩文件(stub)css/spa.chat.css
  • 创建一个提供通用程序的共用模块,供其他所有模块使用js/spa.util.js。
  • 修改浏览文档,引入新的文件。
  • 删除用来开发布局的文件。

      桩文件:css/spa.chat.css,桩是一个故意没有完成的或者是占位用的资源

      文档加载约定:根-》核心工具方法-》Model-》浏览器端工具方法-》Shell-》功能模块

      为什么自己的库要放在最后加载,因为防止第三方库声明名字空间spa.model。

     

4.3 设计方法API

      功能模块之间的相互调用是不允许的。功能模块的唯一数据源或者功能只能来自Shell,在配置和初始化期间以参数的形式传给模块的公开方法。

      锚接口模式


       Chat的配置API:JS中所有复杂数据类型(对象、数组和函数)传递的是引用。

  1. 一个提供”修改URI锚中的chat参数"的功能的函数
  2. 一个提供“发送和接收消息(来自model)”的方法的对象
  3. 一个提供“与一系列用户(来自Model)交互”的方法的对象。
  4. 许多行为设置,比如滑块打开时的高度,滑块的打开时间以及滑块的关闭时间。

        js/spa.chat.js中API规范, configModele, 

       配置和初始化的级联:所有的模块都有公开的initModule方法。只在需要支持设置时,才会提供configModule方法。


4.4 实现功能API

       样式表:css/spa.chat.css


       修改Chat:API实现。js/spa.chat.js


4.5 添加经常使用的方法

      重置方法(removeSlider)和窗口尺寸变化的方法(handleResize)

      如果用户登出的时候,彻底移除聊天滑块。需要删除Chat添加的DOM容器,依次释放初始化和配置信息。

      窗口有些情况下不能工作,需要一些计算。

spa.html

<!doctype html>
<html>
<head>
  <!-- ie9+ rendering support for latest standards -->
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <title>SPA Chapter 4</title>
  <!-- third-party stylesheets -->

  <!-- our stylesheets -->
  <link rel="stylesheet" href="css/spa.css" type="text/css" />
  <link rel="stylesheet" href="css/spa.chat.css" type="text/css" />
  <link rel="stylesheet" href="css/spa.shell.css" type="text/css" />

  <!-- third-party javascript -->
  <script src="js/jq/jquery-3.2.1.js" ></script>
  <script src="js/jq/jquery.uriAnchor.js" ></script>

  <!-- our javascript -->
  <script src="js/spa.js" ></script>
  <script src="js/spa.util.js" ></script>
  <script src="js/spa.model.js" ></script>
  <script src="js/spa.shell.js" ></script>
  <script src="js/spa.chat.js" ></script>
  <script>
    $(function () { 
    	spa.initModule( $('#spa') );
    });
  </script>
</head>
<body>
  <div id="spa"></div>
</body>
</html>
css/spa.css

/** 重置大多数选择器,我们不信任浏览器的默认行为 */
* {
	margin:  0;
	padding: 0;
	-webkit-box-sizing : border-box;
	-moz-box-sizing    : border-box;
	box-sizing         : border-box;
}
h1, h2, h3, h4, h5, h6, p { margin-bottom: 6pt ; }
o1, ul, dl { list-style-position : inside ; }

/** 希望确保跨平台应用有一致的外观。 */
body {
	font : 13px 'Trebuchet MS', Verdana, Helvetica, Arial, sans-serif;
	color   : # 444;
	background-color: #888;
}
a {
	text-decoration: none;
}
a:link, a:visited { color : inherit; }
a:hover { text-decoration : underline; }

strong {
	font-weight:  800;
	color : #000;
}

/** 通常使用根名字作为元素选择器,定义选择器的名字空间*/
#spa {
	position    : absolute;
	top         : 0;
	left        : 0;
	bottom      : 0;
	right       : 0;

	min-height: 15em;
	min-width: 35em;
	overflow : hidden;

	background : #fff;
}

/** 其他模块,以spa-x-作为前缀*/
.spa-x-select {}
.spa-x-clearfloat {
	height      : 0       !important;
	float       : none    !important;
	visibility  : hidden  !important;
	clear       : both    !important;
}
css/spa.shell.css

.spa-shell-head, .spa-shell-head-logo, .spa-shell-head-acct,
.spa-shell-head-search, .spa-shell-main, .spa-shell-main-nav, 
.spa-shell-main-content, .spa-shell-foot, 
.spa-shell-modal {
	position : absolute;
}
.spa-shell-head {
	top     : 0;
	left    : 0;
	right   : 0;
	height  : 40px;
}
.spa-shell-head-logo {
	top     : 4px;
	left    : 4px;
	right   : 32px;
	width   : 128px;
	background  : orange;
}
.spa-shell-head-acct {
	top     : 4px;
	right    : 0;
	height   : 32px;
	width   : 64px;
	background  : green;
}
.spa-shell-head-search {
	top     : 4px;
	right    : 64px;
	height   : 32px;
	width   : 248px;
	background  : blue;
}
.spa-shell-main {
	top     : 40px;
	left    : 0;
	right   : 0;
	bottom  : 40px;
}
.spa-shell-main-content,
.spa-shell-main-nav {
	top     : 0px;
	bottom  : 0px;
}
.spa-shell-main-nav {
	width     : 250px;
	background  : #eee;
}/*使用父类来影响子元素。这大概是CSS的一个最强大的功能,但几乎没被频繁的使用*/
  .spa-x-closed,
  .spa-shell-main-nav {
   	  width     : 0px;
  }
  
.spa-shell-main-content {
	left    : 250px;
	right   : 0;
	background  : #ddd;
}/*缩进派生选择器,紧跟在父选择器的下面*/
  .spa-x-closed,
  .spa-shell-main-content {
	  left     : 0px;
  }
.spa-shell-foot {
	height     : 40px;
	left    : 0;
	right   : 0;
	bottom  : 0;
}

.spa-shell-modal {
	margin-top : -200px;
    margin-left : -200px;
    top     : 50%;
    left :   50%;
	width   : 400px;
	height  : 400px;
	background : #fff;
	z-index  : 2;
	display : none;
}
css/spa.chat.css

/* Chat feature styles
 */

.spa-chat {
	position:  absolute;
	bottom: 0;
	right: 0;
	width: 25em;
	height: 2em;
	background: #fff;
	border-radius: 0.5em 0 0 0;
	border-style: solid;
	border-width: thin 0 0 thin;
	border-color: #888;
	box-shadow: 0 0 0.75em 0 #888;
	z-index : 1 ;
}

.spa-chat-head, spa-chat-closer {
	position: absolute;
	top     : 0;
	height: 2em;
	line-height: 1.8em;
	border-bottom: thin solid #888;
	cursor   : pointer;
	background : #888;
	color: white;
	font-family: arial, helvetica, sans-serif;
	font-weight: 800;
	text-align: center;
}

.spa-chat-head {
	left: 0;
	right: 2em;
	border-radius: 0.3em 0 0 0;
}

.spa-chat-closer {
	right: 0;
	width: 2em;
}
  .spa-chat-closer:hover {
  	background: #800;
  }

.spa-chat-head-toggle {
	position: absolute;
	top: 0;
	left: 0;
	width: 2em;
	bottom: 0;
	border-radius: 0.3em 0 0 0;
}

.spa-chat-head-title {
	position: absolute;
	left: 50%;
	width: 16em;
	margin-left: -8em;
}

.spa-chat-sizer {
	position: absolute;
	top: 2em;
	left: 0;
	right: 0;
}

.spa-chat-msgs {
	position: absolute;
	top: 1em;
	left: 1em;
	right: 1em;
	bottom: 4em;
	padding: 0.5em;
	border : thin solid #888;
	overflow-x: hidden;
	overflow-y: scroll;
}

.spa-chat-box {
	position: absolute;
	height: 2em;
	left: 1em;
	right: 1em;
	bottom: 1em;
	border : thin solid #888;
	background: #888;
}

.spa-chat-box input[type=text] {
	float: left;
	width: 75%;
	height: 100%;
	padding: 0.5em;
	border : 0;
	background: #ddd;
	color: #404040;
}
  .spa-chat-box input[type=text]:focus {
  	background: #fff;
  }

.spa-chat-box div {
	float: left;
	width: 25%;
	height: 2em;
	line-height: 1.9em;
	text-align: center;
	color: #fff;
	font-weight: 800;
	cursor: pointer;
}
  .spa-chat-box div:hover {
  	background-color: #444;
  	color: #ff0;
  }

.spa-chat-head:hover .spa-chat-head-toggle {
	background: #aaa;
}
js/spa.js

/*jslint    browser : true, continue : true,
    devel : true, indent : 2, maxerr : 50,
    newcap : true, nomen : true, plusplus : true,
    regexp : true, sloppy : true, vars : true,
    white : true
*/

var spa = (function ( ) {
	// 初始化
	var initModule = function ( $container ) {
		spa.shell.initModule( $container );
	};
	return { initModule : initModule };  //返回spa名字空间中的对象,只导出了initModele方法
}());
js/spa.shell.js

/*jslint    browser : true, continue : true,
    devel : true, indent : 2, maxerr : 50,
    newcap : true, nomen : true, plusplus : true,
    regexp : true, sloppy : true, vars : true,
    white : true
*/
spa.shell = (function () {
	var configMap = {
		anchor_schema_map : { 
			chat : {opened : true, closed : true }
		},
		main_html : String() 
		  + '<div class="spa-shell-head">'
		    + '<div class="spa-shell-head-logo"></div>'
		    + '<div class="spa-shell-head-acct"></div>'
		    + '<div class="spa-shell-head-search"></div>'
		  + '</div>'
		  + '<div class="spa-shell-main">'
		    + '<div class="spa-shell-main-nav"></div>'
		    + '<div class="spa-shell-main-content"></div>'
		  + '</div>'
		  + '<div class="spa-shell-foot"></div>'
		  + '<div class="spa-shell-modal"></div>',
		resize_interval : 200
	},
	stateMap = { 
		anchor_map  : {},
		resize_idto : undefined
	},  /*在整个模块中共享的动态信息*/
	jqueryMap = { },
	setJqueryMap,  initModule, 
	copyAnchorMap, changeAnchorPart, 
	onHashchange,  setChatAnchor, onResize;
	// 将创建和操作页面元素的函数放在"DOM Methods"区块中
	copyAnchorMap = function () {
		return $.extend( true, {}, stateMap.anchor_map );
	};
	changeAnchorPart = function (arg_map) {
		var anchor_map_revise = copyAnchorMap();
		var bool_return = true;
		var key_name, key_name_dep;
		KEYVAL:
		for (key_name in arg_map) {
			if (arg_map.hasOwnProperty( key_name )) {
				if (key_name.indexOf('_') === 0) { continue KEYVAL; }
				anchor_map_revise[key_name] = arg_map[key_name];
				key_name_dep = '_' + key_name;
				if (arg_map[key_name_dep]) {
					anchor_map_revise[key_name_dep] = arg_map[key_name_de];
				} else {
					delete anchor_map_revise[key_name_dep];
					delete anchor_map_revise['_s' + key_name_dep];
				}
			}
		}
		//
		try {
			$.uriAnchor.setAnchor( anchor_map_revise );
		} catch (error) {
			$.uriAnchor.setAnchor( stateMap.anchor_map, null, true );
			bool_return = false;
		}
		return bool_return;
	};
	onHashchange = function (event) {
		var anchor_map_previous = copyAnchorMap();
		var anchor_map_proposed, _s_chat_previous;
		var _s_chat_proposed, s_chat_proposed;
		var is_ok = true;
		//
		try {
			anchor_map_proposed = $.uriAnchor.makeAnchorMap(); 
		} catch (error) {
			$.uriAnchor.setAnchor( anchor_map_previous, null, true );
			return false;
		}
		stateMap.anchor_map = anchor_map_proposed;
		//
		_s_chat_previous = anchor_map_previous._s_chat;
		_s_chat_proposed = anchor_map_proposed._s_chat;
		//
		if ( ! anchor_map_previous 
			|| _s_chat_previous !== _s_chat_proposed ) {
			s_chat_proposed = anchor_map_proposed.chat;
		    switch (s_chat_proposed) {
		    	case 'opened':
		    	  is_ok = spa.chat.setSliderPosition('opened');
		    	break;
		    	case 'closed':
		    	  is_ok = spa.chat.setSliderPosition('closed');
		    	break;
		    	default:
		    	is_ok = spa.chat.setSliderPosition('closed');
		    	delete anchor_map_proposed.char;
		    	$.uriAnchor.setAnchor( anchor_map_proposed, null, true );
		    }
		}
		if (!is_ok) {
			if (anchor_map_previous) {
				$.uriAnchor.setAnchor( anchor_map_previous, null, true );
				stateMap.anchor_map = anchor_map_previous;
			} else {
				delete anchor_map_proposed.chat;
				$.uriAnchor.setAnchor( anchor_map_proposed, null, true );
			}
		}
		return false;
	};
	// Event handler
	onResize = function () {
		if (stateMap.resize_idto) { return true; }
		spa.chat.handleResize();
		stateMap.resize_idto = setTimeout(
			function () {
				stateMap.resize_idto = undefined;
			}, configMap.resize_interval 
		);
		return true;
	};

	// Begin DOM Methods
	setJqueryMap = function () {    
	    //缓存jQuery集合,几乎我们编写的每个Shell和功能模块都应该有这个函数
	    //可以大大地减少jQuery对文档的遍历次数,能够提高性能。
		var $container = stateMap.$container;
		jqueryMap = { 
			$container : $container
		};
	};


    setChatAnchor = function (position_type) {
    	return changeAnchorPart(
    		{ chat : position_type }
    	);
    }
	initModule = function ( $container ) {
		stateMap.$container  = $container;
		$container.html( configMap.main_html );
		setJqueryMap();

		$.uriAnchor.configModule( {
			schema_map : configMap.anchor_schema_map
		});
		// configure and initialize feature modules
		spa.chat.configModule( {
			chat_model : spa.model.chat,
			set_chat_anchor : setChatAnchor,
			people_model : spa.model.people
		} );
		spa.chat.initModule( jqueryMap.$container );
		//
		$(window) 
		   .bind( 'hashchange', onHashchange )
		   .trigger( 'hashchange' );
	};
	return { initModule : initModule };
} ());

js/spa.util.js

/*jslint    browser : true, continue : true,
    devel : true, indent : 2, maxerr : 50,
    newcap : true, nomen : true, plusplus : true,
    regexp : true, sloppy : true, vars : true,
    white : true
*/

spa.util = (function () {
	var makeError, setConfigMap;
	makeError = function ( name_text, msg_text, data ) {
		var error     = new Error();
		error.name    = name_text;
		error.message = msg_text;
		if (data) {
			error.data = data;
		}
		return error;
	};
	setConfigMap = function (arg_map) {
		var input_map = arg_map.input_map;
		var settable_map  = arg_map.settable_map;
		var config_map = arg_map.config_map;
		var key_name, error;

		for ( key_name in input_map ) {
			if (input_map.hasOwnProperty( key_name ) ) {
				if (settable_map.hasOwnProperty( key_name ) ) {
					config_map[key_name] = input_map[key_name];
				} else {
					error = makeError( 'Bad Input', 'Setting config key |' + key_name + '| is not supported');
					throw error;
				}
			}
		}
	};

	return {
		makeError     : makeError,
		setConfigMap  : setConfigMap
	};
}());

js/spa.model.js

/*jslint    browser : true, continue : true,
    devel : true, indent : 2, maxerr : 50,
    newcap : true, nomen : true, plusplus : true,
    regexp : true, sloppy : true, vars : true,
    white : true
*/

spa.model = (function () {
	return {};
}());
js/spa.chat.js

/*jslint    browser : true, continue : true,
    devel : true, indent : 2, maxerr : 50,
    newcap : true, nomen : true, plusplus : true,
    regexp : true, sloppy : true, vars : true,
    white : true
*/

/*spa.chat名字空间*/
spa.chat = (function () {
	var configMap = {
		main_html : String() 
		  + '<div class="spa-chat">'
		    + '<div class="spa-chat-head">'
		      + '<div class="spa-chat-head-toggle"> + </div>'
		      + '<div class="spa-chat-head-title">'
		        + 'Chat'
		      + '</div>'
		    + '</div>'
		    + '<div class="spa-chat-closer">x</div>'
		    + '<div class="spa-chat-sizer">'
		      + '<div class="spa-chat-msgs"></div>'
		      + '<div class="spa-chat-box">'
		        + '<input type="text"/>'
		        + '<div>send</div'
		      + '</div>'
		    + '</div>'
		  + '</div>',
		settable_map : { 
			slider_open_time   : true,
			slider_close_time  : true,
			slider_opened_em   : true,
			slider_closed_em   : true,
			slider_opened_title : true,
			slider_closed_title : true,

			chat_model     : true,
			people_model   : true,
			set_chat_anchor  :true
		},
		slider_open_time   : 250,
		slider_close_time  : 250,
		slider_opened_em   : 18,
		slider_closed_em   : 2,
		slider_opened_title : 'Click to close',
		slider_closed_title : 'Click to open',
		slider_opened_min_em : 10,   //最小高度
		window_height_min_em : 20,

		chat_model   : null,
		people_model : null,
		set_chat_anchor : null
	},
	stateMap = { 
		$append_target  : null,
		position_type   : 'closed',
		px_per_em       : 0,
		slider_hidden_px : 0,
		slider_closed_px : 0,
		slider_opened_px : 0 
	},
	jqueryMap = {

	},
	setJqueryMap, configModule, initModule,
	getEmSize, setPxSizes, setSliderPosition,
	onClickToggle, removeSlider, handleResize;
	//Begin utility methods
	getEmSize = function ( elem ) {
		return Number(
			getComputedStyle( elem, '' ).fontSize.match(/\d*\.?\d*/)[0]
		);
	};

	//Begin DOM method 
	setJqueryMap = function() {
		var $append_target = stateMap.$append_target;
		var $slider = $append_target.find('.spa-chat');
		jqueryMap = { 
			$slider    : $slider,
			$head      : $slider.find('.spa-chat-head'),
			$toggle    : $slider.find('.spa-chat-head-toggle'),
			$title     : $slider.find('.spa-chat-head-title'),
			$sizer     : $slider.find('.spa-chat-sizer'),
			$msgs      : $slider.find('.spa-chat-msgs'),
			$box       : $slider.find('.spa-chat-box'),
			$input     : $slider.find('.spa-chat-input input[type-text]') 
		};
	};
	setPxSizes = function () {
		var px_per_em, opened_height_em, window_height_em;
		px_per_em = getEmSize( jqueryMap.$slider.get(0) );
		window_height_em = Math.floor( ( $(window).height() / px_per_em ) + 0.5 );
		opened_height_em = window_height_em > configMap.window_height_min_em 
		  ? configMap.slider_opened_em 
		  : configMap.slider_opened_min_em;
		//opened_height_em = configMap.slider_opened_em;
		stateMap.px_per_em = px_per_em;
		stateMap.slider_closed_px = configMap.slider_closed_em * px_per_em;
		stateMap.slider_opened_px = opened_height_em * px_per_em;
		jqueryMap.$sizer.css ( {
			height  : (opened_height_em - 2) * px_per_em
		});
	};
	handleResize = function () {
		if (!jqueryMap.$slider) { return false; }
		setPxSizes();
		if (stateMap.position_type === 'opened') {
			jqueryMap.$slider.css({
 				height : stateMap.slider_opened_px
			});
		}
		return true;
	};

	setSliderPosition = function (position_type, callback) {
		var height_px, animate_time, slider_title, toggle_text;
		if (stateMap.position_type === position_type) {
			return true;
		}
		switch (position_type) {
			case 'opened':
			   height_px = stateMap.slider_opened_px;
			   animate_time = configMap.slider_open_time;
			   slider_title = configMap.slider_opened_title;
			   toggle_text = '=';
			break;

			case 'hidden':
			   height_px = 0;
			   animate_time = configMap.slider_open_time;
			   slider_title = '';
			   toggle_text = '+';
			break;

			case 'closed':
			   height_px = stateMap.slider_closed_px;
			   animate_time = configMap.slider_close_time;
			   slider_title = configMap.slider_closed_title;
			   toggle_text = '+';
			break;

			default : return false;
		}
		// animate slider position change
		stateMap.positiong_type = '';
		jqueryMap.$slider.animate(
       		{ height : height_px },
       		animate_time,
       		function () {
       			jqueryMap.$toggle.prop('title', slider_title);
       			jqueryMap.$toggle.text( toggle_text );
       			stateMap.position_type = position_type;
       			if (callback) { callback( jqueryMap.$slider ); }
       		}
		);
		return true;
	};
	//Begin event handlers
	onClickToggle = function ( event ) {
		var set_chat_anchor = configMap.set_chat_anchor;
		if (stateMap.position_type === 'opened') {
			set_chat_anchor('closed');
		} else if (stateMap.position_type === 'closed') {
			set_chat_anchor('opened');
		}
		return false;
	};

	//Begin public methods
	configModule = function ( input_map ) {
		spa.util.setConfigMap({
			input_map    : input_map,
			settable_map : configMap.settable_map,
			config_map   : configMap
		});
		return true;
	};
	//
	removeSlider = function () {
		if (jqueryMap.$slider) {
			jqueryMap.$slider.remove();
			jqueryMap = {};
		}
		stateMap.$append_target = null;
		stateMap.position_type = 'closed';
		// unwind key configurations
		configMap.chat_model = null;
		configMap.people_model = null;
		configMap.set_chat_anchor = null;
		return true;
	};
	initModule = function ( $append_target ) {
		$append_target.append( configMap.main_html );
		stateMap.$append_target = $append_target;
		setJqueryMap();
		setPxSizes();

		jqueryMap.$toggle.prop('title', configMap.slider_closed_title);
		jqueryMap.$head.click( onClickToggle );
		stateMap.position_type = 'closed';
		return true;
	};
	//这两个方法几乎是所有功能模块的标配方法
	return {
		setSliderPosition : setSliderPosition,
		configModule : configModule,
		initModule   : initModule,
		removeSlider : removeSlider,
		handleResize : handleResize
	};
}());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值