LESS 和 SCSS 有什么区别?

LESS 和 SCSS 的区别

LESS 和 SCSS 都属于 CSS 预处理器的范畴,也就是 CSS 的超集,但是两者的语法、如何使用和具体的功能实现还是有差异的。

下面我试着以代码示例的方式给大家演示一下两者的几个常见区别。

声明和使用变量

LESS 采用 @ 符号,SCSS 采用 $ 符号。

在下面的示例中,我们首先在规则外声明了一个名为 link-color 的变量,然后在名为 #main 的规则内声明一个名为 width 的变量,接着把 width 变量赋值给了 CSS 的 width 属性。

LESS:

@link-color: #428bca;
#main {
  @width: 5em;
  width: @width;
}

SCSS:

$link-color: #428bca;
#main {
  $width: 5em;
  width: $width;
}

变量插值(Variable Interpolation)

LESS 采用 @{xxxx} 的形式,SCSS 采用 ${xxxx} 的形式。

示例一:使用变量插值作为 CSS 选择器

LESS:

// Variables 
@my-selector: banner;

// Usage 
.@{my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

SCSS:

// Variables 
$my-selector: banner;

// Usage 
.#{$my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

Mixins 的定义、使用及参数

定义一个 Mixin

LESS 使用 dot 符号(也就是句点)来定义一个 Mixin,并且可以把任意的 CSS 规则作为 Mixin 使用;SCSS 使用 @mixin 指令来定义一个 Mixin。

示例 – 来自 BootStrap 的 alert-variant Mixin 的定义

LESS:

.alert-variant(@background; @border; @text-color) {
  background-color: @background;
  border-color: @border;
  color: @text-color;

  hr {
    border-top-color: darken(@border, 5%);
  }
  .alert-link {
    color: darken(@text-color, 10%);
  }
}

SCSS:

@mixin alert-variant($background, $border, $text-color) {
  background-color: $background;
  border-color: $border;
  color: $text-color;

  hr {
    border-top-color: darken($border, 5%);
  }
  .alert-link {
    color: darken($text-color, 10%);
  }
}

使用 Mixin

LESS 仍是使用 dot 符号(句点),如果 Mixin 没有参数的话可以省略后面的圆括号;SCSS 使用 @include 指令来引入一个 Mixin。

示例 – 引入一个名为 center-block 的 Mixin。

LESS:

.center-block() {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.a {
    .center-block;
}

SCSS:

@mixin center-block() {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.a {
    @include center-block;
}

参数形式

如果存在多个参数的话,LESS 使用分号分隔;SCSS 使用逗号分隔。两者都支持为参数设置默认值。

示例 – 来在 BootStrap 的 form-control-validation Mixin 的使用

LESS:

@state-success-text:             #3c763d;
@state-success-bg:               #dff0d8;
@state-success-border:           darken(spin(@state-success-bg, -10), 5%);

@state-info-text:                #31708f;
@state-info-bg:                  #d9edf7;
@state-info-border:              darken(spin(@state-info-bg, -10), 7%);

@state-warning-text:             #8a6d3b;
@state-warning-bg:               #fcf8e3;
@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);

@state-danger-text:              #a94442;
@state-danger-bg:                #f2dede;
@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1 
          box-shadow: @shadow;
}

.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
  // Color the label and help text 
  .help-block,
  .control-label,
  .radio,
  .checkbox,
  .radio-inline,
  .checkbox-inline,
  &.radio label,
  &.checkbox label,
  &.radio-inline label,
  &.checkbox-inline label  {
    color: @text-color;
  }
  // Set the border and box shadow on specific inputs to match 
  .form-control {
    border-color: @border-color;
    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work 
    &:focus {
      border-color: darken(@border-color, 10%);
      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);
      .box-shadow(@shadow);
    }
  }
  // Set validation states also for addons 
  .input-group-addon {
    color: @text-color;
    border-color: @border-color;
    background-color: @background-color;
  }
  // Optional feedback icon 
  .form-control-feedback {
    color: @text-color;
  }
}

// Feedback states 
.has-success {
  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);
}
.has-warning {
  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);
}
.has-error {
  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);
}

SCSS:

$state-success-text:             #3c763d;
$state-success-bg:               #dff0d8;
$state-success-border:           darken(adjust_hue($state-success-bg, -10), 5%);

$state-info-text:                #31708f;
$state-info-bg:                  #d9edf7;
$state-info-border:              darken(adjust_hue($state-info-bg, -10), 7%);

$state-warning-text:             #8a6d3b;
$state-warning-bg:               #fcf8e3;
$state-warning-border:           darken(adjust_hue($state-warning-bg, -10), 5%);

$state-danger-text:              #a94442;
$state-danger-bg:                #f2dede;
$state-danger-border:            darken(adjust_hue($state-danger-bg, -10), 5%);

@mixin box-shadow($shadow) {
  -webkit-box-shadow: $shadow; // iOS <4.3 & Android <4.1 
          box-shadow: $shadow;
}

