总结一下写grunt脚本的思路

今天终于写个接近最终版的脚本,简单总结一下思路。
先确定我都要干什么,总结如下:

【cssmin】压缩css

【uglify】压缩js

【cssmin】合并css到style.css

【uglify】合并js库/框架文件到lib.js

【uglify】合并自定义js到script.js

【copy】复制需要处理的html到目标地点(单页面项目只涉及到一个文件)

【usemin】然后处理目标文件

【filerev】处理目标文件版本号问题(对合并后的制定文件,现在只写了js和css)

 

弄清思路后一步步操作,这个脚本总算是正常工作了。

 

和一些说明有所不同的是,我这里没有用concat、jshint、clean。

concat是被cssmin和uglify的功能替代掉了,不写也可以合并。

jshint没用主要是因为对自己代码不是特别有自信,用了以后感觉要花好多时间调试…

clean是真心用不上。

 

参考文章:

http://ericnishio.com/blog/compile-less-files-with-grunt
http://www.xuanfengge.com/npm-installation-did-not-succeed-and-github-will-not-open-solutions.html
https://www.npmjs.org/package/grunt-html-build
https://www.npmjs.org/package/grunt-contrib-concat
https://www.npmjs.org/package/grunt-contrib-less
https://www.npmjs.org/package/grunt-contrib-watch

 

grunt-usemin(中文文档)

grunt-usemin的作用

usemin用来生成优化后(合并,压缩)的脚本,然后以此替换html文件里面的javascript, css和其他脚本的引用

使用方式

npm install grunt-usemin --save-dev

任务

usemin通过两个内置的任务来完成上述的功能,分别是useminPrepare和usemin,同时还需要其它一些优化工具(cssmin,concat等)来完成优化工作。usemin通过动态的为这些优化工具插件生成子任务的方式来实现整个的目标。

usemin的两个内置的任务

  • useminPrepare 准备配置文件。配置文件是根据结构化的文件(如html)里面的块声明来生成的。最终把这些应用替换成优化后的文件引用。在这个过程中,为每个优化的步骤生成了很多的名为generated的子任务,这些优化的步骤每步都是一个grunt插件,下面将会列举出来。
  • usemin 把结构化文件(html)的块声明里面的文件引用替换。如果那些脚本文件有打过版本声明的,将会用版本声明的文件应用来替换。这个个过程会直接修改结构化文件(如html)的内容。

你必须手动的install和load构建过程中需要的依赖插件 基本例子:

grunt.registerTask('build', [
    'useminPrepare',
    'concat:generated',
    'cssmin:generated',
    'uglify:generated',
    'filerev',
    'usemin'
    ]);

useminPrepare任务

useminPrepare 在之前配置基础上,生成新的配置文件,在这个新的配置文件里定义了文件整个处理过程。默认情况下js文件会做concat,uglify处理,css文件会做concat,cssmin处理。

结构化文件(html)的块声明

<!-- build:<type>(alternate search path) <path> -->
    ... HTML Markup, list of script / link tags.
    <!-- endbuild -->
  • type 文件类型 可以是css,js,或者其他自定义类型(后面会讲到)。如果都不是构建脚本会忽略。这个引用块只会在开发环境出现,最后的build成功后不会出现。
  • alternate search path (可选的)默认情况下那些脚本文件的查找都是相对当前处理的文件的。
  • path 优化后文件的输出目录。
<!-- build:js js/app.js -->
    <script src="js/app.js"></script>
    <script src="js/controllers/thing-controller.js"></script>
    <script src="js/models/thing-model.js"></script>
    <script src="js/views/thing-view.js"></script>
    <!-- endbuild -->

处理流程

整个处理流程分很多步骤:每个步骤处理完后,useminPrepare就会修改配置文件,确保整个流程正确的执行。

默认的处理流程是合并后压缩。除此之后,在合并压缩之后可以定义一个后置的处理函数来做一个额外的工作,来修改配置文件。

下面看一个例子,使用了默认的处理流程。

下面是block声明
<!-- build:js js/app.js -->
<script src="js/app.js"></script>
<script src="js/controllers/thing-controller.js"></script>
<script src="js/models/thing-model.js"></script>
<script src="js/views/thing-view.js"></script>
<!-- endbuild -->

