FIS+Webpack在后端模板场景下的混合开发模式探索

ticklishpeacock 发布于2年前
0 条问题

FIS+Webpack在后端模板场景下的混合开发模式探索

原创文章,转载请注明出处。

概述

本文介绍了一种FIS + Webpack的混合前端工程化方案,可以较好地适配后端模板场景下的前端开发,经过具体项目实践和沉淀,我们认为该方案对于提升前端开发效率、改进开发模式、提高代码质量都有积极的影响。

 

业务背景

在百度外卖(乃至百度系)的平台项目中,广泛存在的一种模式是:

 

FIS+Webpack在后端模板场景下的混合开发模式探索

这种模式的几个特点:

  • 用户的请求返回依赖于后端模板解析。
  • 前端通过 fis / fis-plus 的smarty方案,配合后端做资源加载、模块化开发、代码部署等。
  • 用户的首次请求基于后端路由(如app/controller/action的模式)。
  • 比起纯前端的工程,可以加载公共数据(如权限、通用方法等)和接口数据,减少请求次数。

 

在这种场景下,FE同学一般的开发模式为:

  • 配合RD同学定好tpl模板路径。
  • 通过fis release到RD同学的开发机进行开发、联调等。
  • 联调完成,fis进行代码打包、压缩等,上线部署到对应线上ODP server。

 

这种开发模式下,前后端的分工明确,流程清晰,便于执行,代码可以分模块组合、上线;百度外卖的许多平台化项目,都是基于这种模式下的、十分庞大的系统。

但是近些年,随着前端社区和技术的迅猛发展,这种开发模式已经逐渐开始暴露出一些问题。

 

后端模板场景下的fis困局

不可否认,fis作为前端工程化集大成的解决方案,本身的设计思想、开发范式仍有很大的实用价值。但是在2015年fis3发布之后,本身的资源投入似乎在逐步减少,核心开发者也趋于沉寂(从github的commit可以看出),而15年至今,前端社区日新月异,新的技术、框架不断涌现,fis在技术更新的浪潮中已经略显乏力。

举例说明:

包管理体系:fis-components vs npm

fis曾经有自己的组件化体系 fis-components , 可以通过 fis install [dependency]的方式安装、管理项目依赖。

但是这种方式的最大痛点是:无法自由添加组件,必须通过给fis官方提PR的方式来添加组件。

这样不仅增加维护成本,而且用户自由度也很低。

fis-components的组件数量仍很少(与npm上的包数量是天壤之别),此方案现在基本已经被废弃,fis官方现在也推荐使用npm来管理第三方组件,推出了node_modules的方案 fis3-hook-node_modules

在一些纯前端的项目中 fis3-hook-node_modules 可以工作得很好。

 

但是在后端模板场景下,以百度外卖某平台为例:

 

FIS+Webpack在后端模板场景下的混合开发模式探索

平台由多个前端代码模块组成,其中:

  • common模块为公共模块,左侧的导航栏都由common模块生成;
  • 右侧为业务模块,继承common模块的布局模板,承载具体业务逻辑。

前端同学平时做的基本是业务模块开发。一个业务模块的smarty模板如下:

{%extends file="common/page/layout.tpl"%}
{%block name="page-main"%}
    <div id="app">
    </div>
    {%script%}
        require("namespace:widget/xxx/index.js").init();
    {%/script%}
{%/block%}

 

可见,业务模块的smarty模板继承了common的layout模板。在这种 跨模块引用的场景下 , 由于fis3-hook-node_modules的解析机制,会把这部分当作npm模块来解析,导致无法编译:

// 使用fis编译时会无法解析
[WARNI] Can’t resolve nerve_common/page/layout.tpl in file [/page/index.tpl], did you miss npm install nerve_common?

所以在这种场景下,无法使用fis3-hook-node_modules。这样造成业务模块中的第三方依赖只能依靠fis-components管理,或自行复制粘贴。对依赖的管理回到了手工时代。

另外,fis3-hook-node_modules也有自己的一些问题,比如对软链形式的包(如cnpm)支持不好,对像vue这样需要 alias 配置的包处理不便等。

 

开发效率:liveReload vs hotReload

前端同学应该对这两个名词非常熟悉。

fis本身是支持liveReload的,通过开启-L参数即可。liveReload会启动一个Server, 在代码修改时可以通知浏览器刷新页面,避免了手动刷新。

而最近几年webpack为代表的构建工具中提供了“热更新”,即hotReload(HMR, Hot Module Replacement)的能力,即:避免刷新页面,只重新加载页面中受代码修改影响的部分。

hotReload相对于liveReload有很大的效率提升,设想以下场景:

  • 一个表单填写需要3个步骤,如果使用liveReload, 要调整第3个步骤的相关代码时,需要再手动填写1,2步骤,观察第3步骤的效果。
  • 需要对应的过滤条件才能显示的UI,修改代码后需要填写对应过滤条件来“恢复现场”。