@mixin form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) {
  // Color the label and help text 
  .help-block,
  .control-label,
  .radio,
  .checkbox,
  .radio-inline,
  .checkbox-inline,
  &.radio label,
  &.checkbox label,
  &.radio-inline label,
  &.checkbox-inline label  {
    color: $text-color;
  }
  // Set the border and box shadow on specific inputs to match 
  .form-control {
    border-color: $border-color;
    @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work 
    &:focus {
      border-color: darken($border-color, 10%);
      $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($border-color, 20%);
      @include box-shadow($shadow);
    }
  }
  // Set validation states also for addons 
  .input-group-addon {
    color: $text-color;
    border-color: $border-color;
    background-color: $background-color;
  }
  // Optional feedback icon 
  .form-control-feedback {
    color: $text-color;
  }
}

// Feedback states 
.has-success {
  @include form-control-validation($state-success-text, $state-success-text, $state-success-bg);
}
.has-warning {
  @include form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg);
}
.has-error {
  @include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg);
}

函数的使用

字符串函数

LESS 使用 e 或者 ~”xxxx” 这种语法进行 CSS 转义;SCSS 本身并没有提供 CSS 转义的函数,中要达到相同的效果可以使用变量插值(Variable Interpolation)实现。

示例 – CSS 转义

LESS:

@input-border-focus:             #66afe9;

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow; // iOS &lt;4.3 & Android &lt;4.1 
  box-shadow: @shadow;
}

.form-control-focus(@color: @input-border-focus) {
  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);
  &:focus {
    border-color: @color;
    outline: 0;
    .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
  }
}

.form-control {
  .form-control-focus();
}

SCSS:

$input-border-focus:             #66afe9;

@mixin box-shadow($shadow) {
  -webkit-box-shadow: $shadow; // iOS &lt;4.3 & Android &lt;4.1 
  box-shadow: $shadow;
}

@mixin form-control-focus($color: $input-border-focus) {
  $color-rgba: rgba(red($color), green($color), blue($color), .6);
  &:focus {
    border-color: $color;
    outline: 0;
    @include box-shadow(#{inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px $color-rgba});
  }
}

.form-control {
  @include form-control-focus();
}

颜色函数

调节色相,LESS 使用名为 spin() 的函数;SCSS 使用名为 adjust_hue() 的函数。

示例 – 调节色相

LESS:

@state-success-border:           darken(spin(@state-success-bg, -10), 5%);

SCSS:

$state-success-border:           darken(adjust_hue($state-success-bg, -10), 5%);

数学函数

LESS 提供了一些 SCSS 中并不具备的数学函数,在 SCSS 中只能通过自定义函数实现,然后通过 node-sass 的接口传递给编译器。

示例 – 数学函数

SCSS:

// rotate for ie8 and blow
@mixin ie-rotate($rotation) {
  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})";
}

// rotate for ie8 and blow
// degrees unit
@mixin ie-rotate-via-degrees($degrees) {
  /* IE6-IE8 */
  $radians: parseInt("#{$degrees}") * PI() * 2 / 360;
  $costheta: cos("#{$radians}");
  $sintheta: sin("#{$radians}");
  $negsintheta: "#{$sintheta}" * -1;
  -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=@{costheta}, M12=@{negsintheta}, M21=@{sintheta}, M22=@{costheta})";
  zoom: 1;

  :root & {
    filter: none;
  }
}

// support rotate for all browsers
@mixin cross-rotate($degrees) {
  @include rotate($degrees);
  @include ie-rotate-via-degrees($degrees);
}

// Placeholder text
@mixin placeholder($color: $input-placeholder-color) {
  // Firefox
  &::-moz-placeholder {
    color: $color;
    opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526
  }
  // Internet Explorer 10+
  &:-ms-input-placeholder {
    color: $color;
  }
  // Safari and Chrome
  &::-webkit-input-placeholder {
    color: $color;
  }
}

上述 Math 实现所需的 JS 文件:

module.exports = {
  'parseInt($str)': function (str) {
    return parseInt(str, 10);
  },
  'Math.sin($degree)': function (degree) {
    return Math.sin(degree);
  },
  'Math.cos($degree)': function (degree) {
    return Math.cos(degree);
  },
  'Math.PI': Math.PI
}

有关函数的区别还有:

  1. LESS 的 fade() 函数在 SCSS 中只能使用 rgba() 之类的实现,因为 SCSS 也没有这个函数。

@import 的实现

像 @media, @import 这些带 @ 符号的在 CSS 中都称为 At-rules。
值的一提的是 LESS 和 SCSS 对@import 实现的区别。

LESS 的 @import 对文件扩展名的处理:

  • 如果扩展名为 .css,将文件识别为 CSS 文件
  • 其他任何扩展名都将被作为 LESS 文件处理
  • 没有扩展名会被附加一个 .less 的扩展名并且作为 LESS 文件处理

SCSS 的 @import 实现对文件扩展名的处理:

  • 默认情况下,SCSS 的 @import 实现会试图寻找一个 Sass 文件进行导入。
  • 但是在下列情况出现时,@import 会直接被编译为 CSS 的 @import at-rule
    • 文件扩展名是 .css
    • 文件以 http:// 开头
    • 文件名是一个 url()
    • @import 具有媒体查询
  • SCSS 按约定认为下划线开始的文件是内联文件,不会被编译为单独的 CSS 文件输出。

