定义:个人认为,前端自动化就是便捷地将开发代码迅速转化为发布代码的过程。
意义:一般来说,开发代码不会直接部署上线运行,小型项目倒问题不大,但对于大型项目来说,要考虑到服务器的压力以及保密的等原因,需要将代码的体积最小化,减少请求数量,对代码进行混淆和压缩,如果手动完成这个过程,会比较繁琐。
主要用到的技术:Grunt(首先要安装Node.js环境,安装好Node.js环境相当于安装好了npm,在项目建设过程中就是用npm下载各个JS库的)
范例步骤:
一、先看看整个项目文件结构(假定我们已经实现了src以及image文件夹下的所有内容)
这个项目实现的功能是随机显示一个颜色作为网页背景,并随机显示一个0~100的数字。
传统的网页项目只会有名为src和image文件夹下的内容(忽略名为concat的两个文件夹以及文件夹下的内容,这两个文件夹只是在生成过程中的一个副产品^_^),真正部署时用到的是名为dist以及image的文件夹。
我的HTML文件是这样的,引用了两个js文件和一个css文件:
<!DOCTYPE html> <html> <head> <title>Grunt--前端自动化</title> <!-- build:css css/App.min.css --> <link rel="stylesheet" type="text/css" href="css/index.css"> <!-- endbuild --> </head> <body> <a>Grunt--前端自动化</a> <!-- build:js js/App.min.js --> <script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="js/index.js"></script> <!-- endbuild --> </body> </html>
二、定义项目相关信息(package.json)
{ "name": "App", "version": "1.0.0", "description": "Grunt Page", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "Mandy","devDependencies": { "grunt": "^1.0.1", "grunt-contrib-clean": "^1.1.0", "grunt-contrib-concat": "^1.0.1", "grunt-contrib-copy": "^1.0.0", "grunt-contrib-csslint": "^2.0.0", "grunt-contrib-cssmin": "^2.2.0", "grunt-contrib-jshint": "^1.1.0", "grunt-contrib-uglify": "^3.0.1", "grunt-contrib-watch": "^1.0.0", "grunt-usemin": "^3.1.1", "jquery": "^3.2.1" }, "dependencies": { "grunt": "^1.0.1", "grunt-contrib-clean": "^1.1.0", "grunt-contrib-concat": "^1.0.1", "grunt-contrib-copy": "^1.0.0", "grunt-contrib-csslint": "^2.0.0", "grunt-contrib-cssmin": "^2.2.0", "grunt-contrib-jshint": "^1.1.0", "grunt-contrib-uglify": "^3.0.1", "grunt-contrib-watch": "^1.0.0", "grunt-usemin": "^3.1.1",
"jquery": "^3.2.1"
} }
package.json文件会描述这个npm包的所有相关信息,包括作者、简介、包依赖、构建等信息,格式是严格的JSON格式。
所展示的这个文件依次定义了npm包名、版本号、描述、入口文件、脚本说明对象、作者、只在开发时需要的依赖包、当前包所需的依赖包,当然这个文件还可有别的参数,具体可以参考这里或者这里。
ps:之所以处处与npm相关,是因为基于grunt其实等于基于npm,而npm本身就是为了开发者开发各种JS库所诞生的“包管理工具”。
如果一开始项目没有这个文件,可以通过控制台执行npm init,然后根据提示一步一步生成这个文件。
其中dependencies下的条目其实都不是手打的,其实是可以自动生成的,具体看下一步。
三、安装依赖包(我所理解的JS库,或许理解有误,正确称呼应为依赖包)
安装Grunt:控制台执行npm install grunt --save-dev
后面这个--save-dev参数就是生成前面dependencies下内容的关键,如果是--save-dev,就会保存到devDependencies,如果是--save,就会保存到dependencies。
依此类推,安装clean(清空文件夹)、concat(合并文件)、csslint(CSS语法检查)、jshint(JS语法检查)、cssmin(压缩CSS)、uglify(混淆压缩JS)、copy(复制文件)、useminPrepare(usemin的准备)、usemin(使用压缩后的文件)、watch(检测文件变化)。
npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-csslint --save-dev
npm install grunt-contrib-cssmin --save-dev
npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev
npm install grunt-usemin --save-dev
npm install grunt-contrib-copy --save-dev
npm install grunt-contrib-clean --save-dev
安装完之后node_modules文件夹下就会出现截图中所出现的依赖包了。
四、创建Gruntfile.js文件
module.exports = function(grunt){ //初始化grunt 配置 grunt.initConfig({ //获取package.json的信息,也就是后面pkg变量的来源 pkg: grunt.file.readJSON('package.json'), // 各插件的配置信息 clean:{ src:"dist/" }, concat: { options:{ stripBanners:true, // 合并时允许输出头部信息 banner:'/*!<%= pkg.name %> - <%= pkg.version %>-'+'<%=grunt.template.today("yyyy-mm-dd") %> */' }, cssConcat:{ src:['src/css/*.css'], dest:'src/css/concat/<%= pkg.name %> - <%= pkg.version %>.css' // dest 是目的地输出 }, jsConcat:{ src:[ 'node_modules/jquery/dist/jquery.min.js', 'src/js/index.js' ], dest:'src/js/concat/<%=pkg.name %> - <%= pkg.version %>.js' } }, csslint:{ options:{ csslintrc:'.csslint' }, build:['src/css/*.css'] }, jshint:{ options:{ jshintrc:'.jshint' }, build:['Gruntfile.js','src/js/*.js'] }, cssmin:{ options:{ stripBanners:true, // 合并时允许输出头部信息 banner:'/*!<%= pkg.name %> - <%= pkg.version %>-'+'<%=grunt.template.today("yyyy-mm-dd") %> */ ' }, build:{ src:'src/css/concat/<%=pkg.name %> - <%=pkg.version %>.css',// 压缩是要压缩合并了的 dest:'dist/css/<%= pkg.name %>.min.css' // dest 是目的地输出 } }, uglify:{ options:{ stripBanners:true, // 合并时允许输出头部信息 banner:'/*!<%= pkg.name %> - <%= pkg.version %>-'+'<%=grunt.template.today("yyyy-mm-dd") %> */ ' }, build:{ src:'src/js/concat/<%=pkg.name %> - <%=pkg.version %>.js',// 压缩是要压缩合并了的 dest:'dist/js/<%= pkg.name %>.min.js' // dest 是目的地输出 } }, copy:{ html:{ src:'src/index.html', dest:'dist/index.html' } }, useminPrepare:{ html:'index.html', options:{ dest:'dist' } }, usemin:{ html:['dist/index.html'], js:['dist/js/20170711 - 1.0.0.min.js'], css:['dist/css/20170711 - 1.0.0.min.css'] }, //watch自动化 watch:{ build:{ files:['src/js/*.js','src/css/*.css'], tasks:['concat','cssmin','uglify'], options:{spawn:false} } } }); // 告诉grunt我们将使用插件 grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-csslint'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-usemin'); // 告诉grunt当我们在终端输入grunt时需要做些什么 // 先进行语法检查,如果没有问题,再合并,再压缩 grunt.registerTask('default', [ 'clean', 'concat', // 'csslint', // 'jshint', 'cssmin', 'uglify', 'copy', 'useminPrepare', 'usemin', 'watch' ] ); };
这个文件的存在是为了定义当我们在控制台执行grunt的时候所要执行的操作。
initConfig是针对各个功能模块的具体配置
loadNpmTasks是加载完成任务所需的模块
registerTask是定义一个任务的执行过程(当然它不仅仅只能这样做,还可以有更多功能和更多写法,暂且这样认为吧)
五、执行
在控制台中执行grunt命令,执行到以下效果时,说明代码已经生成完毕了。
此时我们可以看到dist文件夹出现在了项目中,接下来我们就只需要将dist文件夹和image文件夹拷贝到服务器上就可以运行了。
生成后的项目只依赖于一个css文件和一个js文件(image文件夹其实我并没有用到),而打开这两个文件,我们会发现css和js代码都已经被压缩成了一行。
HTML文件也变成了这样:
<!DOCTYPE html> <html> <head> <title>Grunt--前端自动化</title> <link rel="stylesheet" href="css/App.min.css"> </head> <body> <a>Grunt--前端自动化</a> <script src="js/App.min.js"></script> </body> </html>
原本依赖的两个js也自动合并成了一个js —— 这里要隆重地说一说这个卡住我好久的问题!!!
一开始我的HTML并没有变化,说明usemin并没有起到效果?于是我为了解决这个问题找了很多资料,期间查找无果搁置了一段时间。
这几天才知道,原来是HTML文件中相应的JS或者CSS引用应该用<!-- build:js js/App.min.js --><!-- endbuild -->和<!-- build:css css/App.min.css --><!-- endbuild -->这样的一个注释括起来,之后usemin才可以正常运作。
其实官方文档已经提到过这个,但是由于是用英文Blocks来描述的,半天反应不过来是指这样的东西。。
没留意里面贴出的代码段,也没想到居然一个注释都能有这样的功能。。