在A(yíng)SP.NET MVC項(xiàng)目中使用RequireJS庫(kù)的用法示例
RequireJS 是一個(gè)前端模塊化開(kāi)發(fā)的流行工具,本身是一個(gè)Javascript的庫(kù)文件,即require.js 。
RequireJs的主要功能:
(1)實(shí)現(xiàn)js文件的異步加載,避免網(wǎng)頁(yè)失去響應(yīng);
(2)管理模塊之間的依賴(lài)性,便于代碼的編寫(xiě)和維護(hù)。
前端模塊化開(kāi)發(fā)現(xiàn)在有好多的工具,大體上分為兩類(lèi),一類(lèi)是像dojo之類(lèi)的高大全,dojo v1.8之后已經(jīng)內(nèi)置了模塊化開(kāi)發(fā)組件;另一類(lèi)是像require.js,sea.js 這種專(zhuān)心做模塊化開(kāi)發(fā)的工具。
從模塊化劃分的規(guī)則來(lái)區(qū)分,主要分為AMD、CMD兩類(lèi),dojo、require.js 遵從前者,而sea.js 依循CMD規(guī)范。
require在單頁(yè)面應(yīng)用中能夠如魚(yú)得水,然而對(duì)于傳統(tǒng)的多頁(yè)面應(yīng)用,使用require多少會(huì)有些困惑和不方便。
本文講解如何在A(yíng)SP.NET MVC的結(jié)構(gòu)中應(yīng)用require,并且給出了壓縮腳本,實(shí)現(xiàn)半自動(dòng)化壓縮。
將js代碼分離
一般而言ASP.NET MVC的一個(gè)路由對(duì)應(yīng)一個(gè)視圖,視圖的文件結(jié)構(gòu)可能如下:
Views |--Shared |--_layout.cshtml |--Home |--Index.cshtml |--Blog |--Create.cshtml |--Edit.cshtml |--Detail.cshtml |--Index.cshtml
這里假設(shè)_layout.cshtml是所有頁(yè)面共享的。一般情況下,我們會(huì)在_layout中引用公共的js類(lèi)庫(kù),比如jQuery,bootstrap等,這樣的話(huà)其他的頁(yè)面就不需要對(duì)這些類(lèi)庫(kù)再引用一遍,提高了編碼的效率。然而,不同的頁(yè)面終究會(huì)依賴(lài)不同的js,尤其是實(shí)現(xiàn)頁(yè)面本身功能的自定義的js,這樣我們不得不在其他頁(yè)面中再引用特殊的js,甚至將js直接寫(xiě)在頁(yè)面中,例如下面的代碼經(jīng)常會(huì)出現(xiàn)在View中:
<script type="text/javascript">
$(function(){...});
</script>
這樣會(huì)導(dǎo)致頁(yè)面比較混亂,而且頁(yè)面<script>標(biāo)簽中代碼不能被瀏覽器緩存,增加了頁(yè)面代碼的長(zhǎng)度。更為重要的缺陷是,諸如jQuery之類(lèi)的類(lèi)庫(kù)會(huì)在加載到頁(yè)面后執(zhí)行匿名函數(shù),這需要一些時(shí)間,而如果有些頁(yè)面根本不需要jQuery的話(huà),只要頁(yè)面把_layout作為布局頁(yè)面,那么jQuery的初始化代碼將不可避免的執(zhí)行,這是一種浪費(fèi)。事實(shí)上,javascript的模塊化加載的思想就是為了解決這些問(wèn)題的。
接下來(lái)我們來(lái)用require規(guī)劃我們的js,構(gòu)建諸如下面結(jié)構(gòu)的js目錄
js |--app |--home.index.js |--blog.create.js |--blog.edit.js |--blog.detail.js |--blog.index.js |--jquery.js |--bootstrap.js |--underscore.js |--jquery.ui.js |--jquery.customplugin.js |--config.js |--require.js
把公共的類(lèi)庫(kù)級(jí)別的js模塊直接放在js目錄下,而把頁(yè)面級(jí)別的js放在一個(gè)app的子目錄下。注意,在app中,每個(gè)頁(yè)面一個(gè)js文件,這意味著我們需要把頁(yè)面各自的js提取出來(lái),雖然這樣增加了結(jié)構(gòu)復(fù)雜度,但是避免了在頁(yè)面中隨手寫(xiě)<script>標(biāo)簽的陋習(xí)。另外,在js目錄下的公共庫(kù),除了第三方的庫(kù),還包括自己開(kāi)發(fā)的庫(kù),還有一個(gè)叫config.js的文件,這個(gè)文件很關(guān)鍵,稍后會(huì)說(shuō)到。
然后,我們可以刪除_layout中所有的js引用,并使用@RenderSection的命令要求子頁(yè)面提供js引用,_layout.cshtml:
<head>
...
@RenderSection("require_js_module", false)
...
</head>
這樣對(duì)js的需求就下放到每個(gè)view頁(yè)面中了,根據(jù)require的用法,我們需要在各個(gè)子View中引用require.js,并指定主模塊,而這些主模塊就是上面app目錄下的一個(gè)個(gè)js
@section require_js_module{
<script src="@Url.Content("~/js/require.js")" data-main="@Url.Content("~/js/app/home.index.js")" ></script>
}
所有的js代碼都將寫(xiě)到app下的js中,這樣規(guī)范了js,使得頁(yè)面更干凈,更為重要的是這些js還可以經(jīng)過(guò)壓縮,以及被瀏覽器緩存等,進(jìn)一步提高執(zhí)行效率
公共的config
我們知道主模塊除了使用require方法外,經(jīng)常需要通過(guò)require.config來(lái)配置其他模塊的路徑,甚至需要shim,例如下面的代碼經(jīng)常會(huì)出現(xiàn)在主模塊的開(kāi)頭:
require.config({
paths: {
"jquery": "lib/jquery.min",
"underscore": "lib/underscore.min",
"backbone": "lib/backbone.min"
},
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
requirejs.config({
paths: {
"jquery": "/js/jquery.min",
"bootstrap": "/js/bootstrap"
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: "jQuery.fn.popover"
}
}
});
config.js的寫(xiě)法沒(méi)有什么特別的,接下來(lái)只要在home.index.js中引用
require(['../config','jquery', 'bootstrap'], function () {
//main module code here
});
不過(guò)這樣寫(xiě)還是不對(duì)的,因?yàn)?,被主模塊依賴(lài)的模塊(這里的config,jquery,bootstrap),在加載的時(shí)候,加載順序是不確定的,但是又需要config模塊在其他模塊之前加載,怎么辦呢?一個(gè)折衷的方案是修改home.index.js,成為如下代碼:
require(['../config'], function () {
require(['home.index2']);
})
, define("home.index2", ['jquery', 'bootstrap'], function () {
//main module code here
})
使用一個(gè)命名的模塊home.index2作為過(guò)渡,在主模塊中手動(dòng)require,這樣可以保證config在主模塊執(zhí)行之前加載,也就使得home.index2在加載的時(shí)候已經(jīng)加載了config了。
壓縮
require提供一個(gè)壓縮工具,用于壓縮和合并js,詳情請(qǐng)移步至http://requirejs.org/docs/optimization.html。簡(jiǎn)單的說(shuō),require提供一個(gè)叫r.js的文件,通過(guò)本地的node程序(Node.js),執(zhí)行這個(gè)r.js并傳入一些參數(shù),即可自動(dòng)分析模塊互相之間的依賴(lài),以達(dá)到合并和壓縮的目的。同樣的,這對(duì)于單頁(yè)面應(yīng)用來(lái)說(shuō)是容易的,因?yàn)橹髂K只有一個(gè),但是對(duì)于多頁(yè)面又如何做呢?好在這個(gè)壓縮工具支持用一個(gè)配置文件來(lái)指導(dǎo)壓縮,這樣的話(huà),我們可以編寫(xiě)下面的配置腳本build.js:
var build = {
appDir: '../js',
baseUrl: '.',
dir: '../js-built',
mainConfigFile: '../js/config.js',
modules: [
//First set up the common build layer.
{
//module names are relative to baseUrl
name: 'config',
//List common dependencies here. Only need to list
//top level dependencies, "include" will find
//nested dependencies.
include: ["bootstrap", "config","jquery"]
},
//Now set up a build layer for each page, but exclude
//the common one. "exclude" will exclude nested
//the nested, built dependencies from "common". Any
//"exclude" that includes built modules should be
//listed before the build layer that wants to exclude it.
//"include" the appropriate "app/main*" module since by default
//it will not get added to the build since it is loaded by a nested
//require in the page*.js files.
{
name:"app/home.index",
exclude:["config"]
},
{
name:"app/blog.create",
exclude:["config"]
},
...
]
}
通過(guò)這個(gè)命令來(lái)執(zhí)行壓縮,壓縮的結(jié)果將被保存到j(luò)s-build目錄:
node.exe r.js -o build.js
build.js腳本實(shí)際上是一個(gè)js對(duì)象,我們將config加入公共模塊,而在各個(gè)主模塊中將其排除。這樣,所有的公共庫(kù)包括config將壓縮成一個(gè)js,而主模塊又不會(huì)包含多余的config。這樣可想而知,每個(gè)頁(yè)面在加載時(shí)最多只會(huì)下載兩個(gè)js,而且公共模塊的代碼會(huì)“按需執(zhí)行”。
執(zhí)行上面的腳本壓縮,需要安裝有node??梢栽趶倪@里下載。
自動(dòng)腳本
但是,隨著主模塊的增加,需要隨時(shí)跟蹤和修改這個(gè)build文件,這也是很麻煩的。于是,筆者基于node.js開(kāi)發(fā)了一個(gè)叫build-build.js的腳本,用來(lái)根據(jù)目錄結(jié)構(gòu)自動(dòng)生成build.js:
fs = require('fs');
var target_build = process.argv[2];
//console.log(__filename);
var pwd = __dirname;
var js_path = pwd.substring(0,pwd.lastIndexOf('\\')) + '\\js';
console.log('js path : ' + js_path);
var app_path = js_path + '\\app';
console.log('js app path : ' +app_path);
var app_modules = [];
var global_modules = [];
//build json object
var build = {
appDir: '../js',
baseUrl: '.',
dir: '../js-built',
modules: [
//First set up the common build layer.
{
//module names are relative to baseUrl
name: 'config',
//List common dependencies here. Only need to list
//top level dependencies, "include" will find
//nested dependencies.
include: []
}
]
}
fs.readdir(app_path,function (err,files) {
// body...
if (err) throw err;
for(var i in files){
//put module in app_modules
var dotindex = files[i].lastIndexOf('.');
if(dotindex >= 0){
var extension = files[i].substring(dotindex+1,files[i].length);
if(extension == 'js'){
app_modules.push({
name: 'app/' + files[i].substring(0,dotindex),
exclude: ['config']
});
}
}
}
for(var j in app_modules){
build.modules.push(app_modules[j]);
}
fs.readdir(js_path,function (err,files){
if (err) throw err;
for(var i in files){
//put module in app_modules
var dotindex = files[i].lastIndexOf('.');
if(dotindex >= 0){
var extension = files[i].substring(dotindex+1,files[i].length);
if(extension == 'js'){
global_modules.push(files[i].substring(0,dotindex));
}
}
}
build.modules[0].include = global_modules;
//console.log(build);
var t = pwd + '\\' + target_build;
console.log(t);
var fd = fs.openSync(t, 'w');
fs.closeSync(fd);
var json = JSON.stringify(build);
fs.writeFileSync(t, json);
});
});
這里的代碼并不復(fù)雜,主要是遍歷目錄,生成對(duì)象,最后將對(duì)象序列化為build.js。讀者可以自行閱讀并修改。最后,編寫(xiě)一個(gè)bat,完成一鍵壓縮功能,build.bat:
@echo off set PWD=%~p0 set PWD=%PWD:\=/% cd "D:\node" node.exe %PWD%build-build.js build.js node.exe %PWD%r.js -o %PWD%build.js cd %~dp0
這樣,我們就簡(jiǎn)單實(shí)現(xiàn)了一個(gè)方便的多頁(yè)面require方案,最后項(xiàng)目目錄可能是這樣的:
Views |--Shared |--_layout.cshtml |--Home |--Index.cshtml |--Blog |--Create.cshtml |--Edit.cshtml |--Detail.cshtml |--Index.cshtml build |--build.js |--r.js |--build-build.js |--build.bat js |--app |--home.index.js |--blog.create.js |--blog.edit.js |--blog.detail.js |--blog.index.js |--jquery.js |--bootstrap.js |--underscore.js |--jquery.ui.js |--jquery.customplugin.js |--config.js |--require.js
相關(guān)文章
Javascript實(shí)現(xiàn)檢測(cè)客戶(hù)端類(lèi)型代碼封包
在以前,總是以為使用用戶(hù)代理字符串檢測(cè)瀏覽器是那種類(lèi)型就行了,這樣確實(shí)大錯(cuò)特錯(cuò)啊,下面就來(lái)說(shuō)說(shuō)如何通過(guò)js判斷出當(dāng)前瀏覽者使用的的設(shè)備類(lèi)型呢2015-12-12
JavaScript使用百度ECharts插件繪制餅圖操作示例
這篇文章主要介紹了JavaScript使用百度ECharts插件繪制餅圖操作,結(jié)合實(shí)例形式分析了JavaScript使用百度ECharts插件繪制餅圖的原理、步驟及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-11-11
from 表單提交返回值用post或者是get方法實(shí)現(xiàn)
from 表單提交的返回值可以用jquery的post或者是get方法去實(shí)現(xiàn),具體如下,感興趣的朋友可以參考下,希望對(duì)大家有所幫助2013-08-08
JS獲取checkbox的個(gè)數(shù)簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇JS獲取checkbox的個(gè)數(shù)簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08
如何利用JavaScript實(shí)現(xiàn)二叉搜索樹(shù)
這篇文章主要給大家介紹了關(guān)于如何利用JavaScript實(shí)現(xiàn)二叉搜索樹(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04

