fis icon indicating copy to clipboard operation
fis copied to clipboard

[FAQ] FIS为何默认会将所有相对路径调整为绝对路径

Open hefangshi opened this issue 10 years ago • 25 comments

绝对路径的调整是必须的,将uri路径调整为绝对路径是为了避免资源嵌入、资源合并后资源引用路径错乱等问题。

以CSS为例

├── moduleA
│   ├── A.png
│   └── A.css   # url(./A.png)
├── moduleB
│   ├── B.png
│   └── B.css   # url(./B.png)

一旦合并,但不处理资源路径问题,就会变成

├── moduleA
│   ├── A.png
│   └── A.css
├── moduleB
│   ├── B.png
│   └── B.css
├── pkg
│   └── pkg.css # url(./A.png) url(./B.png) 

pkg.css中的图片资源就无法被引用到了

如果使用绝对路径后,导致 http://www.example.com/sub 这种路径的访问错误的话,可以利用FIS的Domain功能,添加资源前缀解决

参考文档:http://fis.baidu.com/docs/api/fis-conf.html#roadmapdomain

hefangshi avatar Jun 19 '14 09:06 hefangshi

不合并css,也照样给我改了绝对路径,抓狂好么

superddr avatar Jul 29 '14 03:07 superddr

@superddr

合并css只是一个典型案例,类似的还有资源内嵌,模板引用等等会改变相对路径关系的文件引用问题。所以将工程路径转换为绝对路径是保证代码独立可使用的基本前提,这也是fis最核心的设计理念。所以关闭路径转换,fis会退化为一般的压缩工具,这种情况下可能还不如grunt、gulp好用。

如果确实某些公司的部署有各种限制,使得在构建代码之前不能知道最终部署路径的话,有两种解决办法:

  1. 在fis的roadmap.path配置中设置文件的useStandard属性为false,关闭fis对相对路径的绝对化处理,但这个配置也会同时关闭资源内嵌和依赖分析等功能,这些都是和绝对路径有关的。
  2. 夺回部署方面的话语权,充分利用fis的功能,做真正的前端工程化实践。

fouber avatar Jul 29 '14 04:07 fouber

是的还不如用gulp算了,整体来说, 这工具用起来太麻烦了。自动化程度非常低,应该关联的概念不关联(release居然还得每次指定root,和server start居然不关联),应该自动化计算的路径居然用绝对路径(还号称核心概念,明显可以换算出来的)。用起来心太累了。自己写脚本也不至于这样

superddr avatar Aug 01 '14 03:08 superddr

@superddr

fis release 不指定发布路径就会自动与 fis server start 浏览的内容关联,如果你说的指定root是指 set project path,这里实际上也是没有必要的,FIS与Grunt,Gulp一样,只要你fis release的执行目录是你的项目目录,那么也无需指定root

至于为什么无法换算出正确的相对路径,这实际上是为了完备的实现前端组件化能力导致,例子中的合并CSS只是一种情况,可以再参考一下下面这种情况

├─modules
│  └─sidebar
│      ├─sidebar.js # url('./sidebar.png')
│      ├─sidebar.png
│      └─sidebar.tpl # require('sidebar.js')
└─page
    ├─index.tpl # include('modules/sidebar/sidebar.tpl')
    ├─product
    │  └─index.tpl # include('modules/sidebar/sidebar.tpl')
    └─user
        └─index.tpl # include('modules/sidebar/sidebar.tpl')

完备的组件化能力应该至少满足以下要求

  1. 组件可以在任意页面中使用。
  2. 组件的逻辑脚本与样式应该与组件模板组织在一起。
  3. 组件的脚本与样式之间的资源引用应该支持相对路径,开发友好。
  4. 组件化的脚本与样式应该可以进行任意合并。

那么回头看例子,如果计算sidebar.js中sidebar.png的正确路径,我们应该如何计算呢?由于sidebar.js实际执行的页面可能是 /page/index 也可能是 /page/product/index/user/product/index ,而在javascript中相对路径的起算点是页面路径,那么我们必然无法在sidebar.js中使用相对路径来同时在多个页面中获取到正确的路径关系。

以上依然只是换算成绝对路径的理由之一,我们还可以继续列举,比如如果希望构建工具能够支持CDN路径,很明显是无法通过相对路径来添加CDN路径的。在模板级别添加CDN路径是会存在很多局限性的,比如我希望支持多个CDN并保证CDN针对文件是固定的,或者希望图片能够拥有独立的CDN,这都是传统的模板添加CDN的形式无法实现的。

