GithubHelp home page GithubHelp logo

blog's People

Stargazers

jianghai avatar

Watchers

James Cloos avatar jianghai avatar

Forkers

wz-fork

blog's Issues

关于网格布局

网格布局 Grids Layout,顾名思义,为页面提供网状的框架结构,类比城市之路网。

为何要用这种规范呢?网页和传统媒体类似,都是信息发布、传播的工具,所以设计上有很多通用性。以报纸为例

newspaper

实际应用到网页有以下优点:

  1. 设计师能根据网格参考线合理布局页面内容
  2. 程序员布局思路清晰、代码质量良好
  3. 用户能感知整体的框架结构,整齐划一,恰当的边距作区分,非常容易阅读和理解

从 Web 发展历程来看经历了以下几个阶段:

table 布局

table 除展示数据外还用于整体页面的布局。不难理解,table 本身就是一种网格结构。

<table width="800">
  <tr>
    <td colspan="2">header</td>
  </tr>
  <tr>
    <td width="200">sidebar</td>
    <td>content</td>
  </tr>
</table>

但是这种布局方式带来了很多问题:

  1. 语义,table 本意用于渲染表格数据,用在布局上让代码的可读性、语义性变低
  2. 增加、修改、删除单元格非常繁琐,牵一发动全身,比如为了把上面的 content 分成两列,先在 content 后面追加一个单元格,然后将 header 的 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 像素宽?

  1. 当时的网页还是以 PC 为主,手机上网还没普及,网页还是以门户网站类型为主,屏幕大小比较单一
  2. 960 正好可以被 12、16、24 等整数整除,方便网格设计

这种布局方式相比较 table 布局有了以下改进:

  1. 标签不再局限于 table,虽然没有完全的语义化。完全语义化需要 HTML 底层解决
  2. 增加、修改、删除网格最多只需修改同行布局方式,其它行不受影响,大大提高灵活性

响应式栅格布局

随着移动互联网的普及,人们访问互联网的方式多样化,各种设备不同分辨率五花八门,如何让这些设备都能使用同一套互联网资源成为问题。以百分比布局结合 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 媒体查询可动态响应不同的布局规则,具体规则如下:

网格规则除数字比例外,多了 xssmmdlg 四种模式,即可视区域宽度不小于一定像素时会触发样式

触发规则:

xs sm md lg
可视区域宽度不小于 0 768px 992px 1200px

<div class="col-md-4 col-sm-6"></div>

为例,可视区域宽度在:

  • 768px992px 之间,满足 sm,不满足 md
  • 大于 992px,同时满足 smmd,但预设 css md 靠后,优先级更高,sm 会被覆盖

除网格规则外,container 容器大小也是响应式的,即不同级别的分辨率固定宽度不同,此外,支持 container-fluid 模式,按屏幕百分百大小显示。

display: grid

待研究,参考
https://css-tricks.com/snippets/css/complete-guide-grid/

React 最佳实践

团队开始使用 React 后,不同的人写出的代码还真是千差万别。这里总结一些最佳实践(对比反模式),让团队有个参照。

杜绝不必要的 DOM 操作

例如我们要获取当前点击的 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 毫无关系,自然和谐。

render 方法保持纯粹,避免一些跟 UI 呈现无关的处理逻辑

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
  }
})

一张图了解 JavaScript 核心

JavaScript core
其他对象如 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

软件设计原则

  1. 抽象(避免重复)
  2. 合理的分层(层次尽量少、扁平)
  3. 自上而下(应用接口)与自下而上(通用逻辑库)相结合
  4. 接口设计避免标新立异,考虑用户的习惯和经验

参考

  1. 《UNIX 编程艺术》

面向受众编程

做技术、做架构,首先应该考虑的是用户体验,这里的体验可以面对的是普通用户,也可以是开发者。

首屏首次数据不要异步加载

  • 默认的占位无法预知数据结果,所以无法确定宽高,数据加载后渲染时会造成视觉跳动
  • 打开页面后,异步加载给用户带来的体验是多了一次等待

Clean Code 笔记

命名

  • 通过名称能理解变量的含义、函数的行为、类的权责等等

函数

  • 短小!短小!短小!函数只做一件事且做好这件事,无副作用,代码也更容易阅读和理解
  • 参数尽量少,过多的参数意味做的事太多,同时函数也不好命名,无法准确描述

注释

  • 把一段代码抽象成命名优秀的函数比额外的注释更好
  • 避免用注释弥补代码的不足,思考代码本身的问题吧

  • 短小、职责明确、内聚(内部成员互相访问较多,内聚的类一般比较短小,职责也更明确)

UI 组件封装的思考:流水线与定制化的权衡

UI 组件化早已不是什么新鲜事了,从早期的 Extjs 到这两年 React 的流行带来的 UI 组件化变革,我司内部也搞了一套 BFD UI

HTML、CSS、JS 都封装在一个模块里使用起来确实非常方便,结合 React,我们使用一个组件就像使用一个 HTML 标签那样简单,另外 React 创造性的 JSX 模式可以在 JS 里直接写 HTML,包括参数的传递、事件的定义等非常直观。可以理解一段 HTML 就是一种特殊的数据类型,可以任意使用、传递,这样一些个性化的业务逻辑需求都可以通过传递渲染逻辑来实现。

