ivan 发布于 02月09, 2017

如何优雅地写react

如何优雅的写react

使用react写项目的时候,代码如何写的简洁易懂,小结如下。

优雅的组件应该是这样的....

  • are easy to understand even without comments
  • can be even more performant than clunky code
  • increase the chances of catching bugs before they reach QA
  • are concise and say a lot with a little

"Less is more." — Mies van der Rohe

SFC(The Stateless Functional Component) 函数式无状态组件

我们编写组件的时候,并不是所有的组件都是需要状态的。由于react是以数据驱动,数据决定了react组件的输出结果,所以为了让我们组件更好控制,应该尽量的少使用状态。

栗子

let Hello = props => <div>Hello {props.name}</div>

还可以利用es6的解构赋值再来精简,适用于props的数量少的时候

let Hello = ({name}) => <div>Hello {name}</div>

1.无生命周期函数 一个组件就是一个函数,函数应该是谈不上生命周期的,但是组件却是有生命周期,stateless functions 没有生命周期。当然了,我们其实可以使用高阶组件 去实现生命周期。

2.没有 this 在 stateless functions 中,this 是 undefined,所以是不能使用 this 变量。不过换个角度思考,this 是在运行时随时可以被修改或重新赋值,跟外界环境有着密切的联系,正是不使用this才会让组件变得更纯。

3.使用到refs时不能使用无状态组件,因为无状态组件只是函数,所以它没有实例返回,这点在想用 refs 获取无状态组件的时候要注意

条件判断

JSX不支持if条件判断,使用三元判断来显示不同的组件。

类型1:A或者B

render() {
  <div class="search-results-container">
    {this.props.isGrid
      ? <SearchResultsGrid />
      : <SearchResultsList />}
  </div>
}

类型2:展示或者不展示

render() {
  <div class="search-results-list">
    {this.props.isSoftSort
      ? <SoftSortBanner />
      : null
    }
  </div>
}

这样写没问题,但是那个null总是怪怪的。 一种更优雅的方式:

render() {
  <div class="search-results-list">
    {!!this.props.isSoftSort && <SoftSortBanner />}
  </div>
}

PS:!!不要省略,否则一些逻辑非(比如数字0)就会在react渲染出来。

箭头函数(哦,好用!)

当你只需要一个参数或者多个参数(需要用小括号包裹参数)的简单函数时,可以使用新标准中的箭头函数,它的语法非常简单:标识符=>表达式。无需输入function和return,一些小括号、大括号以及分号也可以省略。

const SoftSort = ({ hardSortUrl, sortByName, onClick }) => {
  return (
    <div className="SearchInfoMessage">
      Showing results sorted by both Relevance and {sortByName}.
      <Link
        href={`?${hardSortUrl}`}
        onClick={(ev) => onClick(ev, hardSortUrl)}>
        Sort results by {sortByName} only
      </Link>
    </div>
  );
};

普通function函数和箭头函数的行为有一个微妙的区别,箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。

阅读全文 »

ivan 发布于 09月09, 2016

javascript随机-只是为了贴代码

/**
 * Randomize array element order in-place.
 * Using Durstenfeld shuffle algorithm.
 */
function shuffleArray(array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}
Array.prototype.shuffle = function() {
  var i = this.length, j, temp;
  if ( i == 0 ) return this;
  while ( --i ) {
     j = Math.floor( Math.random() * ( i + 1 ) );
     temp = this[i];
     this[i] = this[j];
     this[j] = temp;
  }
  return this;
}

阅读全文 »

ivan 发布于 09月08, 2016

javascript随机函数

随机取一个值: 一般情况下代码逻辑放在后端处理,前端只负责展现。 但是若是若是后端不愿意做,e...那么前端需要能够实现。

需求:实现一个数组的打乱重拍。若是直接从数组中随机取一个值比较简单,直接var item = items[Math.floor(Math.random()*items.length)]得到一个随机值。下面看怎么对数组进行重新排序。

so ... 遇到问题第一步,先查查网上有木有已经实现的比较好的方式~ Fisher-Yates binggo!

Fisher-Yates乱序算法