示例 – 使用 @import

LESS:

@import "foo";
@import "bar.less";
@import "foo.php"; // 当成 LESS 文件处理
@import "foo.css";

SCSS:

@import "foo";
@import "foo.scss";

都会导入 foo.scss 文件。

其他区别

引用父选择器 & 符号的使用

LESS 和 SCSS 均使用 & 符号表示父选择器,但是 SCSS 的 & 符号只能出现在一个组合选择器的开始位置,LESS 则没有这个限制。

示例 – & 选择器

LESS:

.bg-variant(@color) {
  background-color: @color;
  a&:hover,
  a&:focus {
    background-color: darken(@color, 10%);
  }
}

SCSS:

a {
  font-weight: bold;
  text-decoration: none;
  &:hover { text-decoration: underline; }
  body.firefox & { font-weight: normal; }
}

SCSS 不支持 LESS 中的 CSS Guard 功能,比如 if, when …,在 SCSS 中需要换种方式实现。

LESS 示例:

.my-optional-style() when (@my-option = true) {
  button {
    color: white;
  }
}
.my-optional-style();

Note:SCSS 需要换一种写法实现同样的功能。

SCSS 支持 !default,一般是用在基础 Rule 的声明中,告诉使用者这是可以被覆盖的。

SCSS 示例:

$primary:       $blue !default;
$secondary:     $gray-600 !default;

有关 extend

SCSS 不像 LESS 一样默认可以把 rule 作为 Mixin 使用,但是 SCSS 有类似的 @extend 指令;而 LESS 的 extend 语法看起来则像是伪类一样。

示例:

LESS:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

SCSS :

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

集成 JavaScript 功能的方式

LESS 使用 @functions 指令,可以把 js 代码直接放到 ~xxx 中间即可;SCSS 可以把 JS 代码放到一个单独的文件中,然后使用 node-sass编译的时候指定参数传给 node-sass。

LESS 示例:ant-design/tinyColor.less

SCSS 示例:
kant/Math.js

编译命令:node-sass --output-style expanded --source-map true --precision 6 --functions components/style/custom.js components/button/style/index.scss components/button/style/index.css

最后两个:

  1. LESS 支持 lazy evaluation,但是 SCSS 不支持,所以在 LESS 中可以先使用再定义,但是在 SCSS 中一定要先定义再使用。
  2. SCSS 是不支持 Mixin 重载的, 也就是说 LESS 可以有同名但是参数个数不同的几个 Mixins, SCSS 同样名字的 Mixin 只能有一个.

–Ending–。

原文地址:Less 和 SCSS有什么区别?

你见过哪些令你瞠目结舌的 JavaScript 代码技巧?

曾经在 Codewars 上作过一个名为 Directions Reduction 的题目,大意是:

一个人在荒野里需要从一个地方移动到另外一个地方,然后他会收到一个包含方向指示的数组,方向包括 “NORTH”, “SOUTH”, “WEST”, “EAST”。很明显呢,”NORTH” 和 “SOUTH” 这两个方向是相反的,”WEST” 和 “EAST” 同理。
因为气候非常恶劣并且饮用水也不多了,他必须要节省体力,因此要避免沿着一个方向前进然后再折返回来这样的无用功。

如果他收到的是像下面这样的一个方向指示:
{ "NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST" };
他首先就会发现先往 “NORTH” 走再往 “SOUTH” 走是不合理的,因为这样相当于没走。你的任务就是给这个人提供一个最简化的路线版本。例如,上面的方向指示简化后就变成了:
["WEST"];
因为首先 “NORTH” 和 “SOUTH” 抵消了、”EAST” 和 “WEST” 抵消了,然后是 “SOUTH” 和 “NORTH” 抵消了,最后只剩下一个 “WEST”。

["NORTH", "EAST", "WEST", "SOUTH", "WEST", "WEST"];
经过简化后变成了:
["WEST", "WEST"];
以上为题目描述和任务要求。
这种题应该怎么解呢?
通常情况下我们都会想到使用 JavaScript 中的 Array.prototype.reduce() 方法。

  1. 将原数组的成员从左往右依次压入一个新的数组中,
  2. 如果原数组的成员可以跟新数组栈顶的成员抵消,则把新数组栈顶的成员弹出;
  3. 如果无法抵消,则把原数组中的该成员压入新数组的栈顶,作为新数组的栈顶跟原数组的下一个成员再进行比较,最后的新数组就是我们想要的结果。

代码如下:

function dirReduc(plan) {
  var opposite = {
    NORTH: "SOUTH",
    EAST: "WEST",
    SOUTH: "NORTH",
    WEST: "EAST"
  };
  return plan.reduce(function(dirs, dir) {
    if (dirs[dirs.length - 1] === opposite[dir])
      dirs.pop();
    else
      dirs.push(dir);
    return dirs;
  }, []);
}

这基本上已经算是最佳实践了,
然而有个让我目瞪狗呆的实现是这样的:

function dirReduc(arr) {
  var str = arr.join(""),
    pattern = /NORTHSOUTH|EASTWEST|SOUTHNORTH|WESTEAST/;
  while (pattern.test(str)) str = str.replace(pattern, "");
  return str.match(/(NORTH|SOUTH|EAST|WEST)/g) || [];
}