借着 React 的东风,我们的组件库迅速崛起,项目开发真的做到了工厂化、流水线的开发模式,产品、UI、前端、测试都省心。

针对 UI 无法完全统一的疑问,起初我是态度是坚决认为 UI 是可以做到统一的,我们的项目偏重功能,同传统的桌面软件开发模式类似,没必要仅仅为了视觉的差异而去定制开发,最主要的还是保证产品质量和开发效率。

React 项目总结

为什么不用 Redux 等数据流方案?

Redux 主要做了两件事:

  1. 抽离 state、action,原本的组件只负责渲染以及响应 action
  2. 统一管理 state,解决了单向数据流下组件间通信的问题

看起来很美好,但为什么不用呢?

React 本身很简单,组件就像一个纯函数,输入什么(props、state)就返回什么(UI 界面),而页面开发就是组件的组合,像搭积木一样简单。每个组件各司其职,小而美。

属于一个组件的 action 应归组件自身管理,而不是和其他组件混在一起,action 越来越多的情况下实在无法区分,违背了组件化思路的初衷。

但是 Redux 很好的解决了组件通信的问题呢?

的确,React 组件之间没有绑定关系,子组件如果依赖父组件传入的信息 (props) 且会发生变化, 那么当子组件需要修改这些信息时就不能一身不吭地改了,否则这些信息父组件不知道发生了变化,下次再传给子组件时,子组件之前默默的修改相当于被重置了。这种场景非常多,而且不仅只有父子之间的通信,这就产生了组件间通信的问题。

既然子组件不能修改外部传入的信息(不可控类型除外),那就告诉父组件来更新(state),仅仅是父子关系尚可

前端模块化的发展

前端模块化发展史

1 前言

前端非常特殊,特殊在由三种语言组成:HTML、CSS、JavaScript。

带来的问题就是资源类型多种多样,随着应用复杂度的提升,资源的类型和数量越来越多:

.html/.css/.js/.json/.svg/.jpg/.png/.less/.scss/.jsx ....

如何管理好这些资源,如何方便地复用,成为业界共同的问题,一路走来,实属不易。

2 HTML 直接管理

早期的前端极其简单,一段 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>

这种情况下暴露出很多问题:

  1. 全局变量过多,容易出现冲突
  2. 手动加载依赖,管理依赖顺序
  3. 关联的资源模块被拆分,不方便统一管理与复用

3 AMD 规范

此时,ServerJS 社区(CommonJS 的前称,发展史下阶段介绍)推出的 Modules/1.0 规范在 Node.js 等环境下比较成功

var module = require('./module')
// do sth... 

于是想在浏览器环境下推广,便将社区改名为 CommonJS,商讨下一版规范,但社区内部分歧较大,产生了三种流派:

  1. Modules/1.x 流派:认为已有规范足够,在浏览器运行前通过转换工具转换即可
  2. Modules/Async 流派:认为浏览器自身的特性应该用异步的方式动态加载,典型代表是 AMD 规范
  3. Modules/2.0 流派:介于二者之间

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...
})

相比第一阶段的优势:

  1. 通过回调函数传递模块,不再产生全局变量
  2. moduleD只需要定义一次,多个页面多次使用时就不需要再加载它的依赖模块了

但 RequireJS 关注的主要是 JS 模块的管理,关联的其他类型的资源模块仍需手动管理(其实这个不属于 AMD 规范的问题,而是 RequireJS 没有去实现其他类型资源的依赖管理)。

4 CommonJS 规范

随着 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>

5 Web Components

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>

6 ES6 模块规范

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

7 总结

技术的发展总是由需求推动的:

  1. 应用复杂度提升,依赖资源越来越多,手动管理不方便,AMD 规范出现
  2. NodeJS 兴起,前后端 JS 需要共享模块,浏览器端应用 CommonJS 规范
  3. 前端不仅仅是 JS,其他资源也需要在一起管理,webpack 等工具出现,此时模块化不仅仅是单纯的 JS 了,正式拉开组件化的序幕
  4. 组件化需要一种组件化的类库去管理组件,Polymer、React、Vue 等类库相辅相成
  5. 面对诸侯割据般的模块规范,统一的语言层面上的模块化规范出现 — ES6 Modules

前端模块不仅仅是 JS,HTML、CSS、图片等等一切资源都是模块,如何复用资源、如何以最小的成本使用,就是本文的目的。

8 参考

Modules/1.0
CommonJS
AMD

造轮子的基本要求

  1. 能解决什么问题?
  2. 为何自己造,已有的方案能否满足?
  3. API 设计,包括基本的命名、思路、结构等,尽量完美,避免后期重构困难
  4. 尽量向下兼容,给使用者减少升级的麻烦
  5. 单元测试,保证持续更新的稳定性
  6. 文档清晰,比如某个 API 要求的版本等,可从代码注释提取

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.