而hotReload可以很好地解决这些问题,一言概之,这种能力缩短了我们初始化应用及手动恢复应用状态的时间。特别是对web前端这样,UI代码占很重一部分工作,随时需要查看应用样式的编程任务来说,这点更为重要。

但是hotReload本身不是银弹,还需要对应的前端框架写入组件级别的热更新API(loader)才能工作。像React、Vue、Angular这类流行前端框架均已实现了webpack的HMR; 但是fis本身似乎没有开发HMR的计划。

 

异构解析:fis-parser vs webpack-loader

一般地,对于一些异构语言,需要相应的解析处理。例如:

  • Jade/pug/tmpl => html;
  • Less/Sass/Stylus => css;
  • ESnext/JSX/Typescript/Coffee => js

在fis3的构建流程中,始终是同步的;而webpack-loader支持同步和异步。

现在社区中的很多库提供都是异步的接口,外卖的前端同学可能体验比较深刻的例子就是Vue, 官方有支持webpack的vue-loader, 支持Browserify的vueify。

而fis并没有官方的fis-parser-vue, 现在只有民间开发者在维护一些fis-parser-vue的方案 ;而至今为止仍没有在功能性方面和官方的loader媲美的parser插件。

这是其中的一个例子。主要的不便在于:

  • 如果loader本身的处理是异步的,要在fis中能使用需要改造成同步的,而且都是非官方的;
  • loader本身的升级会带来一些新特性(如css-modules), 相应的就会给fis-parser带来维护成本。
  • 新的技术、框架一般会适配主流构建工具而不会适配fis, 如果需要使用,就会在写各种fis-parser上疲于奔命。

 

回顾和目标

说了这么多,可以简单回顾一下我们现有的业务场景:

  • fis + node_module的方案并不能很好适配后端模板项目下的前端开发,造成依赖管理效率低,代码冗余等;
  • fis release -w 到RD开发机的开发效率偏低 (特别是在项目文件较多的情况下);
  • fis3-smarty / fis-plus方案下,很难去享受和尝试一些新技术带来的红利。

那么基于此,我们的目标就是,在后端模板的项目场景下:

  • 能使用npm进行依赖管理;
  • 能使用新的开发方式提高效率,比如hotReload;
  • 能够紧跟社区的脚步,方便地使用新技术来提高生产力。

 

方案的探索

使用webpack构建

基于现在webpack的主流地位,很容易想到使用webpack来构建项目。但是纯webpack方案在这种场景下是行不通的,原因很简单:无法解析smarty模板。

在smarty模板依赖后端结合 fis-smarty-plugin 进行解析的客观条件下,显然是不能搞一个smarty-loader进行解析的;即便做到了解析smarty语法,在webpack的编译期也无法对跨模块的引用进行依赖解析,如前面的common模块的layout模板。

 

使用fis结合node_modules

前文已经有过详细介绍,还是存在着无法跨模块解析的问题。此处不再赘述。

 

fis + webpack混合方案

那么我们换一种思路:让fis负责smarty模板的解析;webpack用于构建业务代码。那么这样,至少npm依赖管理是毫无压力了,webpack天生就对node_modules非常友好。

项目中的业务代码均由webpack构建产出,假设webpack构建产出的模块为bundle.js, bundle.css, fis在smarty模板中require这些产出文件,并把这些bundle推到RD开发机。这样的缺点也很明显:

  • 本地机器需要同时开启webpack --watch和fis3 release --watch, 在模块文件较多的时候会占用较多机器资源,造成机器卡顿。
  • 仍然无法享受hotReload。

那么,有没有一种方法可以两全其美呢?在这里我们的目标进一步被缩小:

  • 不需要在本地启两个服务;
  • 在RD开发机页面上实现hotReload。

我们知道,具有hotReload特性的 webpack-dev-server 一般的使用场景是本地开发(在本地启动一个http server),而我们显然也无法在RD的开发机上「布署」一个webpack-dev-server。

但是,解决问题的钥匙也在这里:

能不能本地启动一个webpack-dev-server, 然后RD开发机连接这个server?

 

经过实践,我们发现这样是可行的,而且进行了方案的整理、开发和实践,最终已经运用到线上项目中。

 

方案介绍

我们现在采用的方案的「指导思想」可以一句话概括:

让fis和webpack各司其职,各自做自己擅长的事。

webpack擅长的事:

  • 与node_modules无缝结合;
  • hotReload;
  • module依赖打包;
  • 代码规范检查,比如eslint(fis也可以做,但是我们认为webpack这种可以实现在页面上加全局layer提示的方式更加友好);
  • fis不具备的一些工程化的实用插件(如用于分析源码文件占比的webpack-bundle-analyzer)

fis擅长的事:

  • 资源定位能力,配合smarty模板、map.json资源表结合后端实现;
  • 方便地推送到远端机器的能力;
  • 依赖声明和同名依赖(sameNameRequire)

方案目的也是最大化二者的能力来为我们服务。

由于fis和webpack都是「命令行工具」, 所以我们可以使用 npm scripts 来将二者的工作流结合。

开发阶段

在开发阶段,开发模式如下图:

 