思路,假如你要洗牌,那么最随机的做法就是从牌堆里随便抽一张出来,然后放在一边,之后从剩下的牌里重复之前的操作,直到所有牌都被抽出来放到另外一堆中。

round 1


随机从数组里取出一个元素,保存到另一个数组,然后重复之,直到原数组中所有元素为空。 查看演示

function shuffle(array) {
    var copy = [],
        n = array.length,
        i;
    // 如果还剩有元素则继续。。。
    while (n) {
        // 随机抽取一个元素
        i = Math.floor(Math.random() * array.length);
        // 如果这个元素之前没有被选中过。。
        if (i in array) {
            copy.push(array[i]);
            delete array[i];
            n--;
        }
    }
    return copy;
}

问题:我们新建一个数组,每次都会随机取一个0-len的随机值,随机值可能一样;另外一方面可能永远运行不完。

注: Math.random()产生[0,1)的小数 delete 操作只将数组元素的值删除,但不影响数组长度,删除后原来位置的值变为undefined

round 2


用Array的splice()方法将其从目标数组中移除同时也更新了目标数组的长度,如此一来下次遍历的时候是从新的长度开始,不会重复处理的情况了。 查看演示

function shuffle(array) {
    var copy = [],
        n = array.length,
        i;
    // 如果还剩有元素。。
    while (n) {
        // 随机选取一个元素
        i = Math.floor(Math.random() * n--);
        // 移动到新数组中
        copy.push(array.splice(i, 1)[0]);
    }
    return copy;
}

round 3


因为调用splice来删除数组元素会导致删除位置之后的所有元素要做shift操作来向前补充,从而达到将数组长度减小的目的,当然这是在后台自动完成的,但这无疑增加了算法的复杂度。

注意到我们要做的仅仅是将数组元素重新排序,已经取出来的元素和剩下的元素之和一定是等于数组原来的总元素个数的。所以可以考虑不创建新的数组来保存已经抽取的元素,可以这样,随机从数组中抽出一个元素,然后与最后个元素交换,相当于把这个随机抽取的元素放到了数组最后面去,表示它已经是被随机过了,同时被换走的那个元素跑到前面去了,会在后续的重复操作中被随机掉。一轮操作过后,下一轮我们只在剩下的n-1个元素也就是数组的前n-1个元素中进行相同的操作,直到进行到第一个。 查看演示

function shuffle(array) {
    var m = array.length,
        t, i;
    // 如果还剩有元素…
    while (m) {
        // 随机选取一个元素…
        i = Math.floor(Math.random() * m--);
        // 与当前元素进行交换
        t = array[m];
        array[m] = array[i];
        array[i] = t;
    }
    return array;
}

不急,还有一个,真是简洁。但是随机效果不好。

round 4


function shuffle(array) {
    return array.sort(function() {
        return Math.random() - 0.5
    });
}

⬆️ 这厮有bug! 在IE 11上。sort方法的返回值应该是0,1,-1。这里返回true和false。 所以,改进之后:

function shuffle(array) {
    return array.sort(function() {
        return Math.random() > 0.5 ? 1: -1;
    });
}

生成一个1-len的数组。

  • Array.from(Array(len).keys()); //返回[0-length]的数组
  • Math.pow(2, len).toString(2).split('').map((i,j) => j);
  • Array(N).fill().map((_, i) => i + 1);
  • Array.from(Array(5)).map((_, i) => i + 1);

阅读全文 »

ivan 发布于 09月01, 2016

有感。

大牌前端小记

1.需求方面 - 开发过程中并没有按照需求文档的内容来完善。需求文档很多细节交互不清晰,后期一直在不断的更改需求/新加需求。 页面布局确定之后又会做出调整。

2.代码方面 - 代码的可维护性,写代码的时候需要考虑代码的强壮性,不要单纯的把代码从一个地方复制粘贴到另外一个地方,了解每个代码的含义以及可能的影响内容。另外使用smarty模版写页面的时候,可以统一foreach循环出来,否则相似内容写的很乱,并且操作事件时会有各种绑定事件,混杂并且不精简,不优雅。

3.bug -bug出现之后,不要着急的想用一个很hack的方式去实现,而是需要考虑底层原因,为什么会出现这样的问题?浏览器兼容性问题还是代码逻辑问题?一上来就用一个很hack的方式去解决问题就会导致各种逻辑混杂在一起,第二天就只有上帝知道你代码逻辑了。