处理后的生成的配置文件如下:

{
    concat:
    generated: {
    files: [
    {
    dest: '.tmp/concat/js/app.js',
    src: [
    'app/js/app.js',
    'app/js/controllers/thing-controller.js',
    'app/js/models/thing-model.js',
    'app/js/views/thing-view.js'
    ]
    }
    ]
    }
    },
    uglify: {
    generated: {
    files: [
    {
    dest: 'dist/js/app.js',
    src: [ '.tmp/concat/js/app.js' ]
    }
    ]
    }
    }
    }

指南

默认情况下usemin会把当前处理的文件作为‘根文件系统’,所有其他的相对路径,都是参照此路径。这个方式也同样适用绝对路径。如果需要改变‘根文件系统’,你需要在option中从定义root属性(下面会提到)

concat/cssmin/uglify这些任务的配置是useminPrepare生成的,没有必要专门为这些任务定义配置文件了。

options

dest string类型,默认为null 处理后的文件的基准输出路径。 staging string类型,默认.tmp 临时文件的输出路径(上面的例子提到过) root string|array类型,默认null 处理文件路径时的参考的’根文件系统’ flow object类型 默认{ steps: { js: [‘concat’, ‘uglifyjs’], css: [‘concat’, ‘cssmin’] }, post: {} } 通这个选项能在每个目标之前或者所有目标之前配置处理流程。你也能单独的配置steps属性或者post属性。

例如,为html目标改变js文件的处理流程:

useminPrepare: {
    html: 'index.html',
    options: {
    flow: {
    html: {
    steps: {
    js: ['uglifyjs']
    },
    post: {}
    }
    }
    }
    }

为全部目标改变js文件的处理流程:

useminPrepare: {
    html: 'index.html',
    options: {
    flow: {
    steps: {
    js: ['uglifyjs']
    },
    post: {}
    }
    }
    }

通过post属性,自定义处理配置文件

useminPrepare: {
    html: 'index.html',
    options: {
    flow: {
    steps: {
    js: ['uglifyjs']
    },
    post: {
    js: [{
    name: 'uglifyjs',
    createConfig: function (context, block) {
    var generated = context.options.generated;
    generated.options = {
    foo: 'bar'
    };
    }
    }]
    }
    }
    }
    }

自定义处理步骤和后置处理器

自定义处理步骤和后置处理器有两个属性 + name string类型 在name指定相应的处理步骤上生效 + createConfig 函数 有两个参数 context block 返回配置对象

context conetxt对象包含了当前step/post-processor运行的一些信息,作为主力流程中的一步,必须要有处理输入的相关目录和文件的配置,同时也必须有输出相关的文件和目录。

context属性: + inDir 输入文件的目录 + inFiles 输入文件 + outDir 输出文件的目录 + outFiles 输出文件 + last 是否是处理步骤的最后一步 + options 此处理步骤的一些可选项

block 例子说明:

<!-- build:js scripts/site.js -->
    <script src="foo.js"></script>
    <script src="bar.js"></script>
    <script src="baz.js"></script>
    <!-- endbuild -->

被解析为block对象如下:

var block = {
    type: 'js',
    dest: 'scripts/site.js',
    src: [
    'foo.js',
    'bar.js',
    'baz.js'
    ],
    raw: [
    '    <!-- build:js scripts/site.js -->',
    '    <script src="foo.js"></script>',
    '    <script src="bar.js"></script>',
    '    <script src="baz.js"></script>',
    '    <!-- endbuild -->'
    ]
    };

usemin任务

usemin任务会做两件事 + 首先会把block替换为一行,这个引用指向之前流程生成的优化后的脚本文件。 + 之后如果找到打过版本的文件,就会用打过版本的文件引用替换当前的引用。

脚本文件的搜索

默认情况下,usemin会使用一个grunt-filerev插件map对象, 这个对象是grunt.filerev.summary,如果没找到就会到磁盘去检索,这样会需要更多的时间。

使用options.revmap会提供一个地图对象,供usemin任务查找

说明

当usemin尝试使用打过版本的文件替换当前引用(也就是之前提到的第2步操作时),就会收集脚本的所有搜索目录。最终生成一个目录的树,并尝试找到打过版本的文件。搜索目录默认是结构化文件(html)的目录,可以通过options重新指定。

