Web App 相关技术


往前推2到3年,前端工程师还在忧心忡忡地想,移动互联网时代下,前端是不是没有生存空间了。但今天一看,在我们团队,前端工程师超过一半的工作都是在做移动端的Web或者APP的开发。移动Web或者APP在技术本质上是和做桌面端Web没有本质区别,但是移动端的坑那是非常的多,通过学习这部分内容,让你成为一名桌面移动通吃的前端开发工程师。

概念

上面这个 slide 资料讲的非常好,算是一个入门的介绍吧。带我们建立基本的移动 web 开发知识体系和常见问题的实践。包含以下几个方面:

  • 基本概念
    • Native

      本地应用 使用 Java \ Objective-C \ Swift 开发

    • WebApp

      网页应用 html5 开发

    • Hybrid

      混合应用 ooxx(native, web)

    • 对比

  • 视觉
    • 设备的像素
    • 文字单位使用 rem
    • viewport 属性
    • 横屏竖屏
    • Flex 伸缩布局
    • 响应式设计
    • 软键盘
    • 隐藏地址栏
    • 苹果设备添加到主屏图标
  • 交互
    • Touch
    • click 延迟
    • Scroll
    • Gestures(hammer –A javascript library for multi-touch gestures)
    • 手指友好设计
    • HTML5 APIS(图像,摇动,声音等)
  • 实践
    • 屏蔽点击元素时的阴影
    • 图像(像素、矢量图标、base64 减少请求、lazyload)
    • CSS3(合理使用渐变/圆角/阴影、代替 js 动画、translate3d、解决动画闪烁)
    • localStorage
    • 避免(iframe、fixed + input)
    • SPA 或 Multi page
    • can I use
    • 压缩合并
    • @G/3G 下建立连接时间
  • 调试

head 标签

参考:

上面的链接详细的讲解了:

  • DOCTYPE
  • charset
  • lang属性
  • 优先使用 IE 最新版本和 Chrome
  • 360 使用Google Chrome Frame
  • SEO 优化部分:页面标题标签(head 头部必须),页面关键词 keywords,页面描述内容 description,定义网页作者 author,网页搜索引擎索引方式
  • 为移动设备添加 viewport

    viewport 可以让布局在移动浏览器上显示的更好。 通常会写

<meta name ="viewport" content ="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no">
<!-- `width=device-width` 会导致 iPhone 5 添加到主屏后以 WebApp 全屏模式打开页面时出现黑边 http://bigc.at/ios-webapp-viewport-meta.orz -->
  • content 参数:
    • width viewport 宽度(数值/device-width)
    • height viewport 高度(数值/device-height)
    • initial-scale 初始缩放比例
    • maximum-scale 最大缩放比例
    • minimum-scale 最小缩放比例
    • user-scalable 是否允许用户缩放(yes/no)
  • ios 设备,iOS 图标,Android,Windows 8

总结:

