scss Mixins 各种技巧

转载自:https://www.w3cplus.com/preprocessor/sass-the-mixin-directive.html


为了有效的维护和开发项目,代码的重复利用就显得尤为重要。在Sass中,除了@import@extend可以使你的代码更加具有重复利用性,@mixin指令也同样能提高你代码的重复使用率并简化你的代码。

@extend指令通过继承从而简化了代码,但是它也有缺陷之处,首先是不够灵活,其次它还会将你不需要的其他地方具有相同类名的样式都继承过来。

@mixin指令是另一种简化代码的方法。Mixins可以包含任意内容且可以传递参数,因此比'@extend'更加灵活和强大。

定义Mixins

通过@mixin加名称的方式就可以定义一个Mixins模块,在模块内你可以添加任何你想重复使用的样式。

@mixin button {  
    font-size: 1em; padding: 0.5em 1.0em;  
    text-decoration: none; color: #fff;  } 

由于历史原因,连字符和下划线被认为是相同的,也就是说 @mixin button-large { } 和 @mixin button_large { } 是一样的。

Mixins能够包含任何在 CSS 和 Sass 中有效的内容

@mixin link {  
    a { color: blue;

        &:visited { color: purple;
        }  
        &:hover { color: white;
        }  
        &:active { color: red;
        }  
    }
} 

使用@mixin指令

你可以通过@include来调用具有相同名称的mixin模块。

.button-green { @include button;  
    background-color: green;  
} 

比如通过@include调用我之前创建名为button的mixin模块,那么解析后的CSS就如下所示:

.button-green { font-size: 1em; padding: 0.5em 1.0em; text-decoration: none; color: #fff; background-color: green; } 

正如你所预期的那样,@include被名为button的混合模块内的样式所取代。同时我在.button-green中添加的background-color样式依然保留着。

在mixin模块的定义中还可以包含其他的mixin。比如:

@mixin button-blue { @include button; @include link;  
} 

所以你可以通过以包含多种简单mixin模块的方式创建较为复杂的mixin模块。

当Mixins模块包含选择器和规则集,也就是mixins包含的内容本身就是有效的CSS样式时,他们就可以在其他规则集外被调用。比如本文之前创建的名为link的mixin模块:

@mixin link {  
    a { color: blue;

        &:visited { color: purple;
        }  
        &:hover { color: white;
        }  
        &:active { color: red;
        }  
    }
} 

之后我们可以直接调用这个模块,即使不在一个选择器内。

@include link; 

这段代码将会被解析为:

a { color: blue;

    &:visited { color: purple;
    }  
    &:hover { color: white;
    }  
    &:active { color: red;
    }  
} 

之所以能这样调用是因为这个mixin模块内既包含了选择器也包含了样式,如果没有选择器,那么编译后将不会有内容显示。比如我们在选择器外直接调用button这个mixin模块,那么将不会有东西被编译。

@mixin button {  
    font-size: 1em; padding: 0.5em 1.0em;  
    text-decoration: none; color: #fff;  } @include button; 

不会被编译是因为你的样式没有被应用到任何元素上。

参数的使用

Mixins可以接收和使用参数,这使得它比@extend更加强大和灵活。我更新了之前的button模块,增加了名为background的参数并将其传递给模块。

@mixin button($background) {  
    font-size: 1em; padding: 0.5em 1.0em;  
    text-decoration: none; color: #fff;  background: $background;  
} 

注意到参数被设置为一个变量并成为backround属性的值。如果我们想创建一个绿色的按钮,那么就可以使用以下代码:

.button-green { @include button(green);  
} 

当Sass被编译时,值green就被传递给@mixin成为变量$backround的值。编译后的代码如下:

.button-green { font-size: 1em; padding: 0.5em 1.0em; text-decoration: none; color: #fff; background: green; } 

你还可以在@mixin@include中传递多个参数,参数间用逗号隔开。比如:

@mixin button($background, $color) {  
    font-size: 1em; padding: 0.5em 1.0em;  
    text-decoration: none; color: $color; background: $background;  
}

.button-green { @include button(green, #fff);  } 

给参数设置默认值

你可能会好奇如果在定义mixin时定义了参数,但是在@include调用时没有传递参数会发生什么。这种情况下你会收到一个编译错误的提示。同时我相信这种情况一定不是你想看到的。你可以通过在mixin中定义参数的时候给它设置一个默认值,从而来避免这种错误。

@mixin button($background: green) {  
    font-size: 1em; padding: 0.5em 1.0em;  
    text-decoration: none; color: #fff;  background: $background;  
} 

现在如果你在调用的时候忘记传递参数值

.button-green { @include button;  
} 

你的代码将会使用你设置的参数默认值来解析,在这个例子中也就是green这个值。

.button-green { font-size: 1em; padding: 0.5em 1.0em; text-decoration: none; color: #fff; background: green; } 

当然,你也可以通过提供一个参数值来覆盖这个默认值。

.button-blue { @include button(blue);  
} 

这时将会使用你提供的值来编译代码

.button-blue { font-size: 1em; padding: 0.5em 1.0em; text-decoration: none; color: #fff; background: blue; } 

关键字参数

为了帮助你的代码更加容易理解,你可以在传递值给mixin时将参数名称和参数值一并传递过去。

.button-green { @include button($background: green, $color: #fff);  } 

关键字参数会额外增加一些代码,但是这会使得你的@include更加容易理解。比如,上面那段代码就比下面这段代码更加容易理解,因为上面的代码明确指出了green#fff分别是什么。

.button-green { @include button(green, #fff);  } 

虽然选择器的名称给了我们一些线索,可以大概推断出第一个参数代表什么,但是对于第二个参数却没有任何提示作用,而且一年后你很可能不会再记得这个参数的含义。

一起传递关键字名称和值也被称为命名参数,你也可以以任意顺序传递命名参数。下面两种传递方式解析后会得到相同的结果。

.button-green { @include button($background: green, $color: #fff);  }

.button-green { @include button($color: #fff, $background: green);  } 

因为命名参数是变量名,由于历史原因,下划线和破折号可以互换使用。

数量可变的参数

Mixins可以接收未知数量的参数。比如你可以给同一个元素增加多种box-shadows。这里,我增加了暗灰和浅灰两种阴影。

.container { box-shadow: 0px 1px 2px #333, 2px 3px 4px #ccc; } 

在另一个元素上你可能只想使用一种阴影或者在其他元素上你又想使用三种或者四种阴影。这个时候你就可以创建一个接收数量可变的参数的mixin模块,并且在使用@include指令的时候决定传递参数的数量。

@mixin box-shadows($shadow...) {  
    box-shadow: $shadow;  
}

.container { @include box-shadows(0px 1px 2px #333, 2px 3px 4px #ccc);  } 

通过在变量名后增加三个点(...)来使mixin模块接收数量可变的参数。需要注意的是这些是三个周期的字符,而不是单一的省略号。当你使用@include传递参数的时候,使用逗号将参数分开。

之前的代码将被编译为:

.container { box-shadow: 0px 1px 2px #333, 2px 3px 4px #ccc; } 

Sass将所有参数打包为一个列表,但是在此我没有列出这个列表,相关内容之后我会再做补充。

你同样可以给mixin传递可变的参数

@mixin box-shadows($shadow...) {  
    box-shadow: $shadow;  
} $shadows: 0px 1px 2px #333, 2px 3px 4px #ccc; .container { @include box-shadows($shadows...);  
} 

这里我将变量'$shadows'设置为有两种阴影的list,并在@include中将其作为参数传递。同样,你也可以将参数设置为一个map,并且作为参数传递。你也可以同时传递listmap,只要list作为第一个传递。

@include box-shadows($list..., $map...) 

我将在之后的系列具体谈论这一部分。


`@content`用在`mixin`里面的,当定义一个`mixin`后,并且设置了`@content`;
`@include`的时候可以传入相应的内容到`mixin`里面

官网给的例子:
$color: white;
@mixin colors($color: blue) { background-color: $color;
  @content; border-color: $color;
} .colors {
  @include colors { color: $color; }
}
编译后:
.colors { background-color: blue; color: white; border-color: blue;
}
http://sass-lang.com/documentation/file.SASS_REFERENCE.html


总结

Mixins是一个在提高代码重复使用率方面非常有用的指令。而且能够使用和传递参数这一点使得它非常的强大。

你可以在创建mixins时设置默认值来防止编译错误,同时也可以在调用时覆盖默认值。甚至你还可以更加灵活地传递数量可变的参数。

著作权归作者所有。

商业转载请联系作者获得授权,非商业转载请注明出处。

原文: https://www.w3cplus.com/preprocessor/sass-the-mixin-directive.html © w3cplus.com


new 发布于 2018-7-25 02:59

refusing to merge unrelated histories 各种技巧

先pull,因为两个仓库不同,发现refusing to merge unrelated histories,无法pull





因为他们是两个不同的项目,要把两个不同的项目合并,git需要添加一句代码,在git pull,

这句代码是在git 2.9.2版本发生的,最新的版本需要添加--allow-unrelated-histories

git pull origin master --allow-unrelated-histories


new 发布于 2018-6-28 05:50

js正则替换 各种技巧

代码如下:

        var str = '纳兰雪'; return new Array(str.length).join('*') + str.substr(-1);
                    

这种方法的原理是取最后一个字符,其他的根据字符串长度生成对应的*个数。通过new Array(str.length).join('*')获得元素的间隔,然后拼接这些间隔,拼出来的刚好是少一个字符,再加上截取的最后一个字符。

另一种方法:

        'wuhaidong很牛逼的'.replace(/.(?=.)/g, '*');
                    

这里主要用的是正则的(?=pattern),这东西还真没用过,平时看手册都不会去注意,原来这么有用的。可以理解为不占匹配位,不占匹配位就不会替换。

标签: js

new 发布于 2018-1-23 02:57

Linux 网卡启动失败 各种技巧

Bringing up interface eno16777736:  Error: Connection activation failed: Device not managed by NetworkManager or unavailable




Just follow the below steps and everything will be ok...


1. Remove Network Manager from startup Services.

#chkconfig NetworkManager off



2. Add Default Net Manager

#chkconfig network on



3.Stop NetworkManager first

#service NetworkManager stop



4.and then start Default Manager

#service network start

5./etc/init.d/network restart


new 发布于 2017-10-18 03:35

注册码 各种技巧

webstorm

http://im.js.cn:8888

https://idea.qmanga.com/

http://idea.medeming.com



http://idea.iteblog.com/key.php

20180801可用

http://xdouble.cn:8888/


9MWZD5CC4E-eyJsaWNlbnNlSWQiOiI5TVdaRDVDQzRFIiwibGljZW5zZWVOYW1lIjoiMjAxNzY1MDYxQGNxdS5lZHUuY24gLiIsImFzc2lnbmVlTmFtZSI6IiIsImFzc2lnbmVlRW1haWwiOiIiLCJsaWNlbnNlUmVzdHJpY3Rpb24iOiJGb3IgZWR1Y2F0aW9uYWwgdXNlIG9ubHkiLCJjaGVja0NvbmN1cnJlbnRVc2UiOmZhbHNlLCJwcm9kdWN0cyI6W3siY29kZSI6IklJIiwicGFpZFVwVG8iOiIyMDIwLTAxLTI0In0seyJjb2RlIjoiQUMiLCJwYWlkVXBUbyI6IjIwMjAtMDEtMjQifSx7ImNvZGUiOiJEUE4iLCJwYWlkVXBUbyI6IjIwMjAtMDEtMjQifSx7ImNvZGUiOiJQUyIsInBhaWRVcFRvIjoiMjAyMC0wMS0yNCJ9LHsiY29kZSI6IkdPIiwicGFpZFVwVG8iOiIyMDIwLTAxLTI0In0seyJjb2RlIjoiRE0iLCJwYWlkVXBUbyI6IjIwMjAtMDEtMjQifSx7ImNvZGUiOiJDTCIsInBhaWRVcFRvIjoiMjAyMC0wMS0yNCJ9LHsiY29kZSI6IlJTMCIsInBhaWRVcFRvIjoiMjAyMC0wMS0yNCJ9LHsiY29kZSI6IlJDIiwicGFpZFVwVG8iOiIyMDIwLTAxLTI0In0seyJjb2RlIjoiUkQiLCJwYWlkVXBUbyI6IjIwMjAtMDEtMjQifSx7ImNvZGUiOiJQQyIsInBhaWRVcFRvIjoiMjAyMC0wMS0yNCJ9LHsiY29kZSI6IlJNIiwicGFpZFVwVG8iOiIyMDIwLTAxLTI0In0seyJjb2RlIjoiV1MiLCJwYWlkVXBUbyI6IjIwMjAtMDEtMjQifSx7ImNvZGUiOiJEQiIsInBhaWRVcFRvIjoiMjAyMC0wMS0yNCJ9LHsiY29kZSI6IkRDIiwicGFpZFVwVG8iOiIyMDIwLTAxLTI0In0seyJjb2RlIjoiUlNVIiwicGFpZFVwVG8iOiIyMDIwLTAxLTI0In1dLCJoYXNoIjoiMTE3Mzk0NjcvMCIsImdyYWNlUGVyaW9kRGF5cyI6MCwiYXV0b1Byb2xvbmdhdGVkIjpmYWxzZSwiaXNBdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlfQ==-ocfH5r7aj6756M8f6f+7MXdp2K0RxPCG2Yp9/iIydFQxHCSjqjUopo2yAxH8+EtTGm+w4dKPe+6tLqa/HOExPt9g3yOf29PU7oIwmqnIcnvZYGNCgNsCK7fyfxc2CX1whLZTByQ0LKylRshlsYz+HXg002E7q7bY0y3vbwxWLyNR5qY4EPa4WzfWjdnopCZfijgsU0bTRB51jiqVlLHONKjFFUmzPopUDwEHwDTnmNMEUAsluTaiirWIJwkmuFbsgmir+KUAxHvStOUgo68Vyaas7BicAWI1QiCqd1EvFIN/sGaZkRE9hK6FwkDN/hscDX1DCPY62ttgP/k1GvFOVA==-MIIElTCCAn2gAwIBAgIBCTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE4MTEwMTEyMjk0NloXDTIwMTEwMjEyMjk0NlowaDELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBU51c2xlMQ8wDQYDVQQHDAZQcmFndWUxGTAXBgNVBAoMEEpldEJyYWlucyBzLnIuby4xHTAbBgNVBAMMFHByb2QzeS1mcm9tLTIwMTgxMTAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQAF8uc+YJOHHwOFcPzmbjcxNDuGoOUIP+2h1R75Lecswb7ru2LWWSUMtXVKQzChLNPn/72W0k+oI056tgiwuG7M49LXp4zQVlQnFmWU1wwGvVhq5R63Rpjx1zjGUhcXgayu7+9zMUW596Lbomsg8qVve6euqsrFicYkIIuUu4zYPndJwfe0YkS5nY72SHnNdbPhEnN8wcB2Kz+OIG0lih3yz5EqFhld03bGp222ZQCIghCTVL6QBNadGsiN/lWLl4JdR3lJkZzlpFdiHijoVRdWeSWqM4y0t23c92HXKrgppoSV18XMxrWVdoSM3nuMHwxGhFyde05OdDtLpCv+jlWf5REAHHA201pAU6bJSZINyHDUTB+Beo28rRXSwSh3OUIvYwKNVeoBY+KwOJ7WnuTCUq1meE6GkKc4D/cXmgpOyW/1SmBz3XjVIi/zprZ0zf3qH5mkphtg6ksjKgKjmx1cXfZAAX6wcDBNaCL+Ortep1Dh8xDUbqbBVNBL4jbiL3i3xsfNiyJgaZ5sX7i8tmStEpLbPwvHcByuf59qJhV/bZOl8KqJBETCDJcY6O2aqhTUy+9x93ThKs1GKrRPePrWPluud7ttlgtRveit/pcBrnQcXOl1rHq7ByB8CFAxNotRUYL9IF5n3wJOgkPojMy6jetQA5Ogc8Sm7RG6vg1yow==


vmware


适用于 vmware fusion 10 和 vmware fusion pro  10序列号  

FG3TU-DDX1M-084CY-MFYQX-QC0RD



new 发布于 2017-8-24 03:09

SVN DELETE 各种技巧

常用操作
1.使用svn从工作拷贝删除文件只是预定要删除,当你提交,文件才会从版本库删除。
$ svn delete myfile

2.然而直接删除一个URL,你需要提供一个日志信息:
$ svn delete -m “Deleting file ‘yourfile'” file:///tmp/repos/test/yourfile

3.强制删除本地已修改文件的例子:
$ svn delete –force file


new 发布于 2017-7-24 03:02

nodejs跨域问题 各种技巧

node配置


app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    next();
});



new 发布于 2017-2-14 06:10

node项目自动编译 各种技巧

在开发或调试Node.js应用程序的时候,当你修改js文件后,总是要按下CTRL+C终止程序,然后再重新启动,即使是修改一点小小的参数,也总是要不断地重复这几个很烦人的操作。有没有办法做到当文件修改之后,Node.js自动重新启动(或重新加载文件)以节省时间呢?一开始我是想到用grunt的watch模块来监控文件变化,但后来在网上一查,原来我们想到的,别人早已想到,并且已经做得很好。Node Supervisor正是这样一个可以实现这种需求的Node.js模块。

根据Github上的说明,Node Supervisor原本是用于服务器上Node.js应用崩溃的时候,自己重新启动。当然它也可以监控你的项目的js(或CoffeeScript)文件变化,进而重启来方便我们调试应用程序。

安装方法(以全局模块安装):

复制代码代码如下:

npm install supervisor -g

假设你的Node.js程序主入口是app.js,那么只需要执行以下命令,即可开始监控文件变化。

复制代码代码如下:

supervisor app.js

Supervisor还支持多种参数,列举如下:

复制代码代码如下:

//要监控的文件夹或js文件,默认为'.'
-w|--watch <watchItems>

//要忽略监控的文件夹或js文件  
-i|--ignore <ignoreItems>

//监控文件变化的时间间隔(周期),默认为Node.js内置的时间
-p|--poll-interval <milliseconds>

//要监控的文件扩展名,默认为'node|js'
-e|--extensions <extensions>

//要执行的主应用程序,默认为'node'
-x|--exec <executable>

//开启debug模式(用--debug flag来启动node)
--debug

//安静模式,不显示DEBUG信息
-q|--quiet

例子:

复制代码代码如下:

supervisor myapp.js
supervisor -w py_scripts -e 'py' -x python myapp.py
supervisor -w lib, server.js, config.js, server.js

实现同样功能的类似产品还有Run.jsNodeman,这两个我都没用过。但是从文档上来看,前者和Supervisor一样都是极简的5分钟就可以上手的那种,功能比Supervisor稍弱;后者的feature比较多,对应的文档就特别长,估计要研究透也得至少半个小时。选择哪一个,全看项目需求和个人喜好。


new 发布于 2017-2-8 09:20

Sublime Text 3 Package Control安装 各种技巧

import urllib.request,os,hashlib; h = 'df21e130d211cfc94d9b0905775a7c0f' + '1e3d39e33b79698005270310898eea76'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

或手动安装:
下载文件:Package Control.sublime-package,置于Installed Packages/目录。
 
突然发现一个非常严重的问题,安装好Package Control之后却无法使用,坑坑坑。。。
解决方法:
    打开Preference/Settings-User ,发现     "ignored_packages": 

    [

Package Control

    ] Package Control竟然被ignore了。。。。 
删除即可。

new 发布于 2016-11-8 09:02

【转】Gradle和Maven 各种技巧

Java世界中主要有三大构建工具:Ant、Maven和Gradle。经过几年的发展,Ant几乎销声匿迹、Maven也日薄西山,而Gradle的发展则如日中天。笔者有幸见证了Maven的没落和Gradle的兴起。Maven的主要功能主要分为5点,分别是依赖管理系统、多模块构建、一致的项目结构、一致的构建模型和插件机制。我们可以从这五个方面来分析一下Gradle比起Maven的先进之处。

依赖管理系统

Maven为Java世界引入了一个新的依赖管理系统。在Java世界中,可以用groupId、artifactId、version组成的Coordination(坐标)唯一标识一个依赖。任何基于Maven构建的项目自身也必须定义这三项属性,生成的包可以是Jar包,也可以是war包或者ear包。一个典型的依赖引用如下所示:

1 2 3 4 5 6 7 8 9 10 
<dependency>  <groupId>junit</groupId>  <artifactId>junit</artifactId>  <version>4.12</version>  <scope>test</scope> </dependency> <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-test</artifactId> </dependency> 

从上面可以看出当引用一个依赖时,version可以省略掉,这样在获取依赖时会选择最新的版本。而存储这些组件的仓库有远程仓库和本地仓库之分。远程仓库可以使用世界公用的central仓库,也可以使用Apache Nexus自建私有仓库;本地仓库则在本地计算机上。通过Maven安装目录下的settings.xml文件可以配置本地仓库的路径,以及采用的远程仓库的地址。

Gradle在设计的时候基本沿用了Maven的这套依赖管理体系。不过它在引用依赖时还是进行了一些改进。首先引用依赖方面变得非常简洁。

1 2 3 4 
dependencies {  compile 'org.hibernate:hibernate-core:3.6.7.Final'  testCompile junit:junit:4.+' } 

第二,Maven和Gradle对依赖项的scope有所不同。在Maven世界中,一个依赖项有6种scope,分别是complie(默认)、provided、runtime、test、system、import。而grade将其简化为了4种,compile、runtime、testCompile、testRuntime。那么如果想在gradle使用类似于provided的scope怎么办?别着急,由于gradle语言的强大表现力,我们可以轻松编写代码来实现类似于provided scope的概念(例如How to use provided scope for jar file in Gradle build?)。

第三点是Gradle支持动态的版本依赖。在版本号后面使用+号的方式可以实现动态的版本管理。

第四点是在解决依赖冲突方面Gradle的实现机制更加明确。使用Maven和Gradle进行依赖管理时都采用的是传递性依赖;而如果多个依赖项指向同一个依赖项的不同版本时就会引起依赖冲突。而Maven处理这种依赖关系往往是噩梦一般的存在。而Gradle在解决依赖冲突方面相对来说比较明确。在Chapter 23. Dependency Management 中的23.2.3章节详细解读了gradle是如何处理版本冲突的。

多模块构建

在SOA和微服务的浪潮下,将一个项目分解为多个模块已经是很通用的一种方式。在Maven中需要定义个parent POM作为一组module的聚合POM。在该POM中可以使用<modules>标签来定义一组子模块。parent POM不会有什么实际构建产出。而parent POM中的build配置以及依赖配置都会自动继承给子module。

而Gradle也支持多模块构建。而在parent的build.gradle中可以使用allprojects和subprojects代码块来分别定义里面的配置是应用于所有项目还是子项目。对于子模块的定义是放置在setttings.gradle文件中的。在gradle的设计当中,每个模块都是Project的对象实例。而在parent build.gradle中通过allprojects或subprojects可以对这些对象进行各种操作。这无疑比Maven要灵活的多。

比如在parent的build.gradle中有以下代码:

1 2 3 
allprojects {  task hello << { task -> println "I'm $task.project.name" } } 

执行命令gradle -q hello会依次打印出父module以及各个submodule的项目名称。这种强大的能力能让gradle对各个模块具有更强的定制化。

一致的项目结构

在Ant时代大家创建Java项目目录时比较随意,然后通过Ant配置指定哪些属于source,那些属于testSource等。而Maven在设计之初的理念就是Conversion over configuration(约定大于配置)。其制定了一套项目目录结构作为标准的Java项目结构。一个典型的Maven项目结构如下:

Gradle也沿用了这一标准的目录结构。如果你在Gradle项目中使用了标准的Maven项目结构的话,那么在Gradle中也无需进行多余的配置,只需在文件中包含apply plugin:'java',系统会自动识别source、resource、test srouce、 test resource等相应资源。不过Gradle作为JVM上的构建工具,也同时支持groovy、scala等源代码的构建,甚至支持Java、groovy、scala语言的混合构建。虽然Maven通过一些插件(比如maven-scala-plugin)也能达到相同目的,但配置方面显然Gradle要更优雅一些。

一致的构建模型

为了解决Ant中对项目构建活动缺乏标准化的问题,Maven特意设置了标准的项目构建周期,其默认的构建周期如下所示:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 
<phases>  <phase>validate</phase>  <phase>initialize</phase>  <phase>generate-sources</phase>  <phase>process-sources</phase>  <phase>generate-resources</phase>  <phase>process-resources</phase>  <phase>compile</phase>  <phase>process-classes</phase>  <phase>generate-test-sources</phase>  <phase>process-test-sources</phase>  <phase>generate-test-resources</phase>  <phase>process-test-resources</phase>  <phase>test-compile</phase>  <phase>process-test-classes</phase>  <phase>test</phase>  <phase>prepare-package</phase>  <phase>package</phase>  <phase>pre-integration-test</phase>  <phase>integration-test</phase>  <phase>post-integration-test</phase>  <phase>verify</phase>  <phase>install</phase>  <phase>deploy</phase> </phases> 

而这种构建周期也是Maven最为人诟病的地方。因为Maven将项目的构建周期限制的太死,你无法在构建周期中添加新的phase,只能将插件绑定到已有的phase上。而现在项目的构建过程变得越来越复杂,而且多样化,显然Maven对这种复杂度缺少足够的应变能力。比如你想在项目构建过程中进行一项压缩所有javascript的任务,那么就要绑定到Maven的现有的某个phase上,而显然貌似放在哪个phase都不太合适。而且这些phase都是串行的,整个执行下来是一条线,这也限制了Maven的构建效率。而Gradle在构建模型上则非常灵活。在Gradle世界里可以轻松创建一个task,并随时通过depends语法建立与已有task的依赖关系。甚至对于Java项目的构建来说,Gradle是通过名为java的插件来包含了一个对Java项目的构建周期,这等于Gradle本身直接与项目构建周期是解耦的。

插件机制

Maven和Gradle设计时都采用了插件机制。但显然Gradle更胜一筹。主要原因在于Maven是基于XML进行配置。所以其配置语法太受限于XML。即使实现很小的功能都需要设计一个插件,建立其与XML配置的关联。比如想在Maven中执行一条shell命令,其配置如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
<plugin>  <groupId>org.codehaus.mojo</groupId>  <artifactId>exec-maven-plugin</artifactId>  <version>1.2</version>  <executions>  <execution>  <id>drop DB => db_name</id>  <phase>pre-integration-test</phase>  <goals>  <goal>exec</goal>  </goals>  <configuration>  <executable>curl</executable>  <arguments>  <argument>-s</argument>  <argument>-S</argument>  <argument>-X</argument>  <argument>DELETE</argument>  <argument>http://${db.server}:${db.port}/db_name</argument>  </arguments>  </configuration>  </execution>  </executions> </plugin> 

而在Gradle中则一切变得非常简单。

1 2 3 
task dropDB(type: Exec) {  commandLine curl,-s,s,-x,DELETE,"http://${db.server}:{db.port}/db_name" } 

在创建自定义插件方面,Maven和Gradle的机制都差不多,都是继承自插件基类,然后实现要求的方法。这里就不展开说明。


从以上五个方面可以看出Maven和Gradle的主要差异。Maven的设计核心Convention Over Configuration被Gradle更加发扬光大,而Gradle的配置即代码又超越了Maven。在Gradle中任何配置都可以作为代码被执行的,我们也可以随时使用已有的Ant脚本(Ant task是Gradle中的一等公民)、Java类库、Groovy类库来辅助完成构建任务的编写。

这种采用本身语言实现的DSL对本身语言项目进行构建管理的例子比比皆是。比如Rake和Ruby、Grunt和JavaScript、Sbt和Ruby…..而Gradle之所以使用Groovy语言实现,是因为Groovy比Java语言更具表现力,其语法特性更丰富,又兼具函数式的特点。这几年兴起的语言(比如Scala、Go、Swift)都属于强类型的语言,兼具面向对象和函数式的特点。

最后想说的Gradle的命令行比Maven的要强大的多。以前写过一篇文章专门讲述了Gradle的命令行操作,详情请见Gradle命令行黑魔法


new 发布于 2016-11-3 07:02