index模块重构

index模块包含V业务3个站点的首页和频道页,以下简称a站b站c站

背景

经过两年的业务迭代,index模块又到了难以维护的状态,通用组件复杂度高、代码质量低,给线上业务带来很大风险,急需梳理改造。

历史

模块重构需要从回顾历史开始,通过历史演变过程分析业务需求、总结好与不好的地方、发现研发的根本需求。

目录规范是代码的整体组织结构,能直观的反映出业务演变过程以及开发过程的思考。

0. 最开始,a站首页

基于fis1开发

index
├── page                          // 页面模板
│   └── index
│       └── index.tpl
├── static                        // 静态资源,非组件资源目录
│   ├── index
│   │   └── index
│   │       └── index.css
│   └── ui
│       └── ...
└── widget                        // 组件
    └── index
        └── ...

1. 增加b站首页

index_b
├── page
│   └── index_b
│       └── index.tpl
├── static
│   ├── index_b
│   │   └── index
│   │       └── index.css
│   └── ui
│       └── ...
└── widget
    └── index_b
        └── ...

2. 合并到一个模块

b站首页和a站首页有很多需求需要同步

index
├── page
│   └── index
│       ├── a_index.tpl
│       └── b_index.tpl
├── static
│   ├── index
│   │   ├── a_index
│   │   │   └── a_index.css
│   │   └── b_index
│   │       └── b_index.css
│   └── ui
│       └── ...
└── widget
    └── index
        └── ...

3. 再拆开

a站首页的单独需求变得频繁,开发上线出现多次影响b站首页的线上问题

index
├── page
│   └── index
│       └── index.tpl
├── static
│   ├── index
│   │   └── index
│   │       └── index.css
│   └── ui
│       └── ...
└── widget
    └── index
        └── ...
index_b
├── page
│   └── index_b
│       └── index.tpl
├── static
│   ├── index_b
│   │   └── index
│   │       └── index.css
│   └── ui
│       └── ...
└── widget
    └── index_b
        └── ...

4. fis-plus重构

index
├── page                          // 页面模板
|   ├── data                      // 数据模板,用于数据拼装
|   │   ├── a_home.tpl
|   │   └── b_home.tpl
|   ├── a_home.tpl
|   └── b_home.tpl
├── static                        // 静态资源,非组件资源目录
│   ├── page
│   │   ├── a_home
│   │   │   └── home.less
│   │   └── b_home
│   │       └── home.less
│   └── ui
│       └── ...
└── widget                        // 组件
    ├── global                    // a站、b站公共组件
    │   └── ...
    ├── a                         // a站业务组件
    │   └── ...
    └── b                         // b站业务组件
        └── ...

5. 增加多个频道页

频道页之间有相似的逻辑,a站b站也有相似的逻辑,怎么抽layout模板?

index
├── page
|   ├── data
|   │   ├── a_home.tpl
|   │   ├── a_comic.tpl
|   │   ├── a_movie.tpl
|   │   ├── a_show.tpl
|   │   ├── a_tv.tpl
|   │   ├── b_home.tpl
|   │   ├── b_comic.tpl
|   │   ├── b_movie.tpl
|   │   ├── b_show.tpl
|   │   ├── b_tv.tpl
|   │   └── home.tpl
│   ├── a_layout.tpl
│   ├── b_layout.tpl
│   ├── home.tpl
│   ├── comic.tpl
│   ├── movie.tpl
│   ├── show.tpl
│   └── tv.tpl
├── static
│   ├── page
│   │   ├── a_home.less
│   │   └── b_home.less
│   └── ui
│       └── ...
└── widget
    ├── global
    │   └── ...
    ├── a
    │   └── ...
    └── b
        └── ...

6. 引入FIS静态资源合并系统

基于组件访问的在线统计生成静态资源map表,达到最优的合并打包策略,不支持一个模块产出两套代码,部署上又合并到了一个目录。

7. 后来

因为各种业务需求,又增加了一些新的模板

index
├── page
|   ├── data
|   │   ├── a_home.tpl
|   │   ├── a_comic.tpl
|   │   ├── a_movie.tpl
|   │   ├── a_show.tpl
|   │   ├── a_tv.tpl
|   │   ├── a_customize_home.tpl
|   │   ├── b_home.tpl
|   │   ├── b_comic.tpl
|   │   ├── b_movie.tpl
|   │   ├── b_show.tpl
|   │   ├── b_show_mango.tpl
|   │   ├── b_tv.tpl
|   │   └── home.tpl
│   ├── a_layout.tpl
│   ├── b_layout.tpl
│   ├── c_layout.tpl
│   ├── n_layout.tpl
│   ├── layout_newhome.tpl
│   ├── home_new.tpl
│   ├── home.tpl
│   ├── a_customize_home.tpl
│   ├── home.tpl
│   ├── comic.tpl
│   ├── movie.tpl
│   ├── show.tpl
│   ├── show_mango.tpl
│   └── tv.tpl
├── static
│   ├── page
│   │   ├── a_home.less
│   │   └── b_home.less
│   └── ui
│       └── ...
└── widget
    ├── global
    │   └── ...
    ├── hao123
    │   └── ...
    └── video
        └── ...

模板越来越多,通用组件越来越复杂,线上部署未作隔离,代码未形成良好的规范,业务迭代风险极大。

重新思考,我们的根本需求是什么?

总体设计

根本需求

业务拆分

按业务划分子模块

站点公共模块a站首页、频道页b站首页、频道页c站首页、频道页
commonaindexbindexcindex

资源复用

伪复用

降低复杂度、提升维护效率是基本要求,在这个前提下再考虑代码复用

怎么定义复用范围

有哪些资源?

