fis-plus上线篇之搞定smarty环境


在使用fisp(fis-plus简称)的时候,有些文件看着就是碍眼,有些文件想release到看上去比较顺眼的地方,或者是其他一些看似合乎逻辑的理由想改路径。反正就一个事儿,想更改发布路径。

把想更改发布路径深深的埋藏在心里,苦苦不知道从何下手。翻翻文档吧,都能丢失在文档的海洋里面,心里暗骂“艹,上午的需求还没有完成呢!!!”,无奈心里还是痒痒,想改路径。

我真实的描述了一个拿到fisp想改改路径却没改成功的心情。本篇就是彻底把这坨事情描述明白而生的,另外只此一次过时不候。

Smarty使用介绍

ok,首先我们从使用Smarty说起。Smarty有个响亮的名字叫模板引擎,其实没来度厂之前我对这个很不屑,而且我也知道一些模板引擎是怎么做的,整个正则定义一些指令函数,就能做一个模板引擎;就像,很多前端模板就是这么干的一样。另外一个原因是其实说白了,我直接拿php做模板就好了,何必。。再引入一层Smarty?

后来来到这边以后发现Smarty在各个使用PHP后端的产品线都在使用,然后我就震惊了。想了一万个为什么,最后发现主要原因是使用简单,哦,还有比较棒的block坑可以填,优点暂且就不说了,先来看一下如何用Smarty;

先到Smarty官网上去下载一份smarty,大概下载下来是这个样子的;

$ tree -L 1
.
├── Smarty.class.php
├── SmartyBC.class.php
├── debug.tpl
├── plugins
└── sysplugins

我们暂时只需要关心Smarty.class.php,这个文件里面定义了class Smarty,来一个简短的例子。

<?php
//file: index.php

if (!defined("ROOT")) define("ROOT", __DIR__); //[1]
include (ROOT . '/smarty/Smarty.class.php'); //[2]

$smarty = new Smarty(); //[3]

$smarty->setTemplateDir(ROOT . '/template');//[4]
$smarty->left_delimiter = "<%";//[5]
$smarty->right_delimiter = "%>";//[6]
$smarty->display('a.tpl');//[7]

?>

解释这段代码之前呢,我们先贴上本地的目录;

$ tree -L 1
.
├── index.php
├── smarty
└── template

[4] 设置了一个模板目录,这个非常重要,因为$smarty->display(渲染)的模板都是从这个目录开始找的;比如如上面的a.tpl就是渲染的是template/a.tpl。当然这块需要注意的是假设你display的是一个绝对路径,设置的template_dir就不起效咯。。。

写完了一个简单的渲染的例子,我们再介绍一下Smarty的插件。Smarty的插件的方方面面可以在Smarty官网看到详细的介绍,而我这块只关心插件目录。

//file: index.php
<?php

if (!defined("ROOT")) define("ROOT", __DIR__); //[1]
include (ROOT . '/smarty/Smarty.class.php'); //[2]

$smarty = new Smarty(); //[3]

$smarty->setTemplateDir(ROOT . '/template');//[4]
$smarty->left_delimiter = "<%";//[5]
$smarty->right_delimiter = "%>";//[6]
$smarty->setPluginsDir(ROOT .'/plugin');//[7]
$smarty->display('a.tpl');//[8]

?>

[7]这行设置了一个插件目录,意思是说,如果这个目录下有有效的Smarty插件,就会加载起来,然后在模板里面就可以直接使用,Smarty有很多类型的插件,我们这块使用function插件来举例子。

我们迅速搞一个function插件,就叫function.xdate.php吧。在模板里面这么调用。

<%*file: template/a.tpl*%>
<%xdate%>

这个插件的作用是打出时间;代码如下

<?php
//file: plugin/function.xdate.php
function smarty_function_xdate($params, $template) {
    return date('Y-m-d H:i:s');
}
?>

当渲染a.tpl时,会调用插件xdate得到一个当前的时间。。。

Smarty的例子呢就举到这里;在上面的例子里面我们关注了两个目录

  • template_dir 模板在这个目录下进行查找
  • plugins_dir 插件在这个目录下进行查找

当然,我们也可以var_dump($smarty)来看更多的一些目录配置,不过暂且我们只关心这俩目录。

