深入Liferay 当页面请求css资源利用parseSass()方法解析

深入Liferay 当页面请求css资源利用parseSass()方法解析

前面已经用了几篇文章详细的讲解了DynamicCSSFilter,现在我们来看他们是如何应用到加载css资源文件的。


前提:

假定我们开发了一个theme叫platform-In-theme,它其中我们写了一个main.css,并且在其中我们用@import url()语法包含了若干个其他css,其中有一个是forms.css,并且这个css是用Sass的语法规则写的,我们来深入研究下加载这个2个css文件的异同。


调试分析:

首先,当页面请求main.css的时候,因为它满足DynamicCSSFilter的模式,所以进入processFilter()方法,由前面几篇文章,很快的拿到requestURI,requestPath,contextPath等信息,

深入Liferay 当页面请求css资源利用parseSass()方法解析

从这图上可以很清楚的看到,这个realPath是在我们的tomcat服务器上的webapps目录下的theme部署子目录,所以说这个是正确的,我们的自定义theme都是部署在这个目录上。


现在我们可以看下原始还没有被解析的main.css长什么样子:

深入Liferay 当页面请求css资源利用parseSass()方法解析

很显然,和我们设想的一样,它就是作为一个总的css用来包含其他的css文件。


现在开始解析这个css 文件,调用的是DynamicCSSUtil类的parseSass()方法.


首先它在第用98行利用theme=_getTheme(request,c***ealPath)来

获取当前theme,因为我们的站点使用了platform-In-theme,所以返回这个theme.


深入Liferay 当页面请求css资源利用parseSass()方法解析


然后,它利用_isThemeCssFastLoad(request,themeDisplay)方法获取我们是否启用了fast load ,因为这里我们没有启用fast load,所以返回false.


然后在115行获取c***ealFile的File对象,刚才讲过了,这个theme在服务器的webapps的相应theme目录中。

深入Liferay 当页面请求css资源利用parseSass()方法解析


然后调用SassToCssBuilder.getCacheFile(c***ealPath)方法

深入Liferay 当页面请求css资源利用parseSass()方法解析

来获取这个css资源文件对应的缓存文件,它首先吧真实css资源文件的反斜杠"\\"全部替换成正斜杠"/",然后获取最后一个正斜杠位置,这是用来区分文件路径部分和文件名部分,然后它在中间插入.sass_cache.

(千万注意,这次和上次说的$CATALINA_HOME/TMPDIR 下面的缓存数据文件完全不同,那个文件是用于直接返回到客户端(如果存在并且最新)的最终css文件,如果使用那个文件,就压根不会调用parseSass()方法,而这个缓存的是原始的css资源文件,它是在parseSass()方法中被调用的,所以用途不同)


所以获得的缓存文件的路径就是如下图:

深入Liferay 当页面请求css资源利用parseSass()方法解析


这里有个判断,如果这个缓存文件存在并且是最新的,那么就不用调用jRuby解析,而是直接把这个缓存文件的内容作为最终parseSass()的返回值,也就是作为最终解析完的普通css文件输出,但是我们是第一次,所以没有这个资源文件,只好老老实实走jRuby解析的过程。


(1)正式解析的第一步是调用SassToCssBuilder.parseStaticTokens(content)方法来吧一些静态的符号替换掉:

深入Liferay 当页面请求css资源利用parseSass()方法解析

很容易理解,就是把Sass中的一些特殊内容替换掉,比如文本的宽度,高度,和输入框的高度宽度。

在这一步完结后,我们的main.css不受到影响。


(2)正式解析的第二步是先通过getQueryString方法获得请求字符串。

深入Liferay 当页面请求css资源利用parseSass()方法解析

然后调用_propagateQueryString()方法进行操作:

深入Liferay 当页面请求css资源利用parseSass()方法解析

这步骤的作用是对于每一个@import(url ...)导入的资源文件,都在后面加上请求字符串。所以在这一步之后,我们的main.css变为:

深入Liferay 当页面请求css资源利用parseSass()方法解析

和我们设想的一样,所有的@import url 的url后面都附加了请求字符串。这是为了保证请求一致性(我猜的)


(3)正式解析的第三步就进入了jRuby解析引擎的内核了,这是通过_parseSass私有方法来完成的:

深入Liferay 当页面请求css资源利用parseSass()方法解析

从这里我们可以看出,它首先会构造一个HashMap对象,然后把我们要解析必要的参数,比如被解析的Sass内容(content),css的真实路径(c***ealPath),在当前应用使用的Theme的css路径(cssThemePath),sass的缓存位置(sassCachePath)都放在HashMap中,然后在第295行使用_rubyExecutor.eval()方法调用jRuby解析器(位于/webapps/ROOT/WEB-INF/lib/jruby.jar中)来解析这个Sass文件:

深入Liferay 当页面请求css资源利用parseSass()方法解析


最后解析完的结果通过unsyncPrintWriter输出而返回,对于我们的main.css来说,经过这一步之后,结果还是一样的。因为这文件大多数都是引入其他文件,并没有Sass的语法,所以不受到jRuby解析引擎的 影响。


(4)正式解析的第四步是吧@portal_ctx@和@theme_p_w_picpath_path@注解都替换成相应的字符串,这个工作代码如下:

深入Liferay 当页面请求css资源利用parseSass()方法解析

我们的main.css中没有这些标识符,所以不受到影响。


经过以上4步之后,这个方法就返回了dynamicContent,我们可以看到,它最终 返回到浏览器后的结果和正式解析第二步的一样:

深入Liferay 当页面请求css资源利用parseSass()方法解析



以上我们讲解的是对于main.css的资源访问,我们现在来看下对于其中包含的某个用Sass写的文件,比如说上面的forms.css,这个流程又是这么样的。


其实大体上流程都一致:

深入Liferay 当页面请求css资源利用parseSass()方法解析

我这里也想省略去多数重复的调试流程了,反正最终不一样的就是正式解析的第3步,它会去调用jruby.jar中的解析引擎去完成具体的解析,所以我们开始这个forms.css文件的内容是:

深入Liferay 当页面请求css资源利用parseSass()方法解析

解析完成之后,这个文件内容变为:

深入Liferay 当页面请求css资源利用parseSass()方法解析




总结:

(1)当单个加载Sass资源文件的时候,这种Sass资源文件都有可能有解析完的文件缓存在服务器上,并且缓存文件的位置一定在原始资源文件的上级目录下的.sass_cache目录下,并且文件名和原始Sass资源文件名字相同。

(2)正式解析的第一步是解析Sass文件中的静态符号,比如@model_hints_contant....@

(3) 正式解析的第二步是吧所有Sass中的以@import url()形式给出的外部引入,其被引入的资源文件后面都加上请求字符串,这是为了保证其请求的一致性。

(4)正式解析的第三步是调用jRuby的解析引擎根据解析语法进行解析,它会吧所有的Sass语法替换为普通的css语法,这个解析引擎位于webapps/ROOT/WEB-INF/lib/jruby.jar jar包中

(5)正式解析的第四步是做一些收尾工作,吧一些静态标识符@portal_ctx@和@theme_p_w_picpath_path@解析为字符串,因为在css文件中经常会用到一些外部图片参与到样式中。

(6)对于main.css ,主要影响的是(3)

(7)对于main.css包含的某个css,如果它是用Sass语法写的,那么主要受到影响的是(4)

(8)一般的css,如果页面上有一些特殊的记号,那么受到(2)和 (5)影响。