分类博客下的文章

花花你为什么不叫海鲜 发布于 02月24, 2018

React Fiber 分析

2018年春节后的第一篇文章。主要来分享一下React Fiber架构的实现方式,本文仅代表我个人对React核心算法的理解,如有不对的地方,欢迎指正和讨论。

阅读全文 »

花花你为什么不叫海鲜 发布于 12月16, 2017

【译】展示组件vs容器组件

alt

当我写React应用程序的时候,我发现了一个很有用的简单模式。如果你已经使用了一段时间React框架,那可能你也已经发现了这种模式。这篇文章很好的解释了这个问题,但是这里我想增加一些知识点。

阅读全文 »

花花你为什么不叫海鲜 发布于 01月09, 2017

【译】性能日志 » 使用HTTP2和渐进式JPEG图片更快的加载图像

原文链接:Performance Calendar » Even Faster Images using HTTP2 and Progressive JPEGs

众城平台翻译链接:性能日志 » 使用HTTP2和渐进式JPEG图片更快的加载图像 (感谢月影大大推荐文章)

tl;dr: 渐进式图像在HTTP 2上渲染得更快,从而增加感知性能。控制渐进JPEG扫描图层来实现只用25%的图像数据展示具有核心意义的图像内容。对于渐进式JPEG使用HTTP Server Push,来最大化关键图像的渲染性能。

阅读全文 »

花花你为什么不叫海鲜 发布于 12月21, 2016

【译】多进程架构

题目:多进程架构

原文链接:Multi-process Architecture

不像大多数web浏览器,谷歌浏览器使用了许多操作系统进程来保持各个网站相互分离和计算机的其余部分的分离。通过这篇博客,我将告诉你,在当今的网站,为什么使用混合架构体统会是一个巨大的成功。同时,我也会谈到,浏览器的哪一部分数据哪个进程,在什么情况下浏览器会创建新的进程。

1.为什么在浏览器中使用多进程

当大多数现在的浏览器被设计出来的时候,有那么一段时间,网页是很简单的,有极少的代码或者说干脆就没有代码。那么浏览器渲染在同一个进程中去渲染你所有浏览网页,以保持资源的低利用率就很合理了。

但是,现如今,我们可以看到主要转向活跃的网页内容,从拥有大量JS和Flash的网页到完整的“网络应用app”,比如:Gmail。大量的app都是内嵌浏览器,就像普通的应用跑在一个操作系统上。正如一个操作系统,浏览器必须保证这些应用程序是彼此分离的。

阅读全文 »

花花你为什么不叫海鲜 发布于 12月12, 2016

Linux使用配置

alias使用笔记

alias设置指令的别名。

语法:

#  alias name='command line'

参数:

-p: 打印出现有的别名(唯一的参数)

若不加任何参数,则列出目前所有的别名设置

永久设置:

vim ~/.bash_profile 进行编辑

格式:

alias name='command line'

注意中间没有空格!

eg.

alias newhouse='cd /data0/www/htdocs/'

修改后执行:

source .bash_profile 重新执行文件

阅读全文 »

花花你为什么不叫海鲜 发布于 12月08, 2016

【 译】Page Visibility API

当一个网站是可见或点击选中的状态时 Page Visibility API 可以让你获取到这种状态。 在用户使用切换标签的方式来浏览网页时,非常合理的情况是任何在后台页面都不会展示给用户。 当用户最小化网页或者浏览到其他标签的网页时,API将发送一个关于当前页面的可见信息的事件{{event("visibilitychange")}} 。你可以检测该事件然后执行一些活动或是展示不同的效果。比如,如果你的网站app正在播放一个视频,也许当用户浏览其他浏览器时它可以暂停,那么当用户切换回来的时候,就可以继续播放了。用户可以继续观看,不会因为浏览其他的浏览器导致丢失当前视频的进度。

关于iframe的可见性的状态等同于它的父层iframe。用CSS属性隐藏iframe并不会触发visibility事件也不会改变内容文本的状态。 好处

这个API在节约资源上是非常有用的,当网页不可见时,这个API通过提供给开发者可以操作不必须的任务的接口。

