当某个文件包含字符 __RESOURCE_MAP__,就会用表结构数据替换此字符。这样的好处是不再固定把表结构写入某一个特定文件,方便定制。

    比如在

    php

    js

    1. var _map = __RESOURCE_MAP__;

    假设上面的 php 和 js 为分析静态资源映射表的程序,那么就省去了读 map.json 的过程。

    当然,如果你想继续像 FIS2 一样的产出 map.json,只需要在模块下新建文件 map.json,内容设置为 __RESOURCE_MAP__ 即可。

    模块化开发

    模块化开发是工程实践的最佳手段,分而治之维护上带来了很大的益处。

    说到模块化开发,首先很多人都会想到 AMD、CMD,同时会想到 require.jssea.js 这样的前端模块化框架。主要给 js 提供模块化开发的支持,之后也增加了对 css、前端模板的支持。这些框架就包含了组件依赖分析、保持加载并保持依赖顺序等功能。

    但在 FIS 中,依赖本身在构建过程中就已经分析完成,并记录在静态资源映射表中,那么对于线上运行时,模块化框架就可以省掉依赖分析这个步骤了。

    声明依赖内置语法中提到了几种资源之间标记依赖的语法,这样模板可以依赖 js、css,js 可以依赖某些 css,或者某个类型的组件可以互相依赖。

    另外,考虑到 js 还需要有运行时支持,所以对于不同前端模板化框架,在 js 代码中 FIS 编译分析依赖增加了几种依赖函数的解析。这些包括

    AMD

    1. define()
    2. require([]);
    3. require('');

    seajs

    1. define()
    2. require('')
    3. sea.use([])

    mod.js (extends commonjs)

    1. define()
    2. require('')
    3. require.async('')
    4. require.async([])

    如上面说到的,这个编译插件只是对编译工具做一下扩展,支持前端模块化框架中的组件与组件之间依赖的函数,以及入口函数来标记生成到静态资源映射表中;另外一个功能是针对某些前端模块化框架的特性自动添加 define

    有了依赖表,但如何把资源加载到页面上,需要额外的FIS 构建插件或者方案支持。

    假设以纯前端(没有后端模板)的项目为例,对于依赖组件的加载就靠插件 。其是一种基于构建工具的加载组件的方法,构建出的 html 已经包含了其使用到的组件以及依赖资源的引用。

    1. // npm install -g fis3-postpackager-loader
    2. fis.match('::package', {
    3. postpackager: fis.plugin('loader', {})
    4. });

    为了方便、统一管理组件以及合并时便利,需要把组件统一放到某些文件夹下,并设置此目录下的资源都是组件资源。

    1. // widget 目录下为组件
    2. fis.match('/widget/**.js', {
    3. isMod: true
    4. });

    通过以上三步,纯前端的模块化开发就可实现。

    总结一下;

    • 编译工具扩展:根据不同前端模块化框架,扩展声明依赖能力
    • 静态资源管理:解析静态资源映射表加载页面用到的组件及其组件的依赖
    • 目录规范:设置某个文件夹下资源标记为依赖

    工具扩展、目录规范,前后端的前端工程项目都需要,其不同之处就在于静态资源管理这部分。

    解决方案封装

    解决方案:解决一系列特定问题的工具、规范、开发、上线支持的方案,被称为 解决方案。前端工程的解决方案一般包括:

    1. 研发规范 + 模块化框架 + 测试套件 + 辅助开发工具

    FIS3 中的包装解决方案,就是把这些集成到一个工具中。

    一个解决方案就是继承自 FIS3 并且支持特定模块化开发、特定模板语言、特定处理流程、研发规范的构建工具。

    封装解决方案的必要性

    • 规范开发,对于特定团队业务,应该有特定的目录规范、模块化框架等
    • FIS3 只提供一个方便定制前端工程的构建系统,每个团队需要怎么样去处理工程需要自己定制,定制会引入大量的 FIS3 插件,解决方案可统一规定引入哪些插件
    • 树立独立技术品牌

    解决方案封装

    准备

    • 方案名 foo
    • 构建工具名字 foo
    • 模板语言 PHP
    • 模块化框架选择 require.js
    • 特定目录规范

    目录规范

    1. /static # 静态资源
    2. /page # 页面
    3. /widget # 组件
    4. /fis-conf.js # 配置文件

    部署规范

    1. foo
    2. foo/bin/foo.js
    3. foo/index.js
    4. package.json
    • 基于 FIS3 配置目录规范和部署规范

      1. //vi foo/index.js
      2. fis.require.prefixes.unshift('foo');
      3. fis.cli.name = 'foo';
      4. fis.cli.info = require('./package.json');
      5. fis.match('*', {
      6. release: '/static/$0' // 所有资源发布时产出到 /static 目录下
      7. });
      8. fis.match('*.php', {
      9. release: '/template/$0' // 所有 PHP 模板产出后放到 /template 目录下
      10. });
      11. // 所有js, css 加 hash
      12. fis.match('*.{js,css,less}', {
      13. useHash: true
      14. });
      15. // 所有图片加 hash
      16. fis.match('image', {
      17. useHash: true
      18. });
      19. // fis-parser-less
      20. fis.match('*.less', {
      21. parser: fis.plugin('less'),
      22. rExt: '.css'
      23. });
      24. fis.match('*.js', {
      25. optimizer: fis.plugin('uglify-js')
      26. });
      27. fis.match('*.{css,less}', {
      28. optimizer: fis.plugin('clean-css')
      29. });
      30. fis.match('*.png', {
      31. optimizer: fis.plugin('png-compressor')
      32. });
      33. fis.match('widget/*.{php,js,css}', {
      34. isMod: true
      35. });
      36. spriter: fis.plugin('csssprites')
      37. });
      38. //fis3-hook-module
      39. fis.hook('module', {
      40. mode: 'amd' // 模块化支持 amd 规范,适应 require.js
      41. });
    • 实现 /bin/foo.js

      1. // vi foo/bin/foo.js
      2. var Liftoff = require('liftoff');
      3. var argv = require('minimist')(process.argv.slice(2));
      4. var path = require('path');
      5. var cli = new Liftoff({
      6. name: 'foo', // 命令名字
      7. processTitle: 'foo',
      8. moduleName: 'foo',
      9. configName: 'fis-conf',
      10. // only js supported!
      11. extensions: {
      12. '.js': null
      13. }
      14. });
      15. cli.launch({
      16. cwd: argv.r || argv.root,
      17. configPath: argv.f || argv.file
      18. }, function(env) {
      19. var fis;
      20. if (!env.modulePath) {
      21. fis = require('../');
      22. } else {
      23. fis = require(env.modulePath);
      24. }
      25. // 配置插件查找路径,优先查找本地项目里面的 node_modules
      26. // 然后才是全局环境下面安装的 fis3 目录里面的 node_modules
      27. fis.require.paths.unshift(path.join(env.cwd, 'node_modules'));
      28. fis.require.paths.push(path.join(path.dirname(__dirname), 'node_modules'));
      29. fis.cli.run(argv, env);
      30. });

      以上代码 copy 过来即可,不需要做大的改动,感兴趣可研究其原理。

    • 依赖的 NPM 包,需要在 package.json 中加上依赖:

      • fis-parser-less 解析 less
      • fis-optimizer-uglify-js 压缩 js,fis3 已内置
      • fis-optimizer-clean-css 压缩 css,fis3 已内置
      • fis-optimizer-png-compressor 压缩 png 图片,fis3 已内置
      • fis3-hook-module 模块化支持插件
      • fis3 fis3 核心
      • minimist
      • liftoff
    • package.json 需要添加

      1. "bin": {
      2. }
    • 发布 foo 到 NPM

    通过以上步骤可以简单封装一个解决方案,FIS3 提供了大量的插件,已经几乎极其简单的配置方式来搞定研发规范的设置,很轻松即可打造完整的前端集成解决方案。

    集成了 fis-plus 的目录规范以及处理插件。实现对 Smarty 模板解决方案的工程构建工具支持。

    基于纯PHP的解决方案

    详细见 纯php静态资源管理方案

    解决问题

    • 支持模块化的开发,使用commonJS或者AMD方案来控制前端JS资源的加载
    • 支持组件化开发,使用组件时能自动加载对应依赖的静态资源
    • 自动分析资源依赖关系,确保依赖资源正常下载
    • 自动把css放顶部、JS放底部输出,提升页面渲染性能
    • 支持收集组件中的内嵌样式或脚本,合并输出

    详细请参见