This website requires JavaScript.
BING
Because the mountain is there
OG

一点都不低成本的转码器

2,935 words, 7 min read2017/08/04 AM4,734 views

一个低成本兼容现有业务系统的 Vue & PHP 直出方案,然而此刻发现它不是那么的实用,因为这个方案的出发点就很浑浊。

为什么

基本上是纯粹的工作需要,需要重构公司 X 项目,老项目 jQuery vue.js Vue2 鱼龙混杂,由于历史和实际原因,无法在生产环境上 Node.js,所以需要探索一套方案,满足下面需求:

  • 保证以 vue-cli 初始化的 Webpack 项目作为基本开发方式
  • 满足服务端渲染的需求
  • 保证服务端稳定、高可用

做什么

实际上业务中的需求有这些:

  • 路由由服务端管理,也意味着客户端一般都是以单页面应用为单位
  • 仅需要服务端渲染关键性数据,可以循环渐进地实现转换
  • 有可能需要转换出多语言版本的模板,所以工具需要可配置

谁做了

总结了下,目前社区能找到的各种方案及我要实现的方案如下:

1. SPA

也就是官方的 SPA 模式,不多赘述。

2. node-ssr-render

也是基于官方提供的 SSR 解决方案,由于一些设计和实现问题,目前运行效率太低,CPU 和 内存一定会牺牲一个参数,基本无法用于生产环境。

3. php-ssr-render

来自 GitHub 的国内开发者可发的 PHP 版本的 Vue 渲染器,简单说就是把 node-ssr-render 的核心渲染机制用 PHP 重新实现一边,但是由于开发跟进和兼容等问题,无法满足稳定高可用的需求。

项目及地址: vue-php

4. node-transfer

也就是我正在做的事,简单说就是写一个 Node.js 解析器,把 .vue 文件转码为 .php 模板文件,拿去给 PHP 渲染输出,同时加载 Vue SPA 项目本身的 Js 脚本,剩下的流程就和 vue ssr render 基本一致了。

最新补充:其实这件事不是想的那么简单,严格说应该是 SSR + Transfer。

怎么做

OK,Js 脚本问题通过 Webpack 本身就可以解决了,剩下的就是解析器怎么做。

首先,将 .vue 文件解析为 .php 可以有三个思路:

1. 正则

.vue 文件的模板模板读取,直接使用正则将不同的指令替换为 PHP 相关语句,如遇到类似组件之类的,则根据配置好的路径对相应组件进行替换,同时需要对 props 进行处理。

此方法可能无法实现完全的转换,如遇到 slot 之类的组件就懵逼了。

2. AST

同样的读取模板,将模板使用工具转换为 AST(抽象语法树),之前尝试了一些开源的解析器去解析,但是目前探索到的最佳方案应该是 Vue 官方提供的 vue-template-compiler

严格说这是一个编译器,能把我们的单文件 Vue 组件编译为 render 函数,同时也支持 AST 模板数据的输出。

抽象为 AST 后,所有的组件就变成一个 JSON 树,我们就可以对这个树进行递归循环处理,最终产出 PHP 代码。

3. VNODE

上图中的 4 种方案里,3 和 4 都是自建了 vDom 环境,将各种组件之间的关系计算完成后输出的 HTML,实际上自建 vDom 就意味着又遇到了上面的 开发跟进、兼容性问题上,而且 vDom 的最终输出可能并不是我们所需要的 “模板”,所以这个方法暂时不考虑。

2017-09-07 补充:

这件事比我想象中的复杂大概有10倍,最终代码大概 1500 行,耗时一个月。

最终使用了一个比较 “复杂” 的方案:

  1. .vue 组件 > vue-template-compiler > ssrRender
  2. ssrRender > esprima > js-ast
  3. js-ast > estraverse(各种替换/解析处理) > result-js-ast
  4. result-js-ast > escodegen > result-ssrRender
  5. result-ssrRender > vue-server-rander > html-php-code
  6. 递归处理 html-php-code 中无法解析的 vnode(component) > html-php-code

其中还涉及针对组件本身的依赖和解析的复杂处理

相关工具:

  • esprima: link (将代码转换为标准 AST)
  • espree: link
  • escodegen: link (将 AST 生成为标准 Code)
  • estraverse: link (遍历并且更新抽象语法树)

相关资料:

AST 三板斧:

  • 通过 esprima 把源码转化为 AST
  • 通过 estraverse 遍历并更新 AST
  • 通过 escodegen 将 AST 重新生成源码

esprima 文档:

  1. http://esprima.readthedocs.io/en/latest/getting-started.html#using-node-js-to-play-with-esprima
  2. http://www.cnblogs.com/ziyunfei/p/3183325.html
  3. http://blog.csdn.net/dear_mr/article/details/72587908

已有实践:

剩下的,我抽时间写一个详细的实现思路。

完。

Creative Commons BY-NC 4.0https://surmon.me/article/66
8 / 8 comments
Guest
Join the discussion...
  • 狮子
    狮子🇨🇳CNShanghaiMac OSChrome
    #935

    ~~~

    • Surmon
      Surmon🇨🇳CNShanghaiMac OSChrome
      #936

      reply:

      哇哦~

  • <script>alert(1)</script>\
    <script>alert(1)</script>\🇨🇳CNShanghaiMac OSChrome
    #934

    大神,收下小弟的膝盖

  • 腊肠
    腊肠🇨🇳CNShenzhenAndroidChrome
    #458

    grbrgg

  • 西高新王祖贤
    西高新王祖贤🇨🇳CNXi'anWindowsChrome
    #372

    😃

  • Surmon
    Surmon🇨🇳CNXiamenMac OSChrome
    #369

    ...

  • Tikyz
    Tikyz🇨🇳CNBeijingWindowsChrome
    #367

    哈哈 加油 博主棒棒哒

  • null
    Null🇨🇳CNBeijingWindowsChrome
    #365

    这是你大半夜不睡觉的理由么。。。