资源分类说明
JS模块独立的算法和数据单元
CSS模块独立的功能性样式单元
UI组件独立的可视/交互功能单元
页面UI组件的容器
应用整体项目或站点

从业务需求的角度对资源进行分类

站点公共模块a站首页、频道页b站首页、频道页c站首页、频道页三站公共组件
commonaindexbindexcindexindexcommon

其他类似页面和首页、频道页之间是否需要复用?

如果从业务需求上看相互独立,产品迭代上没有同步需求,在资源划分上应该互不关联。

UI上有相似或相同的区块,应该怎么做?

再次强调,按业务对资源作合理的分类才是重点

业务模块的划分

业务分类子系统
站点通用模块common
首页、频道页aindex、bindex、cindex、indexcommon
筛选页category
详情页detail

数据结构

数据是一切的基础,HTML结构 = 数据 + 模板逻辑,交互功能 = 数据 + JS代码逻辑,数据结构会影响模板逻辑和JS逻辑的复杂度。

V业务的问题

服务端给到的数据结构过于原始,与页面区块缺少清晰的映射关系。

服务端给到的区块数据

$index_show => [
    "num" => 10,
    "items" => [
        [
            "title" => "",
            "url"   => ""
            // ...
        ]
        // ...
    ]
]

一个完整区块,实际需要的数据结构是

$block => [
    "id"     => "xxx",
    "title"  => "标题",
    "more"   => "http://xxx.com/aaa/"
    "items" => [
        [
            "title" => "",
            "url"   => ""
            // ...
        ]
        // ...
    ]
]

$blocks = [
    "id"     => "xxx",
    "title"  => "标题",
    "more"   => "http://xxx.com/aaa/",
    "blocks" => [
        [
            "id"     => "xxx",
            "title"  => "标题",
            "more"   => "http://xxx.com/bbb/",
            "items" => [
                [
                    "title" => "",
                    "url"   => ""
                    // ...
                )
                // ...
            ]
        ],
        [
            "id"     => "xxx",
            "title"  => "标题",
            "more"   => "http://xxx.com/ccc/",
            "items" => [
                [
                    "title" => "",
                    "url"   => ""
                    // ...
                ]
                // ...
            ]
        ]
        // ...
    )
)

受限于内部内容管理系统

怎么改进

模板逻辑如果直接面向原始数据,需要作各种额外的数据处理工作,增加了模板的复杂度,中间需要加一层专门用来处理数据。

现阶段的做法是,把这些数据处理层分离到了一个单独的模板文件中,模板中通过include引入。

<%include file="aindex/page/data/home.tpl"%>

数据结构约定

基本原则

  1. 数据结构的设计一定要有语义,不要根据UI来确定结构。
  2. 在原始数据结构的础上作扩展。
  3. 确定各类基础区块的数据结构,定制化区块数据根据具体业务设计。

服务端原始区块

$mis_block_name = [
    "num"   => 10,
    "items" => [
        [
            "id"          => "",   // id
            "title"       => "",   // 标题
            "url"         => "",   // 页面链接
            "sub_title"   => ""    // 副标题
            "img"         => ""   // 横版图
            // ...
        ]
        // 更多视频,格式同上
    ]
]

组装后的普通区块

// 区块名采用驼峰命名法
$block = [
    "id"     => "",                       // 必填,区块id
    "title"  => "",                       // 可选,区块标题
    "stitle" => "",                       // 可选,副标题
    "more"   => "",                       // 可选,更多链接
    "icon"   => "",                       // 可选,标题图标
    "links"  => $mis_block_a.items,       // 可选,链接列表
    "items"  => $mis_block_b.items,       // 必填,内容列表
    "iframe" => [                         // 可选,插入外部页面
        "url"    => "",                   // 必填,页面url
        "height" => ""                    // 必填,页面高度
    ],
    "config" => [                         // 必填,区块配置
        "limit"  => 10                    // 可选,条数限制
    ]
)

Tab区块

// 区块名采用驼峰命名法
$block = [
    "id"     => "",      // 必填,区块id
    "title"  => "",      // 必填,区块标题
    "stitle" => "",      // 可选,副标题
    "more"   => "",      // 可选,更多链接
    "icon"   => "",      // 可选,标题图标,通常放于标题左侧
    "blocks" => [        // 区块列表
        [
            "id"     => "",                       // 必填,区块id
            "title"  => "",                       // 可选,区块标题
            "more"   => "",                       // 可选,更多链接
            "items"  => $mis_block_b.items,       // 必填,内容列表
            "iframe" => [                         // 可选,插入外部页面
                "url"    => "",                   // 必填,页面url
                "height" => ""                    // 必填,页面高度
            ],
            "config" => [                         // 必填,区块配置
                "limit"  => 10                    // 可选,条数限制
            ]
        )
        // 更多区块,格式同上
    ]
]

禁止写入区块数据的字段

  1. UI层面的描述,如区块是否通栏,这种配置请直接在widget引用时传入
  2. 引用哪个widget,如tab区块,PM可能提出对某个tab内容差异化展现的需求,针对这种需求,首先要思考的是UI差异化背后的语义是什么,tab区块表示某一类数据的集合,分组展示,如果其中某组数据必须要差异化展示,必定有很强的语义支撑,请根据数据特征来满足差异化。widget的引用属于页面组装的逻辑,不要在数据层面描述。

组件开发

例子:内容列表组件A

频道页字段:横版图字段:坚版图字段:标题字段:一句话简介字段:更新至
电影--
电视剧
综艺-
动漫-
区块区块标题区块内容tablinksmore
可选,无值则不显示hd部分a模板 or b模板 or iframe可选可选可选

内部逻辑的适配

创建:皮成,2016-07-01