实现原理是:

  1. 首先把原数组转成字符串,
  2. 然后把能抵消的路线使用正则匹配替换为空,
  3. 最后利用 String.prototype.match() 返回值为数组这个技巧把字符串重新还原为我们想要的数组。

虽然不如第一种方案符合最佳实践,但我觉得的确挺聪明的!
作者心里一定在(ˇˍˇ) 想~:

凡是字符串处理都可以使用正则表达式实现;如果不是字符串,那就先把它转成字符串。

2018年前端流行哪些技术?(搬运一篇我在知乎的回答)

现在(2018年3月)前端流行哪些技术?或者说哪些技术是被前端同学们采用最多的?其实是有一些比较好的总结的的,比如这篇回答:hijiangtao:2017年前端有什么样变化?即将来临的2018有什么样的期待?,以及这篇:2017年前端技术总结

我自己的经验呢:

如果项目要求支持低版本的 IE 的话(比如 IE7,8),就用较传统的jQuery,Bootstrap,jQuery插件/组件以及类似 Knockout.js 能够提供数据绑定的 JS 库,再加上 require.js + gulp 或者 fis3 做模块化和自动化构建。

如果不需要支持低版本 IE 的话,我们主要使用 React:

  1. React – 编写页面组件
  2. Redux – 数据流和状态管理,一般结合 redux-saga 使用
  3. React-router v4 – 前端路由管理(Note:dva 整合了 redux, redux-router 以及 redux-saga。在熟悉了基本的 Redux, Redux-saga, Redux-router 的使用之后,可以尝试用 dva 替代)
  4. Webpack – 前端构建工具

