[译]停止使用 CSS-in-JS 的九个理由

原文: [https://medium.com/@gajus/stop-using-css-in-javascript-for-web-development-fa32fb873dcc]

原文发表于: 2016-04-27

最近社区上 CSS-in-JS 与 CSS-Module 的争论持续不断, 上一篇译文统一样式语言 介绍了关于 CSS-in-JS 的优点. 本篇则是站在反对者的观点上, 反驳了一些(作者认为上的) CSS-in-JS 误区.

关于 CSS

styled-components 的文档说

原始 CSS 是用于文档形式的 web 的. 在1993年, 网页都是一些静态的文档, 那时用 CSS 来解决文档的样式问题. 如今, 我们构建的是更加丰富, 可交互, 面向用户的 APP. CSS 已经不适合构建这样的项目

我不赞同. CSS 也在与时俱进加入了许多新功能. (伪类, 伪元素, CSS 变量, \@media, 帧动画, 组合, 列, flex, 网格, calc, etc…)

从 UI 的角度上, “组件”就是独立的文档碎片. (比如 <button /&rt;就是一个组件) CSS 用来描述文档, 也就是所有组件的集合, 有何不妥呢. 正所谓物尽其用.

Styled-components

styled-components 允许使用模板文本的方式在 JS 中定义 CSS. 通过一种底层构建的方法取消了组件与样式组件之间的关系. demo

这成为了 React 中一种新型定义组件样式的趋势.

先明确一点: styled-components 组件是 CSS 的高阶抽象. 它在 JS 中定义 CSS, 并在 JSX 解析时绑定在对应元素上.

我不喜欢这种趋势, 它充满了太多误解. 调查了IRC, reddit, discord等地方, 人们用 styled-components 的理由. 我把他们称为”幻想” (myths)

Read More

[译]统一样式语言

原文: [https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660]

原文发表于: 2017-05-23

近几年里我们看到了很多 css in js 的尝试.
大多来自 React 社区. 显然这遭到了很多的吐槽, 尤其是那些非常熟练 css 的人, 觉得这种行为难以置信

"为什么会有人想把 css 写在 js 里?"
"这绝对是糟糕的主意!"
"如果他们学过 css 的话!"

如果这是你的第一反应, 那么请读下去. 我们需要继续讨论为什么把 css 写在 js 里并不那么糟糕, 以及为什么我们要着眼于此.

社区的误解

我从19岁就开始从事 css 专业, 在那个用表格布局的黑暗年代. 按照 CSS Zen Garden 中受到启发, 我将过去的代码迁移到语义标签下并使用样式表布局.
不久之后,我开始痴迷于分离样式,使用不起眼的JavaScript来装饰服务器渲染的标记与客户端交互. 有一个小但是活跃的社区从事着这样的实践, 我们成为了第一批前端工程师.

或许你会认为我会反对 React 的 HTML-in-JS, 因为这和我之前的所做完全相反. 在我的经验里, React组件模型及其服务端渲染能力, 最终可以构建一个复杂的单页应用, 从而让我们可以向用户提供快速, 可达, 渐进增强的应用. 我在 Seek 实践了这样的能力, 通过 React 构建了单页面应用, 即使禁用了 js, 搜索依旧可用, 并且通过服务端渲染在旧的浏览器中可以正常工作

所以让两个社区和平共处的橄榄枝是什么呢, 虽然这种方式并不完美, 没有被你用于生产环境, 甚至不是很有说服力, 但至少可以带给我们思考空间.

Why CSS-in-JS?

希望通过范围控制样式的开发者通常喜欢直接使用 CSS 模块, 而非 CSS-in-JS. 我在工作项目也没有使用 CSS-in-JS.

尽管如此我也实时关注 CSS-in-JS社区, 并且我也认为应当如此.

但是为什么需要 CSS-in-JS 呢?

为了清楚这个问题, 我们将讨论这种做法带来的好处:

  1. 范围约束
  2. 关键样式 (Critical CSS)
  3. 更智能的优化
  4. 包管理
  5. 非浏览器平台样式 (Non-browser styling)

Read More

[译]如何更好的组织 React 应用

原文 Url: https://medium.com/@alexmngn/how-to-better-organize-your-react-applications-2fd3ea1920f1


最近几年我和十余个同伴一起致力于大型项目从零开始的开发和维护. 有时候,没有一个良好的架构很难维持代码组织

注1: 我的例子中都使用了 redux, 如果你不了解, 参考 redux文档

注2: 所有的例子都基于 react, 但在 react native 中可以保持相同的结构

创建一个应用有哪些挑战?

这是一个发生或将发生在所有开发者项目中的事情:

  1. 你和几个开发者开始为客户开发一个应用, 一切都很美好

  2. 你的客户提了几个新需求, 嗯, 加上了

  3. 你的客户又要求去掉几个功能, 并加上一些新功能, 有点棘手但并不难实现. 项目开始变得不那么完美

  4. 你的客户继续要求增加/修改/删除需求, 你开始在一些代码上打补丁. 代码不再让人骄傲, 但还能用

  5. 6个月后, 代码变得复杂, 难以理解, 像一团意大利面条

直到有一天客户要求推出一个新版本时, 你放弃了乱成一团的旧代码, 开始了一个新的项目

Read More

开发基于 AST 的前端工具

最近在遇到了两个小需求:

  1. 要给一个已开发完的项目进行国际化, 希望可以遍历获取代码中注释之外的中文文本
  2. 一个较为复杂的项目希望在编译阶段根据代码中的特殊格式语句控制代码执行

这两个需求本质上比较接近: 都是对代码本身进行分析与处理. 而对于代码这种文本的处理, 用正则就太麻烦了. 不仅要考虑上下文的关系, 还要括号配对的行为. 将代码转换成抽象语法树再进行处理则要方便的多.

按照维基百科的定义:

在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。 树上的每个节点都表示源代码中的一种结构。

简单说来 AST 就是将整个代码作为一个根节点, 其中的每一个表达式作为一个子节点, 表达式内部, 又是由若干表达式与字符, 操作符作为子节点, 一点点细分直到所有单独的文本或操作符作为叶子节点… (怀念大学操作系统前几章的介绍状态机解析程序语言的部分)

生成 AST 的过程我们不需要手工完成, 像是 acorn, babylon 等许多模块都可以实现代码文本向 AST 的转换. 本文就是基于 babylon 实现的. 大部分的 AST 解析器功能和思路都比较接近, 我选择 babylon 的原因是 babylon 直接支持 JSX 语法的转译, 对于我们 React 的项目来说省去了很多麻烦.

babylon

babylonbabel 内的 JavaScript 语法分析器. 作为一个功能独立的库, 也可以在 babel 之外调用, 例如:

1
2
3
4
5
6
7
8
9
function parse (text, filePath) {
try {
return babylon.parse(text, {
plugins: ['jsx', 'classProperties'],
})
} catch (e) {
throw new Error(`Parse error: in ${filePath}`, e)
}
}

babylon.parse(code, [options]) 会将代码字符串转译成 AST. 参数中的 pluginsbabylon 内置的一些 ECMA 标准外的其他常用语法的支持. 我的工具打算运行在 React 项目上, jsx 插件可以解析 jsx 语法, 以及 classProperties插件提供一些 ES6 标准之外的语法支持.

babylon-walk

babylon 也提供了一个 babylon-walk 的工具来遍历语法树.

1
2
3
4
5
6
7
const walk = require('babylon-walk');
const visitors = {
StringLiteral: func,
JSXText: func,
}
const state = {}
walk.simple(node, visitors, state);

node是待遍历的 ast 节点, 如果需要遍历整个代码只需传递根节点就好

visitors 是一个 nodeType 与回调函数的对应表. 遍历时如果一个节点的类型存在对应的 visitors, babylon-walk 就会调用对应的回调函数, 并传递 nodestate

state 是一个普通的 object, 或是其他任何东西. 他的作用是在遍历途中保存一些需要的东西, 而不需要在 walk 的作用域外创建一个变量

babylon-walk 除上面用到的simple, 一共支持三种遍历模式:

  1. sample: 最普通的遍历
  2. ancestor: 回调函数中会附加所有遍历的先代节点信息
  3. recursive: 遍历时根据回调函数的返回值选择继续遍历的子节点

Read More

[译]React 性能工程

这是 React 性能工程系列的第一部分, 第二部分见深入了解 React 性能调试

本文是为了复杂的 react 项目. 如果你只是在做一些小型项目, 当还没有面临性能问题时, 请不要考虑优化, 只要构建就好了!

然而, 当开始写一些 DNA 设计工具啦, 凝胶图像分析软件啦, 富文本编辑器或是功能完备的表格系统时, 免不了会遇见性能瓶颈, 并需要尝试寻找解决之道, 本文就是尝试分享我们在此类问题上的一点经验.

本文中, 我将介绍 react 性能分析的基本工具, 一些导致性能瓶颈的常见问题, 调试上的一些关键点.

React 性能基础

用3句话概述浏览器性能: 理想下浏览器渲染是60帧/秒, 即 16.7ms 一帧. 当应用较慢, 通常用户事件, 数据处理会有较长的延迟. 多数情况并不会有复杂的数据处理, 大部分事件会浪费在重新渲染上.

使用 React 可以不做额外工作下立即一些性能优化.

因为 React 托管了所有 DOM 操作, 多数情况下 DOM 的解析与布局问题都可以被避免. 屏幕背后, React 维护了一套虚拟DOM 的机制, 使文档在最小的改变下让文档变回被期待的样子.

由于 React组件在 JavaScript 里存储状态, 不建议访问直接操作 DOM. 一个比较常见的例子是在不恰当的时机访问了一个 DOM, 然后导致了强制布局同步 (e.g. someNode.style.left浏览器会强制渲染一个 frame). 代替这种做法:

1
someNode.style.left = parseInt(someNode.style.left) + 10 + "px";

我们声明了 <SomeComponent style={ {left: this.state.left} } />, 然后通过更新 state 而非读取 DOM 的方式:

1
this.setState({left: this.state.left + 10}).

这些性能优化点并不局限于 React, 建议在做其他事情前先解决此类问题.

简单的 React 应用这些就足够, 在复杂的应用中, 虚拟 DOM 的对比可能成为昂贵的开销. 幸运的是 React 提供了一些性能检测工具来发现并防范问题.

调试带来的性能问题

小心, 一些调试本身就会带来间接成本. 不要在开发环境下调试.

ELEMENTS 窗口

(Chrome dev tools 上的) Elements 面板可以方便直观的看到什么元素被重新渲染了, 当属性变化或一个DOM 节点 被更新/新增/替换时会有一个闪烁的效果. 但是这种检测本身会影响到性能, 如果要准确计算 FPS 时, 请切换到 Consoles 面板.

PROPTYPES

React 开发中, PropType 校验 发生在组件被渲染时.利用 Chrome dev tools 上的Profiler面板可以观察到 React 组件花费了大量时间在校验(validate)的方法上

虽然开发工具的警告在调试阶段很有用, 但是生产环境下缺造成了额外的开销. 有时我会切换到生产模式来忽略这个延迟

Read More

[译]react组件书写最佳实践

原文 Url: https://engineering.musefind.com/our-best-practices-for-writing-react-components-dec3eb5c3fc8


自从我开始学习 React 开始, 就在不同的教程上看到了不同的创建组件的方法. 虽然 React 框架逐渐成熟, 似乎却并没有一套’正确’的组件书写规范.

MuseFind 的一年, 我们写了大量的 React 组件, 最终, 总结出了一个我们用起来很开心的方法. 希望无论是初学者还是经验丰富的人都能从中获得帮助.

开始之前有几点需要注意:

  1. 我们用 ES6 / ES7语法
  2. 如果你不晓得展示(presentational)组件与容器(container)组件的区别, 建议先看这篇 (译注: 中文翻译)
  3. 如果有建议或疑问请去原文评论

基于 Class 的组件

基于 Class 的组件是有状态或包含了一些方法的组件. 建议尽可能谨慎的去使用这种方式, 但其使用场景却是无可取代的.

让我们从头开始构建一个组件:

引入 CSS

import React, {Component} from 'react'
import {observer} from 'mobx-react'

import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

我喜欢在js中关联css. [b]在每个组件中引入 css样式[/b]

在依赖引用与本地引用之间, 建议保留一个空行

初始化 state

import React, {Component} from 'react'
import {observer} from 'mobx-react'

import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

export default class ProfileContainer extends Component {
  state = { expanded: false }

也可以使用在 constructor 函数中声明, 但是推荐这种更简洁的方法.

同时建议直接导出 class

propTypes 与 defaultProps

import React, {Component} from 'react'
import {observer} from 'mobx-react'

import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

export default class ProfileContainer extends Component {
  state = { expanded: false }

  static propTypes = {
    model: React.PropTypes.object.isRequired,
    title: React.PropTypes.string
  }

  static defaultProps = {
    model: {
      id: 0
    },
    title: 'Your Name'
  }

propTypes 与 defaultProps 是静态属性, 应尽可能在组件顶端声明. 他们应该承担文档的职责并可以第一时间被其他开发者看到.

[b]所有组件都应该有 propTypes[/b]

方法

import React, {Component} from 'react'
import {observer} from 'mobx-react'

import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

export default class ProfileContainer extends Component {
  state = { expanded: false }

  static propTypes = {
    model: React.PropTypes.object.isRequired,
    title: React.PropTypes.string
  }

  static defaultProps = {
    model: {
      id: 0
    },
    title: 'Your Name'
  }

  handleSubmit = (e) => {
    e.preventDefault()
    this.props.model.save()
  }

  handleNameChange = (e) => {
    this.props.model.changeName(e.target.value)
  }

  handleExpand = (e) => {
    e.preventDefault()
    this.setState({ expanded: !this.state.expanded })
  }

对于 class 类型的组件, 必须确保方法被调用时 this 指向正确的对象. 这里经常在调用时被写成 this.handleSubmit.bind

但用 es6 的箭头函数去维护上下文正确会更加简洁.

Read More

express-hbs简介

express-hbs 是一个handlebars模板引擎.

虽然express自带的jade也挺不错的, 但不支持html原生语法在很多场合下都会带来不方便 (尤其是copy代码时). 相对的, express-hbs在原生html的基础上用{ { } }来实现布局. 我更青睐这种的.

Read More

Hello World

第一篇测试文章XD

其实半年前就打算在github上搭一个静态站来存技术向的笔记啥的

因为拖延症的缘故一直到最近才搭好/w\

本地搭的jekyllrb, 然后同步到github上.

慢慢更新好了(趴