<!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 -->
<html lang="zh-cmn-Hans"> <!-- 更加标准的 lang 属性写法 http://zhi.hu/XyIa -->
<head>
    <!-- 声明文档使用的字符编码 -->
    <meta charset='utf-8'>
    <!-- 优先使用 IE 最新版本和 Chrome -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <!-- 页面描述 -->
    <meta name="description" content="不超过150个字符"/>
    <!-- 页面关键词 -->
    <meta name="keywords" content=""/>
    <!-- 网页作者 -->
    <meta name="author" content="name, email@gmail.com"/>
    <!-- 搜索引擎抓取 -->
    <meta name="robots" content="index,follow"/>
    <!-- 为移动设备添加 viewport -->
    <meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no">
    <!-- `width=device-width` 会导致 iPhone 5 添加到主屏后以 WebApp 全屏模式打开页面时出现黑边 http://bigc.at/ios-webapp-viewport-meta.orz -->

    <!-- iOS 设备 begin -->
    <meta name="apple-mobile-web-app-title" content="标题">
    <!-- 添加到主屏后的标题(iOS 6 新增) -->
    <meta name="apple-mobile-web-app-capable" content="yes"/>
    <!-- 是否启用 WebApp 全屏模式,删除苹果默认的工具栏和菜单栏 -->

    <meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL">
    <!-- 添加智能 App 广告条 Smart App Banner(iOS 6+ Safari) -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black"/>
    <!-- 设置苹果工具栏颜色 -->
    <meta name="format-detection" content="telphone=no, email=no"/>
    <!-- 忽略页面中的数字识别为电话,忽略email识别 -->
    <!-- 启用360浏览器的极速模式(webkit) -->
    <meta name="renderer" content="webkit">
    <!-- 避免IE使用兼容模式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 -->
    <meta name="HandheldFriendly" content="true">
    <!-- 微软的老式浏览器 -->
    <meta name="MobileOptimized" content="320">
    <!-- uc强制竖屏 -->
    <meta name="screen-orientation" content="portrait">
    <!-- QQ强制竖屏 -->
    <meta name="x5-orientation" content="portrait">
    <!-- UC强制全屏 -->
    <meta name="full-screen" content="yes">
    <!-- QQ强制全屏 -->
    <meta name="x5-fullscreen" content="true">
    <!-- UC应用模式 -->
    <meta name="browsermode" content="application">
    <!-- QQ应用模式 -->
    <meta name="x5-page-mode" content="app">
    <!-- windows phone 点击无高光 -->
    <meta name="msapplication-tap-highlight" content="no">
    <!-- iOS 图标 begin -->
    <link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-57x57-precomposed.png"/>
    <!-- iPhone 和 iTouch,默认 57x57 像素,必须有 -->
    <link rel="apple-touch-icon-precomposed" sizes="114x114" href="/apple-touch-icon-114x114-precomposed.png"/>
    <!-- Retina iPhone 和 Retina iTouch,114x114 像素,可以没有,但推荐有 -->
    <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144x144-precomposed.png"/>
    <!-- Retina iPad,144x144 像素,可以没有,但推荐有 -->
    <!-- iOS 图标 end -->

    <!-- iOS 启动画面 begin -->
    <link rel="apple-touch-startup-image" sizes="768x1004" href="/splash-screen-768x1004.png"/>
    <!-- iPad 竖屏 768 x 1004(标准分辨率) -->
    <link rel="apple-touch-startup-image" sizes="1536x2008" href="/splash-screen-1536x2008.png"/>
    <!-- iPad 竖屏 1536x2008(Retina) -->
    <link rel="apple-touch-startup-image" sizes="1024x748" href="/Default-Portrait-1024x748.png"/>
    <!-- iPad 横屏 1024x748(标准分辨率) -->
    <link rel="apple-touch-startup-image" sizes="2048x1496" href="/splash-screen-2048x1496.png"/>
    <!-- iPad 横屏 2048x1496(Retina) -->

    <link rel="apple-touch-startup-image" href="/splash-screen-320x480.png"/>
    <!-- iPhone/iPod Touch 竖屏 320x480 (标准分辨率) -->
    <link rel="apple-touch-startup-image" sizes="640x960" href="/splash-screen-640x960.png"/>
    <!-- iPhone/iPod Touch 竖屏 640x960 (Retina) -->
    <link rel="apple-touch-startup-image" sizes="640x1136" href="/splash-screen-640x1136.png"/>
    <!-- iPhone 5/iPod Touch 5 竖屏 640x1136 (Retina) -->
    <!-- iOS 启动画面 end -->

    <!-- iOS 设备 end -->
    <meta name="msapplication-TileColor" content="#000"/>
    <!-- Windows 8 磁贴颜色 -->
    <meta name="msapplication-TileImage" content="icon.png"/>
    <!-- Windows 8 磁贴图标 -->

    <link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml"/>
    <!-- 添加 RSS 订阅 -->
    <link rel="shortcut icon" type="image/ico" href="/favicon.ico"/>
    <!-- 添加 favicon icon -->

    <title>标题</title>
</head>
<body>
</body>
</html>

页面切换动画