前端开发我在用到的其他类库,技术选择和工具还包括:

  1. SCSS – CSS 预处理,提供 Variables, Mix-in 等功能
  2. ESLint – JavaScript 代码质量工具,之前使用 airbnb 配置方案,现在使用 standard 配合方案
  3. StyleLint – CSS/SCSS/Less 代码质量工具
  4. EditorConfig – 统一编辑器配置
  5. git – 版本控制工具,结合 git-flow 做分支管理
  6. oh-my-zsh – 提高终端操作效率
  7. Ant Design – React 组件库,适合中后台应用开发,极大提高开发效率。另外一个可以参考的是 Material UI,是 Google Material Design 的 React 实现。
  8. Ant Design Pro – 基于 Ant Design 的中后台项目脚手架,提供了更高层的常用业务组件和最佳实践,大大减少重复性工作
  9. Animate.css – 流行的动画样式库,实现了包括 Attention(吸睛?),Bouncing(弹跳),Fading(淡入/淡出),Flippers(翻转),Lightspeed(光速?),Rotating(旋转),Sliding(滑动),Zoom(缩放)等各种动效。
  10. Alloy Touch – 丝般顺滑的触摸运动方案(官方原文),支持缓动效果,使用了 matrix-3D(transformjs),requestAnimationFrame 以及针对 touchmov 事件的优化({ passive: false, …}),体验与原生非常接近。同时支持各种触控场景:2D/3D 旋转,翻页,步进。我还用它实现了无限滑动的效果。还有一个非常流行的 swiper(nolimits4web/swiper),但是我没有用过。不知道跟 Alloy touch 的区别。
  11. AutoPrefixer – PostCSS 插件,构建时根据 caniuse-lite 数据库以及你设置的或者默认的浏览器列表(browserslist)给 CSS 属性自动增加浏览器厂商前缀。一般结合 gulp 或者 webpack 等 Task runner 使用。这样我们写 CSS 样式的时候只需要写纯 CSS 而不需要考虑厂商前缀的问题。
  12. Async Flow Control – JavaScript 的异步流程控制,大概经历了 callback -> Async.js -> Promise(bluebird -> 原生 Promise 支持) -> yield/generator -> async/await(ES7) 这样一个过程。推荐 async/await + promise 的解决方案,Node.js 现在(8+)也可以用 Util.promisify() 对原先的 callback API 进行封装成 Promise。
  13. Axios – 基于 Promise 的 HTTP 客户端,可以运行于浏览器和 Node.js 环境。我主要是在 Node.js 中使用 Axios,替代了 request;浏览器中还是使用 Fetch API,还没有在浏览器中尝试使用 Axios。
  14. Babel + ES6/6+ – 现代 JavaScript 语法和编译器,Babel 可以让你使用目前主流浏览器尚未全面支持的新的 JavaScript 语法来编写代码,同时帮你编译成现代浏览器都支持的 ES5 语法,还提供了一些 polyfill (通过 core.js)实现了浏览器不支持的 feature。
  15. Ava, Chai.js, Jest, Enzyme, Headless Chrome – 测试框架,runner,断言库,单元测试,组件测试,端对端测试的一些工具。我用 Ava 替代了 Mocha 用来做测试框架和 Runner,Headless Chrome 替代了 PhantomJS 做端对端测试,断言主要用 Chai 里面的 expect 以及 jsonschema 等 Chaijs插件。
  16. CSS Modules – CSS 模块化方案,避免全局作用域/冲突,实现显式依赖。暂时没有使用 css-in-js 方案。
  17. Data Visualization – 数据可视化和一些图表工具,使用最多的还是 ECharts,支持各种图表,交互和渲染模式,PC 端移动端通吃,文档很全,最近还成为了 Apache 孵化项目。另外还用过 g2,感觉也不错,现在也能看到源码了。g2 是 grammer of graphic 的缩写,算是 g2 的理论基础。自己从基础做起的话,可以使用 d3.js,提供了可视化的很多基础模块,基于 SVG。
  18. Docker – 嗯,前端应用部署也可以使用 docker,基于 Nginx image 或者 Node.js image。我以前写过一个构建 docker 的 shell 脚本:分享一个自动构建 docker 并导出 image 的 shell 脚本
  19. esprima, espree, acorn, babel-parser – 进行 JS/JSX 语法解析和句法分析的 JS 库。espree 是 ESLint 使用的 parser,最初 fork 自 Esprima,后来基于 Acorn。Esprima 是最老牌的 js parser,现在使用 TypeScript 实现;Acorn 使用 ES6,特点是模块化,但是 Esprima 的文档比 Acorn 相对要全一些。babel-parser 的解析器 fork 自 Acorn。
  20. Express.js/Koa2/Egg.js/Strongloop – Express 和 Koa 都是 Node.js 的 Web 框架,主要用来实现 API 网关,也可以 serve 一些静态内容。我用过时间最长的是 Express, koa2 也在几个项目中用过,也了解过 loopback, hapi, kraken 等。推荐直接使用 Egg 或者 Strongloop 这种封装了最佳实践的企业级 Node.js 框架,而不是自己基于 express 或者 koa 攒一堆组件。
  21. Functional Programming – 函数式编程,这个真的是前端流行… 还有纯函数,pure render, pure component,immutable 等概念。参考:lodash 中的 FP 实现。嫌 immutable.js 太重可以试试 immer。
  22. GraphQL/thrift/RESTful API/OpenAPI – 各种接口方案。REST 有一种明日黄花的感觉,虽然还在用,但是感觉早晚被 GraphQL 等取代,毕竟我们真的越来越不关心数据是如何获取的,而应该关注在 store 如何设计上,专注在领域分析上面。Thrif 支持跨语言 RPC 调用,比如跨 Node.js 和 Java 等。嗯,Thrif 支持自动生成桩代码,什么定接口、JSON、MOCK 就都是浮云了,不需要纠结是否符合 RESTful 规范了。NOTE:Github 提供的 API 之前是 RESTful 的,现在都是 GraphQL 了。
  23. Homebrew – MAC 软件包管理工具,brew 相当于 Ubuntu 的 apt,RedHat/CentOS 的 yum。
  24. lodash/moment.js – 最常用的两个 JS 库了,lodash 是一个现代、高效、模块化的 JS 功能包,moment.js 主要用来处理日期时间相关的操作。都是即可运行在浏览器环境,也可以运行在 Node.js 环境。
  25. MongoDb/MySQL/Nginx/Redis – 这些都是常用的服务器应用。MongoDB 使用 mongoose,MySQL 使用 sequelizer(都是非常优秀的 Node.js 的 ORM 实现),Nginx 会配置反向代理,URL 重写,缓存设置等即可。
  26. npm/yarn – 包管理工具。我倾向于还是使用 npm,搞清楚 npm 的常用 script;搞清楚npm install 时候依赖安装的流程,以及 package-lock 的作用;能维护和发布自己的 npm 包;知道 npx 是干什么的就可以了。
  27. Performance – 前端性能,善用 Chrome Devtool 中的各种功能,包括 lighthouse。
  28. PostCSS – 本身是一个 CSS 的 parser,最早是从 AutoPrefixer 中抽取出来的,现在已经是 CSS 的瑞士军刀了。可以结合 gulp, Webpack 等 task runner 使用,能够解析 CSS/SCSS/Less 等各种语法,提供了 AutoPrefixer(加浏览器前缀), cssnano(各种严压缩,各种配置), cssnext(用未来的 CSS 语法规范写当前浏览器支持的代码), css moudles(模块化),variables, mix-in(类 SCSS 的预处理支持)等各种丰富插件…没有你还可以自己写(提供插件 boilplate)!
  29. React Native/Flutter – 跨端方案。最近关于 RN 是否凉了?如何评价 Flutter?的问答挺多的,我更关注的可能还是了解他们是如何实现的,以及解决了什么问题吧
  30. source maps – 了解 js、css 的 source maps 是如何生成的,相应的规范,在浏览器、生产环境调试、以及异常追踪系统里面的使用。
  31. TypeScript – 用 TS 实现的项目越来越多了,特别是一些大型项目,流行程度也是越来越高
  32. WebSocket – 需要长连接、实时通信的场景
  33. WebStorm/VS Code – IDE/编辑器,使用最多的就是用这两种了

