【Yeoman】热部署web前端开发环境

时间:2022-09-03 11:37:04

本文来自 “简时空”:《【Yeoman】热部署web前端开发环境》(自动同步导入到博客园)

1、序言

记得去年的暑假看RequireJS的时候,曾少不更事般地惊为前端利器,写了《Speed up! 提速你的网站访问速度[压缩JS与CSS]》。随着学习的深入,发现前端的还有许多东西需要整合,纯手工劳动无疑降低了开发效率。这四天的工作,真是把这两年所学习到的知识综合应用了一番:


软实力层面包括:使用Photoshop+Bootstrap3+Grid System 设计页面UI图;

工具语言包括:CoffeeScript、LESS、Handlebars等;

图形库的使用:Highchart、jvectorMap、D3等这些一年前就开始用的库


>>>>> 页面效果:http://www.janscon.com/weibo/index.html <<<<

当然重点不在于这个,这次学习的重点在于使用Yeoman热部署了前端环境,使用Grunt、NPM、Bower等工具 起“穿针引线”作用将这些技术互相联系起来,使得前端的开发从未如此“一气呵成”~

既然这些工具都把重要的工作做了,那么作为程序猿的我意义何在呢?

呃~ OK,“我们不生产代码,我们只是英文字母的搬运工而已啦”

这几天的工作还是留下遗憾的,就是没能用上前端自动化测试工具——以后得好好学习Qunit+Mocha+ Selenium这些玩意儿了

2、工作准备

如果读者对Grunt、Yeoman还不是很了解,建议先参看这几篇文章,非常适合入门:

① Xianjing.《Grunt - 基于任务的Javascript构建工具》. 2013-05-16

② RIA之家. 《前端项目可以更简单—Yeoman入门指南》.2013-4-25

③ 阮一峰. 《任务管理工具Grunt》.

上面这三篇文章已经将Yeoman、Grunt等语法讲解非常明了了,所以我的文章里就不在这些方面多费口舌。这里将只重点讲解我工作的流程,作文以记之。

找到目标如何使用Yeoman搭建单页面、多页面的开发环境

使用工具:Bootstrap(基于LESS)、Handlebars、CoffeeScript,使用RequireJS组织JS代码

示例代码:

本文所讲的程序代码可以从这儿下载:

① 单页面前端环境搭建示例代码:jscon-single-page.zip (百度云盘)

② 多页面前端环境搭建示例代码:jscon-multi-pages.zip (百度云盘)

3、构建单页面开发环境

3.1、使用Bootstrap-less生成器

使用Yeoman入门的时候,使用的webapp这个生成器,不过里面的Bootstrap是基于SASS的。个人倾向于使用LESS语言的,毕竟它是基于我熟悉的Node环境而非Ruby。

Step 1: 安装Bootstrap-less生成器

npm install generator-bootstrap-less

Step 2: 生成程序脚手架

yo bootstrap-less

Step 3: 代码热部署

grunt server

【Yeoman】热部署web前端开发环境

看到这个自动跳出来的 “Allo,“Allo! 页面,说明已经成功搭建环境了。可以开始在这个基础上编写代码,只是我还有使用Handlebars以及RequireJS,所以还得自己安装这些组件。

在继续之前,在这里顺便对比一下webapp与bootstrap-less这两个脚手架的区别:

【Yeoman】热部署web前端开发环境

如果除去我想要的Bootstrap之外,bootstrap-less生成器是一无所有啊(Bootstrap的JS文件和FontAwesome都勾上吧,因为都要用到的),而webapp还有RequireJS和Modernizr呢,显然是“高富帅”一枚。

不过我还是选bootstrap-less,因为它使用的是LESS而不是SASS(我难道有强迫症?);至于RequireJS和Modernizr的使用可以借鉴webapp生成的index.html中写法即可——我就是这么干的!

3.2、引入Handlebars

