分类 学习 下的文章

翻译:《5分钟入手React.js》

原文地址:[Learn React.js in 5 minutes](https://medium.freecodecamp.org/learn-react-js-in-5-minutes-526472d292f4)。
因为想撸一撸 React ,寻思着直接伸手应该比较愉快,就顺便翻译一篇国外的入门好啦。

5分钟入手 React.js ———— 超热门JavaScript库的快速指南

在这篇教程中,你会通过构建一个超简单小应用的方式对于 React.js 有一个基本的理解,我会把其中我个人认为不是特别核心的知识点暂且抛去不谈哈。

当你阅读这篇文章的时候,你也可以点击链接看一下发布在Scrimba的免费React课程,在那你可以通过48个互动视频来相对完整地学习这个库。

废话少说,整!

基础配置

刚开始使用React的时候,我们应该尽量使用最简单的配置来上手:比如像这样只是用<script />标签引入了ReactReactDOM的简单HTML文件:

    <html>
    <head>
    <script src="https://cdn.bootcss.com/react/15.5.4/react.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.5.4/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/babel-standalone/6.15.0/babel.min.js"></script>
    </head>
    <body>
        <div id="root"></div>
        <script type="text/babel">
        
        /* 
        在此处写入React相关代码
        */
        
        </script>
    </body>
    </html>

译者:原作者使用的免费CDN加速是https://unpkg.com/,为了符合国情我全部替换成了https://www.bootcdn.cn/的同类文件,代码可以放心复制~

同时我们引入了Babel,因为我们在React相关代码中要使用一些用JSX语法来描述HTML内容的部分,此时我们需要将JSX来转化成普通的JavaScript,这样浏览器才能正常解析这段程序。

这里有两个地方需要注意一下:

  • 一个是我们标记了#root这个id的<div />标签。它是我们整个应用的根节点,我们程序的编译结果将出现在这个标签中;
  • 还有<script type="text/babel"></script>这个标签。这个标签是我们将要编写React.js代码的地方。

如果你想实验这段代码,可以查看Scrimba playground

译者:除了Scrimba网站,可以根据2016年10月的这篇文章了解下其他的在线编辑器以及特点。

组件

在React的世界里,一切都是组件,组件通常继承于一个JavaScript类。你可以通过继承React.Component类来创建组件,如下代码所示,我们来创建一个名为“Hello”的组件。

class Hello extends React.Component {
    render() {
        return <h1>Hello world!</h1>;
    }
}

然后,我们可以定义组件的方法,不过我们的示例中只定义了一个render()方法。

render()方法中我们返回了一串希望React渲染到页面中的HTML代码,我们想让React把<h1>Hello world!</h1>代码渲染到根节点中。

然后我们调用ReactDOM.render()来给我们的小应用进行DOM渲染:

ReactDOM.render(
    <Hello />, 
    document.getElementById("root")
);

这段代码是我们让根节点与Hello组件产生关联的关键,结果请点击链接查看。

译者:点击这个CodePen地址查看这个例子。

刚刚出现的与HTML语法很相似的<Hello />就是我们刚刚提到的JSX。它并不是HTML,但是最终会转换成HTML来渲染。

下一步我们来学习数据绑定。

数据绑定

在React中有两种数据:Props 以及 State。最开始我们或许比较难以理解这两者的区别,不用太担心,在开始使用它们之后你就会更容易理解它们的具体意义。

State也可以称之为状态,它是组件所私有的、可以在组件内部修改的;而Props是外部传入的,不受组件本身控制。Props从可以控制数据的高层组件向低层组件传递。

一个组件可以直接地修改它的State,而不能直接修改它被传入的Props。

我们先看一下Props。

Props

我们的“Hello”组件是一个非常基础的组件,它无论如何渲染都智慧渲染出相同的数据。React中一个很重要的部分就是它的复用性,也就是说你可以只编写一次组件、却在各种不同的时候调用它——比如显示不同的信息。

为了给刚刚的组件增加复用性,我们来为它添加 Props 。下面的message="my friend"就是你使用 Props 将数据传递给组件的方式:

ReactDOM.render(
    <Hello message="老铁" />, 
    document.getElementById("root")
);

这个被命名为“message”的prop我们为他赋值为“老铁”,我们在组件内使用this.props.message的语法来调用它:

class Hello extends React.Component {
    render() {
        return <h1>Hello {this.props.message}!</h1>;
    }
}

结果请点击链接查看。

译者:点击这个CodePen地址查看这个例子。

我们在代码中用一个大括号来包裹了{this.props.message}代码,这个大括号是用来进行转义操作的,它来标识这段大括号中的JSX为JavaScript表达式。

那么我们现在就有一个可以复用的组件了,它可以让我们给任何宾语喊“Hello”!真牛逼!

那我们该怎么让组件能够拥有自己修改数据的能力呢?那就要使用State了!

State

State是React中的另一种数据形式,它可以在组件内部由组件自己来直接修改,不需要外部传入。

所以你如果需要组件内部能够通过用户交互等形式让数据产生变化,你就需要把它存在组件的State里。

初始化状态

为了将状态进行初始化,我们需要在组件的constructor()方法中简单设置一下this.state参数。在我们的例子中,我们依然是只设置一个“message”属性。

class Hello extends React.Component {
    
    constructor(){
        super();
        this.state = {
            message: "State中的Message"
        };
    }
    
    render() {
        return <h1>Hello {this.state.message}!</h1>;
    }
}

在我们设置State的具体属性的时候,我们需要先在constructor(){}中调用super()方法,只有这样才能在继承父类的子类中正确获取到类型的this

更新 State 中的数据

想要修改State里面的值,最简单的做法是调用this.setState(),并把要更新的值放置其中。下面我们在组件中定义一个updateMessage()方法来更新数据。

class Hello extends React.Component {
    
    constructor(){
        super();
        this.state = {
            message: "State中的Message"
        };
        this.updateMessage = this.updateMessage.bind(this);
   }
    updateMessage() {
        this.setState({
            message: "更新过的State中的Message"
        });
    }    
    render() {
        return <h1>Hello {this.state.message}!</h1>;
    }
}

这个时候要注意一下,我们在updateMessage()中胡作非为的时候,必须要确认this已经绑定到了组件方法中,不然我们无法访问到正确的this

下一步我们在render()中创建一个点击后就能触发updateMessage()的按钮:

render() {
  return (
     <div>
       <h1>Hello {this.state.message}!</h1>
       <button onClick={this.updateMessage}>点我更新State!</button>
     </div>   
  )
}

这样我们就在按钮上挂了一个事件监听器,来监听onClick事件。当事件触发时,组件就会调用updateMessage()方法。如下为完整组件代码:

class Hello extends React.Component {
    
    constructor(){
        super();
        this.state = {
            message: "State中的Message"
        };
        this.updateMessage = this.updateMessage.bind(this);
   }
    updateMessage() {
        this.setState({
            message: "更新过的State中的Message"
        });
    }    
    render() {
        return (
         <div>
           <h1>Hello {this.state.message}!</h1>
           <button onClick={this.updateMessage}>点我更新State!</button>
         </div>   
        )
    }
}

点击按钮后,updateMessage()方法就会调用this.setState(),并通过传值来修改其中的this.state.message。点击之后的效果点击链接查看。

译者:点击这个CodePen地址查看这个例子。

恭喜你!你已经对于React中比较重要的概念有了一个基本的认识啦!

译者:后面作者的感叹、广告、自我介绍都不翻译了,在DocsChina也有类似的React快速入门的中文文档,需要的小伙伴可以自取。

倒计时功能在JavaScript实现中的毫秒级时间差调试及思考

在一个脑洞里有个倒计时功能,正在想怎么实现,突然想起一个很关键的问题:

之前一些应用场景的倒计时中我使用了 setTimeout 进行递归处理,setTimeout 和 setInterval 一定是有效率区别的,区别有多大?

然后做一个尝试:

先定义提示方法

function timer(id){ console.timeEnd(id); console.time(id); setTimeout('timer("'+id+'")',1000); }

首先定义一下setTimeout的循环计时器。

function timer2(id){ console.timeEnd(id); console.time(id); }

然后定义一下setInterval中循环的方法。

启动!

timer('setTimeout'); setInterval('timer2("interval")',1000)

五年过去了……

如图所示:
运行截图

根据图上打印的日志情况可以粗略猜到了,setInterval计时1000的时候如果因为机器性能等影响到的时候(我在测试的过程中左右切换应用和进行了其他操作),会自己进行一些调节性的操作,很鸡贼;setTimeout就很憨厚了,踏踏实实计时,一次做慢了无所谓下次该咋样还咋样,这种着眼现实的精神很值得我们学习(鼓掌)。

深入一步

刚刚的毕竟是猜测,我们可以继续去研究下它的更向下一些的原理。首先刚刚的demo就不是特别完善,于是我又参考 StackOverflow 的一个回答优化了下撸了另一个可视化程度比较好的 点击查看demo,有兴趣的小伙伴可以点击去看一下。

测试的结果和之前是一样的,都是interval出现了“效率更高一点”的样子。

然后在JavaScriptInfo上查了下资料,资料中显示了如下两张图:

jsinfo-pic1

此图为setInterval(function_name,100)的执行图,

jsinfo-pic2

此图为setTimeout(function() {...}, 100);的递归执行图。

这里给的解释说明了几个事情:

  • 两个计时器时间上的准确是没有问题的,也不存在合理微小延迟的说法
  • setInterval的计时器触发后即刻开始下次计时,setTimeout的计时器触发后到递归到下次计时之间涉及了业务代码的执行周期,所以相对而言会体现出“用时更长”的感觉

看到这里方才明白,并不是setTimeout有延迟,而是在demo中我忽略掉了两个计时器使用后业务代码的执行时间。

应用

如上所述,两种计时器的差别就在于业务代码执行周期产生的延迟了,也就是说并不是setTimeout不准确,只是他们的用途不完全相同。

在应用过程中,使用其中哪个计时器,要取决于我们的业务代码执行周期何如。比如业务代码运算量极其小、或者只是作为本地监听器,用setInterval会更好用一些;如果业务代码执行周期较长,如定期提交用户操作信息,每次执行过程可能会突然出现执行时间超出计时器本身计时周期的情况,用setTimeout显然更稳妥也更合适。

所以说,还是放弃Baidu,健康搜索吧。

文章翻译:《对比PPTP-L2TP-OpenVPN-SSTP-IKEv2》(上)

译者按:这篇文章是Douglas Crawford发表在BestVPN上的文章《PPTP vs L2TP vs OpenVPN vs SSTP vs IKEv2》的大致的译文。在给我的Debian服务器搭建VPN服务的时候,看国内的很多资料抄袭泛滥、模棱两可,这个时候在网上翻到了这篇文章(然而并不能流畅阅读),所以就此翻译一下造福人类(其实就是为了方便自己理解内容和学英语啦)。
内容上可能会有谬误,希望发现的盆友能够及时指出。以下内容均为我个人对于作者原文带有个人色彩的翻译:

因为爱德华·斯诺登对于NSA近几年致力于破解与颠覆VPN加密技术相关信息的惊人披露,也因为相关技术如雨后春笋般被开发、被美国国家标准技术研究所认证、或者疑似开发出来这些日益明显的事实,我们决定在现在对这篇热门文章进行重构和更新。
首先,我们将在查看密码学所涉及的关键概念以及NSA对于加密标准的攻势怎样影响了VPN用户之前,纲要性地列出不同VPN协议之间的主要区别以及不同协议对用户的影响。
尽管我努力尝试将以下技术讨论用通俗易懂的方式写下来,却还是相当地技术化,所以你可以直接跳到文章底部查看一个精简化的内容摘要。
更新:现在作者已经为此文章撰写了两个姊妹篇,分别题为VPN encryption terms explained (AES vs RSA vs SHA etc.)A Complete Guide to IP Leaks,如果你对这个技术课堂非常感兴趣,请务必阅读这两篇文章!

PPTP

PPTP(Point-to-Point Tunneling Protocol,点对点隧道协议)是由微软为了在拨号网络方面创建VPN而成立的一个团队开发而生,因此长期以来一直都是其企业内部的VPN标准协议。它也是一个通过搭配各种认证方法(通常是MS-CHAP v2)以提供安全性的VPN协议。因为PPTP协议作为一个在几乎所有有VPN能力的平台和设备上都可以无需安装额外软件而使用的标准,它至今仍然是企业和VPN供应商们的热门选择。同时,它有着低计算开销即可实现的优势(通俗地说就是运行速度很快)。

然而,虽然现在通常只能看见PPTP使用128位的加密密钥,但它却是早在1999年,第一个被捆绑到Windows 95 OSR2操作系统的协议。在一些被曝光的安全漏洞之中,最严重的一个就是未封装MS-CHAPv2认证的可能性。利用这个漏洞,PPTP曾在2天之内被攻破,尽管微软已经修补了漏洞(通过用作PEAP保护基于MSCHAPv2/PPTP的隧道),却还是提出了“VPN用户应使用L2TP、IPsec或者SSTP作为代替”这样的问题建议

如今大家明知PPTP协议无论如何都不够安全可靠,NSA几乎是肯定能破译PPTP加密通讯标准的事也不再是鲜为人知。而或许更令人所担忧的是,甚至在一些安全专家还认为PPTP是一个安全协议的时候,NSA已经几乎是肯定能破译那些已经储存好的、已经加密过的的旧数据了(或者这个技术此时还在研究进程中)。

优势

  • 几乎所有平台都内置好了PPTP协议的VPN客户端
  • 非常易于搭建
  • 高速

弊端

  • 不够安全(弱势的MSCHAPv2依旧被广泛使用)
  • 绝对妥协于NSA

L2TP 以及 L2TP/IPsec

L2TP(Layer 2 Tunnel Protocol,第二层隧道协议)是一种协议本身不对通过的流量进行加密或实施保密措施的VPN协议。也正是因为这个原因,L2TP通常会结合IPsec加密套件(如下所述,类似于一种密码)来实现,以此提供安全性和隐私性。
L2TP/IPsec已经被内置于所有现代化操作系统以及具有VPN功能的设置,同时它也如PPTP一般,操作简单、可快速搭建(实际上它与PPTP使用的设备通常也是相同的)。然而问题也有所产生,因为L2TP协议使用了更容易被NAT防火墙阻挡的UDP端口500,所以在防火墙后面使用L2TP协议的时候,可能需要使用更先进的配置(端口转发)。(这就不同于可以使用TCP端口443来区分正常的HTTPS流量的SSL了)
IPsec加密目前还没有什么已知大型漏洞,正确实现的情况下应该仍然是安全的,但是爱德华·斯诺登却强烈暗示其标准已向NSA所妥协,同时根据约翰·吉尔摩(安全专家和电子前沿基金会的创始成员之一)在这篇文章中解释道的内容来看,标准在设计阶段的时候可能就已经被故意弱化了。
L2tp/IPsec会进行两次数据封装,这似乎会让速度慢下来。不过首先,它的加密解密行为发生在内核之中,其次LwTP/IPsec也允许多线程(OpenVPN并没有这个功能),这两点完全可以将两次数据封装造成的减速抵消掉,结果是L2TP在理论上会比OpenVPN更快。

优势

  • 除了弊端中提到的几点,通常公认其安全
  • 易于搭建
  • 适用于全部现代化平台
  • 较OpenVPN更快

弊端

  • 或许已经向NSA妥协了(但未被证实)
  • 或许被NSA故意削弱过(但未被证实)
  • 会跟限制性防火墙产生冲突

OpenVPN

OpenVPN可以说是一种崭新的开源技术,它使用了OpenSSL库和SSLv3/TLSv1协议,通过与其他的技术融为一体,来提供一个强大而可靠的VPN解决方案。它的主要优势之一是其高度可配置性,尽管它最好被运行在UDP端口之上,却也还是能运行在其他任意端口上,甚至包括TCP端口433。也正因此,通过OpenVPN的流量与使用SSL加密的标准HTTPS流量无法被区分,同时它也很难被阻塞。
OpenVPN的另外一个优点是,尽管绝大多数的VPN供应商只使用了AES或者Blowfish加密算法,但是OpenSSL库本身支持着非常多的加密算法来提供加密(比如AES,Blowfish,3DES,CAST-128,Camellia等等)。128位的Blowfish是内置于OpenVPN是默认加密算法,虽然大众普遍认为这个算法足够安全,但是它也还是有一些已经知道的缺陷所在,甚至它的创作者在2007年也被引述说:“然而因为这一点,让我非常惊讶的是现在它还在被使用之中。如果人们问到不使用它该使用什么,我还是推荐用Twofish代替掉它。”
AES是比较新的技术,目前还没有已知的缺陷,在涉及到加密话题的时候它通常被认定是“黄金标准”,这就归功于美国政府使用其保护一些“安全”数据的行为。实际上它有128位的体积,相比于只有64位的Blowfish而言,它也能在大文件(超过1GB)的处理上稍胜一筹。然而这两种密码学已经被NIST认证了,这个问题还没被广泛认可,对此我们却仍有一些问题。可以参阅下面对此相关的讨论。
OpenVPN的执行速度取决于其采用了何种加密级别。从技术上来讲的话,IPSec因为在内核执行加解密,所以它的速度会比OpenVPN更快;同时它的多线程性也是OpenVPN所没有的。
OpenVPN已经成为了一种默认的VPN链接方式,虽然它本身是无平台支持的,但是它还是被第三方软件(包括iOS端Android端)广泛地支持着。
相比于PPTP和L2TP/IPSec,它在安装上有些繁琐(不过这是个人非常非常主观的独断)。特定平台使用通用的OpenVPN软件(如Windows端使用标准开源的OpenVPN客户端),不仅必须下载和安装客户端,还要下载和安装额外的配置文件。因此也有许多VPN供应商通过提供定制化的VPN客户端来解决这个问题。
也许在从爱德华·斯诺登处获取到的信息中最重要的一点就是,在它的完全正向加密(是一种临时密钥交换,后面会讨论到)被使用后,就不会被NSA攻破或者削弱。虽然没有人确切知晓NSA的全部能力,但种种迹象和数学运算的结构都表明,如果OpenVPN配合使用了强大的加密算法和临时密钥功能,它将会是唯一值得被考虑到的真正安全的VPN协议。不过不幸的是,并不是所有的VPN供应商都会在实现OpenVPN的过程中使用到PFS完全正向加密。
优势

  • 高度可配置性
  • 非常安全(如果使用了PFS完全正向加密,甚至对NSA都是安全的)
  • 可以绕过防火墙
  • 可以使用多种加密算法
  • 开源(因此可以很容易地去检查项目中的后门情况或者其他有可能被NSA修改过的地方)

弊端

  • 需要第三方软件
  • 安装过程比较繁琐
  • 尽管在努力改善对移动端设备的支持,缺还是不及桌面端的完善程度

SSTP

SSTP(Secure Socket Tunneling Protocol,安全套接字隧道协议)是微软在Windows Vista SP1时推出的,虽然它现在可用于Linux、RouterOS和SEIL,但是它极大程度上还是一种仅限Windows平台的协议(让它支持苹果设备的可能性等同于地狱里面出现了一个雪球)。SSTP使用了SSLv3,因此它能提供与OpenVPN相类似的优势(使用TCP433端口来避免防火墙问题),同时也因为它被直接集成到了Windows里面,所以可能会更加易用和稳定。
但是不像OpenVPN,SSTP是微软拥有的个人标准。这也就意味着代码不会开源给公众监督,同时微软有着和NSA协作的历史,加上近期的有关于Windows系统可能内置了后门的炒作,无一不在影响我们队这个标准的信心。
优势

  • 非常安全(这取决于加密方式,不过通常使用AES就非常强大了)
  • 完全集成到了Windows操作系统中(Windows Vista SP1, Windows 7, Windows 8)
  • 微软官方支持
  • 可以绕过大多数防火墙

弊端

  • 只有在Windows环境才能真正工作
  • 作为微软的专有标准,不能被独立核查出后门以及其他问题

IKEv2

IKEv2(Internet Key Exchange version 2,因特网密钥交换版本2)基于IPSec隧道协议,由微软和思科联合开发,并被兼并到了Windows7及以上版本中。从技术上讲,IKEv2(以及IKE)都不完全是一种VPN协议,而是一种用于IPSec密钥交换的控制协议。尽管如此,然而它却经常作为一个真正的VPN协议,同时它也真的在这方面发挥了非常强大的便利性和实用性。
它是被黑莓设备支持的一个标准,是开源实现可兼容于Linux以及其他操作系统的自主研发的标准。通常我们会对微软开发的任何东西都持保留意见,但是既然是开源版本,便没有什么使用问题了。
被微软称之为VPN连接的IKEv2有着非常实用的自动重连特性,当用户暂时失去互联网连接(比如进出火车隧道)的时候,它会自动重新建立VPN连接。
移动用户很特殊,正因为IKEv2提供的优秀移动性和多主机制(这个机制一般只用在客户端,而不会用在互联网供应商,通过为客户端提供多于一条互联网连接,使当中其中一条连接中断时,系统可以自动切换使用另一条连接),其大量移动端用户得以受益,这也使得它的网络变化弹性很高。这些特性对于手机用户来说确实是喜大普奔,比如那些使用智能手机的用户,从家里外出活动的时候,网络要切换到移动数据、或者从一个WiFi网络切换到另一个WiFi网络热点,在这些情况下就非常受用。
不过IKEv2更加有益于黑莓用户,因为它是黑莓设备所支持的仅有的几个VPN协议之一。
IKEv2在普及程度上不像IPSec那般无处不在(毕竟IPSec支持的平台更多),但是至少在安全性、性能和稳定性这些优秀的方面和L2TP/IPSec旗鼓相当。
也正是这些优势使得即便是旧的IKE标准(IKE/IPSec)仍然能在几乎所有iOS定制的VPN应用上,为了那些使用苹果公司官方VPN API的人无需越狱来使用它(也正因为这些优势,能让VPN供应商能够很容易地把更新配置文件推送到使用VPN的用户或者应用程序上)。
优势

  • 比PPTP,SSTP和L2TP更快,它不涉及在点对点协议(Point-to-Point protocols,PPP)上的开销
  • 非常稳定 - 尤其是切换网络或者在短暂的网络连接丢失之后重新连接的时候
  • 非常安全 - 支持AES 128,AES 192,AES 256以及3DES加密算法
  • 易于安装和配置 - 至少在用户端是如此
  • 协议也支持黑莓的设备

弊端

  • 暂时还不支持很多的平台
  • 在服务器上搭建IKEv2相对来说很费劲,这也是很多问题的隐患所在
  • 我们所对它的信任仅因为它进行了开源

译者:这篇文章拖了一个多月才磨蹭完前半部分。文章的前半部分讲的是各个协议的区别和优劣,作者主观上对于安全性考虑的很多,尤其是对美国国家安全局和微软公司采取了一个怀疑的态度。
文章中,作者多次提到爱德华·斯诺登,他的故事很有意思。他是前美国中央情报局(CIA)职员,美国国家安全局(NSA)外判技术员,后来因于2013年6月在香港将美国国家安全局关于棱镜计划监听项目的秘密文档披露给了英国《卫报》和美国《华盛顿邮报》,遭到美国和英国的通缉。根据维基百科的说明,2014年8月7日,斯诺登获得俄罗斯三年的居留许可证,也就是说他应该还在俄罗斯。这个人对于技术公开这方面付出了很多,在技术上和科学公开化上做了很多,我也只能对其聊表谢意,至少在技术发展的角度,他的行为还是值得肯定的。
文章后面还有个Issues部分,有时间会补上那段的。这篇文章写的还是非常不错的。如果发现有哪些地方出现了翻译错误或者是理解错误还请及时指正,谢谢!

浅谈移动端前端页面开发

前言

外包需要,接触和学习了不少移动端的知识,在运用过程中多次碰壁,有些项目虽然完成了但是自己还是很不满意,于是在刚到家的第一天晚上决定写成文字整理自己脑中的信息,以及进行一个浅层次的科普。

移动端的特性

在写移动端项目的时候,会遇到各种不同的业务应用场景,有些需要定宽,而有些需要适应宽度进行响应式处理,有些需要滑动写死,有些需要限制点击。
移动端也是web前端的一大深坑,iOS设备统一性还好,Android设备琳琅满目而又参差不齐,WP我就不提了……(真心没做过QAQ)。在移动端进行页面开发,根据系统和软件的不同要考虑很多,比如性能上,又比如布局上,亦或是浏览器层的按钮布置/功能特性等等,在不同的场景下都是能够令人一脸懵B的。
以下是我在移动端开发中遇见过并进行了简单研究的部分内容:

  • 定宽
  • 响应式
  • 音频
  • 动画
  • canvas
  • 加载
  • 事件兼容

移动端的小技巧

开发过程中整理的一些小技巧在这里整理一下,方便自己搬砖,需要的可以伸手自提。

定宽

这个是一个非常常见的功能,很多设计稿给出的设计都是在定宽情况下制作的。大多数人选择用一个meta标签来实现这个功能,如:

<meta name="viewport" content="width=640,user-scalable=no">

但是很明显的是,不同的系统中,这个标签的写法之类也有很多讲究。伸手尝试了各路神仙的兼容代码,最终我使用的最多的是这套代码:

<script>
    var mengvalue = -1;
    var phoneWidth = parseInt(window.screen.width);
    var phoneScale = phoneWidth / 640;
    var ua = navigator.userAgent;
    if (/Android (\d+\.\d+)/.test(ua)) {
        var version = parseFloat(RegExp.$1);
        if (version > 2.3) {
            document.write('<meta name="viewport" content="width=640, minimum-scale = ' + phoneScale + ', maximum-scale = ' + phoneScale + ', target-densitydpi=device-dpi">');
        } else {
            document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">');
        }
    } else {
        document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">');
    }
</script>

将其放置于head结束之前即可。因为定宽的情况下,基本上一定会要求禁止缩放,所以上面也包含了相应的内容user-scalable=no,对于缩放系列的参数,关键词scale,想必也不用我赘述。
这套代码来自一直给我项目资源的大哥,在此谢过。

响应式、自适应宽度

写响应式页面的情况,一般会涉及到这个meta标签:

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

这个标签也是Bootstrap基本起步模板的默认标签之一。通过这个设置,使页面宽度等同于设备宽度,能让像素的展现更好。

在写响应式的时候,个人尝试过很多响应式策略,由大到小的兼容、由小到大的兼容、特定尺寸增删标签之类,而写法上并不够优雅。
依照个人之见,兼容移动端的响应式布局,更应该由小到大做兼容,先考虑小屏幕下的布局,再延展成宽屏大屏幕的布局。AmazeUI是一个国产的很优雅的HTML5跨屏前端框架,算是我目前使用过的体验很优良的框架,它在设计的时候正是采用了由小到大的兼容。
这种兼容常见的css写法:

@media screen and (min-width: 480px) { 
    /* something */
}

移动端音频

在移动端,比较常见的音频都是用HTML5的audio元素来实现的,包括我之前做过的一些内容的BGM也是以此元素实现。audio元素算是一个很简单的元素,建议看一下它的具体介绍和与之相关的DOM中的Audio对象的具体介绍,然后在使用的时候,操作其中的一些方法也能更加方便。
audio元素在音频播放方面有一些不足之处,iOS端的audio基本上是没有什么问题的,但是Android端中,audio元素的音频播放是不能叠加的,在你对一个audio元素进行play()的时候,其他播放的audio元素均会进入暂停状态(此时paused属性会为true)。国外对此有一些系统层级的解决策略(很高大上)但是我没(lan)有(de)去尝试所以也不好多说。这个问题目前还处于懵B阶段。

移动端动画

这里的动画指的是一些页面元件的动画效果。移动端的动画实现上,因为设备性能问题,要尽量使用CSS3进行动画而不是使用JS(血的教训)。
很多时候写CSS3动画的时候会写很多不同浏览器的兼容,举个W3School官方的例子

animation: myfirst 5s;
-moz-animation: myfirst 5s; /* Firefox */
-webkit-animation: myfirst 5s;  /* Safari 和 Chrome */
-o-animation: myfirst 5s;   /* Opera */

这里提示一下,如果只做微信内置浏览器的兼容,只需要写正常的属性和-webkit-头的属性就能够实现了。
移动端动画有很多优秀的CSS框架,个人比较看好的是Aniamte.css,这个框架包含了各种常用动画,在此基础上能将动画的实现简单化。
而移动端的整屏滑动我极力推荐Swiper,算的上是移动端滑动、轮播一类效果的优秀解决方案。Swiper中文网中给出了非常简单的将Animate.css结合进Swiper的方法,不妨尝试一下。

移动端canvas

canvas上我目前只用过CreateJS,这也是目前Flash CC转身反杀华丽逆袭的一大杀器。整体上CreateJS算是非常轻量级的一款canvas动画/游戏框架,我之前也多次提到过这货。但是对于SoundJS和PreloadJS我的使用能力仍然有很大问题。
在CreateJS(以下简称cjs)的使用中,性能是一个很重要的点。手机端的图形绘出应该很多人踩进坑里过,不同机型能达到的性能真的差非常非常多。所以在使用cjs制作手机端的动画效果或者游戏的时候,要注意尽量减少遍历和整体的重绘,增加局部重绘,以及能用CSS3实现的动画功能千万避免用js。
同样能够生产出H5动画游戏的Cocos2d-x的js语言版,面对cjs下,性能可以说各有千秋。这篇文章中对于二者的性能比较是测试的相对不错的,引用文章中的一句话就是:

cocos2d-js框架提供的UI编辑器、粒子系统、骨骼动画、瓦片地图等等,都是createjs这个轻量级选手不具备的,createjs只能从零开始,一切都得靠开发者自行实现。因此,cocos2d-js更适合做中大型游戏(大型指的是游戏画面复杂程度,而不是渲染要求高),而createjs更适合做小游戏,例如神经猫级别。

加载

移动端可能会面对很多图片,处理加载项、有一个合理的加载动画就很重要。
目前我的加载流是抄的代码,其中主要用到的是createjs.LoadQueue,因为尚不够完好,所以这里先不贴代码出来,有兴趣的可以邮件/QQ我研究下。
对于移动端的加载我觉得这几点值得注意:

  • 图片和代码体积:用到的非高清图片进行压缩,我使用了工具TinyPNG;代码压缩直接放在gulp的处理流里面
  • loading:加载时间过长的时候,需要有一个loading的界面。目前没写过什么好用的loading层(也没设计出什么不错的),所以还是用的别人的开源的CSS3加载动画。
    在加载的内容比较多的时候,强烈建议加一个进度条,这就跟追女朋友一样,没有一个进度条是很容易放弃继续加载的。
  • 静态资源位置优化:我使用的是upyun,用了很久了,个人觉得非常不错。七牛据说也是不错的,但是没用过,个人觉得它口碑非常不错,不做过多评价。

加载这一点上要多花一点功夫,个人作品的话,一个加载的工具/套件写好了就能一劳永逸了。

事件兼容

感谢前人一路走来为我们留下了那么多优秀的框架!
移动端最该注意的就是点击事件的改变,click变成了一系列的touch
框架上,用过Touch.js,它是移动设备上的手势识别与事件库,由百度云Clouda团队维护。但是后来使用Swiper之后,对事件的考虑并不是很多,只是在之前写canvas游戏的时候使用过。

总结

移动端的水很深,要做移动端就说明要面对更多型号尺寸,也说明要面对的操作系统变得更加丰富。一直用Chrome的开发者工具进行移动端调试,成功之后才放到移动端做真实测试,有的时候对性能的掌握非常不好,要多加小心。
移动端不像PC端,没有控制台显示错误的抛出,所以我也见过开发者为了调试移动端的一些问题,不惜放上大量的alert命令。这里我安利一下jsConsole,可以通过这个在线工具,将移动端的控制台信息全部输出到测试者这里,非常好用。

总而言之,进行移动端开发,多站在用户和低性能机的角度考虑问题、设计流程,会对整个项目更加友好。以上只是我在初入移动端这潭深水的时候总结的一些低端技术点,有错误请直接指出,感谢各位同好Q3Q。

回顾我的服务器使用经历/重新配置Debian服务器

接触过的主机商

为了搭建自己的网站和进行测试,虽然是个前端狗,却不得不使用各种服务器。迄今为止我使用过的主机/VPS有:

  • 90启航(3块钱一个月的主机,速度死慢,用了一个月)
  • LocVPS(最初是和Ricter菊苣、Faceair菊苣和qwe小华一起合租,后来大家纷纷离开这台服务器使用自己的独立服务器,我也随之开始寻觅自己的服务器)
  • hostker主机壳(试用过这家的空间,限制很多,但是价格还可以;这家的VPS没用过,不予置评;点击这个链接注册的话,购买新的虚拟主机可以延长10天)
  • Linode(目前我用过最好用的一个VPS商,需要信用卡支付美金,这里要感谢囧叔一直以来的帮助。点击这个链接使用优惠码注册/购买)

服务器使用历程

我的Linode服务器已经使用了很久了(再次感谢囧叔)。具体的学习历程:

  • 2013年 - 使用服务器是搭建静态的纯HTML的页面做测试,那时候我还在高中,使用的是普通的90启航的静态空间,域名还是最初的nocti.me
  • 2014年上 - 困囿于服务器功能的限制,想自己手动去折腾一些php的东西,在RP主机上面开始跟着教程搭建wordpress。
  • 2014年下 - 合租VPS后,服务器的Apache配置文件当然要自己写,这阵子就开始接触到了Apache和Nginx,当时服务器使用的是Apache。
  • 2015年上 - 开始使用自己的VPS,有了自己的东京Linode服务器,尽管是最低配置($10),但是使用起来还是相当不错。这半年开始自己搭建LNMP环境,进行简单的PHP使用;搭建了自己的ShadowSocks(然而服务器重置之后是yu帮我进行的搭建…);初步开始搭建Node.js环境。
  • 2015年下 - 学习状态比较好,对于服务器的使用次数愈加频繁也更喜欢折腾,完善了Node的运行环境,又在星河的推荐下使用了Gogs,但是这时候Go语言的环境和Node的环境配置没弄好,产生了一定的冲突。
  • 2016年初 - 使用ROOT账户直接操作系统太凶残,而且也很不好,为了解决各种麻烦,让服务器更加干净有条理,将服务器进行了重新配置。
    在面板上把系统重置为Debian8.1,尝试着用更现代更易于管理的方法去使用它。

重新配置Debian服务器

新的服务器上,依旧使用Nginx作为服务器软件,php版本5.6。
数据库方面,使用了Perocna这个MySQL的分支作为数据库软件,版本信息5.6.27-76.0 Percona Server
一直以来都只用root账户对服务器进行操作修改,感觉这样并不方便,所以这次添加了用户Nocti。
在编程环境方面,为了让环境之间能够不冲突,除了php和python,都将使用Docker,将环境放进container,保持服务器整体环境的整洁有序。部分php程序也将放到container里面,做一定量的研究。

过程中的几个小问题

添加用户的问题

useradd添加用户之后,用新建用户登录,shell左侧只能见到一个$符号,而且没有tab补全、也不能使用上下左右方向键。
这个情况是因为,新添加用户所使用的shell软件与root不同。
root账户使用的shell软件是bash,而新账户使用的shell软件地址在/bin/sh,而在这里ls -l一下,就会发现如下情况:

/bin/sh -> /bin/dash

也就是说,我们的新用户使用的shell软件是dash。
此时我们应该修改用户的默认shell软件,让其使用bash。具体方法可分成3类:

  1. chsh命令来设定用户的shell
  2. 编辑/etc/passwd来设定用户的shell
  3. 修改/bin/sh的软连接,让它连接到root使用的bash软件(注:国内大部分教程采用此方法,但是极力不推荐这种方法)

将shell软件重新指定之后,就可以使用bash正常进行补全了。不过,需要重新进行登录。
再强调一次,修改软连接的方法真心不好

shell的左侧只有$符号,可以补全的情况

在修改了shell软件之后,重新登录发现可以正常补全和使用方向键,可左侧还是只有一个$符号,这是shell配置没有生效的原因。此时使用:

cd /home/username
ls -a

查看你的用户根目录有没有.bashrc这个文件。这个文件没有的话,请按照bash官方的手册进行配置或者在网上找一个。已经有这个文件之后,我们在这个目录进行source命令来执行配置:

source ./.bashrc

然后就可以直接看见效果了。左侧的$符号处的具体形态配置,可以搜索关键词PS1
可以点击bash官方文档查看更多bash相关内容,有兴趣的可以自己去折腾一下哈。

Nginx配置不生效

从旧服务器上搬运过来的Nginx配置我丝毫没动,只是简单修正了文件目录的修改,可是在php5-fpm一切正常的情况下却不能打开php文件,进行的任何php操作都是一片空白。
同种情况可能发生的原因,首先是php的起始符号的问题。有时php的配置里面是禁止使用<?这种简写的符号作为php的起始符号的,这时候修改配置(Debian下apt-get到的php的话,配置文件在/etc/php5/fpm/php.ini),修改文件内容使short_open_tag = on就好。可以在文件里进行检索关键词 short ,如果检索到:

; short_open_tag
;   Default Value: On
;   Development Value: Off
;   Production Value: Off

这样的配置,那说明默认已经是short_open_tag = on了。
而我遇到的情况不在这里。经过对于log文件的检索发现,nginx端成功地把请求甩锅给了fastCGI,然后就没有然后了,php5-fpm也接收不到,所以问题应该在fastCGI上面。
经过检索,发现需要修改fastCGI的params,我的配置位置:/etc/nginx/fastcgi_params。里面可能是少了这样一句话:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

当然直接把这句话放进你的nginx网站具体配置也可以

location ~ .*\.php(\/.*)*$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
}

总结

折腾服务器很麻烦,部分软件的服务器环境很难搞定,互相之间的兼容也总是会出现意想不到的问题。
为了保证日后服务器管理的便利,应该熟练Docker,并配置好自己常用的几个container,以便于在更换服务器或者布置项目的时候,在处理解决服务器配置的问题上花费更少的时间。

Time is expensive.