Home >Web Front-end >JS Tutorial >Usage examples of using the RequireJS library in ASP.NET MVC projects_javascript tips
RequireJS is a popular tool for front-end modular development. It is a Javascript library file, namely require.js.
Main functions of RequireJs:
(1) Implement asynchronous loading of js files to avoid web pages losing response;
(2) Manage dependencies between modules to facilitate code writing and maintenance.
There are many tools for front-end modular development, which are generally divided into two categories. One is high-end tools like dojo, which has built-in modular development components after dojo v1.8; the other is tools like require .js, sea.js is a tool that focuses on modular development.
From the rules of modular division, it is mainly divided into two categories: AMD and CMD. Dojo and require.js follow the former, while sea.js follows the CMD specification.
require works well in single-page applications. However, for traditional multi-page applications, using require can be somewhat confusing and inconvenient.
This article explains how to apply require in the structure of ASP.NET MVC, and provides a compression script to achieve semi-automatic compression.
Separate js code
Generally speaking, a route in ASP.NET MVC corresponds to a view. The file structure of the view may be as follows:
Views |--Shared |--_layout.cshtml |--Home |--Index.cshtml |--Blog |--Create.cshtml |--Edit.cshtml |--Detail.cshtml |--Index.cshtml
It is assumed that _layout.cshtml is shared by all pages. Under normal circumstances, we will reference public js libraries in _layout, such as jQuery, bootstrap, etc., so that other pages do not need to reference these libraries again, which improves coding efficiency. However, different pages will eventually rely on different js, especially custom js that implements the functions of the page itself. In this way, we have to reference special js in other pages, or even write js directly in the page, such as the following code Often appears in View:
<script type="text/javascript"> $(function(){...}); </script>
This will cause the page to be confusing, and the code in the 3f1c4e4b6b16bbbd69b2ee476dc4f83a tag of the page cannot be cached by the browser, which increases the length of the page code. The more important flaw is that libraries such as jQuery will execute anonymous functions after loading into the page, which takes some time. If some pages do not need jQuery at all, as long as the page uses _layout as the layout page, then jQuery The initialization code will inevitably be executed, which is a waste. In fact, the idea of modular loading of JavaScript is to solve these problems.
Next we use require to plan our js and build a js directory with the following structure
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
Put public library-level js modules directly in the js directory, and place page-level js in a subdirectory of an app. Note that in the app, each page has a js file, which means that we need to extract the js of each page. Although this increases the structural complexity, it avoids the bad habit of writing 3f1c4e4b6b16bbbd69b2ee476dc4f83a tags in the page. In addition, the public libraries in the js directory, in addition to third-party libraries, also include self-developed libraries, and a file called config.js. This file is very critical and will be discussed later.
Then, we can delete all js references in _layout and use the @RenderSection command to require the subpage to provide js references, _layout.cshtml:
<head> ... @RenderSection("require_js_module", false) ... </head>
In this way, the demand for js is decentralized to each view page. According to the usage of require, we need to reference require.js in each sub-View and specify the main module, and these main modules are in the app directory above Each js
@section require_js_module{ <script src="@Url.Content("~/js/require.js")" data-main="@Url.Content("~/js/app/home.index.js")" ></script> }
All js codes will be written into js under the app, which standardizes js and makes the page cleaner. More importantly, these js can also be compressed and cached by the browser to further improve execution efficiency.
Public config
We know that in addition to using the require method, the main module often needs to configure the paths of other modules through require.config, and even needs shim. For example, the following code often appears at the beginning of the main module:
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" } } });
There is nothing special about how config.js is written. Next, just quote
in home.index.jsrequire(['../config','jquery', 'bootstrap'], function () { //main module code here });
However, it is still wrong to write this way, because when the modules that the main module depends on (config, jquery, bootstrap here) are loaded, the loading order is uncertain, but the config module needs to be loaded before other modules. , what to do? A compromise solution is to modify home.index.js to become the following code:
require(['../config'], function () { require(['home.index2']); }) , define("home.index2", ['jquery', 'bootstrap'], function () { //main module code here })
使用一个命名的模块home.index2作为过渡,在主模块中手动require,这样可以保证config在主模块执行之前加载,也就使得home.index2在加载的时候已经加载了config了。
压缩
require提供一个压缩工具,用于压缩和合并js,详情请移步至http://requirejs.org/docs/optimization.html。简单的说,require提供一个叫r.js的文件,通过本地的node程序(Node.js),执行这个r.js并传入一些参数,即可自动分析模块互相之间的依赖,以达到合并和压缩的目的。同样的,这对于单页面应用来说是容易的,因为主模块只有一个,但是对于多页面又如何做呢?好在这个压缩工具支持用一个配置文件来指导压缩,这样的话,我们可以编写下面的配置脚本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"] }, ... ] }
通过这个命令来执行压缩,压缩的结果将被保存到js-build目录:
node.exe r.js -o build.js
build.js脚本实际上是一个js对象,我们将config加入公共模块,而在各个主模块中将其排除。这样,所有的公共库包括config将压缩成一个js,而主模块又不会包含多余的config。这样可想而知,每个页面在加载时最多只会下载两个js,而且公共模块的代码会“按需执行”。
执行上面的脚本压缩,需要安装有node。可以在从这里下载。
自动脚本
但是,随着主模块的增加,需要随时跟踪和修改这个build文件,这也是很麻烦的。于是,笔者基于node.js开发了一个叫build-build.js的脚本,用来根据目录结构自动生成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); }); });
这里的代码并不复杂,主要是遍历目录,生成对象,最后将对象序列化为build.js。读者可以自行阅读并修改。最后,编写一个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
这样,我们就简单实现了一个方便的多页面require方案,最后项目目录可能是这样的:
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