使用情景

一些例子:

  • 网站有图片轮播效果,只有在用户观看轮播的时候,才会自动展示下一张幻灯片。
  • 显示信息仪表盘的应用程序不希望在页面不可见时轮询服务器进行更新。
  • 页面想要检测是否正在渲染,以便可以准确的计算网页浏览量
  • 当设备进入待机模式时,网站想要关闭设备声音(用户按下电源键关闭屏幕) 开发者在过去使用不完善的代理来处理这一点。比如, 当你的页面不处于活动状态时,在window上声明一个 onblur/onfocus 处理器来帮助你了解页面不是活动状态,但是它并没有告诉你页面是对用户隐藏的。The Page Visibility API 解决了这个问题。(在和window上声明 onblur/onfocus 处理器这种方式相比,关键不同在于当另一个页面处于激活状态并且浏览器窗口丢失了焦点时,页面不会隐藏。 页面只有在用户切换到其他标签或最小化窗口时,才会被隐藏。)

例子

看一个 在线的例子 (带声音的视频) 在此例中,当你切换到另一个标签时视频会暂停,当你返回到当前标签时视频会再次播放,代码如下:

// 设置隐藏属性和改变可见属性的事件的名称
var hidden, visibilityChange; 
if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support 
  hidden = "hidden";
  visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
  hidden = "msHidden";
  visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
  hidden = "webkitHidden";
  visibilityChange = "webkitvisibilitychange";
}

var videoElement = document.getElementById("videoElement");

// 如果页面是隐藏状态,则暂停视频
// 如果页面是展示状态,则播放视频
function handleVisibilityChange() {
  if (document[hidden]) {
    videoElement.pause();
  } else {
    videoElement.play();
  }
}

// 如果浏览器不支持addEventListener 或 Page Visibility API 给出警告
if (typeof document.addEventListener === "undefined" || typeof document[hidden] === "undefined") {
  console.log("This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.");
} else {
  // 处理页面可见属性的改变
  document.addEventListener(visibilityChange, handleVisibilityChange, false);

  // 当视频暂停,设置title
  // This shows the paused
  videoElement.addEventListener("pause", function(){
    document.title = "Paused";
  }, false);

  // 当视频播放,设置title
  videoElement.addEventListener("play", function(){
    document.title = "Playing"; 
  }, false);

}

属性

document.hidden Read only

如果页面处于被用户认为是隐藏状态时返回true,否则返回false。

document.visibilityState Read only

是一个用来展示文档可见性状态的字符串。可能的值:

  • visible : 页面内容至少是部分可见。 在实际中,这意味着页面时非最小化窗口的前景选项卡。
  • hidden : 页面内容对用户不可见。 在实际中,这意味着文档可以是一个后台标签,或是最小化窗口的一部分,或是在操作系统锁屏激活的状态下。
  • prerender : 页面内容正在被预渲染且没有对用户是不可见的(被当做隐藏,以 document.hidden 为目的 ). 文档可能初始状态为prerender,但绝不会从其它值转为该值。 注释:浏览器支持是可选的。
  • unloaded : 页面正在从内存中卸载。 注释:浏览器支持是可选的。
//startSimulation 和 pauseSimulation 在其他地方定义
function handleVisibilityChange() {
  if (document.hidden) {
    pauseSimulation();
  } else  {
    startSimulation();
  }
}

document.addEventListener("visibilitychange", handleVisibilityChange, false);

附:

MDN翻译链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Page_Visibility_API

MDN英文文档链接:https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API

阅读全文 »

花花你为什么不叫海鲜 发布于 11月24, 2016

Linux中软链接与硬链接

Linux链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link)。默认情况下,ln命令产生硬链接。

#Hard Link: 在Linux中,多个文件名指向同一索引节点是存在的。一般这种连接就是硬连接。硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。 硬连接的2个限制: l 不允许给目录创建硬链接 l 只有在同一文件系统中的文件之间才能创建链接。 即不同硬盘分区上的两个文件之间不能够建立硬链接。这是因为硬链接是通过结> 点指向原始文件的,而文件的i-结点在不同的文件系统中可能会不同。