例子1: dist/html/index.html有如下内容

<link rel="stylesheet" href="styles/main.css">
    <img src="../images/test.png">

默认情况下就会在dist/html/下搜索打过版本的文件。 + style/main.css将会在dist/html/styles搜索那么版本文件,例如:dist/html/style/main.1234.css就匹配,最终会替换。 + images/test.png将会在 dist/iamges搜索。

换成如下内容

<link rel="stylesheet" href="/styles/main.css">
    <img src="/images/test.png">

搜索的路径仍然不变是上面的dist/html/stylesdist/html/images,现在假如我们的脚本是在另一个目录dist/assets下,我们可以配置options,使得最终的搜索路径在dist/assets下面,例如可能就会有这样的结果:dist/assets/images/test.875487.pngdist/assets/styles/main.98090.css

options

assetsDirs array 类型 脚本搜索的目录 patterns object 类型 用户自定义替换。加入要把js文件里面的所有image.png替换为打过版本后的文件,就可以采用如下定义方式:

usemin: {
    js: '*.js',
    options: {
    assetsDirs: 'images',
    patterns: {
    js: [
    [/(image\.png)/, 'Replacing reference to image.png']
    ]
    }
    }
    }

patterns的key必须出现目标targets中,每个pattern有四个参数,最后两个是可选的,第一个参数表示那些引用是要替换的blockReplacements object 类型 默认值{ css: function (block) { … }, js: function (block) { … } } 由此可以控制替换的具体细节,返回值为最终替换的字符串。 例如:

usemin: {
    html: index.html,
    options: {
    blockReplacements: {
    less: function (block) {
    return '<link rel="stylesheet" href="' + block.dest + '">';
    }
    }
    }
    }
    //less必须和block的type相匹配.

revmap object 类型 声明版本的文件匹配,之后在版本文件的查找替换时是一个依据。 例如:

{
    "foo.png": "foo.1234.png"
    }

小结

useminPrepare任务必须声明输入,临时目录和输出路径。最终输出正确的配置文件,确保正确的文件处理流程。usemin任务,只是处理最终的输出文件。所有的脚本文件必须输出到最后的输出目录中。

useminPrepare 为整个构建过程,生成正确的配置文件,确保每步流程的处理顺序和期望的一致。在block声明的文件,有的是绝对路径,有的是相对路径,路径的文件引用是在搜索路径下(input)查找, 默认情况下就是正在文件本身所在的路径。

usemin 把images,scripts,css…的这些引用,替换为打过版本的文件引用。

例子

app
    |
    +- assets
    |  +- js
    |     +- index.js
    |     +- rank.js
    |  +- css
    |     +- layout.css
    +- index.html
    +- dist

index.html的内容

<!-- build:css(.) /assets/css/layout.min.css -->
    <link rel="stylesheet" href="/assets/css/layout.css"/>
    <!-- endbuild-->
    <!--build:js(.) /assets/js/app.min.js -->
    <script type="text/javascript" src="/assets/js/rank.js"></script>
    <script type="text/javascript" src="/assets/js/index.js"></script>
    <!-- endbuild -->

Gruntfile.js

/**
    * Created by Administrator on 2014/7/15.
    */
    module.exports = function(grunt){
    grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    clean: {
    build: ['.tmp/'],
    release: ['dist/']
    },
    jshint: {
    all: ['assets/**/*.js']
    },
    copy: {
    html: {
    files: [
    {src: ["index.html"], dest: "dist/"}
    ]
    }
    },
    useminPrepare: {
    html: ['dist/index.html']
    },
    filerev: {
    options: {
    length: 8
    },
    generated: {
    files:[
    {src: "dist/assets/**/*.min.js"},
    {src: "dist/assets/**/*.min.css"}
    ]
    }
    },
    usemin: {
    options: {
    assetsDirs: ['dist/']
    },
    html: 'dist/index.html'
    }
    });
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-usemin');
    grunt.loadNpmTasks('grunt-filerev');
    grunt.registerTask('build', [
    'clean',
    'jshint',
    'copy',
    'useminPrepare',
    'concat:generated',
    'cssmin:generated',
    'uglify:generated',
    'filerev',
    'usemin',
    'clean:build'
    ]);
    }