关于 HammerJS 的一个中文文档


CSS Processing

CSS语言由于其自身语言设计的问题,加上一些浏览器兼容性问题,往往会使得我们在写它的时候,要写很多冗余代码,或者为了兼容性对同一个样式设定写好几遍。针对这些问题,诞生了CSS预处理和后处理的概念及相关方法、工具。

这些工具和方法帮助我们能够更加高效地书写可维护性更强的CSS代码。

这里我尝试使用了 Sass,果然很好用。下面记录几个 sass 教程。

安装

首先要有 ruby 环境。

由于国内网络原因(你懂的),导致 rubygems.org 存放在 Amazon S3 上面的资源文件间歇性连接失败。这时候我们可以通过gem sources命令来配置源,先移除默认的 https://rubygems.org 源,然后添加淘宝的源 https://ruby.taobao.org/,然后查看下当前使用的源是哪个,如果是淘宝的,则表示可以输入 sass 安装命令 gem install sass 了。

$ gem sources --remove https://rubygems.org/
$ gem sources -a https://ruby.taobao.org/
$ gem sources -l
*** CURRENT SOURCES ***

https://ruby.taobao.org
# 请确保只有 ruby.taobao.org
$ gem install sass

编译

sass --watch style.scss:style.css --style expanded

补充

rem

字体单位使用 rem,用户在手机上设置了字体大小时,不会打破布局,造成混乱。


安全

安全是大家经常容易忽视,但其实一旦出现影响会非常大的问题,尤其对于没有经历过企业开发,或者没有踩过坑的同学,如果等到公司工作,做实际项目后非常容易发生安全问题。

分类

WEB基本攻击大致可以分为三大类:“资源枚举”、“参数操纵” 和 “其它攻击”

  • 资源枚举
  • 参数操纵
    • SQL注入
    • XPath注入
    • cgi命令执行
    • XXS(cross-site scripting跨域脚本攻击)其重点是“跨域”和“客户端执行”
      • Reflected XSS ——基于反射的XSS攻击。主要依靠站点服务端返回脚本,在客户端触发执行从而发起WEB攻击。
      • DOM-based or local XSS——基于DOM或本地的XSS攻击
      • Stored XSS——基于存储的XSS攻击
    • 会话劫持
  • 其它攻击
    • CSRF(cross-site request forgery)跨站请求伪造
    • 钓鱼攻击指的是网站的伪造,比如ta0bao.com,然后在其中应用XSS等方式发起攻击。
    • 拒绝服务(DoS)指的是向网站发起洪水一样的请求(Traffic Floor),导致服务器超负荷并关闭,处理方法常规是采用QoS(Quality of Service)的软硬件解决方案。

关于 XSS

跨网站脚本(Cross-site scripting,通常简称为XSS或跨站脚本或跨站脚本攻击)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java, VBScript, ActiveX, Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

——维基百科

XSS 防护

  1. 浏览器解析顺序:

    HTML Parser » CSS Parser » JavaScript Parser

  2. 浏览器解码顺序:

    HTML Decoding » URL Decoding » JavaScript Decoding

  3. 具体的防护方式:

    • 验证输入并且基于语境和按照正确的顺序转义不可信数据
      • HTML 中的字符串
      • HTML 属性中的字符串
      • 事件句柄属性和 JavaScript 中的字符串
      • HTML 属性中的 URL 路径
      • HTML 风格属性和 CSS 中的字符串
      • JavaScript 中的 HTML
    • 始终遵循白名单优于黑名单的做法
    • 使用 UTF-8 为默认的字符编码以及设置 content 为 text/html
    • 不要将用户可以控制的文本放在标签前。通过使用不同的字符集注射可以导致 XSS。
    • 使用 <!DOCTYPE html>
    • 使用推荐的 HTTP 响应头进行 XSS 防护
    • 防止 CRLF 注入/HTTP 响应拆分
    • 禁止 TRACE 和其他非必要方法

对于 innerHTML 的方式输出的,我们可以采用如下的方式转码