fisp里面定制了一些插件,所以需要把这些插件上线的时候放到Smaty的插件目录(plugins_dir)下;fisp编译产出后有一些模板,显然也需要把这些模板放到Smarty的模板目录(template_dir)下。

有的时候可能会发现都不知道后端框架把Smarty隐藏到什么地方了,更别说设置了(一般都是已经设置好了的),一般得问下后端开发的同学,再要不,到display模板的地方var_dump模板对象就能看得到上面说的两个目录的位置了。

其次来看看fisp的产出目录是怎么样的;

$ fisp install pc-demo #下载一个demo
$ cd pc-demo/common
$ fisp release -d output
$ cd output
$ tree -L 1
.
├── config
├── plugin
├── static
└── template

看明白了吗,意思是plugin下的资源放到plugins_dir;template下的内容放到template_dir

当然还出现了两个目录,一个config、一个static。OH,其实config目录对应于$smarty->config_dir目录。static目录呢,直接copy到www目录下即可(静态服务器的DOCUMENT_ROOT下)。

OK,标准上线目录已经说完了,我们现在进入正题;关于想修改目录的时候肿么办的问题

利用fis的roadmap.path更改产出路径、URL

用了fisp大概知道fis-conf.js,一个牛逼而且有点晦涩的配置文件,后缀是js,告诉我们它是个JavaScript文件,可以任意写JavaScript代码。

在改目录之前,我们先来调教一下这个配置文件;

在整个FIS的配置中,提供了很多为了符合个性化开发需要配置的属性值,不是特别多,大概有projectmodulesroadmappacksettingsserver这几类,其他如果再出现一些稀奇古怪的,那就是解决方案自定义的了。

所有的配置项目可以在https://github.com/fex-team/fis/wiki上找到。不过我们可以在这块稍微了解一下。

  • project 就是对项目全局做一些配置,什么编码、md5戳的长度等
  • modules 配置FIS的几个插件流程上面那些文件该用什么插件处理
  • roadmap 配置某一类文件有哪些属性,并且产出到什么地方去,包括添加什么CDN等等,也是最复杂的一块
  • pack 备份静态资源时需要配置这个,你可以把备份认为是合并静态资源
  • settings 相对应与modules填写的一些插件进行某些配置
  • server 本地server的一些相关配置,比如执行fis server clean的时候保留哪些文件等

为了实现上面移动目录的那个目标,我们只关心roadmap的配置情况。

一般,fisp这样的解决方案,里面已经配置了一个牛逼哄哄的配置,特别是fisp经过多年对度厂前端项目的积累,所以是万众挑一的。我们可以很方便的查看这些配置的信息。

// file: fis-conf.js
console.log(fis.config.get('roadmap'));

可以哗啦打出一大串的配置信息;负责文件产出、url更改的配置项是roadmap.path

// file: fis-conf.js
console.log(fis.config.get('roadmap.path'));

不出意外的打出了

[ { reg: '/fis_translate.tpl',
    release: '${templates}/${namespace}/widget/fis_translate.tpl' },
  { reg: /\/lang\/([^\/]+)\.po/i,
    release: '/config/lang/${namespace}.$1.po' },
  { reg: /^\/widget\/(.*\.tpl)$/i,
    isMod: true,
    url: '${namespace}/widget/$1',
    release: '${templates}/${namespace}/widget/$1' },
  { reg: /^\/widget\/(.*\.(js|css))$/i,
    isMod: true,
    release: '${statics}/${namespace}/widget/$1' },
  { reg: /^\/page\/(.+\.tpl)$/i,
    isMod: true,
    release: '${templates}/${namespace}/page/$1',
    extras: { isPage: true } },
  { reg: /\.tmpl$/i, release: false, useOptimizer: false },
  { reg: /^\/(static)\/(.*)/i,
    release: '${statics}/${namespace}/$2' },
  { reg: /^\/(config|test)\/(.*)/i,
    isMod: false,
    release: '/$1/${namespace}/$2' },
  { reg: /^\/(plugin|smarty\.conf$)|\.php$/i },
  { reg: 'server.conf',
    release: '/server-conf/${namespace}.conf' },
  { reg: 'domain.conf', release: '/config/$&' },
  { reg: 'build.sh', release: false },
  { reg: '${namespace}-map.json',
    release: '/config/${namespace}-map.json' },
  { reg: /^.+$/, release: '${statics}/${namespace}$&' } ]