引入Handlebars是看中了它使用方便且能够预编译这两优点的。一般使用bower工具引入所需要的包,不过Handlebar是个例外,这是因为官方Github并不提供现成的前端页面的Handlebar.js文件,需要通过其文档主页到Amazon的S3平台(http://handlebarsjs.com/)上下载;所以不要使用bower install handlebar.js命令

在模板预编译的时候是需要借助Node环境,所以使用npm安装Handlebars插件:

Step 1:  使用npm,下载contrib模块

npm install --save-dev  grunt-contrib-handlebars 

Step 2:  同时在Gruntfile.js中注册下面的Task:

(在Gruntfile.js文件中修改)

    handlebars:{
dist: {
options: {
namespace: "JST",
wrapped:true
},
// files: {"<%= yeoman.app %>/hbs/templates.js":["<%= yeoman.app %>/hbs/*.hb"]}
expand:true,
src:"<%= yeoman.app %>/hbs/*.hb",
ext:".js"
}
}

默认的namespace是“Handlebars.templates”,后期使用uglify.js进行优化的时候会将Handlebars用变量“a”(或者其他名字)代替,从而提示该变量没有templates属性;因此推荐像上面那样使用“JST”等作为命名空间。

Step 3: 还需要配置编译及livereload功能

首先在watch任务中添加对.hb文件的监视即可

(在Gruntfile.js文件中修改)

    watch: {
handlebars:{
files:['<%= yeoman.app %>/hbs/{,*/}*.hb'],
tasks:['handlebars']
},
….
livereload: {
files: [
'<%= yeoman.app %>/*.html',
'{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css',
'{.tmp,<%= yeoman.app %>}/{scripts,hbs}/{,*/}*.js',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
],
tasks: ['livereload']
}
},

Step 4: 注册预编译任务

在concurrent任务中注册handlebars任务,确保项目发布的时候所有模板都经过编译:

(在Gruntfile.js文件中修改)

    concurrent: {
dist: [
'handlebars',
'coffee',
'recess',
'imagemin',
'svgmin',
'htmlmin'
]

配置完后,这里就简单的举个例子表明如何使用:

Step 1:创建模板

创建hbs文件夹,并在其下面新建一个messages.hb文件,此时文件夹结构如下:

【Yeoman】热部署web前端开发环境

messages.hb内容如下:

   {{#messages}}
<div class="message">
<h2 class="name">{{name}}</h2>
<div class="msgContent">{{msgContent}}</div>
<div class="msgTime">{{msgTime}}</div>
</div>
{{/messages}}

Step 2:添加JS代码

先在index.html页面中添加ID为“list”的空白DIV:

<div id="list"></div>

再在index.html文件中添加runtime.js官方文件,以及messages.js文件(注意不是messages.hb文件,热部署的时候会自动调用Node将其编译成messages.js文件):

(在app/index.html文件中修改)

<!-- build:js scripts/main.js -->
<script src="bower_components/jquery/jquery.js"></script>
<script src="scripts/lib/handlebars/handlebars.runtime-v1.1.2.js"></script>
<script type="text/javascript" src="hbs/messages.js"></script>
<script src="scripts/main.js"></script>
<!-- endbuild -->

其中的main.js是逻辑实现代码:

app/scripts/main.js

$(function(){
var data = {
messages:
[
{name:"Zhang",msgContent:"I'm San",msgTime:"Yesterday"},
{name:"Li",msgContent:"I'm Si",msgTime:"Today"},
{name:"Wang",msgContent:"I'm Wu",msgTime:"Tomorrow"}
],
name:"jscon"
};
var template = JST["app/hbs/messages.hb"];
console.log(template)
$("#list").html(template(data));
});

这里下划线标出的:

var template = JST["app/hbs/messages.hb"];

需要注意两个地方,一个是命名空间“JST”要与配置文件中保持一致;另外一个当调用模板的时候注意路径是相对app的路径。至于如何去掉“app/hbs”这个路径,目前还不知道如何解决。

Step 3:查看效果

在程序根目录下运行:

grunt server

发现还是之前的页面,调出chrome console会提示找不到“messages.js”文件;好吧下面见证奇迹的时刻,打开messages.hb文件,直接按下“Ctrl+S”保存文件,触发watch任务,其中就包括执行handlebars任务(其他的还有coffee、recess任务),然后自动执行livereload任务刷新页面。现在看看效果页面:

【Yeoman】热部署web前端开发环境

参考文章:

[1] 官方Github文档《grunt-contrib-handlebars

[2] 官方Grunt文档:https://npmjs.org/package/grunt-contrib-handlebars

3.3、使用RequireJS组织JS文件

我们看看现在的index.html页面的截图:

【Yeoman】热部署web前端开发环境

可以看到这么简单的页面里面有一大串的JS文件需要加载,主要包括:

1) 个人的JS文件,比如上节讲的模板文件(message.js)和逻辑文件(main.js)。

2) 官方的JS库文件,比如jQuery、还有许多BootStrap需要的组件JS文件;

通过RequireJS组织JS文件,到时就只用一句话就够了:

<!-- build:js scripts/main.js -->
<script data-main="scripts/main" src="bower_components/requirejs/require.js"></script>
<!-- endbuild -->

Step 1:安装RequireJS包

bower install --save requirejs

执行此语句之后,就会自动更新bower.json文件,同时在app/bower_components中下载官方的requirejs组件。

Step 2:添加RequireJS的Node模块

npm install grunt-contrib-requirejs --save-dev

这样能够将grunt-contrib-requirejs组件自动下载到node_modules文件夹下,同时因为使用了--save-dev会自动更新package.json文件。

官方文档:https://github.com/gruntjs/grunt-contrib-requirejs

 Step 3:修改Gruntfils.js文件,配置requirejs任务

为了能够利用RequireJS的r.js文件对输出文件进行优化,需要在Gruntfiles.js中注册相应任务。关于r.js的详细配置可以参考官方示例

(在Gruntfile.js文件中修改)

requirejs: {
dist: {
// Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
options: {
// `name` and `out` is set by grunt-usemin
baseUrl: yeomanConfig.app + '/scripts',
mainConfigFile:'<%= yeoman.app %>/scripts/config.js',
optimize: 'none',
// TODO: Figure out how to make sourcemaps work with grunt-usemin
// https://github.com/yeoman/grunt-usemin/issues/30
//generateSourceMaps: true,
// required to support SourceMaps
// http://requirejs.org/docs/errors.html#sourcemapcomments
preserveLicenseComments: false,
useStrict: true,
wrap: true
//uglify2: {} // https://github.com/mishoo/UglifyJS2
}
}
},

这里的config.js是专门的RequireJS配置文件,主要是所有文件中的依赖关系,配置了paths和shim项:

app/scripts/config.coffee

require.config
paths:
## jQuery
'jquery':'../bower_components/jquery/jquery' ## BootStrap
'bootstrap-affix': "../bower_components/bootstrap/js/affix"
'bootstrap-transition': "../bower_components/bootstrap/js/transition"
"bootstrap-alert": "../bower_components/bootstrap/js/alert"
"bootstrap-button": "../bower_components/bootstrap/js/button"
"bootstrap-collapse": "../bower_components/bootstrap/js/collapse"
"bootstrap-dropdown": "../bower_components/bootstrap/js/dropdown"
"bootstrap-modal": "../bower_components/bootstrap/js/modal"
"bootstrap-tooltip": "../bower_components/bootstrap/js/tooltip"
"bootstrap-popover": "../bower_components/bootstrap/js/popover"
"bootstrap-scrollspy": "../bower_components/bootstrap/js/scrollspy"
"bootstrap-tab": "../bower_components/bootstrap/js/tab"
"bootstrap-carousel": "../bower_components/bootstrap/js/carousel" ## Handlebars runtime
'runtime':'lib/handlebars/handlebars.runtime-v1.1.2' ## Templates
'messages':'../hbs/messages'

Step 4:将requirejs添加build任务中

为了能够在发布时,使用r.js进行页面优化(合并、压缩等),需要将requirejs任务作为build任务的子任务:

(在Gruntfile.js文件中修改)

grunt.registerTask('build', [
'clean:dist',
'useminPrepare',
'concurrent:dist',
'autoprefixer',
'requirejs',
'concat',
'cssmin',
'uglify',
'modernizr',
'copy:dist',
'rev',
'usemin'
]);

Step 5:重新组织index.html页面中的js文件

接下来删除Figure 9中的所有script标签,代之以下面的语句:

(在app/index.html文件中修改)

<!-- build:js scripts/main.js -->
<script data-main="scripts/main" src="bower_components/requirejs/require.js"></script>
<!-- endbuild -->

入口文件还是main.js,只是现在改成RequireJS要求的格式:

app/scripts/main.coffee

require.config
paths:
'jquery':'../bower_components/jquery/jquery',
'runtime':'lib/handlebars/handlebars.runtime-v1.1.2',
'messages':'../hbs/messages' require ['jquery','runtime','messages','bootstrap'],($)->
'use strict';
$ ->
data =
messages:[
{name:"Zhang",msgContent:"I'm San",msgTime:"Yesterday"}
{name:"Li",msgContent:"I'm Si",msgTime:"Today"}
{name:"Wang",msgContent:"I'm Wu",msgTime:"Tomorrow"}
]
name:"jscon"
template = JST["app/hbs/messages.hb"]
$("#list").html(template(data))

注意这里的bootstrap依赖文件用来配置需要哪些bootstrap组件用的,可以自己定制所需要的插件内容,挺方便的。这里给出最全的配置,内容如下:(参考自https://gist.github.com/taxilian/4790603

app/scripts/bootstrap.coffee

require.config
paths:
'jquery':'../bower_components/jquery/jquery',
'bootstrap-affix':"../bower_components/bootstrap/js/affix",
'bootstrap-transition':"../bower_components/bootstrap/js/transition",
"bootstrap-alert":"../bower_components/bootstrap/js/alert",
"bootstrap-button":"../bower_components/bootstrap/js/button",
"bootstrap-collapse":"../bower_components/bootstrap/js/collapse",
"bootstrap-dropdown":"../bower_components/bootstrap/js/dropdown",
"bootstrap-modal":"../bower_components/bootstrap/js/modal",
"bootstrap-tooltip":"../bower_components/bootstrap/js/tooltip",
"bootstrap-popover":"../bower_components/bootstrap/js/popover",
"bootstrap-scrollspy":"../bower_components/bootstrap/js/scrollspy",
"bootstrap-tab":"../bower_components/bootstrap/js/tab",
"bootstrap-carousel":"../bower_components/bootstrap/js/carousel"
shim:
"bootstrap-affix": ["jquery"],
"bootstrap-transition": ["bootstrap-affix"],
"bootstrap-alert": ["bootstrap-transition"],
"bootstrap-button": ["bootstrap-alert"],
"bootstrap-collapse": ["bootstrap-button"],
"bootstrap-dropdown": ["bootstrap-collapse"],
"bootstrap-modal": ["bootstrap-dropdown"],
"bootstrap-tooltip": ["bootstrap-modal"],
"bootstrap-popover": ["bootstrap-tooltip"],
"bootstrap-scrollspy": ["bootstrap-popover"],
"bootstrap-tab": ["bootstrap-scrollspy"],
"bootstrap-carousel": ["bootstrap-tab"]
define ['jquery',
"bootstrap-affix",
"bootstrap-transition",
"bootstrap-alert",
"bootstrap-button",
"bootstrap-collapse",
"bootstrap-dropdown",
"bootstrap-modal",
"bootstrap-tooltip",
"bootstrap-popover",
"bootstrap-scrollspy",
"bootstrap-tab",
"bootstrap-carousel"
],($)->

至此配置完成,在命令行中输入 grunt server 就可以看到以前熟悉的页面了,没错,you make it!

3.4、发布程序

程序的发布,使用

grunt build --force  或者  grunt --force

加force选项的目的是为了在执行任务时的出现warning提示时,并不中断任务的执行而是继续执行到完成(或出现Error)。

此时你会在根目录下出现一个dist文件夹, 这个文件夹的结构和app文件夹相似

【Yeoman】热部署web前端开发环境

可以发现只有一个js文件和css文件,都是经过压缩的。js的压缩是RequireJS、concat和uglify共同的作用结果,css的压缩则是concat和cssmin的作用结果。使用RequireJS会根据入口文件main.js中找到所有的依赖,然后合并成一个大的main.js文件:

【Yeoman】热部署web前端开发环境

这个dist文件夹就是可以发布的版本了,(改个名字后)扔到服务器上就可以了。

4、构建多页面前端环境

还记得小时候看过的童话故事中的那只偷香油的小老鼠么?有时候,一种优势在另外一种情况下就成了弊端。上面我们讲的usemin就是这个一个情况。

我们回过头来看发布时命令窗口中的几行提示:

【Yeoman】热部署web前端开发环境

这些反馈信息都来自usemin的工作,usemin组件能够自动更新Gruntfile中诸如concat、uglify、requirejs的配置文件,所以前面我们在单页面环境时根本不用考虑如何合并、压缩文件,因为usemin默默地帮你完成了——真是人民的好公仆呢。

usemin所做的工作对单页面来讲非常有用,增加了配置过程的自动化。不过在RequireJS用于多页面开发时,需要使用dir以及modules配置,但是usemin“仍然不知情”,还是会自动给requirejs任务添加name属性和out属性(单页面配置),从而导致配置冲突。

要想自动部署多页面,那么只能忍痛割爱抛弃usemin组件,需要自己写concat和uglify任务,还好这些都不难。在前面单页面环境搭建的基础上,配置多页面环境也是非常方便快捷的。

4.1、抛弃usemin任务

因为usemin和requirejs任务都是用于发布时执行的,所以只要在build任务中除名即可,除名之后build任务的配置如下:

grunt.registerTask('build', [
'clean:dist',
'copy:server',
'concurrent',
'requirejs',
'cssmin',
'concat',
'uglify',
'copy'
]);

4.2、修改requirejs任务

修改requirejs任务,添加用于多任务的dir和modules配置:

(在Gruntfile.js文件中修改)

 requirejs: {
dist: {

baseUrl:'<%= yeoman.app %>/scripts',
mainConfigFile:'<%= yeoman.app %>/scripts/config.js',
optimize: 'none',
dir: '.tmp/scripts/requirejs/',
modules:[
{name:"main"},
{name:"main2"}
],

}
},

注意

① 输出的文件暂时放在 .tmp临时文件夹下,这样到时clean任务会清空这个临时文件夹。

② 这里的modules中模块的名字都是以“main”开头的,是为了方便后面concat任务找文件。

4.3、修改concat、uglify任务

移走了usemin任务之后,concat和uglify需要自己定制。其实concat任务更像是copy功能,因为合并的工作已经由requirejs做了。

(在Gruntfile.js文件中修改)

concat:{
dist:{
expand:true,
cwd:".tmp/scripts/requirejs/",
src:['main*.js'],
dest:'<%= yeoman.dist %>/scripts/',
ext:'.js'
},
dep:{
files:{
"<%= yeoman.dist %>/scripts/vendor/modernizr.js":["<%= yeoman.app %>/bower_components/modernizr/modernizr.js"],
'<%= yeoman.dist %>/bower_components/requirejs/require.js':['<%= yeoman.app %>/bower_components/requirejs/require.js']
}
}
},

这里的“dist”任务是用来搬运的之前requirejs放在.tmp中的入口文件(main*.js)到结果文件夹dist下。而“dep”任务则是搬运其他需要的js文件,这里是modernizr.js和require.js两个文件。

由于requirejs和concat已经把该合并的文件都放到目标文件下面去了,所以uglify的任务就非常简单了,把这些目标文件下的文件“就地”压缩一下即可:

uglify: {
src:['<%= yeoman.dist %>/{,*/}*.js']
},

4.4、测试

把index.html另存一份为index2.html,并将其中的入口文件改成main2:

<script data-main="scripts/main2" src="bower_components/requirejs/require.js"></script>

然后把main.js另存为一份main2.js。接下去使用 grunt --force 发布程序,可以看到效果。

【Yeoman】热部署web前端开发环境

参考文献:

[1] 科学的爱情. 《Grunt + RequireJS with multi-page website》. 2013-03-11

[2] Xianjing《玩转Grunt(一): Minification》.2013-10-7

最后友情提醒一句,本文所讲的程序代码可以从这儿下载:

单页面前端环境搭建示例代码:jscon-single-page.zip

多页面前端环境搭建示例代码:jscon-multi-pages.zip

~~The End~~

【Yeoman】热部署web前端开发环境的更多相关文章

  1. 用grunt搭建自动化的web前端开发环境实战教程&lpar;详细步骤&rpar;

    用grunt搭建自动化的web前端开发环境实战教程(详细步骤) jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用!前端自动化, ...

  2. 用grunt搭建web前端开发环境

    1.前言 本文章旨在讲解grunt入门,以及讲解grunt最常用的几个插件的使用. 2.安装node.js Grunt和所有grunt插件都是基于nodejs来运行的,如果你的电脑上没有nodejs, ...

  3. 【前端福利】用grunt搭建自动化的web前端开发环境-完整教程

    jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用! 1. 前言 各位web前端开发人员,如果你现在还不知道grunt或者听说过 ...

  4. 转:【前端福利】用grunt搭建自动化的web前端开发环境-完整教程

    原文地址:http://blog.csdn.net/wangfupeng1988/article/details/46418203 jQuery在使用grunt,bootstrap在使用grunt,百 ...

  5. 用grunt搭建自动化的web前端开发环境-完整教程

    原稿:http://www.cnblogs.com/wangfupeng1988/p/4561993.html#!comments jQuery在使用grunt,bootstrap在使用grunt,百 ...

  6. Web 前端开发环境

    创建 Web 前端开发环境 Web 前端开发涉及多种工具,这里将常用工具的安装和配置进行说明,提供了详细的说明,为后继的开发创建一个坚实的基础. 本文介绍的工具有:NodeJS, NPM, Bower ...

  7. 用grunt搭建自动化的web前端开发环境

    用grunt搭建自动化的web前端开发环境 jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用! 1. 前言 各位web前端开发 ...

  8. Grunt搭建自动化web前端开发环境--完整流程

    Grunt搭建自动化web前端开发环境-完整流程 jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用! 1. 前言 各位web前 ...

  9. grunt搭建自动化的web前端开发环境(转)

    1. 前言 各位web前端开发人员,如果你现在还不知道grunt或者听说过.但是不会熟练使用grunt,那你就真的真的真的out了(三个“真的”重复,表示重点).至于grunt的作用,这里不详细说了, ...

随机推荐

  1. AVSampleBufferDisplayLayer----转

    http://blog.csdn.net/fernandowei/article/details/52179631 目前大多数iOS端的视频渲染都使用OpenGLES,但如果仅仅为了渲染而不做其他的例 ...

  2. Arcgis10&period;2 破解注意问题

    Arcgis10.2 破解注意问题 2014年11月17日 20:22 按照网上教程直接替换service.txt和arcgis.exe之后 需要重新启动license服务 可是无法启动 解决办法 修 ...

  3. KMP 算法总结

    KMP算法是基本的字符串匹配算法,但是代码实现上有一些细节容易错.这篇随笔将认真总结一下. KMP算法的核心是: The KMP algorithm searches for occurrences ...

  4. iOS工程如何支持64-bit

    苹果在2014年10月20号发布了一条消息:从明年的二月一号开始,提交到App Store的应用必须支持64-bit.详细消息地址为:https://developer.apple.com/news/ ...

  5. linux命令(2):df 磁盘占用

    在这里先讲讲linux命令df的资料: df 命令: linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信 ...

  6. Android之TextView的样式类Span的使用具体解释

    Android中的TextView是个显示文字的的UI类,在现实中的需求中,文字有各式各样的样式.TextView本身没有属性去设置实现,我们能够通过Android提供的 SpannableStrin ...

  7. linux下不重启加硬盘

    linux下热加载磁盘 临时给虚拟机加了一块硬盘,增加后懒得重启,于是看了看热加载 [root@centos5 ~]# cat /proc/scsi/scsi Attached devices: Ho ...

  8. tasksetCPU亲和力&amp&semi;docke容器资源限制

    [taskset详解] taskset设置cpu亲和力,taskset能够将一个或者多个进程绑定到一个或者多个处理器上运行 参数: 选项: -a, --all-tasks 在给定 pid 的所有任务( ...

  9. gitlab4&period;0备份还原

    一,备份 备份默认路径查看: gitlab/config/gitlab.yml 中的backup: 默认tmp/backups ====>这个是gitlab/tmp/backups/  可不是系 ...

  10. mui&period;ajax&lpar;&rpar;和asp&period;net sql服务器数据交互【2】json数组和封装

    今天没有做循环创建显示:可以参考张鑫旭的文章:<基于HTML模板和JSON数据的JavaScript交互> 1.ashx页面代码 //下面的封装一般框架底层都是写好的:连接 数据库和获取D ...