# fis-didi

[![NPM](https://nodei.co/npm/fis-didi.png?downloads=true)](https://nodei.co/npm/fis-didi/)

基于fis且适用于滴滴前端集成解决方案

## 为什么使用fis-didi

- 从开发流程&效率层面：从开发、测试到上线全面规范前端开发流程,提升开发效率，让fe专注开发。
- 从生产力层面：基于git提供模块按版本发布、安装，让前端组件可以积累、共享，减少生产力浪费。
- 从页面性能方面：自动CSS、JS/Image、font压缩、合并、base64、Sprite等让页面速度更快；模块化开发让页面可维护性更好。

## 关键词

weinre调试、无线端console调试、压缩、打包、md5戳、加域名、csssprite、文件监听、自动刷新、测试、发布目录、自动上传、内置服务器、数据模拟、请求模拟、获取组件、资源定位、资源内嵌、依赖声明、模块化规范、模块化框架、模块化规范、静态资源表、前端模块化、组件化规范、组件仓库、组件生态、插件扩展、coffee|less|stylus|sass编译...



## 快速开始

### 安装didi
```
npm i -g fis-didi
```
MAC遇到权限问题？

删除缓存目录，以后不要用`sudo`来执行`npm install`啦
```
sudo rm -rf `npm config get cache`
```

查看安装是否成功
```bash
didi -v
```
![Alt text](https://cloud.githubusercontent.com/assets/3262834/9603511/a044d5ac-50e3-11e5-86ee-25085ac2f4ed.png)
### 开始新项目

```bash
didi init project
```
![Alt text](https://cloud.githubusercontent.com/assets/3262834/9603448/73b70b68-50e3-11e5-97b2-4caadec2fad0.png)

```
├── component.json          // 目前主要作用记录项目使用了哪些模块（由此命令安装：didi install <namespace>/<component name>）
├── fis-conf.js             // fis-didi的配置文件
├── css                       // 项目的基础css文件
│   └── lib.css             // 基础HTML文件
├── img                     // 基础图片文件
│   └── didi.png
├── lib                     // 基础JS文件，主意里面文件都是 非模块化文件
│   ├── lib.js              // 负责将其他文件内嵌进来
│   ├── mod.js              // 模块加载器，配合fis-didi的发布来实现模块化
│   └── zepto.js            // 号称移动端的jquery
├── page                    // 页面根目录
│   └── pop                 // 示例页面文件夹
│       ├── animate.css     // 示例页面分出来的单个CSS
│       ├── main.css        // 示例页面的入口CSS
│       ├── main.html       // 示例页面的入口HTML
│       └── main.js
├── test                    // 模拟数据目录，特别注意的是其内部的结构和page文件夹一致
│   └── pop                 // 示例页面pop的模拟数据文件夹
│       └── main.php        // 示例页面pop的模拟数据
└── components              // 项目工程模块的跟目录
    ├── first               // first模块文件夹
    │   ├── first.css       // first模块的入口css，文件名须与模块名(first)相同 
    │   ├── first.html      // first模块的入口HTML，文件名须与模块名(first)相同
    │   └── first.js        // first模块的入口JS，文件名须与模块名(first)相同，可通过 require('first') 来获取模块
    ├── second
    │   ├── second.css
    │   ├── second.html
    │   └── second.js
    └── third
        ├── third.css
        ├── third.html
        └── third.js
```

### 初始化本地预览服务器

本地预览服务器依赖`php-cgi`和`JDK`

- [安装php-cgi](http://oak.baidu.com/fis-plus/document.html#%E5%AE%89%E8%A3%85%20php-cgi)
- [安装JAVA](http://oak.baidu.com/fis-plus/document.html#%E5%AE%89%E8%A3%85%20Java)

[有同学反馈MAC的JDK下载很慢？](./jdk-8u73-macosx-x64.dmg)

```
didi server init
```

### 启动本地预览服务器

```bash
didi server start
```

### 发布项目到本地预览服务器

```
didi release 
```

打开浏览器访问 http://127.0.0.1:8080/pop/ 即可预览项目效果。



### 模拟假数据


#### [模拟php下发的假数据](#mockdata)

#### 模拟页面中请求的数据

例如，我们滴滴打车行程中页面，需要不断去轮询当前页面状态；
我们需要ajax请求`/beatles/webapi/passenger/order/getstatus?status=1`，
这时候，我想在本地测试模拟一下假数据。

- 一、 项目根目录下新建文件`rewrite.conf`
- 二、项目根目录下新建文件夹`mock`，新建文件`mock/status.php`，并且在该文件输入

```php
<?php
//模拟自己写的假数据，也可以其他方式获取
$data = <<<json
{
    "status": "123"
}
json;
$data = json_decode($data, true);
echo json_encode($data);
```

- 三、打开`rewrite.conf`，加入以下代码。

```bash
rewrite \/beatles\/webapi\/passenger\/order\/getstatus\?.* mock/status.php
```
- 四、 打开 `http://127.0.0.1:8080/beatles/webapi/passenger/order/getstatus?status=1` 可以看到由`mock/status.php` 输出的数据啦。

```
{
    status: "123"
}
```
而且按照我们的规范的文件不会发布到线上哦！！


在项目目录下通过配置文件 rewrite.conf 文件进行配置，配置方式是：

```
每一行被翻译成一条匹配规则，自上而下的匹配。
匹配类型 (空格) 匹配url正则 (空格) 命中正则后的目的文件路径或者url

rewrite ： 匹配规则后使用该文件输出的结果
redirect ： 匹配规则后302重定向到另一个url

rewrite ^\/news\?.*tn\=[a-zA-Z0-9]+.* app/data/news.php
redirect ^\/index\?.* /home/page/index
```



## [发布]功能介绍

### 开启[weinre](http://people.apache.org/~pmuellr/weinre/docs/latest/)调试功能

```
didi release -W
```
![Alt text](https://cloud.githubusercontent.com/assets/3262834/9603521/bc427732-50e3-11e5-8287-45cbe329ea7f.png)

### 代码压缩

```
didi release -o
```
1. 所有的JS/CSS代码都被压缩
2. 所有的被引用的`ttf`文件自动压缩：根据CSS文件样式所匹配的DOM元素，自动抽取该DOM元素里面的文字，产生缩减版本字体文件。

例如以下 文件夹结构

```
page/pop
├── main.css
├── main.html
├── xiaofeng.ttf
```

`page/pop/main.html`里面有以下代码片段

```html
<div class="didi_font">
    bealtes-fe
    <p>
        遇见美好
    </p>
</div>
```

`page/pop/main.css`里面有以下代码片段

```css
/*声明 WebFont*/
@font-face {
  font-family: 'xiaofeng';
  src: url('xiaofeng.eot');
  src:
    url('xiaofeng.eot?#fis-didi') format('embedded-opentype'),
    url('xiaofeng.woff') format('woff'),
    url('xiaofeng.ttf') format('truetype'),
    url('xiaofeng.svg') format('svg');
  font-weight: normal;
  font-style: normal;
}

/*使用选择器指定字体*/
.didi_font{
    font-family: 'xiaofeng';
}
```

将会自动新增`woff/svg/eot`等文件，同时他们都是抽取了`beatles-fe遇见美好`等文字的缩减版本哦

分分钟3MB变3KB



### 代码合并

```
didi release -p
```

按照配置规则自动合并代码，并且自动使用合并后的代码包。

### 代码监听修改自动发布

```bash
#修改即编译
didi release -w
#修改即编译并且自动刷新浏览器
didi release -w -L
```

### 更多[发布]功能功能请查看帮助

```
didi release -h
```
```bash
  Usage: release [options]

  Options:

    -h, --help             output usage information
    -d, --dest <names>     release output destination
    -m, --md5 [level]      md5 release option
    -D, --domains          add domain name
    -l, --lint             with lint
    -t, --test             with unit testing
    -o, --optimize         with optimizing
    -p, --pack             with package
    -w, --watch            monitor the changes of project
    -L, --live             automatically reload your browser
    -C, --console          console.log tool within phone browser
    -W, --weinre [client]  use weinre debugger for web pages
    -c, --clean            clean compile cache
    -r, --root <path>      set project root
    -f, --file <filename>  set fis-conf file
    -u, --unique           use unique compile caching
    --verbose              enable verbose output
```

## [预览]功能介绍
```
 Usage: preview <command> [options]

  Commands:

    open                   open pages preview in browser
    create                 create pages preview and save to server root

  Options:

    -h, --help  output usage information
```
自动读取本地server中的页面列表，并使用phantomjs生成页面预览，适合项目交接中使用，有利于把控整个仓库的所有页面。

## 语言实现功能介绍

### `__inline`语法
#### 预编译的前端模板

项目中任意的`tmpl`扩展名的文件，里面书写了简易模板[underscore.js的template](http://underscorejs.org/#template)的语法代码。
```
<!--a.tmpl---->
<div class="layer_box_card">
    <img src="<%=img_src%>"/>
</div>
```
项目中某个JS文件可以这样使用它
```
//main.js
var a_tmpl = __inline('./a.tmpl');
var a_tmpl_content = a_tmpl({
    img_src: 'http://xxx.png'
});
```

在浏览器的效果是这个样子的，在`main.js`已经将`a.tmpl`**预编译**成一个函数。
```javascript
var a_tmpl = function(obj){
    var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
    with(obj||{}){
    __p+='<div style="display:none" class="layer_box_card">\n\t<img src="'+
    ((__t=(img_src))==null?'':__t)+
    '"/>\n</div>';
    }
    return __p;
};

var a_tmpl_content = a_tmpl({
    img_src: 'http://xxx.png'
});

/*
* a_tmpl_content等于
* <div style="display:none" class="layer_box_card">
*   <img src="http://xxx.png"/>
* </div>
*
*/
```
细心的读者注意到这里的`__inline`函数，这个函数可以在任意的JS块里面使用，它的作用顾名思义是将一个文件里面的代码`嵌入`进来。
而嵌入`.tmpl`扩展名的文件更是可以预编译成为一个JS文件。

聪明的读者可能会想到能不能将CSS、HTML嵌入进来呢？答案是肯定的！这是`FIS`实现的基础功能。[猛击看文档](http://fex-team.github.io/fis-site/docs/more/fis-standard.html#内容嵌入)


### 模块化开发

在项目中任何一个JS都可以被`require`获取，就像NODEJS一样。
**同时页面中会自动加载依赖的模块文件**

#### require可以接受3种类型
- 相对路径
以点`.`为起点
```
var a = require('./a.js');
```

- 绝对路径
以项目`/`根目录（fis-conf.js所在目录)为起点
```
var a = require('/page/pop/a.js')
```

- component模块名称
```
var dd = require('dd');
```

### 模块生态

如上所述，`didi`的component模块可以有2种来源

#### 工程模块
 
例如工程模块a，被放在项目的`/compoents/`目录下


```
├── components
│   ├── a
│   │   ├── README.md
│   │   ├── a.css
│   │   ├── a.js
│   │   └── component.json
```

`/components/a/a.js`

```javascript
exports.name = '我是a模块';
exports.sayName = function(){
    console.log(exports.name);
}
```

#### 生态模块

将会被安装在项目`/component_modules/`目录下

```bash
didi install didi-component/dd
```

```
├── component_modules
│   └── didi-component-dd
│       ├── component.json
│       └── dd.js
```

可以使用`didi install`命令安装任何一个发布在[github](https://github.com)或者滴滴[GitLab](https://git.xiaojukeji.com)平台上，且符合component规范的模块包。

以component为规范的groups
- xiaojukeji GitLab: https://git.xiaojukeji.com/didi-component
- github：https://github.com/component

#### 创建工程模块

通过上面的命令，可以一键创建`a`模块

```bash
didi init component
```
该模块将会自动放在`/comopnents/a`文件夹内
该模块的基本形态
```bash
├── README.md        //  模块的说明
├── component.json   //  模块的表单
├── a.css            //  模块的入口CSS
├── a.js             //  模块的入口JS
└── dialog.js        //  其他JS
```

#### 发布生态模块

等我们把模块开发好了，可以分享给其他人使用，软件开发的最佳实践DRY (Don't Repeat Yourself) 。

我们为`a`模块在[GitLab](https://git.xiaojukeji.com/)创建一个仓库。

上文说过我们可以从[github](https://github.com)或者滴滴[GitLab](https://git.xiaojukeji.com)下载一个模块包，我们可以根据模块任意选择发布到哪一个平台。

将所有的修改提交(`git commit`)之后，推送到远程仓库（git push）。

之后我们就可以发布一个新版本了，正如软件发包一样。

##### 创建一个仓库
可以是任何groups的命名空间(namespace)
例如在我的`zhangnan03`下创建仓库。
```
https://git.xiaojukeji.com/zhangnan03/a.git
```

进入a模块的根目录

```
cd components/a
```

将模块提交到仓库
 
```
git init
git add -A
git remote add origin https://git.xiaojukeji.com/zhangnan03/a.git
git commit -am 'first comment for didi-compnent'
git push -u origin master
```

确认所有修改已经提交且推送到远程仓库。

##### 发布新版本

通过`didi publish -t <version>`命令来发布模块
`-t`是指定版本号
`<version>`是特定版本号
版本号的规则应该是符合`/^\d[0-9\.]+\d$/`规则。
```
didi publish -t 0.0.2
```
你可能看到以下类似输出信息，就代表发布成功啦。
```bash
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
origin  https://git.xiaojukeji.com/zhangnan03/a.git (fetch)
origin  https://git.xiaojukeji.com/zhangnan03/a.git (push)
[master bebcd89] update new tag 0.0.2 use [didi publish]
 1 file changed, 2 insertions(+), 2 deletions(-)
To https://git.xiaojukeji.com/didi-component/ddplayer.git
   31c4f8f..bebcd89  master -> master
To https://git.xiaojukeji.com/didi-component/ddplayer.git
 * [new tag]         0.0.2 -> 0.0.2
```

其他人可以直接使用，以下命令来安装你的dd模块了

```
didi install zhangnan03/dd
```

其他人也可以指定安装某(如0.0.1)版本

```
didi install zhangnan03/dd@0.01
```

然后在JS项目里面如下使用

```
var dd = require('dd');
dd.sayName();
```


#### 工程模块类型

工程模块可以是`JS`、`CSS`、JS+CSS+HTML混合模块。

以下例子我们都使用`a`模块举例。

#####  JS+CSS+HTML

通过`didi init component`命令创建的模块，模块中包含与模块同名的`CSS`+`JS`+`HTML`文件。

```javascript
var a = require('a')
```
当我们使用上面的语法调用时，`/component/a/a.js`和`/components/a/a.cs`被自动加载

然后

HTML文件只能嵌入使用

`/page/pop/main.html`

```
<link rel="import" href="/components/a/a.html?__inline">
```


##### CSS+HTML

如果你不需要JS文件，把`/components/a/a.js`删除了。这样模块变成纯CSS的模块啦。
你可以在HTML文件里面使用这种语法载入

```
<!--
    @require a
-->
```

也可以在你的HTML使用到(依赖链上)JS/CSS文件里面
```javascript
/**
 * @require a
 */
```
HTML文件同样只能同上使用嵌入语法使用。

##### JS+HTML

使用上和JS+CSS+HTML上并没有区别。


#### 生态模块类型

生态模块以模块`component.json`的`main`字段所指文件（JS/CSS文件皆可）为入口。
 
##### JS+CSS
 
`/component_modules/didi-component-dd/dd.js`

`/component_modules/didi-component-dd/dd.css`

`/component_modules/didi-component-dd/component.json`


`/component_modules/didi-component-dd/component.json`main字段的值`dd.js`，被依赖(require)时若存在`同文件夹`且`同名`的CSS也会被依赖，从而自动加载。
```
var dd = require('dd');
```

##### CSS

`/component_modules/didi-component-dd/dd.css`

`/component_modules/didi-component-dd/component.json`

在HTML里面使用

```html
<!--
    @require a
-->
```

也可以你的HTML使用到(依赖链上)JS/CSS文件里面

```js
/**
 * @require a
 */
```
##### HTML+CSS+JS

生态模块不建议使用HTML文件，而使用`tmpl`扩展名的模板文件替代。
 




## 页面开发

### 一键创建页面

进入项目根(fis-conf.js所在)目录

```
cd <project_root>
```
```
didi init page
```
创建`newpage`页面

![Alt text](https://cloud.githubusercontent.com/assets/3262834/9603625/7ca8a352-50e4-11e5-9618-3f72e22d22de.png)

```
├── page
│   ├── newpage          //newpage页面文件夹
│   │   ├── main.css    //newpage页面的CSS入口文件
│   │   ├── main.html   //newpage页面的HTML文件
│   │   └── main.js     //newpage页面的JS入口文件
└── test
    ├── newpage
    │   └── main.php
```

<a name="mockdata"></a>
#### 假数据文件夹
首先来介绍`test/newpage/main.php`，这是在本地预览服务的数据模拟的。他负责模拟
`/page/newpage/main.html`的数据。
`main.html`默认是认为使用codeigniter的简单模板语法。

如果想要使用`smarty`语法

```
didi init page --smarty
```

创建好的页面，发布一下(`didi release`)就可以看效果啦

- /page/newpage/main.html ===> http://127.0.0.1:8080/newpage/
- /template/newpage/main.tpl ===> http://127.0.0.1:8080/smarty/newpage/





## 资源依赖的作用

在fis-didi里面







## 总结
恭喜你，看到这里你已经基本掌握了`didi`的基本日常使用啦。

我们学习了

- 快速创建一个项目 
 
```
didi init project 
```

- 快速创建一个页面

```
didi init page
```

- 快速创建一个工程模块

```
didi init component
```

- 发布一个模块到生态圈

```
didi publish 
```

- 安装一个生态模块

```
didi install didi-component/dd
```

- 启动一个本地测试服务

```
didi server start
```

- 将项目发布到本地测试服务目录

```
didi release
```



## 相关链接

- [优雅didi心得-如何快速发布(deploy)到远端测试机](http://fe.in.didialift.com:8088/fe/zhangnan03/receiver/receiver.md.html)

- [优雅didi心得-如何轻松调试客户端Webview](#)

- [优雅didi心得-如何轻松发布的上线](#)

- [优雅didi心得-如何快速的线上调试](#)