出于以上种种原因,FIS将所有相对路径均调整为了绝对路径,实现完备的资源路径管理。

至于你抓狂的原因我推测可能是不外乎以下几个原因

  1. 无法通过硬盘文件直接浏览文件,必须使用Webserver浏览
  2. 目标部署路径是http://www.example.com/path/ 这样的二级目录,绝对路径导致路径失效
  3. 类库型项目构建无需调整绝对路径

对于第一个问题的确没有别的解决办法,建议使用fis server浏览,就像最开始说的,只要不指定目录,fis server与fis release的目录是默认关联的。

至于第二个问题,FIS提供了 roadmap.domain 配置处理二级目录问题,可以参考roadmap详解

第三个问题就像云龙说的,你可以通过配置useStandard: false关闭绝对路径计算,因为类库构建的确不需要绝对路径处理。

使用绝对路径是一个利远大于弊的设计,我们的目标不是仅仅简单的进行文件的合并,我们希望能够理解整合前端项目,将整个前端项目的资源路径管理完备的处理,而不是留下一个隐藏的深坑等着你。所以完全可以看淡绝对路径调整这个事情。

如果认为绝对路径还有其他令你抓狂地方也可以指出来看看如何解决。

hefangshi avatar Aug 01 '14 04:08 hefangshi

多谢指点,原来还有fis还支持.tpl这么个东西,我去看看具体怎么工作的。如果tpl是每次直接include的那是确实没办法,如果是经过fis处理后合成输出html的话, 我觉得还是可以算出相对路径的。(在tpl里面用绝对路径,在合成的时候再换算成相对路径)

superddr avatar Aug 01 '14 09:08 superddr

@superddr 别看偏了~tpl的动态include支持是指各种后端模板的能力,而不是FIS的能力。

FIS支持的嵌入都是静态的。

但是这里的tpl,即使你看为html,每次静态嵌入,问题也是一样的,因为路径是由sidebar.js设置的,sidebar.js中的路径对不同目录级别的网页计算结果是不同的,除非你给每个页面都产出一份独立的sidebar.js才能解决路径问题。

那么在“明明是同一个逻辑脚本,却无法公用缓存”和“调整脚本的路径为绝对路径适应所有页面”之间,我们很自然会选择后者吧?

还是前面说的,使用绝对路径是一个利远大于弊的设计,当你处理的问题越来越复杂,希望组件化越来越完善的时候,就越会体会到FIS将路径调整为绝对路径是一个明智的选择。

hefangshi avatar Aug 01 '14 10:08 hefangshi

那这里就有个问题了,如果是个动态include的话,制作者本来就不可能使用相对路径,自然会用绝对路径来弄,对吧? 所以如果制作者用了绝对路径,就保持。如果用了相对路径,也保持呗?

superddr avatar Aug 01 '14 11:08 superddr

为何动态include,开发人员就不能使用相对路径呢?作为组件化模块,编写绝对路径首先会限制这个组件的传播和复用能力,在不同的目录结构中就会出现无法使用的情况,其次在开发体验上,组件化内的资源引用使用相对路径是十分自然的想法,这种写法可以告知开发人员这个资源是属于本组件的,而不是外部资源,这对于项目维护也是十分重要的。

姑且不说对相对路径和绝对路径做各式各样的特殊处理和适配的复杂程度和可行性,对于后端模版tpl而言,一般会将其放置在template目录中,而js,css等静态资源将会放在static目录中,换而言之,在最终发布目录结构中,静态资源与模版资源的路径关系甚至会完全无关,在这么多需求的要求下,坚持使用相对路径未免不太明智。统一调整为绝对路径不仅增强了系统的灵活性,还降低了系统设计的复杂性。

我们可以解决统一修改为绝对路径后导致的各种问题。但是如果保持相对路径,你确定可以解决这么多需要解决的问题么;)

hefangshi avatar Aug 01 '14 12:08 hefangshi

@superddr

这么做,根本目的是为了更好的模块化开发。

模块化开发

一直以来,前端工程希望实现模块化开发,然而真正意义的模块化开发,可不是仅仅js实现了就可以的,模块应该具有独立性,因此,fis提倡的模块化,是能将一个模块所依赖的所有js、css、图片、模板等资源维护在一个目录下的开发模式。

