Welcome to the fis-plus wiki!
fis-plus@0.8.2
fisp install
调整为 fisp init
fisp install
被调整用作组件支持,可以方便安装组件node v0.12.0
fisp init -h
命令可查看详细信息.po
文件解析成 .json
的插件,方便国际化支持fisp server init
安装,提供 Smarty 模板的解析,数据模拟,以及 URL 转发功能;fis-plus 的 自动化 / 辅助开发工具 被发布为一套 npm 包,它对环境的要求是:
如果是百度 OCEAN 开发机器 ,直接执行以下脚本即可,所有 node & fis 的环境都被安装了;
bash -c "$( curl http://fis-cloud.bj.bcebos.com/install.sh -k )" && source ~/.bashrc
npm 是 nodejs 的包管理工具。安装 nodejs 后,npm 就自动一起安装了。
由于npm经常被墙,安装fis的时候会出现速度过慢,或者安装不上的问题
。--registry
参数指定仓库。指定国内的npm镜像来解决npm被墙的问题。例如:
npm install <some npm module> -g --registry=镜像
nodejs 安装好后,命令行执行
npm install -g fis-plus
安装好 fis-plus 之后,执行 fisp -v,如果能看到以下信息,则表明安装成功。 如果安装过程中遇到什么问题,可以到 https://github.com/fex-team/fis-plus/issues?state=open 提问题,或者 QQ 询问。
lights 是 fis 提供的包管理工具,托管了 fis 所有资源。是使用 fis 的时候,必不可少的利器。
npm install -g lights
mac 下安装 php-cgi 有多种方法,这里只介绍比较简单的两个方法;
直接下载安装 XAMPP
如果安装了 xcode,那么推荐使用 brew 来安装 php。详细使用方法见 官网,这里只说明如何装 php-cgi;
$ brew install php55 --with-cgi
如果安装提示没有 php55,请用 brew tap homebrew/homebrew-php 后再安装
如上,方法很简单,等安装成功后即可使用;
到 XAMPP 官网 下载 Mac 版本,双击安装;等安装成功后需要把 XAMPP 的 bin 目录设置到 环境变量里面;
使用 zsh
$ echo 'export PATH=/Applications/XAMPP/bin:$PATH' >> ~/.zshrc
$ source ~/.zshrc
使用 bash
$ echo 'export PATH=/Applications/XAMPP/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
windows 安装方法很多,下面介绍最简单的一种。
地址:[http://www.apachefriends.org/zh_cn/index.html]
安装 JAVA 和 php-cgi 是由于 fis-plus 内置了 jetty 服务框架来解析 php 脚本,如果你自己有本地 Web 服务,可以用你自己的, 详见 ;
以下示例都是在命令行下操作的,如果你是 windows 用户,请打命令的时候忽略命令前的 $
,而且请打开cmd
来执行这些操作
已经准备好了一个 fis-plus 的前端项目,只需要经过以下四步,就可以完整运行这个项目,并看到结果。
为了前后端开发分离,来并行开发,fis-plus 提供了一套本地环境模拟的工具,安装并初始化它后就能方便的模拟线上环境了。
$ fisp server init
### 下载 Demo
FIS 的所有示例及其组件都用包管理工具 lights
进行管理,使用 lights
安装 demo。
$ lights install pc-demo
其实 fisp 已经集成了 lights
的客户端。
和上面等值的用法;
$ fisp init pc-demo
$ fisp install pc-demo
如果下载失败,可直接从 GitHub 下载,https://github.com/fex-team/fis-plus-pc-demo
$ cd pc-demo
$ fisp release -r common
$ fisp release -r home
$ fisp server start #启动服务器
启动服务的时候,启动成功后会自动打开浏览器,访问首页,这时候你应该打开 demo 首页,并和下图是一致的。
自此,一个前端项目已经运行起来了。你可以看一下 pc-demo 的源码,其中包含两个模块
模块
这个词会贯穿整个文档,以及整个 fis-plus
的使用。为什么会有模块这种东西?
当前端代码很多时,不得不面临分组件,分页面。为了发布迭代方便,不得不把它们分为不同的子系统。比如用户信息、首页、详情页等等。
模块
就是一个子系统,而在 fis 项目中用 namespace
和fis-conf.js
来区分。每一个模块会有一个配置文件fis-conf.js
,还会取名不同的namespace
。这主要是为了区分模块之前的静态资源。
继续进入 home 模块,可以看看有几个目录及其文件
.
├── fis-conf.js
├── page
├── server.conf
├── static
├── test
└── widget
无疑,这就是使用 fis-plus
需要遵循的目录规范,为什么要有目录规范,可能在网上可以找到很多答案,这里就不再赘述。
说一下这几个目录所代表的意思;
lazyload.js
page
下的模板相对应,表明哪个模板用哪个数据文件进行渲染ajax
请求等。细心的你有可能发现了一个比较专业的词汇 组件化,组件化的细节比较繁多,准备新开一节说明
如示例中 index.tpl
如何应用 widget/header/header.tpl
? 如果用过 smarty 的你可能会想到include
,但在 FIS-PLUS 中引入 widget 有个跟include
类似的插件完成,widget
。
{%widget name="home:widget/header/header.tpl"%}
home:widget/header/header.tpl
这个是 FIS 中 静态资源 id
根据目录规范 widget
目录下的 js 将被进行 组件化封装 ,根据 同名依赖 原则;
由于 js 进行了组件化封装,比如通过 require
或者 require.async
函数来执行其中逻辑。
{%script%}
require('/widget/a.js');
{%/script%}
如上,在模板中使用 widget
下的 js,必须放到 {%script%}
{%/script%}
之间,用它来代替 js 的内联用法。
说完加载 js 的方法,css 如何引入呢?
require
插件{%require name="home:widget/a.css"%}
静态资源 id 会贯穿整个用户文档,跟使用密切相关,所以它很重要。现在需要弄清楚两件事情
fis-plus
中计算的?fis-plus
中计算的?fis-plus
必须要指定模块的 namespace
,所以静态资源 id 被标记为
namespace:< 资源相对于模块根目录的路径 >
比如:
home/static/a.js
home目录为模块跟目录,home
为namespace
,则静态资源id就为 home:static/a.js
namespace
可以为任意值,可以不跟模块根目录相同。widget
、require
、html
等smarty插件时,必须指定资源的idrequire
、require.async
等JavaScript函数,可以使用id规范是辅助用户开发的利器,按照规范进行开发,可以极大的提升开发效率。 在 FIS 中,定义了一套默认的模块化开发规范。
业务功能模块化,针对常见的业务模型,抽象出以下层级关系:
站点(site)
:一般指能独立提供服务,具有单独二级域名的产品线。如旅游产品线或者特大站点的子站点(lv.baidu.com)。模块(module)
:具有较清晰业务逻辑关系的功能业务集合,一般也叫系统子模块,多个子系统构成一个站点。页面(page)
: 具有独立URL的输出内容,多个页面一般可组成子系统。组件(widget)
:能独立提供功能且能够复用的独立资源,它可以是独立的JS、CSS或者是由JS、CSS和页面组成的页面碎片。静态资源(static)
:非组件资源目录,包括模板页面引用的静态资源和其他静态资源(favicon,crossdomain.xml等)。插件(plugin)
: 模板插件目录(可选,对于特殊需要的产品线,可以独立维护)。测试数据(test)
: 页面对应的测试数据目录。FIS 规范定义了两类模块: common 模块 与 业务模块。
common模块
: 为其他业务模块提供规范、资源复用的通用模块。业务模块
: 根据业务、URI等将站点进行划分的子系统站点模块。站点整体目录结构示意:
|---site
| |---common #通用子系统
| | |---config #smarty配置文件
| | |---page #模板页面文件目录,也包含用于继承的模板页面
| | └── layout.tpl
| | |---widget #组件的资源目录,包括模板组件,JS组件,CSS组件等
| | | └── menu #widget模板组件
| | | | └── menu.tpl
| | | | └── menu.js
| | | | └── menu.css
| | | └── ui #UI组件
| | | └── dialog #JS组件
| | | | └──dialog.js
| | | | └──dialog.css
| | | └── reset #CSS组件
| | | └── reset.css
| | |---static #非组件静态资源目录,包括模板页面引用的静态资源和其他静态资源
| | |---plugin #模板插件目录(可选,对于特殊需要的产品线,可以独立维护)
| | |---fis-conf.js #fis配置文件
| |---module1 #module1子系统
| | |---test
| | |---config
| | |---page
| | └── index.tpl
| | |---widget
| | |---static
| | | └── index #index.tpl模板对应的静态资源
| | | └── index.js
| | | └── index.css
| | |---fis-conf.js #fis配置文件
......
为什么 FIS 要按照上述模块化方式定义规范呢?
模块化是一种处理复杂系统分解成为更好的可管理模块的方式,它可以把系统代码划分为一系列职责单一,高度解耦且可替换的模块,系统中某一部分的变化将如何影响其它部分就会变得显而易见,系统的可维护性更加简单易得。
想了解的更多,可以查看 FIS 模块化
页面 (page)
:存放在 模块根目录 /page 下,url 访问路径为 / 模块名 /page/ 页面名,例如 path_to_user_module/page/view.tpl,访问 url 为:/user/page/view。页面静态资源存储的位置为:
tpl :path_to_module/page/页面名.tpl
js :path_to_module/page/页面名.js
css :path_to_module/page/页面名.css
css 组件
:一般来说,CSS 组件是最简单的组件,它的存储方式为:
#widget目录下的css文件皆为css组件,建议存放在widget/ui目录下
css :path_to_module/widget/ui/组件名/组件名.css
js 组件
:支持 AMD 规范的 js 组件,js 组件存储的方式为:
#widget目录下的js文件皆为js组件,建议存放在widget/ui目录下
js :path_to_module/widget/ui/组件名/组件名.js
模板组件
:存放在 模块根目录 /widget 下,每个 widget 包含至少一个与 widget 目录 同名 的 tpl,同时可以有与 widget 同名 的 js、css 作为其静态资源。组件存储方式为:
tpl :path_to_module/widget/组件名/组件名.tpl
js :path_to_module/widget/组件名/组件名.js
css :path_to_module/widget/组件名/组件名.css
配置文件 (fis-conf.js)
:fis 配置文件存放在模块根目录下 path_to_user_module/fis-conf.js ,smarty 配置文件存放在:
conf:path_to_module/config/模块名/
smarty 插件
:与 smarty 插件相关的都存放在 plugin 目录下,存储位置为:
插件:path_to_module/plugin/
测试数据 (test)
:fis 开发环境允许在本地开发中设置测试数据进行调试,测试数据以页面模板为单位进行组织,其存储方式为:
tpl:path_to_module/page/模块名/页面名.tpl
data:path_to_module/test/page/页面名.json(或php)
模块的 widget 目录默认为组件目录,组件化按照代码的组织方式,分为以下三种:
CSS 组件:独立 css 代码片段。可以被其他 css,js,模板引用。
JS 组件:独立 js 代码片段,JS 组件可以封装 CSS 组件的代码。
模板组件:涉及代码最多,有模板代码,JS 代码,css 代码和 HTML 代码。 建议,模板组件中的 js 仅被这个 widget 使用,保持 widget 的独立。
下面的文档会详细介绍组件的使用方式。
假设有自定义特殊目录规范的需求,比如某些图片需要挪动到自定义的目录下时,需要通过如下方式设置, 切记 。
具体设置属性参考 roadmap.path 设置
fis.config.set('roadmap.path', [
{ // 规则1
reg: '匹配文件正则或者glob',
release: '文件想产出的路径'
},
{ // 规则2
reg: '匹配文件正则或者glob',
release: '文件想产出的路径'
}
].concat(fis.config.get('roadmap.path', [])));
在 fis-plus 中使用 Smarty 插件的方式也实现了 三种语言能力 :require
、uri
以及编译期的widget_inline
{%require%}
name
、src
例子:
{%require name="common:static/lib/jquery.js"%}
或
{%require src="http://www.baidu.com/cdn/jquery.js"%} {%*外链*%}
{%uri%}
name
例子:
<script src="{%uri name="common:static/lib/jquery.js"%}" type="text/javascript"></script>
widget_inline
使用如下方式获取最新 Smarty 插件
git clone https://github.com/fex-team/fis-plus-smarty-plugin plugin
后进入 plugin 目录 删除.git 目录或 点击下载
在模块中,用户可直接访问浏览的页面称为页面模板,文件在 模块根目录 /page/ 下。FIS 提供提供了很多模板插件替换原生 html 标签,为页面模板开发提供使用。
从 demo 中 common 模块的 layout.tpl,可以了解到如何通过后端框架进行开发,组织整个页面:
<!DOCTYPE html>
{%* 使用html插件替换普通html标签,同时注册JS组件化库 *%}
{%html framework="common:static/mod.js" class="expanded"%}
{%* 使用head插件替换head标签,主要为控制加载同步静态资源使用 *%}
{%head%}
<meta charset="utf-8"/>
<meta content="{%$description%}" name="description">
<title>{%$title%}</title>
{%block name="block_head_static"%}{%/block%}
{%/head%}
{%* 使用body插件替换body标签,主要为可控制加载JS资源 *%}
{%body%}
{%block name="content"%}{%/block%}
{%/body%}
{%/html%}
在 demo-home 模块中的 index.tpl,加载页面模板对应的静态资源,通过模板组件组织页面:
{%extends file="common/page/layout.tpl"%}
{%block name="block_head_static"%}
<!--[if lt IE 9]>
<script src="/lib/js/html5.js"></script>
<![endif]-->
{%* 模板中加载静态资源 *%}
{%require name="home:static/lib/css/bootstrap.css"%}
{%require name="home:static/lib/css/bootstrap-responsive.css"%}
{%require name="home:static/lib/js/jquery-1.10.1.js"%}
{%/block%}
{%block name="content"%}
<div id="wrapper">
<div id="sidebar">
{%* 通过widget插件加载模块化页面片段,name属性对应文件路径,模块名:文件目录路径 *%}
{%widget
name="common:widget/sidebar/sidebar.tpl"
data=$docs
%}
</div>
<div id="container">
{%widget name="home:widget/slogan/slogan.tpl"%}
{%foreach $docs as $index=>$doc%}
{%widget
name="home:widget/section/section.tpl"
call="section"
data=$doc index=$index
%}
{%/foreach%}
</div>
</div>
{%require name="home:static/index/index.css"%}
{%* 通过script插件收集JS片段 *%}
{%script%}var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");
document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3F70b541fe48dd916f7163051b0ce5a0e3' type='text/javascript'%3E%3C/script%3E"));{%/script%}
{%/block%}
在模板框架中,对应的文件调用路径均为 modulename: 相对模块根目录的相对路径
:
//home为模块名,static/lib/css/bootstrap.css为绝对路径截取home模块根目录后的路径
{%require name="home:static/lib/css/bootstrap.css"%}
在 FIS 中,集成了 百度前端模板。在编译过程中,会静态编译 baidu template,不需要线上编译,提高页面运行效率。
在 JS 代码中,通过 __inline 方式进行编译处理前端模板。同时规定以 tmpl 为后缀的文件为前端模板,使用方式:
//编译前
var demoTemplate = __inline('demo.tmpl');
//编译后
var demoTemplate = function (_template_object) {
.......
};
在使用前端模板时需要注意的问题:
//在__inline前端模板前加载template对象
require("common:static/lib/template.js")
//或者通过全局加载的方式
{%require name="common:static/lib/template.js"%}
template 的安装方式
//在common的static/lib目录下执行下面命令
$ lights install template
模板组件是能独立提供功能且能够复用的页面片段,所在规范目录 模块根目录 /widget/ ,模板组件以模板、JS 组件、CSS 组件组成(默认必须有模板)。
只要在 widget 目录下的 Smarty 模板即为模板组件, 目录下有与模板同名的 JS、CSS 文件 FIS 会自动添加依赖关系处理,在模板渲染时进行同步加载
。
调用一个模板组件需要通过 widget 语法,如我们想调用 home 下的 section 模板组件,则语法为
//调用模板的路径为 modulename:模板在widget目录下路径
{%widget name="home:widget/section/section.tpl" %}
widget 插件可以直接调用某个 smarty 的 function 函数,使用方式为:
//在模板中定义function函数
{%function name="functionDemo"%}
.............
{%/function%}
------------------------------------
//在模板中调用此函数
{%widget call="functionDemo" %}
FIS-PLUS 提供一套基于 smarty(version >= 3.1.13) 插件的后台运行框架,针对静态资源管理、模板模块化、pagelet 等功能。
{%html framework="home:static/lib/mod.js"%}
....
{%/html%}
{%html framework="home:static/lib/mod.js"%}
{%head%}
<meta charset="utf-8"/>
{%/head%}
{%/html%}
{%html framework="home:static/lib/mod.js"%}
{%head%}
<meta charset="utf-8"/>
{%/head%}
{%body%}
....
{%/body%}
{%/html%}
<script>
标签,收集使用JS组件的代码块,控制输出至页面底部。<script>
标签{%html framework="home:static/lib/mod.js"%}
{%head%}
<meta charset="utf-8"/>
{*通过script插件收集加载组件化JS代码*}
{%script%}
require.async("home:static/ui/B/B.js");
{%/script%}
{%/head%}
{%body%}
...
{%/body%}
{%/html%}
{%html framework="home:static/lib/mod.js"%}
{%head%}
<meta charset="utf-8"/>
{*通过script插件收集加载组件化JS代码*}
{%script%}
require.async("home:static/ui/B/B.js");
{%/script%}
{%/head%}
{%body%}
{%require name="home:static/index/index.css"%}
...
{%/body%}
{%/html%}
{%html framework="home:static/lib/mod.js"%}
{%head%}
<meta charset="utf-8"/>
{*通过script插件收集加载组件化JS代码*}
{%script%}
require.async("home:static/ui/B/B.js");
{%/script%}
{%/head%}
{%body%}
{%require name="home:static/index/index.css"%}
{%widget name="home:widget/A/A.tpl"%}
{%/body%}
{%/html%}
require
插件方式加载。
在 模块根目录 /widget/ 下的 JS 资源皆为组件化资源,可以通过 require
和 require.async
进行调用,则在编译处理过程中会进行 组件化封装。
只有
widget
目录下的资源进行了组件化封装,可以在 js 中通过require
和require.async
调用。其他目录下的资源除非进行了组件化的封装,不然是无法用这两个函数进行调用的。
mod.js 是一套的前端模块加载解决方案。与传统的模块加载相比,ModJS 会根据产品实际使用场景,自动选择一种相应的方案,使最终的实现非常轻量简洁。
作为 FIS-PLUS 前端组件化框架,mod.js 和 AMD (require.js) 或者 CMD (seajs) 等都是互斥的, 无法同时在同一个项目中使用 。
我们可以到 https://github.com/fex-team/mod 下载最新版本的 mod.js
同时在开发中需要使用 mod.js,则需要通过 模板插件语法 html进行注册。
modjs 使用 define
来定义一个模块( 类似 nodeJS 中的 module):
/**
* @param {String} id 唯一标识
* @param {Function} factory factory提供了3个参数,require, exports, module ,用于模块的引用和导出。
*/
define (id, factory)
Example
define('module/id', function (require, exports, module) {
exports.run = function() {
//....
};
});
在平常开发中,我们只需写 factory 中的代码即可,无需手动定义模块
。发布工具会自动将模块代码嵌入 factory 的闭包里。
在编译处理过程中会对 widget
JS 文件进行组件化 define
封装,如;
如果不是
widget
目录下的资源也想让工具自动封装,请查询文档 roadmap.pathisMod
属性。
JS 源码:
//common/widget/menu/menu.js
var $ = require('common:widget/jquery/jquery.js');
exports.init = function() {
$('.menu-ui ul li a').click(function(event) {
var self = this;
$('.menu-ui ul li a.active').removeClass('active');
$(self).addClass('active');
event.preventDefault();
});
};
编译后代码
define('common:widget/menu/menu.js', function(require, exports, module){
var $ = require('common:widget/jquery/jquery.js');
exports.init = function() {
$('.menu-ui ul li a').click(function(event) {
var self = this;
$('.menu-ui ul li a.active').removeClass('active');
$(self).addClass('active');
event.preventDefault();
});
};
});
自动封装的 ID
自动封装的 ID 是根据源码路径计算的,id
= <namespace>:<file.subpath>
// namespace = 'common'
proj/widget/a.js
// id = 'common:widget/a.js'
proj/widget/foo/foo.js
// id = 'common:widget/foo/foo.js'
modJS 的发布工具会保证你的程序在使用之前,所有依赖的模块都已加载。因此当我们需要一个模块时,只需提供一个模块名即可获取:
require (id)
//id可为相对路径,或FIS中组件调用路径 模块名:文件所在widget中路径
require("common:widget/ui/a/a.js")
因为所需的模块都已预先加载,因此 require 可以立即返回该模块。
考虑到有些模块无需在启动时载入,因此 modJS 提供了可以在运行时 异步加载模块的接口
:
require.async (names, callback)
names 可以是一个 id,或者是数组形式的 id 列表。
当所有都加载都完成时,callback 被调用,names 对应的模块实例将依次传入。
使用 require.async 获取的模块不会被发布工具安排在预加载中,因此在完成回调之前 require 将会抛出模块未定义错误。
//id可为相对路径,或FIS-Plus中组件调用路径 模块名:文件所在widget中路径
require.async(["common:widget/menu/menu.js"],function(menu){
menu.init()
})
在非 widget 目录下的 JS 资源,皆为非组件化资源。用户可以通过 script 标签、require 插件 等方式进行调用.
与模板组件同名的静态资源,FIS 会自动添加依赖关系,同时会对 JS、CSS 进行同步加载。
tpl :模板根目录/widget/widgetName/widgetName.tpl
js :模板根目录/widget/widgetName/widgetName.js
css :模板根目录/widget/widgetName/widgetName.css
在前端开发中,CSS 资源占了很大一部分比例,在 FIS 中,我们将 CSS 资源分为组件和非组件类。CSS 组件会绑定到同名 JS 组件、模板组件上,进行加载管理,用户不需要关心加载方式;非组件类 CSS 资源通过 link 标签、require 插件方式进行加载。
在 模块根目录 /widget 目录下的 CSS 资源,皆为组件。在模板组件中以及 JS 组件中对应同名的 CSS 组件会自动与模板组件、JS 组件添加依赖关系,进行加载管理,用户不需要显示进行调用加载。
在非 widget 目录下的 CSS 资源,皆为非组件化资源。用户可以通过 link 标签、require 插件 等方式进行调用.
与模板组件同名的静态资源,FIS 会自动添加依赖关系,同时会对 JS、CSS 进行同步加载
。
tpl :模板根目录/widget/widgetName/widgetName.tpl
js :模板根目录/widget/widgetName/widgetName.js
css :模板根目录/widget/widgetName/widgetName.css
在 FIS 中默认配置了对 less 资源处理插件,less 文件编译处理后变为 css 文件。
fis.config.set('modules.optimizer.tpl', 'smarty-xss');
fis.config.set('settings.optimizer.smarty-xss', {
'escapeMap' : {
'js' : 'f_escape_js', //配置js_escape指定js转义插件的名称
'html' : 'f_escape_xml', //配置html_escape指定对于xml转义插件的名称
'data' : 'f_escape_data', //配置data_escape指定data转义插件的名称
'path' : 'f_escape_path', //配置path_escape指定url、path转义插件的名称
'event' : 'f_escape_event', //配置event_escape指定event转义插件的名称
'no_escape' : 'escape:none' //不需要添加转义的变量标志
},
'leftDelimiter' : '{%', //smarty左定界符
'rightDelimiter' : '%}', //smarty右定界符
'xssSafeVars' :[ //配置白名单
'fis_safe' //支持正则
]
});
fis.config.set('settings.spriter.csssprites', {
//图之间的边距
margin: 10
});
fis.config.set('modules.optimizer.tpl', 'html-compress');
fis.config.set('modules.optimizer.js', 'uglify-js');
fis.config.set('modules.optimizer.css', 'clean-css');
FIS-Plus 提供静态资源管理系统,支持通过运行时计算静态资源使用情况来加载对应的静态资源。为了达到合理的高性能的加载方式,用户可以将静态资源进行合并。
用户可以在配置文件中,通过配置将静态资源进行合并处理,对 demo-home 模块增加打包配置:
//fis-conf.js
fis.config.set('pack', {
//打包所有static目录下的JS文件
'pkg/aio.js' : /^\/static\/(.*\.js)$/i,
'pkg/widget.js' : [
/^\/widget\/ui\/(.*\.js)$/i,
'/widget/menu/menu.js'
],
//打包所有的css文件
//将内容输出为static/pkg/aio.css文件
'pkg/aio.css' : '**.css'
});
打包配置支持正则、字符串以及通配符,用户可灵活根据自己需求进行资源打包,同时注意以下打包细节:
输出结果:使用命令 fis release --pack --md5 --dest ./output 编译项目,然后到 output 目录下查看产出的 home-map.json 内容.fis 内置的 打包原理 与传统的打包概念不同,fis 的打包实际上是在建立一个资源表,并将其描述并产出为一份 map.json 文件,用户应该围绕着这份描述文件来设计前后端运行框架,从而实现运行时判断打包输出策略的架构。
手工维护资源打包工作存在未能及时排除废弃资源、需持续维护、成本大等缺陷。针对此问题,FIS 提供静态资源自动合并服务自动生成资源打包配置,根据网站页面 pv 以及页面静态资源使用情况, 自动计算静态资源合并方案,减少人工管理静态资源成本和风险
。
自动打包使用方式与现有一致,接入简单。提供简版和高端版两个版本:
具体使用可以进入官网查看 此文档
每个模块都有对应的模块名,同时需要在配置文件中申明,配置如下。
fis.config.set('namespace', 'common');
模块编译输出时默认都为 utf8 编码,如果需要做修改,可以进行如下配置:
fis.config.set('project.charset', 'gbk');
对静态资源进行 MD5 戳编译时,默认长度都为 7,当然你也可以进行修改:
fis.config.set('project.md5Length', 8);
在 fis-config.js 配置文件中,可在 setting 节点下配置 smarty 节点,修改定界符。
fis.config.set('settings.smarty', {
'left_delimiter' : '<{',
'right_delimiter' : '}>'
})
在模块根目录下放置 smarty.conf
将其内容设置为:
left_delimiter="<{"
right_delimiter="}>"
smarty.conf 文件为一个
ini
文件,需要保证 phpparse_ini_file
能够解析它
假设有多个模块,只需要在 common 模块根目录下放置 smarty.conf
设置静态资源域名,domain 的值如果不是特殊需要,请不要以 "/" 结尾。
//fis-conf.js
//用法一
fis.config.set('roadmap.domain', 'http://s1.example.com, http://s2.example.com');
//用法二
fis.config.set('roadmap.domain', {
//widget目录下的所有css文件使用 http://css1.example.com 作为域名
'widget/**.css' : 'http://css1.example.com',
//所有的js文件使用 http://js1.example.com 或者 http://js2.example.com 作为域名
'**.js' : ['http://js1.example.com', 'http://js2.example.com']
});
FIS 根据目录规范默认设置了文件的产出路径:
└── config
│ └── modulename-map.json 静态资源表
├── template
│ ├──home
│ │ └── page
│ │ └── index.tpl page级模板文件
│ │ └── widget
│ │ └── section
│ │ └── section.tpl widget模板文件
├── static
│ └── home
│ │ ├── pkg
│ │ │ ├── demo.css 打包css文件
│ │ │ └── demo.js 打包js文件
│ │ ├── index
│ │ │ ├── index.css page级css文件
│ │ │ └── index.js page级js文件
│ │ └── widget widget组件目录
│ │ └── section
│ │ └── section.css
├── plugin
├── test
用户根据产出后的目录,只需要将 config、template、static、plugin 目录上传至联调机器进行项目联调。
用户通过配置 deploy 节点 进行文件上传,fis 支持使用 post 请求向 http 服务器发送文件,服务器端可以使用 php、java 等后端逻辑进行接收,fis-command-release 插件中提供了一个这样的 php 版示例,用户可以直接部署此文件于接收端服务器上。
注意: receiver.php 放到测试机后,请在浏览器中直接访问该文件,保证可以访问。 可以访问的情况下,页面会显示 I'm ready for that, you know.
根据产出目录以及联调机器,进行上传配置:
//上传到一个 remote测试机
fis.config.set('deploy', {
//使用fis release --dest static来使用这个配置
remote: [{
//如果配置了receiver,fis会把文件逐个post到接收端上
receiver : 'http://www.example.com/path/to/receiver.php',
//从产出的结果的static目录下找文件
from : '/static',
//上传目录从static下一级开始不包括static目录
subOnly : true,
//保存到远端机器的/home/fis/www/static目录下
//这个参数会跟随post请求一起发送
to : '/home/fis/www/',
//某些后缀的文件不进行上传
exclude : /.*\.(?:svn|cvs|tar|rar|psd).*/
},
{
//如果配置了receiver,fis会把文件逐个post到接收端上
receiver : 'http://www.example.com/path/to/receiver.php',
//从产出的结果的config目录下找文件
from : '/config',
//保存到远端机器的/home/fis/www/config目录下
//这个参数会跟随post请求一起发送
to : '/home/fis/www/',
//某些后缀的文件不进行上传
exclude : /.*\.(?:svn|cvs|tar|rar|psd).*/
},
{
//如果配置了receiver,fis会把文件逐个post到接收端上
receiver : 'http://www.example.com/path/to/receiver.php',
//从产出的结果的template目录下找文件
from : '/config',
//保存到远端机器的/home/fis/www/template目录下
//这个参数会跟随post请求一起发送
to : '/home/fis/www/',
//某些后缀的文件不进行上传
exclude : /.*\.(?:svn|cvs|tar|rar|psd).*/
},
{
//如果配置了receiver,fis会把文件逐个post到接收端上
receiver : 'http://www.example.com/path/to/receiver.php',
//从产出的结果的plugin目录下找文件
from : '/plugin',
//保存到远端机器的/home/fis/www/plugin目录下
//这个参数会跟随post请求一起发送
to : '/home/fis/www/',
//某些后缀的文件不进行上传
exclude : /.*\.(?:svn|cvs|tar|rar|psd).*/
}]
});
//上传到remote,local,remote2三台机器
//fis-conf.js
fis.config.set('deploy', {
//使用fis release --dest remote来使用这个配置
remote : {
//如果配置了receiver,fis会把文件逐个post到接收端上
receiver : 'http://www.example.com/path/to/receiver.php',
//从产出的结果的static目录下找文件
from : '/static',
//保存到远端机器的/home/fis/www/static目录下
//这个参数会跟随post请求一起发送
to : '/home/fis/www/',
//通配或正则过滤文件,表示只上传所有的js文件
include : '**.js',
//widget目录下的那些文件就不要发布了
exclude : /\/widget\//i,
//支持对文件进行字符串替换
replace : {
from : 'http://www.online.com',
to : 'http://www.offline.com'
}
},
//名字随便取的,没有特殊含义
local : {
//from参数省略,表示从发布后的根目录开始上传
//发布到当前项目的上一级的output目录中
to : '../output'
},
//也可以是一个数组
remote2 : [
{
//将static目录上传到/home/fis/www/webroot下
//上传文件路径为/home/fis/www/webroot/static/xxxx
receiver : 'http://www.example.com/path/to/receiver.php',
from : '/static',
to : '/home/fis/www/webroot'
},
{
//将template目录内的文件(不包括template一级)
//上传到/home/fis/www/tpl下
//上传文件路径为/home/fis/www/tpl/xxxx
receiver : 'http://www.example.com/path/to/receiver.php',
//from支持正则
from : '/template',
to : '/home/fis/www/tpl',
//subOnly仅上传
subOnly : true
}
]
});
--dest 参数 支持使用逗号(,)分割多个发布配置,比如上面的例子,我们可以使用 fis release --dest remote,plugin 命令在一次编译中同时发布多个目标。
subOnly 参数
默认上传 from 整个目录到测试机。添加 subOnly 参数仅上传 from 目录下文件。
replace 替换多个字符串 需要 replace 替换多个字符串,可以使用正则的方式。例如:
replace : {
from : /www\.a\.com|www\.b\.com/,
to : function(m){
if(m === 'www.a.com') return 'www.x.com';
if(m === 'www.b.com') return 'www.y.com';
}
}
上传到一半突然中断, 怎么办?
A: 请关闭测试机的服务器的防火墙,然后重新上传。至于服务器防火墙怎么关闭,不同服务器不一样,所以还是查文档或者 google 一下吧
fis 通过简单的上传配置,把编译后文件上传到测试机, 方便联调。
提供基础的 build.sh 编译脚本,用于上线时候自动编译项目。
#!/bin/bash
MOD_NAME="output"
TAR="$MOD_NAME.tar.gz"
# add path
export PATH=/home/fis/npm/bin:$PATH
#show fis-plus version
fisp --version --no-color
#通过fis-plus release 命令进行模块编译 开启optimize、md5、打包功能,同时需开启-u 独立缓存编译方式,产出到同目录下output中
fisp release -uompd output --no-color
#进入output目录
cd output
#删除产出的test目录
rm -rf test
#将output目录进行打包
tar zcf $TAR ./*
mv $TAR ../
cd ..
rm -rf output
mkdir output
mv $TAR output/
echo "build end"
为了做到前后端分离,fisp 提供了一个 web 服务,提供了一套本地渲染 Smarty 的 APP 套件,支持 URL 转发、测试数据模拟等功能;
本地 Web 服务是通过 fisp server
命令操作的;它是一个完整的 Web 服务,可以解析 PHP;
开启服务
fisp server start
关闭服务
fisp server stop
重启服务
fisp server restart
查看服务器信息
fisp server info
打开服务器根目录
fisp server open
如果你在本地不想用 fisp 提供的 Web 服务,可以用其他服务替代,只是可能需要你自己开发一套 APP 套件或者直接把后端代码部署到你自己的机器上,方便开发。
如果用户没有配置 URL 转发,是按照如下方式去匹配到一个页面的;
比如我有代码路径
fis-conf.js
page/index.tpl
page/home.tpl
假设项目的 namespace = 'work'
那么 release 之后,可以通过如下方式来访问上面提到的两个页面;
page/index.tpl
=> http://127.0.0.1:8080/work/page/index
page/home.tpl
=> http://127.0.0.1:8080/work/page/home
如果配置了 url 转发,请按照 url 转发配置规则访问这些页面;具体配置参考 本地 URL 模拟转发
FIS 提供了本地调试数据功能,本地预览模板时会根据选择的测试数据进行展现,默认的 smarty 版本为 3.1.13。
//新建浏览器书签,网址为以下内容
javascript:void function(){var d=new Date();d.setFullYear(d.getFullYear()+1);document.cookie='FIS_DEBUG=YlwtSmt;path=/;expires='+d.toGMTString()+'';document.cookie='FIS_DEBUG_EDIT=1;path=/;expires='+d.toGMTString()+'';document.cookie='LITE_DEBUG=model;expires='+new(Date)(+new(Date)+1000).toGMTString();location.reload();}();
当模块进行编译发布后,在预览的时候点击书签,进入数据管理页面,修改数据后再进行渲染:
修改保存的数据会临时保存在发布目录中,如果需要保存在 test 目录下还需要单独粘贴复制。
测试数据存放在模块 test 目录下,模板文件和测试数据文件对应关系如下:
模板:
photo/page/index.tpl
对应数据文件:
0. photo/test/page/index.php (php格式)
0. photo/test/page/index.json (json格式)
也支持多份数据 (php 格式为例):
0. photo/test/page/index/index_1.php
0. photo/test/page/index/index_2.php
...
文件名: index_\d+.php
FIS 的 rewrite 模块。用于本地浏览时,url 的转发,覆盖 fis 默认的 url 规则,模拟线上 url 规则,ajax 请求,文件转发等。
在模块目录下通过配置文件 server.conf 文件进行配置,配置方式是:
rewrite 和 redirect 开头的会被翻译成一条匹配规则,自上而下的匹配。所有非 rewrite 和 redirect 开头的会被当做注释处理。
rewrite : 匹配规则后转发到一个文件
template : 匹配规则后转发到一个模板文件,但url不改变,只针对模板
redirect : 匹配规则后重定向到另一个url
rewrite ^\/news\?.*tn\=[a-zA-Z0-9]+.* app/data/news.php
template ^\/(.*)\?.* /home/page/index.tpl
redirect ^\/index\?.* /home/page/index
注意:
如果你在配置文件中进行静态资源打包配置,在编译后会产出资源表以及相对应的打包文件。在页面渲染加载时,FIS 后端框架会根据页面静态资源使用情况加载对应的资源包及静态资源。 如果在联调或者线上出现问题时,需要独立加载静态资源不想加载资源包进行问题定位怎么设置呢?
你只需要在 url 上加上 fis_debug
参数,即可获取静态资源的独立加载,而不是加载资源包。
如访问demo-home中的index:
http://localhost:8080/home/page/index?fis_debug
此时页面渲染加载的都是独立的静态资源。
Quickling 适用于网络高延迟、低带宽场景的解决方案。
Quickling 这个词诞生自 facebook Web 优化方案 ,它指的是页面的某一个块可以通过 Ajax 请求,包括这块使用到的静态资源,然后通过 JSON 方式返回给前端加载器,前端加载器先加载静态资源然后渲染块,这样得到一个可展示的页面局部,可以把它放到当前页的任何地方。
Quickling 解决方案 也使用相同的原理。得益于 FIS 2.0,我们很轻松就可以搞定整个逻辑的实现。
介绍 Quickling 是如何工作。解决方案特性:
使用场景一栏,主要给大家展示一些案例,来引导理解整个解决方案。
项目 A 中使用方案提供的最初使用前端模板实现 webapp 一站式效果,但是前端渲染的形式,在展示的时候后端获取数据分为两步,展现页面时只能等数据拿到以后才能进行展现,而恰巧获取数据时比较慢,导致页面出现卡顿。那么我们用 Quickling 解决方案 如何解决这个问题呢?
答案很简单,展现页面的时候也分为两步走,第一次渲染的时候拿到比较重要那块的数据,并渲染对应的部分页面。再发起一次异步请求,请求剩下的部分页面。这样至少用户不会感觉到卡顿。是不是看着似曾相识,这个就好比纯的 WebApp 在渲染一个页面时,请求两次数据并渲染页面一样。但这个是后端模板层面支持的。
项目 B 主要服务于东南亚地区,这些国家的网络有个特点,那就是 慢,有 IPHONE 并使用移动号的同学拿出手机访问一下某网站试试,就那种感觉。通过项目 B 同学的反馈以及统计数据显示,下载 HTML 的速度都慢的可怜。还有一个问题并发时下载资源之间抢带宽,阻塞页面的渲染。
总结一下俩问题上面两个案例的问题
那通过 Quickling 解决方案 如何解决问题呢。可以通过,
案例一 和 案例二 中可以看到,Quickling 解决方案很好的解决了这些遇到的问题,而案例中说到的情况就是方案已知的适用场景,其他场景还有待发现。
很多同学到这里就有疑问了,如此复杂的请求方式,一个页面可以分块请求,是不是需要在开发的时候实现很多东西,维护起来很麻烦。答案是 否定 的。整个方案依托于 FIS 2.0 的前端架构思想,从目录结构到静态资源管理。只需要做很小的工作就瞬间享受到 Quickling 解决方案 带来的新特性。
首先得有一个后端模板是 Smarty 的项目,并且是使用 FIS 制定的目录规范以及用 FIS 编译。目录结构是这样的;
├── build.sh
├── config
├── fis-conf.js
├── page
├── static
├── test
└── widget
每个目录放些什么,就不一一说明了,见 FIS2.0 文档 。我们只关注 widget 和 page 。
假设有一个 widget widget_A ,包括一个模板文件 widget_A.tpl 和一个 js 文件 widget_A.js 还有一个 css 文件 widget_A.css。有个页面 index.tpl 要使用这个 widget。
├── page
│ └── index.tpl
└── widget
└── widget_A
├── widget_A.js
├── widget_A.css
└── widget_A.tpl
网站展示时渲染 index.tpl ,widget_A 是页面中的一部分。
//index.tpl
{%widget name="demo:widget/widget_A/widget_A.tpl"%}
当页面被渲染时,widget_A 就展现在页面上了。
<html>
...
<link href="widget_A.css" rel="stylesheet" type="text/css" />
....
<div> 我是widget_A </div>
....
<script src="widget_A.js" type="text/javascript"></script>
</html>
上面是正常的使用方式,就像 方案二 中说到的,如何让渲染 index.tpl 时不展示 widget_A 呢。
{%widget name="demo:widget/widget_A/widget_A.tpl" mode="quickling" pagelet_id="widget_A"%}
OK,改造完成。
加了 mode="quickling"
和pagelet_id="widget_A"
这俩参数。
这时候渲染页面的结果是什么呢?
<html>
.....
<textarea class="fis_g_bigrender" style=“display:none”>BigPipe.asyncLoad({id: "widget_A"})</textarea>
<div id="widget_A"></div>
.....
</html>
如上代码,做了俩事情。
<div id="widget_A"></div>
,异步请求回来的widget_A的html就放在这个坑了。等页面渲染完后,开发的同学需要做什么,他只需要把 textarea 里面的代码根据自己的需求执行就成,比如滚轮滚那个地方,domready 后。。。这个自己决定。
说到这里我想你也知道如何使用了。
使用步骤 :
quickling
,mode="quickling"
pagelet_id="widget_A"
相关资源 :
有了使用场景而且也知道如何使用,那现在开始总结一下它到底有哪些优点,事物都是双面的当然也有缺点。这栏总结一下整个方案的优缺点。按照一贯的做法,先说优点。
性能本来就是一个折中,方案有优缺点,就看具体场景需要了。
国际化解决方案适应于国际化多语言支持的解决方案.
国际化是指产品、应用或者内容的设计和开发可以使得不同语言、文化、地区的目标受众容易接受和实现本地化这个单词一般被缩写成 i18n
国际化过程由如下基本的必要属性:
设计和开发方式必须要能够容易的实现本地化和通用部署,包括使用 unicode 字符,确保对老的字符编码做了处理等等
提供一些只有在某些情况下才会使用的本地化特性。比如在 HTML 的 DTD 中支持双向的文本展示特性,或者标示出语言,或者在 CSS 中提供支持文字垂直方向展示等等
能够支持一些本地区域的语言和文化展现,如加入一些预定义的本地化数据或者从当地图书馆导出的用户属性,比如时间和日期的格式化展现,数字格式化,排序方式,用户名展示方式等等
从源码中区分本地化的元素和内容,比如本地化备选展现方式可以通过用户在界面上选择后再独立展现
根据 gettext 的思路,一份代码在不同语言环境下展示不同语言。
按照上面说的使用方法,已经在本地模拟一个国际化项目。修改测试数据 i18n
字段,为 en_US、ja_JP 或其他分别看看效果。
<?php
$fis_data = array(
'i18n' => 'en_US'
);
?>
进入模块 i18n,详细了解一下目录结构;
└── i18n
├── fis-conf.js
├── lang #语言目录
│ ├── en_US.po
│ └── ja_JP.po
├── page
├── server.conf
├── static
├── test
└── widget
当这个模块 release 以后得到如下结果;
.
├── config
│ ├── i18n-map.json
│ └── lang
│ ├── i18n.en_US.json
│ └── i18n.ja_JP.json
├── server-conf
│ └── i18n.conf
├── static
│ └── i18n
│ ├── mod.js
│ └── widget
├── template
│ └── i18n
│ ├── page
│ └── widget
└── test
└── i18n
└── page
可以看到 config/lang 目录下是一些 JSON 翻译文件。翻译文件格式为;
{
"原文": "译文",
"原文1": "译文1"
}
这样当运行时调用了翻译函数,翻译函数读取对应的翻译文件展示译文。
js 中的语言:
var str = __('中文');
Smarty 模板中的语言:
{%'中文'|gettext%}
综述,开发过程;
fisp xgettext
抽取所有的词条,产出<namespace>.en_US.po
等语言文件po
文件fisp release -r project
po
文件,生成<namespace>.en_US.json
语言翻译文件key=>value
{%'xxxx'|gettext%}
函数时,根据语言类型,读取对应的翻译文字并展示更多国际化资料 [http://fe.baidu.com/doc/i18n/]