另外,我觉得大家关注讨论比较多的但是我自己还没有实践过的前端技术还包括:

  1. rx.js
  2. mobx
  3. 小程序 – 包括 wepy, mpvue 等
  4. rollup(侧重 JS library 的打包构建)
  5. parcel(约定式的打包构建工具)
  6. 服务端渲染(SSR)
  7. 前后端同构
  8. createReactApp – React 项目脚手架工具

postcss-less2scss:将 Less 转换成 Scss 的 PostCSS 插件

发布一个 PostCSS 插件:postcss-less2scss,可以将 Less 文件转换为 Scss。
* GitHub:https://www.npmjs.com/package/postcss-less2scss
* NPM:https://www.npmjs.com/package/postcss-less2scss

转换变量

转换变量的定义和使用

  • 不属于任何一个 Rule 的变量

Less:

@link-color: #428bca;

Scss:

$link-color: #428bca;
  • 在某个 Rule 中定义的变量

Less:

#main {
  @width: 5em;
  width: @width;
}

Scss:

#main {
  $width: 5em;
  width: $width;
}
  • 在 Declaration 的 value 中使用的变量

Less:

@text-color: @gray-dark;
@link-color-hover:  darken(@link-color, 10%);

Scss:

$text-color: $gray-dark;
$link-color-hover:  darken($link-color, 10%);
  • 在某个 Rule 中的一个 Declaration 的 value 中使用的变量

Less:

a:hover {
  color: @link-color-hover;
}

Scss:

a:hover {
  color: $link-color-hover;
}
  • 转换 At-Rules 中的变量

Less:

@screen-sm:                  768px;
@screen-sm-min:              @screen-sm;

.form-inline {

  // Kick in the inline
  @media (min-width: @screen-sm-min) {
    // Inline-block all the things for "inline"
    .form-group {
      display: inline-block;
      margin-bottom: 0;
      vertical-align: middle;
    }
  }
}

Scss:

$screen-sm:                  768px;
$screen-sm-min:              $screen-sm;

.form-inline {

  // Kick in the inline
  @media (min-width: $screen-sm-min) {
    // Inline-block all the things for "inline"
    .form-group {
      display: inline-block;
      margin-bottom: 0;
      vertical-align: middle;
    }
  }
}

Variable Interpolation

  • 转换选择器中的 variable interpolation

Less:

// Variables
@my-selector: banner;

// Usage
.@{my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

Scss:

// Variables
$my-selector: banner;

// Usage
.#{$my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

转换 Mixins

  • 转换 Mixins 的定义(可以支持默认参数)

Less:

.alert-variant(@background; @border; @text-color) {
  background-color: @background;
  border-color: @border;
  color: @text-color;

  hr {
    border-top-color: darken(@border, 5%);
  }
  .alert-link {
    color: darken(@text-color, 10%);
  }
}

Scss:

@mixin alert-variant($background, $border, $text-color) {
  background-color: $background;
  border-color: $border;
  color: $text-color;

  hr {
    border-top-color: darken($border, 5%);
  }
  .alert-link {
    color: darken($text-color, 10%);
  }
}
  • 转换 Mixins 的使用

Less:

.a {
    .center-block;
}

Scss:

.a {
    @include center-block;
}
  • 支持 Mixins 的具有默认值的参数

Less:

@state-success-text:             #3c763d;
@state-success-bg:               #dff0d8;
@state-success-border:           darken(spin(@state-success-bg, -10), 5%);

@state-info-text:                #31708f;
@state-info-bg:                  #d9edf7;
@state-info-border:              darken(spin(@state-info-bg, -10), 7%);

@state-warning-text:             #8a6d3b;
@state-warning-bg:               #fcf8e3;
@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);

@state-danger-text:              #a94442;
@state-danger-bg:                #f2dede;
@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
          box-shadow: @shadow;
}

.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
  // Color the label and help text
  .help-block,
  .control-label,
  .radio,
  .checkbox,
  .radio-inline,
  .checkbox-inline,
  &.radio label,
  &.checkbox label,
  &.radio-inline label,
  &.checkbox-inline label  {
    color: @text-color;
  }
  // Set the border and box shadow on specific inputs to match
  .form-control {
    border-color: @border-color;
    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
    &:focus {
      border-color: darken(@border-color, 10%);
      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);
      .box-shadow(@shadow);
    }
  }
  // Set validation states also for addons
  .input-group-addon {
    color: @text-color;
    border-color: @border-color;
    background-color: @background-color;
  }
  // Optional feedback icon
  .form-control-feedback {
    color: @text-color;
  }
}

// Feedback states
.has-success {
  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);
}
.has-warning {
  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);
}
.has-error {
  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);
}

Scss:

$state-success-text:             #3c763d;
$state-success-bg:               #dff0d8;
$state-success-border:           darken(adjust_hue($state-success-bg, -10), 5%);

$state-info-text:                #31708f;
$state-info-bg:                  #d9edf7;
$state-info-border:              darken(adjust_hue($state-info-bg, -10), 7%);

$state-warning-text:             #8a6d3b;
$state-warning-bg:               #fcf8e3;
$state-warning-border:           darken(adjust_hue($state-warning-bg, -10), 5%);

$state-danger-text:              #a94442;
$state-danger-bg:                #f2dede;
$state-danger-border:            darken(adjust_hue($state-danger-bg, -10), 5%);