如果你不认可这种模式,那么fis的很多功能对你来说可能意义不大,但这种开发方式给人的印象非常深刻,这也是fis一直致力倡导的。

接下来,为了保证模块的独立性、可移植性和可组装性,就要面临一个问题:相对路径 or 绝对路径。

如果采用相对路径,那么将大大降低模块的可组装性,一个模块中的资源都是相对路径,别的模块引用它的js或者css的时候,如果路径不处理,就可能引起相对路径定位错误的问题。包括模板合并、js中访问资源、css中访问资源等情况都会受到影响。

然后你觉得都写绝对路径就ok了是么?但这又会使得这个模块失去了可移植性,你在代码里把路径都写死了,别的项目还用不了?另外,写绝对路径还要考虑浏览器缓存更新问题,这个也是绝对路径书写时带上的么?它显然不适合人工维护。

所以,fis综合了所有前端工程问题中关于路径处理的诉求,决定:

  1. 源码中使用相对路径
  2. 构建后统一替换成绝对路径
  3. 在替换绝对路径的过程中添加缓存更新戳

以最自然的方式编码(源码写相对路径),以最透明的方式优化(工具替换成绝对路径),保证模块的独立性、可移植性、可组合性。

这是fis的设计思想,如果你觉得现阶段用不到也没关系,因为随着工程的复杂度增加,人员和规模的扩张,上面图片展示的模块化开发方式会让你为之着迷,一旦你希望这么维护你的系统,一旦你希望以搭积木的方式开发应用,你就会遇到相对路径问题对源码使用的限制,你也就会理解fis为什么要这么做了。

fouber avatar Aug 01 '14 12:08 fouber

动态include,如果用的相对路径的话,在css内当然没问题,在js内和html内就有个问题:不用fis的时候就不能运行了。既然动态模版不是由fis来负责总装的,那也没有理由由fis来改变路径啊。更现实的做法是:在js内或者php内拼装路径。(没fis的时候我们就是这么做的啊,另外.net天生支持这样的路径:~/sidebar/css.css, 这个根目录是可以由iis决定的,自动变化的)

我总是认为,开发的时候减少依赖性更好。一个开发目录应该本身就能运行,用fis起到优化流程的作用。而不是要依赖fis来参与拼装,导致开发文件夹本身不能正常运行了,还得先运行个fis的命令,然后才能到另一个目录里去运行。那对新手接手项目进行维护的时候,将是有害的。

另外我没有说要坚持用相对路径啊,我是说人家如果本来用的绝对路径来搞,就绝对路径;至于本来用相对的,既然人家原来能正常运行,没必要给人搞成绝对的啊?现在其实焦点还是集中在:这事儿不好做,给fis的开发带来困难了。但是相信我,给你足够时间你肯定能想出两全其美的办法的,不过必然会有很多额外的工作量的。

@fouber 你的帖子比较长,我等会儿再看。。。。

superddr avatar Aug 01 '14 13:08 superddr

@fouber 这篇就讲得比较全面了,也让我明白了fis的思想,和我原先的思维模型是不一样的。原来fis是希望用户依赖它,靠它来改善整个开发流程以及部署流程。而我来学fis的时候只是希望它改善我们的部署流程,没打算让它参与开发;因为原先的依赖项少不了,现在多一个依赖项总是有点犹豫的。

那现在这个tpl就成为核心焦点了,它到底是怎么用的呢?在fis里面为每个组件写个配置,指定这个目录下的组件将来都会被发布到哪里去,然后由fis来改变里面的每个url?期望能有个这方面的最佳实践的介绍,好让大家更深入理解一下这个问题。

我感觉fis索性把这个tpl组装的事情一起搞定算了啊(要啥自行车,要啥php啊),那样就又少依赖php一点了,那倒真不错。

最后,如果组装tpl也由fis来负责了,那么fis不是又有机会去计算相对路径了么,fis知道这个html用到了哪个tpl,换算就可以由fis来执行了。多好,两全其美。

superddr avatar Aug 01 '14 14:08 superddr

@superddr

相对路径是在运行时发生的,你不知道一个模块被运行时用在了什么地方,所以相对路径是不安全的。

接下来,说一下tpl的问题。

tpl在我们这里上下文中有完全不同的含义。前面 @hefangshi 说的tpl,是服务端运行的模板,这种前端开发模型你可以理解为“前端工程师写好了html,后端工程师把它动态化成tpl”中的那个tpl。fis在研究这种开发模型的时候,发现可以在模板中实现一种类似requirejs的服务端静态资源管理框架,来管理前端资源,这个说起来比较复杂,以后可以展开论述,这里的问题并不关心它。