FIS+Webpack在后端模板场景下的混合开发模式探索

 

前端同学一开始只需要执行以下命令:

$ npm install // 安装依赖
$ npm start -- RDName  // 开始开发

npm start 这条命令实际执行的是:

$ cross-env HOT=true fis3 release RDName -c && npm run dev

具体解释一下:

  1. 首先通过cross-env设置一个环境变量HOT为true.
  2. 进行fis3 release的过程中,如果检测到这个变量有效,会通过一个fis的后处理器 fis-postprocessor-smarty-hmr 对smarty模板文件(*.tpl)进行预处理,往smarty模板中注入一段js,指向本地dev-server。之后执行release, 把修改后的smarty模板推到RD开发机。(这里无需开启fis3的—watch模式)。
  3. 启动一个本地的webpack dev-server.
  4. 访问RD开发机对应的前端页面,此时页面会和本地的webpack-dev-server进行连接。
  5. 本地的IDE修改了代码后,webpack dev-server自动刷新,在RD开发机的前端页面就实现了hotReload。

 

FIS+Webpack在后端模板场景下的混合开发模式探索

 

原理如上图,一言蔽之:

RD开发机页面实际是套着smarty的壳,加载webpack bundle的“心”;

当然,这里需要做一些简单配置,可以让fis和webpack在这种环境下协同工作。

  • fis-conf.js里面需要对fis-postprocessor-smarty-hmr这个插件做一些简单配置,如配置tpl需要加载的bundle, 是否生效等。具体可以看 这里
  • 本地的dev-server基于express和webpack-dev-middleware。要把本地的dev-client(基于webpack-hot-middleware)加入webpack entry, 并强制指向localhost;dev环境下的webpack publicPath也需要强制指向localhost; 否则在RD开发机页面上不会访问localhost://__hmr这个hotReload服务。
  • 显然,开发机页面上访问localhost服务是跨域的,需要将express开启CORS。
  • webpack无需做uglify等压缩操作,统一交给fis;
  • fis无需做babel的parse, 统一交给webpack。

这里一些更细节的配置不再赘述,大体上,webpack的配置都可以在各个前端业务模块中通用,仅有的区别是根据项目不同对 webpack entry 和 fis-postprocessor-smarty-hmr 作一些简单的差异化配置,即可启用。

 

验证阶段

前面的开发阶段,RD开发机页面资源依赖于本地的dev-server。也许有同学会问,如果我把自己电脑一关,别人不就访问不了这个页面?的确是这样的。

在这种情况下,前端同学只要执行以下命令:

$ npm run release:dist -- RDName

这条命令实际做的事:

$ npm run build && fis3 release RDName

实际先执行了webpack bundle的打包,再执行fis release。

这里与前面一阶段的不同就在于 没有设置环境变量HOT , 那么相应的smarty模板就不会走 fis-postprocessor-smarty-hmr的后处理,这样开发机页面加载的就是已经推到开发机的资源。

我们避免在webpack里面执行uglify, 也是为了把这种耗时操作留到 真正需要时 再做(也就是上线阶段)。

提测/上线阶段

提测/上线阶段,在外卖的环境下就是配合持续集成平台做相应的脚本配置,打包出ODP需要的资源。

在这里只需要在持续集成的脚本文件ci.yml中加入相关脚本即可:

Image:
    type : wm-fe-centos6u5
BeforeBuild:
    script : ./beforeBuild.sh
Build:
    script : ./build.sh

beforeBuild.sh只需执行:

$ npm install
$ npm run build

让webpack切换到生产环境配置,打包出bundle文件,接着执行build.sh,由fis接管进行压缩、打包等操作,产出符合ODP规范的包。

这样整个前端开发流程就结束了,fis和webpack分工明确,执行得很好。

总结

前文提到的方案,有以下优势:

  1. 在后端模板的场景下,解决了node_module的包管理问题;
  2. 享受hotReload等其他webpack带来的便利,提高开发效率的同时,可以紧贴社区脚步,方便地实践与运用新技术;
  3. fis和webpack分工合作,可以充分发挥两大工程化方案的价值;
  4. 方案同时可兼容fis3和fis-plus,便于现有项目改造。

目前存在的小问题是,需要同时做fis和webpack的配置,实际需要的配置点虽不多,但是依赖纯手工配置,略显繁琐。但是我们认为在项目初始时,花一些时间来针对项目做具体的配置是「磨刀不误砍柴工」的,毕竟配置完之后,后续就可以延用配置成果了。

而且实际上,webpack的大多数配置可以基本固定,后续我们也将产出一些命令行工具来方便地进行初始化fis+webpack后端模板工程化方案的配置(譬如将webpack配置抽象在cli端,使项目文件更加简洁)。

以上,希望本文能对在后端模板场景下的前端开发同学提供一些思路和探讨。共勉~

 

查看原文: FIS+Webpack在后端模板场景下的混合开发模式探索

  • heavypanda
  • purplebutterfly
  • lazylion
  • goldensnake
  • blackkoala
  • yellowostrich
  • redostrich
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。