#Symbolic Link: 软链接文件有类似于Windows的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。

使用:

Hard Link: ln [源文件] [目标文件]

Symbolic Link: ln -s [源文件] [目标文件]

查看: ls -l

举个栗子: 建立单个文件的软链接: 建立文件夹的软链接,注意源文件的目录为文件夹中的内容,所以此处用: [源文件夹]/。目标文件的目录为正常路径。 我创建了link1作为源文件,linkHard作为硬链接文件,linkSymbolic作为软链接文件 编辑link1后,两个文件都同步更新了,当删除link1时,软链接会自动失效

综上:

  1. 删除源文件->软链接失效文件被删除,硬链接依然存在
  2. 删除硬链接 -> 删除源文件且删除硬链接文件
  3. 删除软链接,使用rm

摘录自:http://www.cnblogs.com/sonic4x/archive/2011/08/05/2128543.html

阅读全文 »

花花你为什么不叫海鲜 发布于 11月21, 2016

国内npm镜像推荐

淘宝npm镜像

搜索地址:http://npm.taobao.org/ registry地址:http://registry.npm.taobao.org/

cnpmjs镜像

搜索地址:http://cnpmjs.org/ registry地址:http://r.cnpmjs.org/

使用方式:

1.一次性

npm --registry https://registry.npm.taobao.org install express

2.长期设置

npm config set registry https://registry.npm.taobao.org

// 配置后可通过下面方式来验证是否成功
npm config get registry
// 或
npm info express

3.通过cnpm使用

npm install -g cnpm --registry=https://registry.npm.taobao.org

// 使用
cnpm install express

4.通过qnpm使用

npm install -g @q/qnpm --registry=http://registry.npm.qiwoo.org

// 使用
cnpm install express

附:相关文档http://qnpm.qiwoo.org/

阅读全文 »

花花你为什么不叫海鲜 发布于 08月21, 2016

Thinkjs实现异步加载

今天来总结一下关于使用Thinkjs来实现页面异步加载的原理和做法。 我从前端讲起,首先流程中包含一下几部分:

View Controller Model
筛选框(filter)用于筛选内容,位置在母模版 母模版对应的Action 调用数据库
异步加载内容(content),位置在子模板 ajax判断后的处理 调用数据库

下面根据代码具体解释一下: 母模版的view层demo:

/*这里是一个filter*/
<div class="filter">
    <dl class="row">
        <dt>大小</dt>
        <dd>
            <a href="#">全部</a>
            <a href="#"></a>
            <a href="#"></a>
            <a href="#"></a>
        </dd>
    </dl>
</div>
/*这里是一个content*/
<div class="content">
    <%include ajax/tplselectajax.html%>     /*这里include进来一个子模板,将来异步刷新时就只刷新子模板数据*/
</div>

子模板的view层demo: 可以看到从后端传来一个tpllist的数据字段,相当于是一个数组,我们把它循环输出出来,同时一会异步刷新的时候我们也只是刷新这个子模板的数据,而不会改变上面的母模板的数据。

<div class="cont-wrap">
    <ul>
        <%tpllist.forEach(function(item){ %>
            <li>
                <p><%=item.desc%></p>
                <p><%=item.name%></p>
            </li>
        <%})%>
    </ul>
</div>

当然这里需要js来帮忙使用ajax来进行一步调用:

$("a").on("click",function(e){
    e.preventDefault();
    $.ajax({
        url:"",/*填写请求路径*/
        dataTpe:"jsonp",
        data:"",/*传送的数据*/
        success:function(){

        },
        fail:function(){

        }

    });
});

Controller层的处理:

