详解使用grunt完成requirejs的合并压缩和js文件的版本控制


Posted in Javascript onMarch 02, 2017

最近有一个项目使用了 requirejs 来解决前端的模块化,但是随着页面和模块的越来越多,我发现我快要hold不住这些可爱的js文件了,具体表现在每个页面都要设置一堆 requirejs 的配置( baseUrl paths 之类的)。

不知谁说过,一些事重复做了三次,就该考虑一下自动化了,于是我小心翼翼的掏出了我的 grunt

我们得使用 grunt-contrib-requirejs 这个插件来实现如上所说的自动化功能,这个就是根据 r.js 封装的 grunt 插件。

安装 grunt-contrib-requirejs

npm i --save-dev grunt-contrib-requirejs

配置 Grantfile

首先我们来看下项目目录

详解使用grunt完成requirejs的合并压缩和js文件的版本控制

src 是每个页面的依赖文件

moduleslib 是一些模块和库

dist 是合并压缩后的文件

Gruntfile 中首先得到需要处理的文件列表,并创建一个空对象,用来装requirejs的配置

var files = grunt.file.expand('static/js/src/*.js');
var requireOptions = {};

然后遍历这个文件列表数组,得到js文件的名称:

files.forEach(function (file) {
  var filenamelist = file.split('/');
  var num = filenamelist.length;
  var filename = filenamelist[num - 1].replace(/\.js$/,'');
}

接下来为每个js文件配置一个任务,任务名称就是js的文件名称:

files.forEach(function (file) {
  requireOptions[filename] = {
    options: {
      baseUrl: 'static/js',
      paths: {
        jquery: 'lib/jquery.min',
        lrz: 'lib/lrz.all.bundle',
        zepto: 'lib/zepto.min',
        ajax: 'modules/ajax',
        validators: 'modules/validators',
        page: 'modules/mixins/to_page',
        dialog: 'modules/mixins/toggle_login_dialog',
      },
      optimizeAllPluginResources: true,
      name: 'src/' + filename,
      out: 'static/js/dist/' + filename + '.js'
    }
  };
}

接着初始化 grunt 配置并加载并注册任务

grunt.initConfig({
  requirejs: requireOptions
})

grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.registerTask('require', ['requirejs']);

到这里 requirejs 的配置部分就结束了,在命令行输入 grunt require 就会看到 static/js/dist 目录下面有东西蹦出来了,而且全部都是合并后并压缩好的。

在html页面中只需要:

<script src="static/js/require.js"></script>
<script src="static/js/dist/index.js"></script>

就能成功加载了。

增加js文件的版本号

浏览器有时会对加载过的js或css进行缓存,如果你的某些js依赖发生改变,那么就可能发生错误,解决办法是在文件后面增加查询字符串,例如 a.js?v=dsd712sd

那么如何控制版本,首先我们肯定想到用 new Date() ,但是如果每次发布都让浏览器重新加载(尽管有些文件根本就没有改变),难免会造成浪费。正确的方案是根据文件内容生成MD5值来作为版本号,这样当文件没有改变时,hash就不会变。

那么如何自动解决版本号的问题,我们可以用到 asset-cache-control 这个grunt插件

首先安装:

npm i --save-dev asset-cache-control

asset-cache-control 的用法个很简单,只要设置一个源文件,再设置html文件的路径就可以了

grunt.initConfig({
  cache: {
    demo: {
      assetUrl: 'js/demo.js',
      tmp: ['demo.html']
    }
  }
})

注意的是:html文件中需要引入 js/demo.js

<script src='js/demo.js'></script>

然后加载和注册 asset-cache-control 插件

grunt.loadNpmTasks('asset-cache-control');
grunt.registerTask('cache', ['cache']);

接着在命令行敲 grunt cache 就会发现 index.html 中的 script 标签加上了查询字符串。

<script src='js/demo.js?t=92e26c5d'></script>

对每个js文件配置 cache 的任务:

var files = grunt.file.expand('static/js/src/*.js');
var cacheOptions ={};
files.forEach(function (file) {
  var filenamelist = file.split('/');
  var num = filenamelist.length;
  var filename = filenamelist[num - 1].replace(/\.js$/,'');
  cacheOptions[filename] = {
    assetUrl: 'static/js/dist/' + filename +'.js',
    files: {
      'tmp': [filename+'.php']
    }
  }
});

检测每个文件的变化,自动执行任务

用到 grunt-contrib-watch 这个官方组件

grunt.initConfig 中配置:

watch: {
  files: ['static/js/src/*.js','static/js/modules/*.js'],
  tasks: ['requirejs', 'cache'],
  options: {
    spawn: false
  }
}

这样,当你修改 static/js/src/ static/js/modules/ 下的所有js文件时,就会执行 requirejs cache 任务。

完整配置清单

module.exports = function (grunt) {
  var files = grunt.file.expand('static/js/src/*.js');
  var requireOptions = {};
  var cacheOptions ={};
  files.forEach(function (file) {
    var filenamelist = file.split('/');
    var num = filenamelist.length;
    var filename = filenamelist[num - 1].replace(/\.js$/,'');
    requireOptions[filename] = {
      options: {
        baseUrl: 'static/js',
        paths: {
          jquery: 'lib/jquery.min',
          lrz: 'lib/lrz.all.bundle',
          zepto: 'lib/zepto.min',
          ajax: 'modules/ajax',
          validators: 'modules/validators',
          page: 'modules/mixins/to_page',
          dialog: 'modules/mixins/toggle_login_dialog',
        },
        optimizeAllPluginResources: true,
        name: 'src/' + filename,
        out: 'static/js/dist/' + filename + '.js'
      }
    };
    cacheOptions[filename] = {
      assetUrl: 'static/js/dist/' + filename +'.js',
      files: {
        'tmp': [filename+'.php']
      }
    }
  });

  grunt.initConfig({
    requirejs: requireOptions,
    cache: cacheOptions,
    watch: {
      files: ['static/js/src/*.js','static/js/modules/*.js'],
      tasks: ['requirejs', 'cache'],
      options: {
        spawn: false
      }
    }
  });

  grunt.loadNpmTasks('asset-cache-control'); 
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.registerTask('require', ['requirejs','cache'])
};

另外,浏览器加载一个大文件比加载n个小文件的效率要高很多,所以模块的合并对性能也有很大的提高。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
对象的类型:本地对象(1)
Dec 29 Javascript
jQuery autocomplate 自扩展插件、自动完成示例代码
Mar 28 Javascript
JS 获取select(多选下拉)中所选值的示例代码
Aug 02 Javascript
jQuery实现字符串按指定长度加入特定内容的方法
Mar 11 Javascript
Angularjs中UI Router的使用方法
May 14 Javascript
如何使用Vuex+Vue.js构建单页应用
Oct 27 Javascript
EasyUI学习之Combobox级联下拉列表(2)
Dec 29 Javascript
在一个页面实现两个zTree联动的方法
Dec 20 Javascript
JS实现导出Excel的五种方法详解【附源码下载】
Mar 15 Javascript
JS实现可针对算术表达式求值的计算器功能示例
Sep 04 Javascript
JavaScript解析JSON数据示例
Jul 16 Javascript
node.js基于dgram数据报模块创建UDP服务器和客户端操作示例
Feb 12 Javascript
jQuery上传多张图片带进度条样式(DEMO)
Mar 02 #Javascript
jquery仿京东侧边栏导航效果
Mar 02 #Javascript
关于jQuery EasyUI 中刷新Tab选项卡后一个页面变形的解决方法
Mar 02 #Javascript
JavaScript html5利用FileReader实现上传功能
Mar 27 #Javascript
利用ES6语法重构React组件详解
Mar 02 #Javascript
深入理解在JS中通过四种设置事件处理程序的方法
Mar 02 #Javascript
JavaScript表单验证完美代码
Mar 02 #Javascript
You might like
php数组查找函数in_array()、array_search()、array_key_exists()使用实例
2014/04/29 PHP
基于ThinkPHP实现批量删除
2015/12/18 PHP
eclipse php wamp配置教程
2016/06/30 PHP
基于PHP实现解密或加密Cloudflar邮箱保护
2020/06/24 PHP
关于jQuery的inArray 方法介绍
2011/10/08 Javascript
JavaScript对象创建及继承原理实例解剖
2013/02/28 Javascript
阻止子元素继承父元素事件具体思路及实现
2013/05/02 Javascript
深入浅析knockout源码分析之订阅
2016/07/12 Javascript
AngularJS 自定义指令详解及示例代码
2016/08/17 Javascript
jQuery.uploadify文件上传组件实例讲解
2016/09/23 Javascript
用JS动态设置CSS样式常见方法小结(推荐)
2016/11/10 Javascript
C#实现将一个字符转换为整数
2017/12/12 Javascript
解决vue 路由变化页面数据不刷新的问题
2018/03/13 Javascript
JavaScript原型对象、构造函数和实例对象功能与用法详解
2018/08/04 Javascript
解决Vue.js父组件$on无法监听子组件$emit触发事件的问题
2018/09/12 Javascript
如何解决React官方脚手架不支持Less的问题(小结)
2018/09/12 Javascript
[13:21]DOTA2国际邀请赛采访专栏:RSnake战队国士无双,Fnatic.Fly
2013/08/06 DOTA
Python反转序列的方法实例分析
2018/03/21 Python
Python简单实现两个任意字符串乘积的方法示例
2018/04/12 Python
在Django中输出matplotlib生成的图片方法
2018/05/24 Python
Python在图片中插入大量文字并且自动换行
2019/01/02 Python
pandas的连接函数concat()函数的具体使用方法
2019/07/09 Python
PyCharm无法识别PyQt5的2种解决方法,ModuleNotFoundError: No module named 'pyqt5'
2020/02/17 Python
Jupyter notebook无法导入第三方模块的解决方式
2020/04/15 Python
益模软件Java笔试题
2012/03/27 面试题
结构和类有什么异同
2012/07/16 面试题
新学期班主任寄语
2014/01/18 职场文书
教师业务学习制度
2014/01/25 职场文书
遗产继承公证书
2014/04/09 职场文书
校长竞聘演讲稿
2014/05/16 职场文书
如何写股份合作协议书
2014/09/11 职场文书
个人租房协议书范本
2014/09/30 职场文书
竞聘报告优秀范文
2014/11/06 职场文书
成本会计岗位职责
2015/02/03 职场文书
师范生小学见习总结
2015/06/23 职场文书
导游词之河姆渡遗址博物馆
2019/10/10 职场文书