这样一个结果;

其实就一个数组,里面是一个个的对象;在FIS处理的时候是从上到下进行匹配的,假设某一个文件被上面的匹配到了,那么就没有下面规则什么事情了。

var path = [
    {
        reg: /.*\.js$/,
        release: '/static/js/$&'
    },
    {
        reg: /.*\.css$/,
        release: '/static/css/$&'
    }
];

其中对象的

  • reg 是说要匹配某些文件
  • release 是说要产出到什么地方去

当然每个对象中都会由很多其他的一些属性,我们可以把这段配置理解为,给某些文件添加一些属性,这个属性包括FIS已经占用的,可以在roadmap.path这个地方看到,还有一部分可以为了某些插件实现起来方便定义一些私有属性。

当设置了某些属性,这些属性会伴随这个文件在整个编译期内。

OK,先不关心那些其他的属性了。我们来看看regrelease的奇妙使用方式。

由于在整个fisp当中,已经配置roadmap.path,而且获取出来以后是一个数组。所以就不能用fis.config.merge来处理自定义的一些规则了。这是为什么呢,因为其实fis.config.merge就是一个深度merge的函数。俩数组里面都是对象再合并,其效果可想而知,会合并的乱糟糟的,如果你用了merge来合并一条自定义的属性,可能你已经多次怀疑自己的人生了吧。

那肿么设置呢,它不是一个数组嘛,在JavaScript里面数组有很多的方法可以添加一些项。当然添加的时候要放在最前面,用户优先嘛。

  • 方法一,arr.unshift

      //file: fis-conf.js
      var path = fis.config.get('roadmap.path');
    
      path.unshift({
          reg: /\/widget\/some\/.*\.tpl/$,
          release: '/template/$&'
      });
    
    
  • 方法二,arr.concat

      var path = fis.config.get('roadmap.path');
    
      var userPath = [{
          reg: /\/widget\/some\/.*\.tpl/$,
          release: '/template/$&'
      }];
    
      userPath = userPath.concat(path); //userPath在默认path前
      fis.config.set('roadmap.path', userPath);
    
  • 方法三,自己想

所以,配置文件是一个Js文件,可以有很多想象。说道这里,把在fisp里面配置的方法教给大家了,至于你要不要怎么用怎么灵活的用,还得看你想不想了。

有了配置的方法,那就看配置几种含义吧。其实吧,每一个path都对应与一次URL、产出路径的改变。所以稍微拿捏不准,可能就开始骂娘了,如果是我的话基本是怀疑人生。

  • release 不写

      {
          reg: '**.js'
      }
    

    假设不写release,则产出路径就是源码路径;比如我们有个静态资源叫/static/a.js,使用的时候是这么使用的<script src="/static/a.js"></script>,则产出的时候产出目录以及URL都不会被修改(相对路径引用除外)。

  • release 写了

      {
          reg: /\/static\/(.*\.js)/,
          release: '/static/js/$1'
      }
    

    那按照上面的例子,产出后/static/a.js产出到output/static/js/a.js,而使用的地方会从<script src="/static/a.js"></script>变为<script src="/static/js/a.js"></script>

  • release 写了,但是不想改URI [注意]

      {
          reg: /\/static\/(.*\.js)/,
          url: '$&',
          release: '/www/static/$1'
      }
    

    我设置产出到了www目录下,但是我不想修改引入时的URI,还想保持原来的使用方法,该如何办呢,咦,恰巧上面给了一个例子,可以设置url来特定化引用的url。

  • release false

      {
          reg: '**.js',
          release: false
      }
    

    所有的js不产出鸟。。。

如果细心的同学看到了,我在上面打上了[注意]字眼,这个地方确实需要注意,因为这个关乎上线,关乎运行得起来还是跑不起来的问题。

假设把这条弄懂了,整个FIS的目录操作者可就没有什么不能解决的了。