tplselectAction: function(){
    var self = this;
    /*获取get请求的参数,在这个demo中其实就是筛选条件*/
    var data = this.get();  
    var pg = data.pg;
    /*判断时候是ajax请求,如果是则结果返回true*/
    var isAjax = self.isAjax();   
    /*子模板渲染路径,注意此处根目录是Home*/
    var tpl = "Home/ajax/build_tplselectajax.html";         
    if(!isAjax){
        /*如果不是ajax请求,也就是浏览器首次渲染数据时,默认选择展示全部数据*/
        data = { cateId: "全部"}
    }
    /*调用数据库获取数据,调用ajaxdata方法*/
    D("Template").ajaxdata(data,pg).then(function(data){
        var list = shiftObject(data,"data");  /*将数据库data字段整体存入list*/
        self.assign("tpllist",list);  
        self.assign("isAjax",isAjax);
        if(isAjax){
            /*如果是ajax模板,则利用fetch这个方法对模板进行重新渲染,并返回相应json格式的状态值*/
            if(list.length !== 0){
                self.fetch(tpl).then(function(content){
                   self.jsonp({status:"success",cont:content});
                });
            }else{
                self.jsonp({status:"fail",cont:"暂时木有相应的模板,搜搜别的试试吧!"});
            }
        }else{
            /*首次加载页面则直接展示*/
            self.display();
        }
    });
}

Model层的处理demo:

ajaxdata: function(data){
    var self = this;
    if(data.cateId = "全部"){
        return self.order({"id":"desc"}).select().then(function(data){
            return data;
        });
    }else{
        return self.where({"title":data.cateId}).order({"id":"desc"}).select().then(function(data){
            return data;    
        });
    }
}

Model层的处理: 这里是项目源代码的查询:

ajaxdata: function(data,pg){
        var self = this;
        var where = where || {};
        /*此处包含其他参数的复合查询,展示出来,在demo中暂时不考虑*/
        /*var join = {
            templatecate: {
                join: "left",
                as: "c",
                on: ["cateId","id"]
            }
        };
        var pg = pg || (isNumber(where) ? where : null);
        var lastCond = {};
        if(!isEmpty(where)){
            for(var i in where){
                var find = false;
                for(var s in fields){
                    if(fields[s].indexOf(i) > -1){
                        lastCond[s + "." + i] = where[i];
                        find = true;
                        break;
                    }
                }
                if(find)continue;
                lastCond["t." + i] = where[i];
            }
        }*/
        if(data.cateId == "全部"){
            var per = C("db_nums_per_page");
            /*在筛选框为‘全部’时,将数据中全部数据按条件查询出来,并且进行分页查询(分页查询此处我们暂时不讨论)*/
            return self.where({"endType":data.endType,"isForbidden":0}).order({"id": "desc"}).page(pg).select().then(function(data){
                    return data;
            }).then(function(datalist){
                /*将返回结果进行处理,并返回相应的参数*/
                return self.field("count(id) as count").where({"endType":data.endType}).find().then(function(res){
                    return {
                        data: datalist,
                        count: res.count,
                        page: pg || 1,
                        num: per,
                        total: Math.ceil(res.count / per)
                    };
                });
            }); 
        }else{
            /ajax请求是查询结果,按照cateId的值进行查询/
            var per = C("db_nums_per_page");
            return self.alias("t").field("t.*").join(join).where({"c.title":data.cateId,"t.endType":data.endType,"isForbidden":0}).order({"t.id": "desc"}).page(pg).select().then(function(data){
                    return data;
            }).then(function(datalist){
                return self.alias("t").field("count(t.id) as count").join(join).where({"c.title":data.cateId,"t.endType":data.endType}).find().then(function(res){
                    return {
                        data: datalist,
                        count: res.count,
                        page: pg || 1,
                        num: per,
                        total: Math.ceil(res.count / per)
                    };
                });
            }); 
        }

    }

关于Thinkjs里面的具体语法我就不一一解释了,这里主要来讲一下大概的思路,首先前端需要在模板中将需要异步加载的模板进行include,方便后续的异步刷洗页面,同时你需要用js来进行一个ajax的通信,接着就是Controller层对模板的渲染,在拿到前端传来的请求数据后,调用model层的方法从数据库中取出数据,此时需要使用feteh这个方法重新渲染页面。(其实也可以使用display(渲染模板路径)这个方法,只是渲染的方式不用而已,结果是一样的)感兴趣的同学可以搜一下API。 sql语句写的不好,还望大家见谅~~哈哈哈,先就这么多~

阅读全文 »