A PHP Error was encountered

Severity: Notice

Message: Only variable references should be returned by reference

Filename: core/Common.php

Line Number: 257

江海城 - 说说前端组件化
home page banner

说说前端组件化

2015-07-25 14:38:26

昨天看了Ant Design这个项目, 突然心生很多感触, 加上我这最近一个月来做的一点事情, 觉得很有必要记录下我的些许见解。

什么是前端组件化呢。简单来说, 组件化的意义体现在可重用性上, 当我们写下一个表单时, 我们会觉得事情还好, 因为还没有产生重复性的代码, 但是当我们要不停地用html写下一个个的form时, 作为程序员估计就得疯了。

当然如果写完就可以收工那也就算了, 但通常并不是这么一回事。除了html要重复, 验证逻辑也要重复, 表单提交代码也要大量重复。DRY说的就是这么回事, 当大量重复时, 不仅不易于维护(假想之后我们发现有某个验证逻辑写错了, 那修改起来就蛋疼了, 当然有一些库已经做了一些封装的工作, 暂且不放在讨论范围内), 还造成程序员的大量时间浪费, 要解决这个重复性的问题, 我们就要分离可变的部分, 将不变的部分抽象成某种准则, extraction便是这样一种手段。

我们考虑一种最简单的情况——登录, 作为开发人员, 我们可能希望提供的最简信息就是:

{
  "url": "login",
  "method": "POST",
  "inputs": [
    {
      "name": "name",
      "length": "8-10"
    },
    {
      "name": "password",
      "length": 6
    }
  ]
}

这当然是一种简化, 但要表达的意图很明显, 我只表达我需要的, 至于其他东西我就不管了, 比如你怎么展示, 怎么提交(ajax还是submit), 这些都属于准则那一块的内容了.

这样想来, 我们就会设计这样一份准则的实现(当然了, 我只是表达这样一个意思):

define('form', ['jquery'], function (require, exports, module) {
    var $ = require('jquery');

    var Form = function(config) {
        this.config = config;
    };

    Form.prototype.render = function () {
        var input = $("<input>").attr('type', ...);
        // ...
    };

    Form.prototype.mount = function (node) {
        // ...
        $(node).append(this.form);
    };
};

//// Usage
var Form = require('form');
new Form({/ config /}).mount($('#main'));

上面的代码肯定是没法工作的, 只是为了表达这样一种理念, 当我们在在写一个界面时, 我们看到的不再是最底层的html, 因为写html这种工作没必要重复, 我们看到就是一个表单, 而且是一个提供一点配置信息立马拿来就可以用的东西。这就是组件化的最简单解释.

现在我们再来反思另外一个问题, 关于模板的使用, 比如我们有一个用户管理页面, 要展示这些用户信息, 我们很有可能会采用下面方式.

define('UserManagement', ['lib/handlebars', 'api/users'], function (require, exports, module) {
    var Handlebars = require('lib/handlebars');

    var API = require('api/users');

    var users = [];

    // 模块入口
    exports.run = function () {
        API.getUserList().then(function(data) {
            users = data;
        }).then(function() {
            // 模板渲染
            $('#main').html(Handlebars.compile(/* view template, see below */)(users));
        });
    };
});

下面是一份简单地模板文件:

<table>
    <thead>
        <th>姓名</th>
        <th>操作</th>
    </thead>
    <tbody>
    {{#each users}}
        <tr>
            <td>{{name}}</td>
            <td>...</td>
        </tr>
    {{/each}}
    </tbody>
</table>

这样的工作很好, 但仍然没有达到足够好, 首先我们发现在模板中的逻辑与代码中的逻辑分开来了, 更直观的解释是这两个东西天生就该在一块, 为什么要分成两个文件呢. 借由上面的例子, 我们也可以做一个表单组件, 也能部分解决问题, 这里就不细说了.

不过对于有代码洁癖的人来说, 至少对于我来说, 我觉得非常很不爽, 为什么呢, 在js模块代码里大量操纵DOM本身看起来就像是有点问题. 光是想想Form中的render方法实现, 就略略感到头晕脑胀.

Form.prototype.render = function () {
    // Form
    var form = $('<form>').attr('method', this.config.method); //...
    this.config.inputs.forEach(function (ipcfg) {
        // Input
        var input = $('<input>').attr('name', ...).attr('type', ..) //...
        // What's the fuck...
    });
};

可以看到, 纯html的语言张力实在太弱。不过, 幸好我们有React. 在React的理念中, 我们不再直接面对原生的DOM, 而是一个抽象DOM树. 我从Ant-Design中摘下这段代码:

var dataSource = {
    url: '/api/users',
    resolve: function(result) {
        return result.data;
    },
    getPagination: function(result) {}
    getParams: function(pagination, filters, sorter) {}
};
var columns = [
    title: "姓名",
    dataIndex: "name",
    render: function(text) {
        return <a href="javascript:;">{text}</a>
    }
];
<Table dataSource={dataSource} columns={columns} />

这便是我目前的理解了, 关于React还在学习中, 就目前感觉来说, 这种前端组件化的理念还是很吸引人的, 目前这种理念也正在App端进行尝试. 也许不久的将来, 我们写前端页面就会越来越简约了.

嗯, 就先写这么些了.

comments powered by Disqus
Free Web Hosting