为了表述更清楚,我们细化例子(其实就是把所有的东西都说出来)。

  • 源码

      src
      .
      ├── index.html
      └── static
          └── a.js
    
  • roadmap.path

      fis.config.set('roadmap.path', [{
          reg: /\/static\/(.*\.js)/,
          url: '$&',
          release: '/www/static/$1'
      }]);
    
  • 产出

      output
      .
      ├── index.html
      └── www
          └── static
              └── a.js
    
  • index.html

    源码 产出
    <script src="/static/a.js"></script> <script src="/static/a.js"></script>

    当然是相同的,因为虽然有了不同的release但是url保持不变(当然你也可以设置其他的一些URL,可以用url属性)。

    注意,假设编译后的url是/static/a.js这个样子,那么你就得把www这个目录作为静态资源请求的根目录。或者是做个路由,把/static开头的URL全都定位到www目录下,才能请求到想要的结果。

ok,其实上面这个例子已经把静态文件换个产出路径的方方面面说完了,你学会了吗?

那么进入Smarty的世界,我们要解决修改fisp的模板路径,在Smarty里面怎么运行。

fis-plus里面涉及到Smarty的路径,以及如何修改并能正确运行

在fisp里面,涉及路径的Smarty函数(指令)常用的有

  • extends

      <%extends file="<filepath>"%>
    
  • include

      <%include file="<filepath>"%>
    
  • load_config

      <%load_config file="<filepath>"%>
    
  • require

      <%require name="<id>"%>
    
  • widget

      <%widget name="<id>"%>
    
  • html

      <%html framework="<id>"%>
      <%/html%>
    

其中require、widget、html是fisp独有的定制的一些函数(其实就是插件)。它们位fisp服务,并支撑fisp的正常运行(怎么做到的,可以看源码)。

其中<filepath>就是相对于$smarty->template_dir的相对路径或者是相对于系统根目录的绝对路径。

SO,假设你要这么用;

<%include file="widget/a/a.tpl"%>

那么渲染的文件是SMARTY_TEMPLATE_DIR/widget/a/a.tol,假设你写错了,写成了。

<%include file="/widget/a/a.tpl"%>

这种情况下,会去系统根目录下查找/widget/a/a.tpl,咦,一般都会挂掉。

说完<filepath>,再来说一下<id>;fisp里面要使用一些资源,至少是在Smarty里面用fisp自定义的一些函数,必须指定资源的<id>,这个<id>是fisp自定义的一种标识符。规则很简单<namespace>:<subpath>,当然这块的<subpath>指的是源码的subpath。

举个小例子,假设在common下有个资源/static/a.js的ID就是common:static/a.js注意subpath第一个字符没有斜杠/

搞清楚了<id>的规则,还得弄懂它是如何工作的,才能解开我们心中的谜团。

在模板渲染的时候,使用<id>加载的资源,有一个特殊的操作,那就是拿这个<id>map.json里面找一把,找到对应ID的uri并加载渲染之。

那么问题就来了,其实对于widget渲染的模板,其实它的路径在map.json里面,而不像<filepath>一样,在源码里面就已经指定了

map.json里面的uri就是通过roadmap.pathurl调整的,OK,这块uri和url不太一样是吧,其实也算是个设计缺陷吧,不过无伤大雅。

那么这时候,假设我更改了一个tpl的产出路径,那么就意味着uri发生了变化。只有uri能和上线时上传的文件路径保持一致才能被正确的渲染。所有的文件路径、url都遵循这个规则(可想而知)。

所以,修改产出路径的时候,千万要注意修改的目录跟引用的路径是否一致,是否匹配。

<id>引用路径在map.json里面<id>对应的uri,而<filepath>呢,直接就是相对或者绝对路径。上线代码时一定一定要保持引用路径能找到磁盘上的文件,不然不报错才怪。

嗯,我想我应该画个图来表征这个关系,但暂时先不画了,等看不懂文字的时候再画。不过我想我已经几近大白话说的,应该不会出现对不上号的情况。

最后,写了很长一个篇幅介绍上线这回事儿,其实主要是smarty这块,因为出问题比较多的也是这块。

总结一下,fisp需要把产出的Smarty相关资源放到config_dirplugins_dirtemplate_dir这三个目录,渲染就没问题了,当然需要保持引用路径跟磁盘文件路径对上号