为angular强化Http请求

我先说问题,代码下面一起给出

添加patch

前两天了解到除了get、post、put还有一个叫patch的相对较新的提交方法,主要是用来做局部更新的意思。
但是angular没有这个方法。

改变请求的格式

angular在提交中文内容是会有乱码出现的情况,编码解码也不行,如果用过zepto的同学可能也遇到过这样的问题。结果导致无法提交服务端想要的信息。

增加请求拦截

对于请求的成功和失败进行拦截操作,比如对401做跳转登录页面的操作。

下面是代码
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
//增加拦截服务
$httpProvider.interceptors.push('userStateInterceptor');
//增加patch请求方法
$httpProvider.defaults.headers.patch = {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
}
/**
* The workhorse; converts an object to x-www-form-urlencoded serialization.
* @param {Object} obj
* @return {String}
*/
var param = function (obj) {
var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
for (name in obj) {
value = obj[name];
if (value instanceof Array) {
for (i = 0; i < value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if (value instanceof Object) {
for (subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if (value !== undefined && value !== null)
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}
return query.length ? query.substr(0, query.length - 1) : query;
};
// Override $http service's default transformRequest
$httpProvider.defaults.transformRequest = [function (data) {
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];
}]).service("userStateInterceptor", ['$location','$q', function ($location,$q) {
//定义拦截操作
var interceptor = {
'request': function (config) {
return config;
},
'response': function (response) {
return response;
},
'requestError': function (rejection) {
return $q.reject(response);
},
'responseError': function (response) {
//这里做401的拦截
switch (response.status) {
case 401:
$location.path('login');
break;
}
return $q.reject(response);
}
}
return interceptor;
}]);

angularjs里面的事件

书上对事件的描述并不多,但实践中却是比较好用的东西。

先粘一下概念和API:

 

如同浏览器响应浏览器层的事件,比如鼠标点击、页面滚动那样,Angular应用也可以响应Angular事件。这使我们可以在应用中嵌套的各组件之间进行通信,即使这些组件在创建时并未考虑到其他组件。

 

注意:Angular事件系统并不与浏览器的事件系统相通,这意味着,我们只能在作用域上监听Angular事件而不是DOM事件。

 

  • $emit只能向parent controller传递event与data
  • $broadcast只能向child controller传递event与data
  • $on用于接收event与data

 

事件属性 目的
event.targetScope 发出或者传播原始事件的作用域
event.currentScope 目前正在处理的事件的作用域
event.name 事件名称
event.stopPropagation() 一个防止事件进一步传播(冒泡/捕获)的函数(这只适用于使用`$emit`发出的事件)
event.preventDefault() 这个方法实际上不会做什么事,但是会设置`defaultPrevented`为true。直到事件监听器的实现者采取行动之前它才会检查`defaultPrevented`的值。
event.defaultPrevented  

数据可视化初接触

之前都不怎么接触图表类的制作任务,也不是很关心,最近偶然机会有了接触,在这里记录一下。

可能大多数人接触比较多的是百度的ECharts,但是我没有从ECharts入手

最先接触的是d3.js.

展示数据的类型很多,但是不支持ie8,暂时放到了一边;

之后看了HighCharts

这个图表库支持ie8,效果也很好,功能上少d3很多,但是够用了,所以我在项目中暂时引用了HighCharts来构建图表。

回过来说百度的ECharts

ECharts本身是不支持ie8的,需要引入excanvas来达到支持,功能也很强大,符合国人需求,个人感觉跟HCharts的功能差不多,只是实现方式不同。

 

因为没有测试大数据,所以这部分先保留,不做评价。

然而三种图表主要都是两种技术(其实图标库基本都是这两种),SVG和Canvas,当然了,百度这边ie8的支持是通过转为div来实现的。

关于效率问题,引用一篇w3school上面的一篇文章

 

Canvas 和 SVG 都允许您在浏览器中创建图形,但是它们在根本上是不同的。

SVG

SVG 是一种使用 XML 描述 2D 图形的语言。

SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的。您可以为某个元素附加 JavaScript 事件处理器。

在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。

Canvas

Canvas 通过 JavaScript 来绘制 2D 图形。

Canvas 是逐像素进行渲染的。

在 canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。

Canvas 与 SVG 的比较

下表列出了 canvas 与 SVG 之间的一些不同之处。

Canvas

  • 依赖分辨率
  • 不支持事件处理器
  • 弱的文本渲染能力
  • 能够以 .png 或 .jpg 格式保存结果图像
  • 最适合图像密集型的游戏,其中的许多对象会被频繁重绘

SVG

  • 不依赖分辨率
  • 支持事件处理器
  • 最适合带有大型渲染区域的应用程序(比如谷歌地图)
  • 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
  • 不适合游戏应用

 

因为最近接触了很多数据有关的东西,发现数据可视化还是很值得挖的东西,无论是对产品还是技术,也是考研程序效率的一个大块。

 

d3js有专门的书供学习,最近准备入一本。

 

 

VS下面的Emmet(ZenCoding)

新工作的开发环境倾向我并不是很熟悉的VS2012,代码习惯和快捷键也进入了新的环境。

经过了一番自定义以后,发现VS2012自带的类似ZenCoding的功能并不是很完整,于是…

可以去官网下载相应扩展,VS2012是最低的支持版本。

ZenCoding的新名字是Emmet(其实也不新了);

 

官方下载Emmet

 

直接在开发环境里面安装可以依据以下步骤

工具->扩展和更新->搜索Emmet 找到以后安装即可。

 

不过VS下面的快捷键不一样~

补全是Ctrl+1

还有我最爱的块选择是Ctrl+3和Ctrl+4

当然你也可以重新自定义,有兴趣的快去试试吧。

angular1.2到1.3有给新手的坑

今天跟小伙伴散布angular,我下了最新的项目seed给小伙伴们用,然后小伙伴们用原始的教程进入angularJs。

然后就悲剧了…

最新版本1.3.8不再支持函数的控制器匹配
function PhoneListCtrl($scope) {
$scope.phones = [
{"name": "Nexus S",
"snippet": "Fast just got faster with Nexus S."},
{"name": "Motorola XOOM™ with Wi-Fi",
"snippet": "The Next, Next Generation tablet."},
{"name": "MOTOROLA XOOM™",
"snippet": "The Next, Next Generation tablet."}
];
}

会报error:ng[area]…..这样的错误。

虽然这个方式实在各个读物和入门示例中广泛使用的方式,但因为该方式生成了全局的函数,这并不是推荐的,也不符合MVC思想,所以…

不过还是等我读了源码再说更深入,那么现在只能是以下面的方式引入控制器:


angular.module('myApp', []).controller('PhoneListCtrl',['$scope',
function($scope){
}]);

jQuery无冲突处理方案

有一些特殊情况,jQuery可能会与其他的类库同时存在,比如zepto(通常是在某些人犯迷糊的时候)。

在这个时候“$”这个命名空间会出现冲突(或覆盖)。

简单的做法我们可以用“jQuery”或“Zepto”来替代“$”,并在代码中建立属于相关类库的作用域即可,作为命名空间来避免冲突。

那么有没有其他的办法呢。

 

jQuery 核心 – noConflict() 方法

在jquery里面有一个这样的方法可以改变jQuery的命名空间。

可以去看w3cSchool里面的解释

在《JavaScript框架设计》的第一章中就两次提到了这个方案。

我这里cv一下jquery的源代码供参考
//jquery1.2
var _jQuery = window.jQuery , _$ = window.$;
jQuery.extend({
noConflict: function(deep){
window.$ = _$;
if(deep)
window.jQuery = _jQuery
return jQuery;
}
})

要注意的是该方法只对单文件的类库框架有用。

如何对动态加载方式引入的文件解决冲突问题之后再做讨论,不在这里赘述。

Unable to execute dex: Java heap space报错

环境是在mac下面,据说是设定的虚拟内存不足,要修改eclipse.ini这个文件。
mac下面eclipse.ini的位置在哪里呢?

 

在eclipse图标上右键,点显示包内容
contents/macOS下面

 


-Xms40m
-Xmx384m
改为
-Xms512m
-Xmx1024m

 

该方法windows系统也通用,只是windows下面eclipse.ini在eclipse根目录。