一个 “普通” 项目的 “另类” 复盘

2015. 5. 22

@CSS魔法

如何在一个无聊的项目中找乐子……

2015. 5. 22

@CSS魔法

一个 “普通” 项目的 “另类” 复盘

一个 “无聊” 的项目

  • 传统项目

  • 无法拒绝

  • 无追求

  • 幸好:不着急

(45 分钟)

  • Part 1

  • Part 2

  • Part 3

Part 1

给自己打 Log

  • 精确统计

  • 分析

  • 评估成本

简单的统计

  • 实际工期:2014.11.14 ~ 2015.01.24

  • 总耗时: 112.8 小时

  • 折算工作日: 18 ~ 28 天

  • 成本估算:……

各环节耗时占比

兼容 IE 8 的代价

Part 2

各种实践

  • 前端架构

  • 前后端配合

  • 开发环境

/
├── html/
│
├── static/
│   ├── _building/
│   ├── img/
│   ├── pic/
│   ├── css/
│   │   ├── dist/
│   │   └── src/
│   ├── js/
│   │   ├── dist/
│   │   └── src/
│   └── json/
│
└── index.html

目录结构概览

  • 纵深 vs 扁平

  • 只占用两个一级目录

  • 易整合

前后端配合

  • 远程协作:版本控制 + 在线演示

  • 后端架构:LAMP

  • 整合方式:静态页面 + “套程序”(后端整合)

“静态页面”

/
├── html/
│   ├── _component/
│   │   ├── header.php
│   │   ├── footer.php
│   │   ├── news-detail.php
│   │   └── ...
│   ├── _env.php
│   ├── home.php
│   ├── news.php
│   ├── member.php
│   └── ...
│
├── static/
│   └── ...
└── index.html
首页(未登录):/html/home.php
首页(已登录):/html/home.php?login=1
<?php require_once('_env.php'); ?>
<?php $login = isset($_GET['login']) ? $_GET['login'] : '0'; ?>
<!DOCTYPE html>
<html lang="zh-cmn-Hans-CN">
<head>
<meta charset="UTF-8">
<title>首页 - <?= $login === '0' ? '未登录' : '已登录' ?></title>
<?php require_once('component/head.php'); ?>
<link rel="stylesheet" href="<?= $pathCSS ?>/home.css">
</head>

<body>
<?php
    $navItem = 'home';
    require_once('component/header.php');
    require_once('component/header-nav.php');
?>
<?php if ($login == '0') {
    require_once('component/dialog-login.php');
} ?>
<div id="main-body">
    ...
</div>
<?php require_once('component/footer.php'); ?>
<?php require_once('component/side-toolbar.php'); ?>

<script src="<?= $pathJS ?>/global.js"></script>
<script src="<?= $pathJS ?>/home.js"></script>
</body>
</html>

版本控制

  • Git / GitHub

版本控制

  • Git / GitHub

  • SVN

在线演示

  • SVN + PHP => SAE

  • PHP 5.3

$gradeTypes = [
	'1' => '初级',
	'2' => '中级',
	'3' => '高级',
];
$gradeTypes = array(
	'1' => '初级',
	'2' => '中级',
	'3' => '高级',
);

前端源码组织

  • CSS:Stylus

  • JS:JS  模块化(CommonJS / Sea.js / ...)

前端源码组织

  • CSS:Stylus

  • JS:JS  模块化(CommonJS / Sea.js / ...)

  • JS:JS  Combo

前端目录组织

└── static/
    │
    ├── css/
    │   ├── dist/
    │   └── src/
    │       ├── basic/
    │       ├── module/
    │       │   ├── _component/
    │       │   │   ├── header.styl
    │       │   │   ├── footer.styl
    │       │   │   ├── news-detail.styl
    │       │   │   └── ...
    │       │   ├── home/
    │       │   ├── news/
    │       │   ├── member/
    │       │   ├── .../
    │       │   ├── _index.styl
    │       │   └── ...
    │       ├── home.styl
    │       ├── news.styl
    │       ├── member.styl
    │       └── ...
    │
    └── js/
        ├── dist/
        └── src/
            ├── _common/
            ├── home/
            ├── news/
            ├── member/
            └── .../
└── html/
    │
    ├── _component/
    │   ├── header.php
    │   ├── footer.php
    │   ├── news-detail.php
    │   └── ...
    │
    ├── _env.php
    ├── home.php
    ├── news.php
    ├── member.php
    └── ...

开发环境

(略)

开发环境

Booster 在这一刻灵魂附体!

/
├── html/
├── static/
│   ├── _building/
│   │   ├── bower_components/
│   │   ├── node_modules/
│   │   ├── util/
│   │   │   ├── config-js-combo.js
│   │   │   ├── config-nproxy.js
│   │   │   ├── config-stylus.js
│   │   │   └── stylus.js
│   │   ├── bower.json
│   │   ├── package.json
│   │   └── gulpfile.js
│   └── ...
└── index.html

开发阶段

 

 

本地 Stylus

编译服务

 

本地

代理

 

Stylus

CSS

JS

开发者

(资源替换)

(文件合并)

开发阶段

部署阶段

 

 

本地 Stylus

