jianghai / blog Goto Github PK
View Code? Open in Web Editor NEW生活 & 工作
生活 & 工作
网格布局 Grids Layout,顾名思义,为页面提供网状的框架结构,类比城市之路网。
为何要用这种规范呢?网页和传统媒体类似,都是信息发布、传播的工具,所以设计上有很多通用性。以报纸为例
实际应用到网页有以下优点:
从 Web 发展历程来看经历了以下几个阶段:
table 除展示数据外还用于整体页面的布局。不难理解,table 本身就是一种网格结构。
<table width="800">
<tr>
<td colspan="2">header</td>
</tr>
<tr>
<td width="200">sidebar</td>
<td>content</td>
</tr>
</table>
但是这种布局方式带来了很多问题:
colspan
修改成 3 才算完成随着 css 应用的普及,table 天然的网格属性可以很容易被 css 取代,于是一些 css 布局框架也开始出现,以经典的 960.gs 为例:
<div class="container_12">
<div class="grid_12"></div>
<div class="grid_4"></div>
<div class="grid_8"></div>
<div class="grid_6 prefix_6"></div>
</div>
将会产生以下布局
----------------------
grid_12
----------------------
grid_4 | grid_8
----------------------
grid_6
----------------------
更多用法查看官网 demo
看完 demo 后,可能会产生疑问,为何最外层是 960 像素宽?
这种布局方式相比较 table 布局有了以下改进:
随着移动互联网的普及,人们访问互联网的方式多样化,各种设备不同分辨率五花八门,如何让这些设备都能使用同一套互联网资源成为问题。以百分比布局结合 css3 媒体查询的新的布局思路应运而生。这其中以著名开源项目 bootstrap 中的栅格布局为例:
<div class="container">
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-8"></div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-6"></div>
</div>
</div>
看起来与之前的非响应式栅格布局相同,响应式体现在哪?
如果 md-4
仅仅表示 12 格的宽度占 4 格(33.3333%),同等比例小屏幕下太小。如何解决呢?
试想一下,可否在小屏幕下分配的多一些?就是这样,bootstrap 布局**利用 css3 媒体查询可动态响应不同的布局规则,具体规则如下:
网格规则除数字比例外,多了 xs
、sm
、md
、lg
四种模式,即可视区域宽度不小于一定像素时会触发样式
触发规则:
xs | sm | md | lg | |
---|---|---|---|---|
可视区域宽度不小于 | 0 | 768px | 992px | 1200px |
以
<div class="col-md-4 col-sm-6"></div>
为例,可视区域宽度在:
768px
至 992px
之间,满足 sm
,不满足 md
992px
,同时满足 sm
、md
,但预设 css md
靠后,优先级更高,sm
会被覆盖除网格规则外,container
容器大小也是响应式的,即不同级别的分辨率固定宽度不同,此外,支持 container-fluid
模式,按屏幕百分百大小显示。
待研究,参考
https://css-tricks.com/snippets/css/complete-guide-grid/
团队开始使用 React 后,不同的人写出的代码还真是千差万别。这里总结一些最佳实践(对比反模式),让团队有个参照。
例如我们要获取当前点击的 ID,jQuery 写多的人可能思维还没有转换过来,我要拿到这个元素,个元素,元素。。。
反模式
const App = React.createClass({
getInitialState: {
return {
items: [{id: 1, name: 'one'}, {id: 2, name: 'two'}]
}
},
handleClick(e) {
console.log(e.target.getAttribute('data-id'))
},
render() {
return (
<ul>
{this.state.items.map(item => {
return <li key={item.id} data-id={item.id} onClick={this.handleClick}>{item.name}</li>
})}
</ul>
)
}
})
操作 DOM,即 JS 和 DOM 之间的通信耗费的资源比较大,既要给元素写数据,又要从元素中读取数据。而熟悉 React 的都知道,状态(state/props)决定了 UI 的最终呈现,状态跟 DOM 之间形成一种有序的对应关系,所以只需要知道当前点击的索引即可拿到对应的数据。
最佳实践
const App = React.createClass({
getInitialState: {
return {
items: [{id: 1, name: 'one'}, {id: 2, name: 'two'}]
}
},
handleClick(i) {
console.log(this.state.items[i].id)
},
render() {
return (
<ul>
{this.state.items.map((item, i) => {
return <li key={item.id} onClick={this.handleClick.bind(this, i)}>{item.name}</li>
})}
</ul>
)
}
})
巧妙的利用 bind 方法绑定额外的参数,操作过程跟 DOM 毫无关系,自然和谐。
state 和 props 的变化会重新执行一次 render(除非 shouldComponentUpdate 返回 false),如果 render 里的一些逻辑跟 DOM 结构无关,则用 state 保持,避免不必要的性能消耗。
反模式
const App = React.createClass({
getInitialState: {
return {
items: [{id: 1, name: 'one'}, {id: 2, name: 'two'}],
index: 0
}
},
handleClick(index) {
this.setState({index})
},
render() {
const url = '/data/test.do?index=' + this.state.index
return (
<ul url={url}>
{this.state.items.map((item, i) => {
return <li key={item.id} onClick={this.handleClick.bind(this, i)}>{item.name}</li>
})}
</ul>
)
}
})
最佳实践
const App = React.createClass({
getInitialState: {
return {
items: [{id: 1, name: 'one'}, {id: 2, name: 'two'}],
url: '/data/test.do?index=0'
}
},
handleClick(index) {
this.setState({url: '/data/test.do?index=' + index})
},
render() {
return (
<ul url={this.state.url}>
{this.state.items.map((item, i) => {
return <li key={item.id} onClick={this.handleClick.bind(this, i)}>{item.name}</li>
})}
</ul>
)
}
})
url 只是一种数据,跟具体的 DOM 结构无关,所以可以抽离出来处理,提升 render 的处理速度。
完整的属性验证既能检查外部传入的数据(不可控因素),减少出错后查找 bug 所耗费的时间,又能让人一目了然了解组件所有对外的接口。
最佳实践
import React, { PropTypes } from 'react'
const MyComponent = React.createClass({
propTypes: {
items: PropTypes.array.isRequired,
onChange: PropTypes.fun
}
})
其他对象如 Number
, String
等与 Array
同理
几个有意思的事实:
Function.__proto__ === Function.prototype // true
Function instanceof Object // true, Function.__proto__.__proto__ === Object.prototype
Object instanceof Function // true, Object.__proto__ === Function.prototype
Function instanceof Function // true, Function.__proto__ === Function.prototype
Object instanceof Object // true, Object.__proto__.__proto__ === Object.prototype
参考
做技术、做架构,首先应该考虑的是用户体验,这里的体验可以面对的是普通用户,也可以是开发者。
UI 组件化早已不是什么新鲜事了,从早期的 Extjs 到这两年 React 的流行带来的 UI 组件化变革,我司内部也搞了一套 BFD UI。
HTML、CSS、JS 都封装在一个模块里使用起来确实非常方便,结合 React,我们使用一个组件就像使用一个 HTML 标签那样简单,另外 React 创造性的 JSX 模式可以在 JS 里直接写 HTML,包括参数的传递、事件的定义等非常直观。可以理解一段 HTML 就是一种特殊的数据类型,可以任意使用、传递,这样一些个性化的业务逻辑需求都可以通过传递渲染逻辑来实现。
借着 React 的东风,我们的组件库迅速崛起,项目开发真的做到了工厂化、流水线的开发模式,产品、UI、前端、测试都省心。
针对 UI 无法完全统一的疑问,起初我是态度是坚决认为 UI 是可以做到统一的,我们的项目偏重功能,同传统的桌面软件开发模式类似,没必要仅仅为了视觉的差异而去定制开发,最主要的还是保证产品质量和开发效率。
Redux 主要做了两件事:
看起来很美好,但为什么不用呢?
React 本身很简单,组件就像一个纯函数,输入什么(props、state)就返回什么(UI 界面),而页面开发就是组件的组合,像搭积木一样简单。每个组件各司其职,小而美。
属于一个组件的 action 应归组件自身管理,而不是和其他组件混在一起,action 越来越多的情况下实在无法区分,违背了组件化思路的初衷。
但是 Redux 很好的解决了组件通信的问题呢?
的确,React 组件之间没有绑定关系,子组件如果依赖父组件传入的信息 (props) 且会发生变化, 那么当子组件需要修改这些信息时就不能一身不吭地改了,否则这些信息父组件不知道发生了变化,下次再传给子组件时,子组件之前默默的修改相当于被重置了。这种场景非常多,而且不仅只有父子之间的通信,这就产生了组件间通信的问题。
既然子组件不能修改外部传入的信息(不可控类型除外),那就告诉父组件来更新(state),仅仅是父子关系尚可
前端非常特殊,特殊在由三种语言组成:HTML、CSS、JavaScript。
带来的问题就是资源类型多种多样,随着应用复杂度的提升,资源的类型和数量越来越多:
.html/.css/.js/.json/.svg/.jpg/.png/.less/.scss/.jsx ....
如何管理好这些资源,如何方便地复用,成为业界共同的问题,一路走来,实属不易。
早期的前端极其简单,一段 HTML,少量 CSS 加上少量 JS
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="main.css">
<script src="main.js"></script>
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
这个时期,应用非常简单,依赖的资源很少,所以不需要任何模块化管理方案,HTML 本身就能表示出整个模块的依赖关系。
但是随着应用复杂度的提升,依赖的资源越来越多:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/moduleA.css">
<link rel="stylesheet" href="css/moduleB.css">
<link rel="stylesheet" href="css/moduleC.css">
<link rel="stylesheet" href="css/moduleD.css">
<!-- D 依赖 A、B、C -->
<script src="js/moduleA.js"></script>
<script src="js/moduleB.js"></script>
<script src="js/moduleC.js"></script>
<script src="js/moduleD.js"></script>
<script src="js/main.js"></script>
</head>
<body>
<div class="moduleD">
moduleD html content
</div>
<div class="moduleD">
moduleD html content
</div>
</body>
</html>
这种情况下暴露出很多问题:
此时,ServerJS 社区(CommonJS 的前称,发展史下阶段介绍)推出的 Modules/1.0 规范在 Node.js 等环境下比较成功
var module = require('./module')
// do sth...
于是想在浏览器环境下推广,便将社区改名为 CommonJS,商讨下一版规范,但社区内部分歧较大,产生了三种流派:
AMD 规范一度非常流行,很大程度上解决了模块依赖的问题,典型的实现者 RequireJS 使用方式如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/moduleA.css">
<link rel="stylesheet" href="css/moduleB.css">
<link rel="stylesheet" href="css/moduleC.css">
<link rel="stylesheet" href="css/moduleD.css">
</head>
<body>
<div class="moduleD">
moduleD html content
</div>
<div class="moduleD">
moduleD html content
</div>
<script src="js/main.js"></script>
</body>
</html>
moduleD.js
define(['moduleA', 'moduleB', 'moduleC'], function(A, B, C) {
// moduleD logic
return moduleD
})
main.js
require(['moduleD'], function(D) {
// do sth...
})
相比第一阶段的优势:
但 RequireJS 关注的主要是 JS 模块的管理,关联的其他类型的资源模块仍需手动管理(其实这个不属于 AMD 规范的问题,而是 RequireJS 没有去实现其他类型资源的依赖管理)。
随着 NodeJS 的兴起,写 JS 却用两套规范非常不方便,规范不一致,接口不一致,不利于模块共享。所以干脆在浏览器端也用 CommonJS 规范好了。
浏览器端不支持同步加载 JS,那么就在运行前打包好不就可以了,早期实现者 browserify 使用方式如下:
moduleD.js
var moduleA = require('./moduleA.js')
var moduleB = require('./moduleB.js')
var moduleC = require('./moduleC.js')
// moduleD logic
module.exports = moduleD
main.js
var moduleD = require('./moduleD.js')
// do sth...
$ browserify main.js -o bundle.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/moduleA.css">
<link rel="stylesheet" href="css/moduleB.css">
<link rel="stylesheet" href="css/moduleC.css">
<link rel="stylesheet" href="css/moduleD.css">
</head>
<body>
<div class="moduleD">
moduleD html content
</div>
<div class="moduleD">
moduleD html content
</div>
<script src="bundle.js"></script>
</body>
</html>
browserify 实现的仍然是 JS 模块管理,如何管理 CSS、图片、HTML 片段等资源呢?webpack 登场
webpack 可以管理所有类型资源的依赖,开发者再也不需要单独加载 JS 以外的依赖了
moduleD.js
var moduleA = require('./moduleA')
var moduleB = require('./moduleB')
var moduleC = require('./moduleC')
require('./moduleD.css')
require('./moduleD.tpl')
// moduleD logic
module.exports = moduleD
main.js
var moduleD = require('./moduleD')
// do sth...
$ webpack main.js bundle.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="moduleD"></div>
<div class="moduleD"></div>
<script src="bundle.js"></script>
</body>
</html>
此时,moduleD 已经上升到组件的概念了,与外部唯一的接口就是DOM容器了。
但是调用组件、组件嵌套组件仍然需要手动与 DOM 树挂钩。
场景一,调用组件 ModuleD:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="moduleD"></div>
<div class="moduleD"></div>
<script src="bundle.js"></script>
</body>
</html>
var ModuleD = require('./moduleD')
var containers = document.querySelectorAll('.moduleD')
new ModuleD({
el: containers[0]
})
new ModuleD({
el: containers[1]
})
场景二,ModuleD 嵌套 ModuleE:
moduleD.tpl
<div>
<div>Hello, E</div>
<div class="moduleE"></div>
</div>
moduleD.js
var ModuleE = require('./moduleE')
new ModuleE({
el: moduleE-element
})
使用起来非常繁琐,有没有一个组件化的类库来管理组件呢?比如最终代码可以这样写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<moduleD>
<div>Hello, E</div>
<moduleE></moduleE>
</moduleD>
<moduleD></moduleD>
<script src="bundle.js"></script>
</body>
</html>
其实,Web 组件化的概念早就提出,一些类库如 Polymer、React、Vue 等也已经实现。以 React 为例:
moduleD.jsx
var React = require('react')
var ModuleA = require('./ModuleA')
var ModuleB = require('./ModuleB')
var ModuleC = require('./ModuleC')
require('./ModuleD.css')
var ModuleD = React.createClass({
render() {
return (
<div>
<ModuleA />
<ModuleB />
<ModuleC />
</div>
)
}
})
module.exports = ModuleD
main.jsx
var React = require('react')
var ReactDOM = require('react-dom')
var ModuleD = require('./ModuleD')
var App = React.createClass({
render() {
return (
<div>
<ModuleD />
</div>
)
}
})
ReactDOM.render(<App />, document.getElementById('app'))
为什么把 HTML 放在 JS 中?
HTML 严格来说不属于编程语言,无法描述程序逻辑,所以如果要实现必然需要一些程序上的预处理等操作,与其在 HTML 上面附属一堆需要额外学习的逻辑语法:
<thead>
<tr>
<th v-for="key in columns"
@click="sortBy(key)"
:class="{active: sortKey == key}">
{{key | capitalize}}
<span class="arrow"
:class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
不如直接用纯粹的 JS 语法去描述。
<tr>
{columns.map(function(item) {
return <th>{item.name}</th>
})}
</tr>
也看个人喜好,各个类库设计思路不同罢了。
最终入口:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
2015年6月,ECMAScript 6 正式发布,终于,JavaScript 有了自己的模块化体系。
目前浏览器不支持,需要一些 transpiler 转换成已有的模块打包工具可识别的语法:
b.js
import a form './a'
import './b.css'
// do sth...
export default b
转换后
var a = require('./a')
require('./b.css')
// do sth...
module.exports = b
技术的发展总是由需求推动的:
前端模块不仅仅是 JS,HTML、CSS、图片等等一切资源都是模块,如何复用资源、如何以最小的成本使用,就是本文的目的。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.