import main from '../../main.js';
import OrderQuery from '../../views/order/OrderQuery';
import OrderDetail from '../../views/order/OrderDetail';
// 定义路由规则
let routeMap = {
'/': {
name: 'orderQuery',
component: OrderQuery
},
'/order-detail': {
name: 'orderDetail',
// 异步加载会导致丢失Style.(未被抽取到common.css中,也不在当前的chunks bundle js中)
component: function (resolve) {
require(['../../views/order/OrderDetail'], resolve);
}
// component: OrderDetail
}
};
// 启动应用
main.startApp(routeMap);
CommonsChunkPlugin
提取Entry间的公共依赖,防止重复加载。
可指定 Common Chunks 的name
可指定相关的 Entry
plugins: [
// If 'chunks' option omitted then all entry chunks are selected
new CommonsChunkPlugin({name: 'commons'}),
// Mark meta.js as entry chunk instead commons.js.
new CommonsChunkPlugin({name: 'meta', chunks: ['commons']})
]
// generate dist index.html with correct asset hash for each entry.
Object.keys(baseConfig.entry).forEach(function (name) {
var htmlConf = {
filename: '../'.concat(name).concat('.html'),
template: './src/index.html',
inject: true,
chunks: ['meta', 'commons', name],
chunksSortMode: function (a, b) {
var index = {'meta': 1, 'commons': 2};
index[name] = 3;
var ai = index[a.names[0]],
bi = index[b.names[0]];
return ai && bi ? ai - bi : -1;
},
minify: {
// more options: https://github.com/kangax/html-minifier#options-quick-reference
removeComments: true,
collapseWhitespace: true
}
};
baseConfig.plugins.push(new HtmlWebpackPlugin(htmlConf));
});
本文写于2016年06月22日,现在读起来仍能温故知新。
从模块化谈起
模块的定义
模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。
模块的特征
前端为什么要模块化
Javascript模块化编程
很久很久以前,JSer把完成特定功能的代码放入独立的文件中,以此来实现“模块化”。
作为Netscape 10天就开发出来的玩具语言,它为JSer们提供了太多大展拳脚的机会。
原始写法
文件作为模块组织单元
缺点
对象写法
优点
缺点
立即执行函数表达式写法
立即执行函数表达式(IIFE)
优点
模块的基本写法(ES5)
IIFE是模块化的基础(ES5)
放大模式
扩展了module1模块
优点
缺点
宽放大模式
优点:
输入全局变量
依赖模块通过参数传入
优点
思考
函数声明 & 函数表达式
回调函数 & 模块化 & 闭包
为什么需要模块化规范
大家都以同样的方式编写、加载模块,更易阅读和协作。
Javascript模块化规范
CommonJS
优点
缺点
实现
AMD - Asynchronous Module Definition
优点
缺点
实现
CMD - Common Module Definition
优点
缺点
实现
UMD - Universal Module Definition
UMD规范类似于兼容 CommonJS 和 AMD 的语法糖
模块定义的跨平台解决方案
ES6 Module
优点
缺点
实现
不仅仅是Javascript
CSS
IMG
WebFonts
HTML / Template
......
Webpack
Webpack是什么
Webpack 是一个模块打包器
根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源
Webpack的特点
Code Splitting(代码拆分)
Loaders(加载器/转换器)
Clever parsing(智能解析)
Plugin system(插件系统)
Efficient(高性能)
在项目中使用Webpack
Hot Reload(HMR)
Express
webpack-dev-middleware
webpack-hot-middleware
Mock Server
Long-Term Caching
"There are only two hard things in Computer Science: cache invalidation and naming things."
Phil Karlton
output.filename
除了可以指定具体的文件名以外,还可以使用一些占位符,包括:
name
hash
chunkhash
Option 1: One hash for the bundle
Option 2: One hash per chunk
Code Splitting
代码分隔,按需加载。
CommonsChunkPlugin
UglifyJsPlugin
压缩和混淆代码,减小 Bundle 的大小,提高加载速度,节省流量。
HtmlWebpackPlugin
Loaders
响应式设计
什么是响应式设计
网页设计应该做到根据不同设备环境自动响应及调整。
不仅仅是关于屏幕分辨率自适应以及自动缩放图片等。
更是一种对于设计的全新思维模式:移动优先、向下兼容。
适配不同终端
遵循移动优先原则,交互&设计应以移动端为主,PC则作为移动端的一个扩展。
适配不同终端有两个关键点:响应式布局、响应式内容(图片、多媒体)。
响应式布局
CSS Media Queries
viewport
Flex
响应式图片
屏幕尺寸和分辨率
屏幕的DPI/PPI
移动端适配
一些概念
物理像素(Physical Pixel)
设备独立像素(Density-Independent Pixel)
设备像素比(Device Pixel Ratio)
位图像素(Bitmap Pixel)
物理像素(Physical Pixel)
一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个物理像素都有自己的颜色值和亮度值。
设备独立像素(Density-Independent Pixel)
也叫密度无关像素、CSS 像素、逻辑像素。
一种Web编程上的概念,是一个抽象的单位。
设备像素比(Device Pixel Ratio)
设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系。
设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向
在javascript中,可以通过window.devicePixelRatio获取到当前设备的dpr。
位图像素(Bitmap Pixel)
位图像素是栅格图像(如:png, jpg, gif等)最小的数据单元。
每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)。
以 iPhone6s 为例
设备宽高为375×667,可以理解为设备独立像素(或CSS像素)。
dpr为2,根据上面的计算公式,其物理像素就应该 ×2,为 750×1334。
用一张图来说明
图片模糊
理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。
假设在普通屏幕下完美展示,但是在Retina屏幕下就会出现位图像素点不够,从而导致图片模糊。
图片缺少锐利度
两倍图在普通屏幕下显示,一个物理像素点对应4个位图像素点。
它的取色也只能通过一定的算法来实现(显示结果就是一张只有原图像素总数四分之一,我们称这个过程叫做downsampling)
图片显示 Demo
100×100的图片,分别放在100×100,50×50,25×25的img容器中,在Retina屏幕下的显示效果:
爱字图,可以通过看文字"爱"来区分图片模糊还是清晰。
条形图,通过放大镜其实可以看出边界像素点取值的不同:
图片高清适配方案
不同 dpr 下,加载不同尺寸的图片。
布局适配方案
目前比较流行的、也是助手采用的布局适配方案是使用相对单位 rem。
rem 是什么
div 的实际宽度 width = 75px * 2 (即 150px)
还得聊聊视觉稿
在前端开发之前,VD 会给我们一个psd文件,称之为视觉稿。
移动端开发中,为了做到页面高清的效果,视觉稿往往会遵循以下两点:
首先,选取一款手机的屏幕宽高作为基准(以前是iPhone4s的320×480,现在更多的是iPhone6的375×667)。
对于Retina屏幕(如dpr=2),为了达到高清效果,视觉稿的画布大小会是基准的2倍,也就是说像素点个数是原来的4倍(对iPhone6而言,原先的375×667,就会变成750×1334)。
rem 方案原理
针对不同屏幕尺寸和dpr动态改变根节点html的font-size(基准值)。
document.documentElement.clientWidth * dpr / 10
乘以dpr,是因为页面有可能为了实现1px border页面会缩放(scale) 1/dpr 倍(如果没有,dpr=1)。
除以10,是为了取整,方便计算(理论上可以是任何值)
CSS方式
动态改变根节点 html 的 font-sizeJS方式
动态改变根节点 html 的 font-size同时使用
CSS方式
和JS方式
如果仅使用
JS方式
,那么页面在加载后可能会重新布局(relayout/reflow),产生抖动。所以先通过
CSS方式
,基本确定页面布局;然后再通过JS方式
精确计算后,页面进行微调。如何在 CSS 中还原视觉稿的真实宽高
width: 10rem; // rem基准值 = 75px
height: 4rem; // rem基准值 = 75px
如何自动适配 iPhone4s
width: 10rem; => width: 640px;
height: 4rem; => height: 256px;
px2rem 自动化
利用 Webpack 的 px2rem-loader 实现 px2rem 的自动转换。
不需要人肉进行 px2rem 的转换
调整样式时更简单、直观
发现问题了吗?
iPhone6s的物理像素是 750×1334
VD 视觉稿也是基于 iPhone6s的物理像素 750×1334
CSS 中使用的逻辑像素也是基于 750×1334
CSS像素 = 物理像素 / dpr
页面缩放
字体大小问题
在讨论字体是否需要缩放时,通常要考虑设计师的要求:
字体大小要统一,大屏显示更多的文字。
字体大小随着屏幕的大小进行相应的缩放。
字体大小的统一和缩放
Retina下,border: 1px; 问题
这大概是设计师最敏感,最关心的问题了。
设计师想要的 Retina 下 1px边框,其实是1物理像素。对于CSS而言,相当于0.5px边框,这是 Retina下(dpr=2)下能显示的最小单位。
border: 1px solid #dcdcdc; // 跳过 px2rem 的处理
scale = 1 / dpr
viewport
先聊聊 PC 浏览器下的 viewport
缩放
Original & Zoom Out & Zoom In
屏幕尺寸
窗口尺寸
滚动距离
viewport的概念
viewport的功能是用来
约束
网站中最顶级包含块元素(containing block)<html>
的。从技术上来说
<body>
元素(并且你还没有给它设置过宽度),所以问题就变成了<body>
的宽度是哪个?<body>
元素和它的父元素<html>
一样宽。<html>
元素的宽度是多少?它的宽度和浏览器窗口宽度一样。这就是为什么拥有 width: 10% 属性的 div 会占据整个浏览器窗口的10%。从理论上来说
<html>
元素的宽度是被 viewport 的宽度所限制的,<html>
元素使用viewport宽度的100%。viewport 实际上等于浏览器窗口,它就是这么定义的。
viewport 不是一个 html 结构,所以你不能用 CSS 来改变它。
度量 viewport
两个属性对
document.documentElement.clientWidth/Height
不包含滚动条。window.innerWidth/Height
包含滚动条。度量
<html>
元素<html>
)的尺寸。事件中的坐标
媒体查询
你可以声明「只在页面宽度大于,等于或者小于一个特定尺寸的时候才会被执行」的特殊的CSS规则。
移动浏览器的问题
屏幕尺寸小,为 PC 设计的网站在移动浏览器中显示的内容明显要少。
对网页进行缩放直到文字小得无法阅读。
以合适的尺寸只显示网页中的一小部分内容。
两个 viewport
明显的解决方案是使 viewport 变宽一些,这样就产生了两个视口:
layout viewport
(布局视口)visual viewport
(视觉视口)George Cummins在Stack Overflow上给出了最佳解释:
layout viewport
?
)。visual viewport
visual viewport 是页面当前显示在屏幕上的部分,可以简单的认为是手持设备物理屏幕的可视区域。
可以通过滚动来改变所看到的页面的部分,或者通过缩放来改变 visual viewport 的大小。
缩放
很显然两个 viewport 都是以 CSS 像素度量的。
但是当进行缩放(如果你放大,屏幕上的 CSS 像素会变少)的时候,
visual viewport
的尺寸会发生变化,layout viewport
的尺寸仍然跟之前的一样。理解这两个 viewport
以完全缩小的模式来展示任何页面
,此时 visual viewport = layout viewport。完全缩小模式下
旋转手机至横屏时,visual viewport 会发生变化,但浏览器通过轻微的放大来适配这个新的朝向,所以 layout viewport 又和 visual viewport 一样宽了。这对 layout viewport 的高度会有影响,现在的高度比肖像模式(竖屏)要小。但是web开发者不在乎高度,只在乎宽度。
度量 layout viewport
度量 visual viewport
滚动距离 scrolling offset
度量
<html>
元素<html>
元素的整个尺寸,完全支持 iPhone 和 Android。事件坐标
pageX/Y
仍然是相对于页面,以 CSS 像素为单位。clientX/Y
是相对于visual viewport来计算,以CSS像素为单位。screenX/Y
是相对于屏幕来计算,以物理像素为单位。(这和clientX/Y用的参照系是一样的)媒体查询
媒体查询和其在 PC 上的工作方式一样。
width/height 使用 layout viewport做为参照物,并且以 CSS像素进行度量。
device-width/height 使用设备屏幕,以设备物理像素进行度量。
viewport meta标签
<head>
下的<meta>
标签html {width: 320px}
,现在<html>
元素收缩了,并且其他元素现在使用的是 320px 的 100%。这在用户进行放大操作的时候有用(无需左右滑动),但是在初始状态是没用的,你什么都看不清。为了绕开这个问题(
初始状态无效、需要手动放大
)苹果发明了viewport meta标签。当你设置的时候,你就设置了 layout viewport 的宽度为320px。现在页面的初始状态也是正确的:
viewport meta 标签的6个属性
width
设置layout viewport 的宽度,正整数或"width-device"。initial-scale
设置页面的初始缩放值,数字且可带小数。minimum-scale
允许用户缩放的最小倍数值,数字且可带小数。maximum-scale
允许用户缩放的最大倍数值,数字且可带小数。height
设置 layout viewport 的高度,但并不被支持。user-scalable
是否允许用户进行缩放,值为"no"或"yes"。ideal viewport(完美视口)
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
前端开发模式演变
简单明快的早期时代(Web 1.0 时代)
BAD
JSP 里揉杂大量Java业务代码,代码的可维护性差。BAD
前后端没有合理分工。后端为主的 MVC 时代
代码可维护性得到明显好转,MVC 是个非常好的协作模式,从架构层面让开发者懂得什么代码应该写在什么地方。
为了让 View 层更简单干脆,可以选择 Velocity、Freemaker 等模板,使得模板里写不了 Java 代码,前后端分工更清晰。
BAD
前端开发重度依赖开发环境。BAD
前后端职责依旧纠缠不清。Ajax 带来的 SPA 时代
前后端的分工非常清晰,前后端的关键协作点是 Ajax 接口。
复杂度从服务端的 JSP 里移到了浏览器的 JavaScript,浏览器端变得很复杂。
类似 Spring MVC,这个时代开始出现浏览器端的分层架构,如 Backbone。
不足
前后端接口的约定。不足
前端开发的复杂度控制。前端为主的 MV* 时代
前后端分工很清晰,可以让开发并行,测试数据的模拟不难,前端可以本地开发,后端则可以专注于业务逻辑的处理。
前端开发的复杂度可控,前端代码很重,但合理的分层(比如Templates、Controllers、Models等),让前端代码能各司其职。
后端一套Server API,多端使用(Web、移动APP等)
部署相对独立,产品体验可以快速改进。
不足
代码不能复用。不足
全异步,对SEO(搜索引擎优化)不利,往往还需要服务端做同步渲染的降级方案。不足
性能并非最佳,特别是移动互联网环境下。不足
SPA 不能满足所有需求,依旧存在大量多页面应用。URL Design 需要后端配合,前端无法完全掌控。Node 带来的全栈时代
挑战
前端需要对服务端编程有更进一步的认识,比如 network/tcp等知识的掌握。挑战
架构上多了一层 Node,提升了系统的复杂度和风险,也肯定会有一定的性能损耗。由此带来的损失,一定要从其他方面弥补回来。挑战
对部署、运维层面的熟练了解,需要更多知识点和实操经验,无法一蹴而就。挑战
大量历史遗留问题如何过渡,这可能是最大的阻力。(如何把一个已有的项目,过渡为这种模式?)前端开发模式总结
思考
为什么需要前后端分离?
前后端分离为什么需要 Node ?
页面性能优化
https://developer.yahoo.com/performance/rules.html
http://fengzheng369.blog.163.com/blog/static/75220979201242333359239/
Best Practices and Rules
Content
Server
Cookie
CSS
JavaScript
Images
Content
Make Fewer HTTP Requests
CSS Sprites
Inline images
Reduce DNS Lookups
Avoid Redirects
使用重定向会降低用户体验,它会使加载延时。
Make Ajax Cacheable
Post-load/Preload Components
Post-load
只加载当前页面需要的模块资源,以最快的速度展现当前页面,提升用户体验。Preload
利用浏览器的空闲时间,加载下一页面将要需要的模块资源,这样下一页面将更快速的展现给用户。Reduce the Number of DOM Elements
Split Components Across Domains
把资源分散到多个域名下可以尽量多的并行下载,但是不要超过2-4个域名,因为 DNS 解析会也有一定的损耗。
Avoid 404s
Server
Use a Content Delivery Network
Add an Expires or a Cache-Control Header
Gzip Components
Use GET for Ajax Requests
Avoid Empty Image src
Cookie
Reduce Cookie Size
客户端在跟服务端通信时,会带上Cookie信息,所以减小 Cookie 长度,可以一定程度的降低响应时间。
Use Cookie-free Domains for Components
CSS
Put Stylesheets at Top
Avoid CSS Expressions
网页渲染 或者 窗口缩放 时 expression 会被重新计算。
页面滚动 或者 鼠标移动 时都会重新计算。
Choose over @import
在 IE 上用 @import 的作用与把 放入网页底部的作用是一样的,所以不要使用。
Javascript
Put Scripts at Bottom
如果一个 js 脚本可以被推迟加载,那就把它放到页面最下面吧,这样会让你的页面以最快的速度展现给用户。
Make JavaScript and CSS External
Minify JavaScript and CSS
Remove Duplicate Scripts
Minimize DOM Access
Image
Optimize Images
Optimize CSS Sprites
Do Not Scale Images in HTML
当你需要100×100的图片时,不要使用500×500的图片进行缩放,直接提供一张100×100图片就好。
Make favicon.ico Small and Cacheable
检测工具
PageSpeed Insights
YSlow
Chrome DevTools
Frontend Knowledge Structure
Frontend Books
Commit message
了解更多