/**
 * 转码 XSS 防护
 * @param  {String} str 用户输入的字符串
 * @return {String}     转码后的字符串
 */
function changeCode(str) {
    str = str.replace(/&/g, "&amp;")
              .replace(/</g, "&lt;")
              .replace(/>/g, "&gt;")
              .replace(/"/g, "&quot;")
              .replace(/'/g, "&#x27;")
              .replace(/\//g, "&#x2f;");
    return str;
}

参考:


性能优化

在自己做一些小项目时,可能是学校的一些网站项目,流量可能日均都不超过500,而且大多是校园局域网内访问;或者是开发一些实验室的MIS系统,这辈子你都不会去使用你开发的这个系统。在这样一些项目中,性能优化往往会被你忽略。

但是如果你是做一个日均PV数万、数十万、甚至更大的量级,开发的页面会被全国各地,不同网络条件的用户来进行访问。这个时候,性能问题就无法忽视了。在当今的网络条件下,如果你的页面3秒都无法完成首屏渲染,一定会让你的网站流失很多用户。

整个网站的性能优化有很多的环节和工作,大多数时候,不是前端工程师单独就能完成的,尤其在职能划分明确的公司中,往往需要前后端、运维、DBA等多个职位协同完成。所以,在我们的课程中,主要让你了解整个性能优化都涉及哪些方面的工作,同时,我们会专注介绍一些在前端领域可以重点关注的技术点。

这里就是网页的打开速度,如果你的网页打开速度很慢,那么一定会有用户的流失。所以性能优化很重要。

  • 网页内容
    • 减少http请求次数
    • 减少DNS查询次数
    • 避免页面跳转
    • 缓存Ajax
    • 延迟加载
    • 提前加载
    • 减少DOM元素数量
    • 根据域名划分内容
    • 减少iframe数量
    • 避免404
  • 服务器
    • 使用CDN
    • 添加Expires 或Cache-Control报文头
    • Gzip压缩传输文件
    • 配置ETags
    • 尽早flush输出
    • 使用GET Ajax请求
    • 避免空的图片src
  • Cookie
    • 减少Cookie大小
    • 页面内容使用无cookie域名
  • CSS
    • 将样式表置顶
    • 避免CSS表达式
    • 用<link>代替@import
    • 避免使用Filters
  • Javascript
    • 将脚本置底
    • 使用外部Javascirpt和CSS文件
    • 精简Javascript和CSS
    • 去除重复脚本
    • 减少DOM访问
    • 使用智能事件处理
  • 图片
    • 优化图像
    • 优化CSS Sprite
    • 不要在HTML中缩放图片
    • 使用小且可缓存的favicon.ico
  • 移动客户端
    • 保持单个内容小于25KB
    • 打包组建成符合文档

具体细节参考文章:

我在 ToDo 这个任务中主要使用了 CDN 来加载静态资源。比如我使用了 百度静态资源公共库。引用了里面的 fontawesome,速度果然比在 GitHub 仓库里快很多。下一步是压缩我自己写的静态资源。

其他参考资料:


模块化

对于一个复杂项目,特别是多人协作的复杂项目,如何合理划分模块,如何更加方便地进行模块加载,如何管理模块之间的依赖,是一个项目团队都会面临的问题,目前业界已经有了一些较为普遍的解决方案,如AMD。这个部分希望你能够通过学习JavaScript的模块化,学习如何合理地规划项目模块,合理使用模块化工具来优化你的项目代码结构。

一个模块就是实现特定功能的文件,有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发需要遵循一定的规范,否则就都乱套了。

根据AMD规范,我们可以使用 define 定义模块,使用 require 调用模块。

目前,通行的 js 模块规范主要有两种:CommonJSAMD

AMD规范

AMD 即 Asynchronous Module Definition,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,服务器端的规范是 CommonJS

模块将被异步加载,模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。

AMD 是 RequireJS 在推广过程中对模块定义的规范化的产出。

详细 API 如下:


CommonJS规范

CommonJS 是服务器端模块的规范,Node.js 采用了这个规范。Node.JS 首先采用了 js 模块化的概念。

根据 CommonJS 规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为 global 对象的属性。

输出模块变量的最好方法是使用 module.exports 对象。


为什么要用 requireJS

试想一下,如果一个网页有很多的js文件,那么浏览器在下载该页面的时候会先加载js文件,从而停止了网页的渲染,如果文件越多,浏览器可能失去响应。其次,要保证js文件的依赖性,依赖性最大的模块(文件)要放在最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

RequireJS就是为了解决这两个问题而诞生的:

(1)实现js文件的异步加载,避免网页失去响应; (2)管理模块之间的依赖性,便于代码的编写和维护。

requireJS


AMD和CMD

CMD(Common Module Definition) 通用模块定义。该规范明确了模块的基本书写格式和基本交互规则。该规范是在国内发展出来的。AMD是依赖关系前置,CMD是按需加载。

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。 CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。

AMD:提前执行(异步加载:依赖先执行)+延迟执行 CMD:延迟执行(运行到需加载,根据顺序执行)


参考


前端工程化

业界目前有非常多的前端开发工具,完成一些开发过程中可以自动化完成的工作,提高研发效率,并且可以提高多人协作时的开发过程一致性,提高整个项目的运维效率。

在EFE日常工作中,我们是基于EDP,完成项目开发过程中的项目构建、包管理、调试、单测、静态检测、打包、压缩、优化、项目部署等一系列所有工作。

注:

如果网络不好,可以使用 淘宝 NPM 镜像

参考


最终作品

在任务三中,做了一个 PC 端的 ToDo 应用。任务四是将它优化,以适应移动端设备。

ToDo WebApp Version

Details

  • 数据存储

    以 JSON 模拟数据表的形式存储于 LocalStorage 中

       使用数据库的思想,构建3张表。
       cateJson 分类
       childCateJson 子分类
       taskJson 任务
    
       分类表 cate
       ----------------------
       id* | name | child(FK)
       ----------------------
    
       子分类表 childCate
       --------------------------------
       id* | pid(FK) | name | child(FK)
       --------------------------------
    
       任务表 task
       ----------------------------------------------
       id* | pid(FK) | finish | name | date | content
       ----------------------------------------------
    
  • 使用 Sass 重构了 CSS 代码

    使用分块、继承等方式,使得代码更加清晰明了。

  • 响应式布局

    针对手机端细节做了很多调整,更符合手机上的视觉交互习惯。

  • 加入页面切换效果

    使用 translate3d(),纯 CSS3 切换动画效果。

  • 处理了 XSS 防护

    对可能造成破坏的字符进行转码。

  • 性能优化

    使用 CDN 处理静态资源 fontAwesome,压缩静态资源等

  • 模块化

    使用 requireJS 模块化 JavaScript 代码。重构 JavaScript 代码。优化之前写的耦合性高的绑定事件,重新绑定事件,降低耦合性。期间根据具体需求重写了事件代理的代码。

  • 前端工程化

    使用 gulp,自动编译 Sass,压缩 CSS 和 JavaScript 代码。并且配置了自动流程。


其他

-webkit-tap-highlight-color 属性

感谢 fiona 指出。

safari移动端点击的时候会闪一下加上 -webkit-tap-highlight-color: transparent; 就不会闪了。

参考:


textarea 标签 disabled 颜色

  • 为什么用 disabled 属性?

    因为我发现仅仅使用 readonly 属性,在 IE 下是显示光标的。于是使用 disabled。

  • 出现的问题

    各家浏览器对于 disabled 属性有自己的样式设定,比如 IE 下是灰色的。苹果设备下也是。改变这些样式的方法也不是统一的。如果要兼容 Safari 必须加上

background: #fff;
-webkit-text-fill-color: rgba(0, 0, 0, 1);
-webkit-opacity: 1;

于是最终代码如下:

textarea:disabled {
    color:#000;
    background: #fff;
    -webkit-text-fill-color: rgba(0, 0, 0, 1);
    -webkit-opacity: 1;
}

Content