所以,你说“fis可以tpl组装也一起搞了”,但前文说的tpl是后端动态语言模板,fis是静态编译工具,所以不能直接处理动态模板,比如同一个页面(tpl),登陆用户展现一种样子,非登录用户展现另一种,只有运行起来才知道,线下构建是处理不了的。

而我也理解你所说的tpl,其实是前端模板,或者说是碎片化的html,这部分fis也有支持。如果是纯前端项目,fis支持把前端模板代码嵌入到js中使用,这样用户最终就使用js就好了,模板连请求都省了:

fis内嵌tpl

模板中的img、audio、video等资源标签所引用的url地址fis都能给你转成绝对路径,这样js把这段html插入到任何页面都是正确运行的了

fouber avatar Aug 01 '14 15:08 fouber

@superddr

总结一下:

  1. 前面 @hefangshi 说的tpl,是后端动态模板文件,这种开发模型fis有另一种思路解决
  2. 你说的tpl,是前端模板文件,在fis中使用,通常是利用fis的内嵌语法 __inline 嵌入到js或者html文件中使用,fis不但能帮你把文件嵌入进去,还能把里面引用资源的路径变成工程部署后的绝对路径,保证模块的独立性。

fis的设计思路还是那几句话:

  1. 用最自然的方式写码
  2. 用最透明的方式优化

顺便说一下,资源内嵌也是保证模块独立的基本能力,它的安全性是建立在绝对路径转换这个基础上的。

fouber avatar Aug 01 '14 15:08 fouber

请问这个__inline方法有关的文档在哪里看呢?

superddr avatar Aug 01 '14 15:08 superddr

@superddr

https://github.com/fex-team/fis/wiki/%E5%B5%8C%E5%85%A5%E8%B5%84%E6%BA%90

fouber avatar Aug 01 '14 15:08 fouber

看完了,占个座

looping84 avatar Sep 03 '14 10:09 looping84

看了下,占座。

oxUnd avatar Sep 03 '14 13:09 oxUnd

看完,占座,以后遇到相似场景回顾

youyudehexie avatar Sep 14 '14 04:09 youyudehexie

@xiangshouding 我在使用fis的时候也遇到这个问题我的fis-config跟我的html文件夹page在同一级目录,然后我的page下面还有一个compent目录下面也存放着html文件,但是我打包之后我compent下的html引用的路径全部是是错的本来是../../xxx_autocombine_xxx.js结果写成了../xxx_autocombine_xxx.js,导致文件引用不正确,请问我应该如何去做?

VicKun4937 avatar Oct 16 '14 02:10 VicKun4937

回答得太细致了,学习一下,感谢^-^。

lovefishs avatar Nov 09 '14 14:11 lovefishs

fis release -p 的时候,页面底部的javascript

<script data-main="js/special/main" src="lib/requirejs/require.js"></script>

被替换成了,无法执行,请问也是domain 的问题吗?

<script type="text/javascript" charset="utf-8" src="/lib/requirejs/require.js"></script>

热心人士说是roadmap.domain(解释:设置静态资源的域名前缀)的问题,看了roadmap.domain 也没发现是什么问题,设置roadmap,fis release -pDd ..\test 还是一样的问题。
有空的话帮忙回答下,感激不尽,谢谢。

Megasu avatar Jan 23 '15 08:01 Megasu

确实存在打包替换引入冗余的问题

hooper-hc avatar Mar 16 '15 08:03 hooper-hc

roadmap[http://fis.baidu.com/docs/advance/roadmap.html#%E5%9F%9F%E5%90%8D%E9%85%8D%E7%BD%AE] 文档链接挂了

LipsonX avatar Nov 18 '16 06:11 LipsonX

FIS2的文档在此查看 http://fex-team.github.io/fis-site/docs/advance/roadmap.html

hefangshi avatar Nov 18 '16 06:11 hefangshi

为什么我使用了这种方式打算在发布时保持相对路径 fis.config.set('roadmap.path', [{ reg: '**', useStandard: false }]); 但没有效果,原本@import url(./shared.css);,发布后变成了@import url(/Widgets/shared.css); fis-conf.js和Widgets目录在同一级。

libofei2004 avatar Feb 24 '17 16:02 libofei2004