编译服务

 

本地

代理

 

Stylus

CSS

JS

Stylus

JS

Gulp

构建任务

SAE/服务器

开发者

协作者/用户

CSS

JS

(资源替换)

(文件合并)

优点

  • 完全按需

  • “快”

缺点

  • 功能有限:脚本的编译环节待开发

  • 逻辑重复:无法复用构建任务

Part 3

Stylus 练级

  • 编码规范

  • 寻找最佳实践

  • 验证各种理论

编码规范

全局变量与局部变量

// 定义全局变量
$line-height = 1.5

btn()
    // 定义局部变量
    $-color = #a1c444
    $-color-shadow = #6ea928

    // 使用全局变量
    line-height $line-height

    // 使用局部变量
    background-color $-color
    box-shadow 0 1px 0 0 $-color-shadow
    ...

公开 Mixin 与非公开 Mixin

// 定义非公开 mixin
-icon-size($size)
    width $size
    height $size

// 定义公开 mixin
icon-20()
    // 使用非公开 mixin
    -icon-size(20)
icon-30()
    // 使用非公开 mixin
    -icon-size(30)

样式模块的内容

“样式模块” 是什么?

页面

“样式入口”​

<!DOCTYPE html>
<html lang="zh-cmn-Hans-CN">
<head>
...
<link rel="stylesheet" href="home.css">
...
</head>

<body>
...
</body>
</html>
// home.styl

@import './module/_component/header'
@import './module/_component/breadcrumb'
@import './module/_component/footer'
@import './module/btn'

...

“样式模块​”

// btn.styl

.btn
    ...
    &.success
        ...
// footer.styl

#footer
    ...
// breadcrumb.styl

#breadcrumb
    ...
// header.styl

#header
    ...
// header.styl

#header

    .logo-box
        float left
        width 250px
        a.logo
            float left
            size 110px 80px
        p.slogan
            float left
            size 130px 80px

    .hotline-box
        float right
        i.hotline
            margin-right 10px
            size 20px
// news-box.styl

news-box()

    > .header
        headline()
        a
            font-size 12px

    ul
        li
            margin-bottom 10px
            margin-left 15px
            a
                display block
            i.icon
                display inline-block
            span.time
                color #979797

A: 代码块

B: Mixin

两种形式如何选择?

// header.styl

#header

    .logo-box
        float left
        width 250px
        a.logo
            float left
            size 110px 80px
        p.slogan
            float left
            size 130px 80px

    .hotline-box
        float right
        i.hotline
            margin-right 10px
            size 20px
// header.styl

header()

    .logo-box
        float left
        width 250px
        a.logo
            float left
            size 110px 80px
        p.slogan
            float left
            size 130px 80px

    .hotline-box
        float right
        i.hotline
            margin-right 10px
            size 20px

最终决定:统一为 Mixin 的方式

@import './module/_component/header'
@import './module/_component/breadcrumb'
@import './module/_component/footer'
@import './module/btn'








#login-form
    .btn
        btn()

#feedback-form
    .submit > button
        btn()
@import './module/_component/header'
@import './module/_component/breadcrumb'
@import './module/_component/footer'
@import './module/btn'

#header
    header()
#breadcrumb
    breadcrumb()
#footer
    footer()

#login-form
    .btn
        btn()

#feedback-form
    .submit > button
        btn()

在页面样式入口文件中调用

@import './module/_component/header'
@import './module/_component/breadcrumb'
@import './module/_component/footer'


#header
    header()
#breadcrumb
    breadcrumb()
#footer
    footer()

#login-form
    .btn
        @import './module/btn'
        btn()

#feedback-form
    .submit > button
        @import './module/btn'
        btn()

样式库暴露的接口

  • Mixin

(“样式库” 即类似 Bootstrap 这样的 UI 元素样式集合。)

Mixin

// icon.styl

-icon-basic()
    display block
    margin-left auto
    margin-right auto
-icon-type($type)
    ...

.icon
    -icon-basic()
    &.icon-error
        -icon-type('error')
// icon.styl

-icon-basic()
    display block
    margin-left auto
    margin-right auto
-icon-type($type)
    ...

icon-error()
    -icon-basic()
    -icon-type('error')
// home.styl

.msg
    > i
        icon-error()
<!-- home.php -->

<div class="msg">
    <i></i>
    <p>出错了……</p>
</div>
<!-- home.php -->

<div class="msg">
    <i class="icon icon-error"></i>
    <p>出错了……</p>
</div>

MIXIN 接口的缺点

  • HTML 干净

  • 生成的 CSS 代码更少

  • 不需要多个接口的组合

“文艺项目” 必选!

MIXIN 接口的优点

  • 多一步:胶合代码

但是,在 “普通项目” 中:

  • 多一步:胶合代码

  • HTML 干净

  • 生成的 CSS 代码更少

  • 不需要多个接口的组合

完全无所谓

不明显

文档+习惯

“普通项目” 还是……

麻烦

Q & A

THANK YOU!

一个 “普通” 项目的 “另类” 复盘

By CSS魔法

一个 “普通” 项目的 “另类” 复盘

如何在一个无聊的项目中找乐子……

  • 967