@mixin box-shadow($shadow) {
  -webkit-box-shadow: $shadow; // iOS <4.3 & Android <4.1
          box-shadow: $shadow;
}

@mixin form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) {
  // Color the label and help text
  .help-block,
  .control-label,
  .radio,
  .checkbox,
  .radio-inline,
  .checkbox-inline,
  &.radio label,
  &.checkbox label,
  &.radio-inline label,
  &.checkbox-inline label  {
    color: $text-color;
  }
  // Set the border and box shadow on specific inputs to match
  .form-control {
    border-color: $border-color;
    @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
    &:focus {
      border-color: darken($border-color, 10%);
      $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($border-color, 20%);
      @include box-shadow($shadow);
    }
  }
  // Set validation states also for addons
  .input-group-addon {
    color: $text-color;
    border-color: $border-color;
    background-color: $background-color;
  }
  // Optional feedback icon
  .form-control-feedback {
    color: $text-color;
  }
}

// Feedback states
.has-success {
  @include form-control-validation($state-success-text, $state-success-text, $state-success-bg);
}
.has-warning {
  @include form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg);
}
.has-error {
  @include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg);
}

转换函数

字符串函数

  • 转换 CSS 转义函数,也就是:~”xxx”

Less:

@input-border-focus:             #66afe9;

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow; // iOS &lt;4.3 & Android &lt;4.1
  box-shadow: @shadow;
}

.form-control-focus(@color: @input-border-focus) {
  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);
  &:focus {
    border-color: @color;
    outline: 0;
    .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
  }
}

.form-control {
  .form-control-focus();
}

Scss:

$input-border-focus:             #66afe9;

@mixin box-shadow($shadow) {
  -webkit-box-shadow: $shadow; // iOS &lt;4.3 & Android &lt;4.1
  box-shadow: $shadow;
}