4.心态 -问题一个一个来,并不是每一个需求都要改并不是每个任务都很紧急,对所做事件进行合理排期,不慌张效率才会高。

另外,解别人代码bug一定要三思。改动较小则好,改动较大,建议提早重构。

阅读全文 »

ivan 发布于 08月24, 2016

浏览器窗口定位

需求:

如下图所示,需要知道黄色模块距离浏览器底部的距离,若距离底部距离小于0,需要向上滚动至露出黄色模块。具体布局参数见代码:

<style>
   html,body{padding: 0;margin:0;}
   .wrap{position: relative; width: 100%;height: 800px;  background: #eaeaea;}
   .poi{position:absolute; margin-top: 300px; width: 100%; height: 100px; background: #ef0; text-align: right;}
</style>
<div class="wrap">
   <div class="poi">poi</div>
</div>

不解释:

<script>
   $(function(){
       var wh = $(window).height();
       var dh = $(document).height();
       var sh = $(window).scrollTop();
       var bh = $(".poi").offset().top;
       var h = $(".poi").height();
       var to_btm = wh - bh + sh -h;
       console.log("浏览器视窗高度:"+wh);
       console.log("页面文档高度:"+dh);
       console.log("页面向上滚动高度:"+sh);
       console.log("元素距离页面顶部高度:"+bh);
       console.log("元素自身高度:"+h);
       console.log("元素底部距离浏览器的高度:"+to_btm);
   });
</script>

解释如图:

ps:

$('html,body').animate({scrollTop:bh+100},500);

阅读全文 »

ivan 发布于 08月19, 2016

node路径问题&path模块处理文件路径

常见路径:

node中的文件路径主要有__dirname__filenameprocess.cwd()./../,前三个都是绝对路径,为了便于比较,./../我们通过path.resolve('./')来转换为绝对路径。

举个栗子:

假如我们有这样一个目录结构:

app/
    -lib/
        -common.js
    -model/
        -task.js
        -test.js

在test.js里编写如下的代码:

var path = require("path");
console.log(__dirname);
console.log(__filename);
console.log(process.cwd());
console.log(path.resolve("./"));

在model目录下运行node task.js得到的输出是:

/home/ivan/app/model
/home/ivan/app/model/task.js
/home/ivan/app/model
/home/ivan/app/model

然后在app目录下运行node model/task.js,得到输出结果:

/home/ivan/app/model
/home/ivan/app/model/task.js
/home/ivan/app
/home/ivan/app

依上,我们是否可以得到这样的一个结论:

  • __dirname总是返回被执行的js所在文件夹的绝对路径。
  • __filename总是返回被执行js文件的绝对路径。
  • process.cwd()总是返回运行node命令所在的文件夹的绝对路径。
  • ./process.cwd()一样一样的。

但是经常见到 require('../lib/common')里一直都是各种相对路径写,也没见什么报错呀, 再举个栗子,还是上面的结构,model/task.js里的代码改成:

var fs = require("fs");
var common = require("../lib/common");

fs.readFile("../lib/common.js", function (err, data) {
    if (err) return console.log(err);
    console.log(data);
});

在model目录下运行node task.js,一切OK,都没有报错。然后在app目录下运行node model/task.js,然后很果断的就报错了:

{ [Error: ENOENT: no such file or directory, open "../lib/common.js"]
  errno: -2,
  code: "ENOENT",
  syscall: "open",
  path: "../lib/common.js" }

所以,问题来了。按照前面得到的结论,./process.cwd()一样一样的。那么在model目录下执行的时候,../lib/common.js会被转变成/home/ivan/app/lib/common.js,在app目录下执行的时候,../lib/common.js会被转变成/home/ivan/lib/common.js,这个路径是不存在的,但是从运行结果来看,require('../lib/common')是OK的,只是readFile时报错了。

那么关于./正确的结论出来了: 在require()中使用的是跟__dirname的效果相同,不会因为启动脚本的目录不一样而改变,在其他情况下跟process.cwd()效果相同,是相对于启动脚本所在目录的路径。

总结

所以事实证明前端得到的结论中1,2,3是OK的,最后一个不准确。

只有在require()时才使用相对路径的写法,其他地方一律使用绝对路径,如下:

var path= require(“path”);
//当前目录下
path.dirname(__filename)+"/test.js";
//相邻目录下
path.resolve(__dirname, "../lib/common.js");

扩展

Node.js使用path模块处理文件路径

  • path.normalize(p) 文件路径处理 path模块中的normalize()方法用来规范化路径字符串。可用于处理路径中的"//"、".."、"."等字符

      var path = require("path");
      path.normalize("/foo/bar//baz/asdf/..");
      // 处理后
      "/foo/bar/baz/asdf"
    
  • path.join([path1][, path2][, ...]) 连接路径

    path.join()方法可以连接任意多个路径字符串。要连接的多个路径可做为参数传入。path.join()方法在接边路径的同时也会对路径进行规范化。例如:

      var path = require("path");
      //合法的字符串连接
      path.join("/foo", "bar", "baz/asdf", "..")
      // 连接后
      "/foo/bar/baz/asdf"
    
      //不合法的字符串将抛出异常
      path.join("foo", {}, "bar")
      // 抛出的异常
      TypeError: Arguments to path.join must be strings"
    
  • path.resolve([from ...], to) 路径解析 path.resolve()方法可以将多个路径解析为一个规范化的绝对路径。其处理方式类似于对这些路径逐一进行cd操作,与cd操作不同的是,这引起路径可以是文件,并且可不必实际存在(resolve()方法不会利用底层的文件系统判断路径是否存在,而只是进行路径字符串操作)。例如:

      path.resolve("foo/bar", "/tmp/file/", "..", "a/../subfile")
    

    其处理方式类型于

      cd foo/bar
      cd /tmp/file/
      cd ..
      cd a/../subfile
      pwd
    

    如果解析的不是绝对路径,path.resolve()会将当前工作目录加到解析结果的前面。例如:

      path.resolve("/foo/bar", "./baz")
      // 输出结果为
      "/foo/bar/baz"
    
      path.resolve("/foo/bar", "/tmp/file/")
      // 输出结果为
      "/tmp/file"
    
      path.resolve("wwwroot", "static_files/png/", "../gif/image.gif")
      // 当前的工作路径是 /home/ivan/test,则输出结果为
      "/home/ivan/test/wwwroot/static_files/gif/image.gif"
    
  • path.relative(from, to) 查找两个绝对路径的相对关系 path.relative()方法可以找出一个绝对路径到另一个绝对路径的相对关系。例如:

      var path = require("path");
      path.relative("/Users/ivan/test/app/demo", "/Users/ivan/test/lib");
      // 结果
      "../../lib"
    
  • path.dirname(p) path.basename(p[, ext]) path.extname(p) 提取路径的组成部分

    path.dirname()方法可以提取出一个文件路径中的目录的部分。

    path.basename()方法可以提取出一个文件路径中的文件的部分。

    path.extname()方法可以提取文件的扩展名。例如:

var path = require("path");
path.dirname("/Users/ivan/demo/path.js")
// 结果
"/Users/ivan/demo"

path.basename("/Users/ivan/demo/path.js")
// 结果
"path.js"

path.extname("/Users/ivan/demo/path.js")
// 结果
".js"

path.basename()方法还可以指定第二个参数:文件的扩展名,指定后可以提取文件名。指定扩展名不合法时将返回文件全名。例如:

var path = require("path");

path.basename("/Users/ivan/demo/path.js", ".js")
// 结果
"path"

path.basename("/Users/ivan/demo/path.js", ".html")
// 结果
"path.js"

在早期的Node.js版本中path模块还有个path.exists()方法,用于判断文件是否存在,但已被fs模块中的fs.exists()代替。

以上。

阅读全文 »

ivan 发布于 07月28, 2016

Linux发邮件的方式及乱码问题

Linux发邮件

  • python脚本
  • shell脚本

之前一直使用python脚本来发送邮件,后来发现直接使用sendmail服务来发送邮件更方便一些,所以本文就说一下使用mail来发送邮件。

1 shell作为编辑器,编辑完成后ctrl+d结束

mail -s test qh_test@126.com

2 利用管道发送邮件

echo "mail content" | mail -s "mail title" qh_test@126.com

3 以file的内容为邮件内容发信

mail -s "mail title" qh_test@126.com < file

4 以file的内容为邮件内容向多个用户发信

mail -s "mail title" -c qh_test@126.com qh_test1@126.com < file

问题:

使用第二种发式发送邮件时,本地中文不会出现乱码,但是部署到服务端就会出现乱码,标题稳定出现乱码,内容随机出现乱码。

解决:

1 解决标题乱码

利用base64编码标题内容,例如,如果UTF-8编码的字符串标题进行base64编码后的内容为5qCH6aKY,则邮件标题为:

Subject:=?UTF-8?B?5qCH6aKY?=

2 解决内容乱码

解决内容乱码比较简单,首先内容使用UTF-8编码,然后再修改邮件为Content-type为:

Content-Type:text/html;charset=UTF-8

基于此,那么在nodejs代码中发送邮件就可以这样写:

var title = new Buffer("【ONEBOX】报警");
var mail_title = title.toString("base64");
var mail_title = "=?UTF-8?B?"+mail_title+"?=";
var mail_cont = "这里是发送邮件内容";
var mailList = "qh_test@126.com qh_test1@126.com";
var exec = require("child_process").exec;
exec(`echo "${mail_cont}" | mail -s "$(echo -e "${mail_title}\nContent-Type: text/html")" ${mailList}`);

以上。

阅读全文 »

ivan 发布于 06月13, 2016

使用css border三角形.

css border实现

一个div或者元素的border并不是我们直观意义上的一条有高度的线,而是一个等高梯形或者三角形(宽高为0时),可以看一下效果:

div定义:

<div class="arrow1"></div>

css:

.arrow1{
    width: 0px;
    height:0px;
    border-width: 30px;
    border-style: solid;
    border-color: #007998 #47A447 #3C6AB9 #D2322D;
}

可以看到每一个方向的border都是一个三角形,那么我们只需把对应方向剩余其他方向的border设置为透明或者隐藏掉就可以得到任何方向的一个三角形了。如果我们想得到一个下拉图标,我们可以将border的左右和下边框改为透明,css改动如下:

.arrow1{
    width: 0px;
    height:0px;
    border-width: 30px;
    border-style: solid;
    border-color: #007998 transparent transparent transparent;
}

注:transparent 属性用来在 background 中将 background-color 选项设置为背景颜色透明

bingo!就是我们想要的效果,但是在ie6下杯具鸟! 长长的大黑框,让人无法直视,这是因为ie6不支持transparent 透明属性,这时候我们可以将对应区域的border的样式设置为dashed,dashed在边框宽度很大时,会隐藏掉。css修改如下:

.arrow1{
        width: 0px;
        height:0px;
        border-width: 30px;
        border-style: solid dashed dashed;
        border-color: #007998 transparent transparent transparent;
}

嗯,IE6上显示正常

但是,到此仍没有结束,我们设置个阴影来查看下最终生成的效果: 但是,到此仍没有结束,我们设置个阴影来查看下最终生成的效果:

即便是我们看到的已经生成一个我们需要的三角形,但是三角形的占用高度仍是原高度,这会导致在和其他元素使用时,造成上移的效果。此时,我们需要把下边框的高度设置为0:

.arrow1{
        width: 0px;
        height:0px;
        border-width: 30px 30px 0;
        border-style: solid dashed dashed;
        border-color: #007998 transparent transparent transparent;
}

似乎仍是不太友好,在使用时我们仍需要修改对应的颜色,能不能根据父元素设定的颜色,显示对应的颜色呢?我们需要把border-color修改下:

    width: 0px;
    height:0px;
    line-height: 0px;
    border-width: 30px 30px 0;
    border-style: solid dashed dashed dashed;
    border-left-color: transparent;
    border-right-color: transparent;
}

效果(使用当前字体颜色): 当然除了利用css border生成外,我们还可以使用特殊字符◇叠加定位来生成,也可以使用css3的旋转来生成(ie6下需要hack处理)。利用border是一种常用而且简单兼容的方式。

阅读全文 »