@mixin form-control-focus($color: $input-border-focus) {
  $color-rgba: rgba(red($color), green($color), blue($color), .6);
  &:focus {
    border-color: $color;
    outline: 0;
    @include box-shadow(#{inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px $color-rgba});
  }
}

.form-control {
  @include form-control-focus();
}

颜色函数

  • 将 Less 的 spin() 函数转换为 Scss 的 adjust_hue() 函数

Less:

@state-success-border:           darken(spin(@state-success-bg, -10), 5%);

Scss:

$state-success-border:           darken(adjust_hue($state-success-bg, -10), 5%);

转换 @import At-Rules

Less:

@import "foo";
@import "foo.less";
@import "foo.php";
@import "foo.css";

Scss:

@import "foo";
@import "foo";
@import "foo";
@import "foo.css";

如果使用 postcss-less2scss 插件

const postcss = require('postcss')
const syntax = require('postcss-less')
const converter = require('postcss-less2scss')

const lessText = `
// Variables
@link-color:        #428bca; // sea blue
@link-color-hover:  darken(@link-color, 10%);

// Usage
a,
.link {
  color: @link-color;
}
a:hover {
  color: @link-color-hover;
}
.widget {
  color: #fff;
  background: @link-color;
}
`
const scssText = postcss([converter])
  .process(lessText, { syntax })
  .css
console.log('lessText = ', lessText, ', \nscssText = ', scssText)

和 gulp 集成

/**
 * Use postcss-less2scss to convert bootstrap v3.3.7 styles from less to scss
 */
gulp.task('less2scss', () => {
  return gulp.src('assets/less/bootstrap3/**/*.less')
    .pipe(postcss([less2scss], {
      syntax: less
    }))
    .pipe(rename(path => {
      if (path.basename !== 'bootstrap') {
        path.basename = '_' + path.basename
      }
      path.extname = '.scss'
    }))
    .pipe(gulp.dest('build/scss/bootstrap3/'))
})

postcss-remove-global: 从代码中移除 :global 的 PostCSS 插件

postcss-remove-global

发布一个 PostCSS 插件,用来移除样式中的 :global 标识符。
* github 链接:https://github.com/princetoad/postcss-remove-global
* npm 链接:https://www.npmjs.com/package/postcss-remove-global

目前支持三种场景,第一种是 :global 作为一个单独的选择器,第二种是 :global 作为选择器的一部分,第三种是 :global 作为 @keyframe 属性的一部分。分别对应下面的三个示例。

示例

  1. Remove :global as a single selector
:global {
a { }
}
a { }
  1. Remove :global as part of a selecotr
.root :global .text { margin: 0 6px; }
.root .text { margin: 0 6px; }
  1. Remove :global as part of params of @keyframe
@keyframes :global(zoomIn) { }
@keyframes zoomIn { }

如何使用

使用 postcss API

const postcss = require('postcss')
const removeGlobal = require('postcss-remove-global')

const css = postcss()
.use(removeGlobal())
.process(':global { a {color: gray(85); background-color: gray(10%, .25)}}')
.css
console.log('css = ', css)
//= 'a {color: gray(85); background-color: gray(10%, .25)}'

const css2 = postcss([removeGlobal])
.process('.root :global .text { margin: 0 6px; }')
.css
console.log('css2 = ', css2)
//= '.root .text { margin: 0 6px; }'

const css3 = postcss([removeGlobal])
.process('@keyframes :global(zoomIn) { }')
.css
console.log('css3 = ', css3)
//= '@keyframes zoomIn { }'

参见:https://github.com/princetoad/try-postcss/blob/master/src/Plugin/plugin-remove-global.js

结合 gulp

gulp.task('global', () => {
return gulp.src('assets/*.css')
.pipe(postcss([removeGlobal]))
.pipe(gulp.dest('build/'))
})

参见:https://github.com/princetoad/try-postcss/blob/master/gulpfile.js

generator-criket: 一个 Node.js 命令行项目脚手架

一看 generator-criket 这个名字,有经验的人就知道这是基于 yeoman 的.
至于为什么命名为 criket (蛐蛐)? 则是因为我正在看王世襄先生的<京华忆往>,里面有讲到百灵( Lark) 和蛐蛐( criket). Lark 已经被人用了, 有一个 Lark.js 以及对应的 generator-lark, 我就只能用蛐蛐了.

蛐蛐的用法很简单, 装好 yeoman(npm install -g yo) 之后,直接运行 yo criket,然后按照提示一步一步输入相关信息即可,然后一个 Node.js 命令行项目就创建完成了.
我的 augustine,frege 等命令行工具都是基于这个脚手架的.

Frege – 由 package.json 逆向生成 npm install/yarn add 安装命令的工具

更新 v0.3: 使用 -l -u 或者 --latest --update 选项可以直接运行生成的最新版本的依赖包安装脚本, 将 package.json 以及 node_moduels下面的依赖包都更新至最新版本. Note: 生产项目慎用


更新 v0.2: 支持生成 yarn 安装脚本, 使用 -y 或者 --yarnInstall 参数


frege 是什么?

frege 可以从一个现有的 package.json 逆向生成安装所需的 npm install 脚本. 特点是可以选择仅生成生产环境或者开发环境包安装脚本, 并且能够正确将 version range 转成 npm install 所需的语法.

安装

建议把 frege 安装到全局, 然后就可以在命令下直接使用了.

npm install frege -g

使用

参数说明

frege [options]

基本配置:
-f, --file String 要解析的 package.json 文件, 默认会解析当前目录下名为 package.json 的文件 - default: package.json
-l, --latest 不管 package.json 中 npm 包的具体版本号, 安装该包的最新版本 - default: false
-p, --productionOnly 仅生成 dependencies 项目下 npm 包的安装脚本. - default: false
-d, --devOnly 仅生成 devDependencies 项目下的 npm 包的安装脚本, 即开发使用的. - default: false
-h, --help Show help
-v, --version Output the version number

示例

frege

frege

不带任何参数直接运行 frege 命令将生成当前目录下 package.json 文件中 dependenciesdevDependencis(如果有的话) 全部 npm 包的安装脚本, 例如:

npm i -S debug@">=2.6.8 <3.0.0" optionator@">=0.8.2 <0.9.0" semver@">=5.3.0 <6.0.0" npm i -D ava@">=0.19.1 <0.20.0" chai@">=4.0.1 <5.0.0" eslint@">=3.19.0 <4.0.0" tap-nyan@">=1.1.0 <2.0.0"

frege -p

frege --productionOnly

的缩写, 仅生成 dependencis 下面 npm 包的安装脚本.

npm i -S debug@">=2.6.8 <3.0.0" optionator@">=0.8.2 <0.9.0" semver@">=5.3.0 <6.0.0"

frege -d

frege --devOnly

的缩写, 仅生成 devDependencis 项下面 npm 包的安装脚本.

npm i -D ava@">=0.19.1 <0.20.0" chai@">=4.0.1 <5.0.0" eslint@">=3.19.0 <4.0.0" tap-nyan@">=1.1.0 <2.0.0"

frege -l

frege --latest

的简写, 安装 npm 包的最新版本, 而不是原有 package.json 中指定的版本范围.

npm i -S debug optionator semver
npm i -D ava chai eslint tap-nyan

frege -f ../augustine/package.json

指定要解析的 package.json 文件的完整路径.

有问题欢迎反馈!

github 地址: https://github.com/princetoad/frege
npm 地址: https://www.npmjs.com/package/frege

范圣刚 <tom@tfan.org>

Augustine – 一个简单的静态文件 HTTP Server

发布了一个简单的基于 node.js 的 HTTP Server, 可以用来 serve 静态资源, 作为纯前端项目的 Web 服务器.

特点: 简单, 简小, 可以通过 npm 安装.

功能:

  1. 支持 HTTP Status Code – 404 Not Found
  2. 如果路径是目录的话, 支持默认到 index.html 文件
  3. 支持 debug 信息(DEBUG=augustine)

使用 npm 安装

npm install augustine --save-dev

如何使用

在作为 Web 目录的文件夹下新建一个例如名为 index.js 文件, 加入下面两行代码:

const augustine = require('augustine');
augustine.start(8080);

然后运行

node index.js

即可通过 8080 端口访问该目录下的内容了.

欢迎参与

github 地址: https://github.com/princetoad/augustine
npm 地址: https://www.npmjs.com/package/augustine