把 vue 组件转换为任何 web 类模板
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src
test
LICENSE
README.md
package.json

README.md

vue2any

Transfer vue file to any language code.

解决方案

  1. ast(已暂停)
  2. ssrRender(已暂停)
  3. ssrRenderAst(最终方案)

todo

  1. v-if v-else-if v-else 三目运算 AST的抽象及转译
  2. list 内部嵌套的解析
  3. 组件的嵌套解析
  4. slot的解析处理
  5. 局部组件依赖的处理(组件会被解析为一个vnode而非string + vue文件内的依赖需要转化)
  6. 全局组件的处理
  7. 组件 + v-if 的处理(三目运算的计算从正则迁移至AST即可解决)
  8. 动态组件
  9. 特殊工具方法的处理

工作原理

  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

核心问题:

  1. js 方法无法转换为 PHP 方法
  2. 组件的依赖无法被捕获

相关工具:

相关资料:

AST三板斧:

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

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

已有实践:

解决痛点:通过专业的语法解析工具,避免了单薄可用性低的正则匹配,且可以根据不同的(ES默认方)法来定制转译后的 PHP 方法,以支持 PHP 基本方法的使用,可以参考 js2php

如何安装

npm i vue-to-any --save

基础使用

const VueToAny = require('vue-to-any')

// 实例化(options部分可选)
const vueToAny = new VueToAny({

  // 目标语言(default: php)
  language: 'php',

  // 转译使用的服务端通讯字段,无需$等特殊符(default: PHPDATA)
  variable: 'PHPDATA',

  // 文件是否压缩后输出(default: false)
  minify: false,

  // 是否展示调试信息
  debug: false,

  // 是否输出进度
  progress: true,

  // 分别为 [ 入口/出口 ] 文件的 [ 路径和文件名字 ](必须指定,默认指向开发文件夹)
  entryFile: path.join(__dirname, '/..', '/test', '/vue/', 'Test.vue'),
  outFile: path.join(__dirname, '/..', '/test', '/out/', 'test.php')
})

// 执行转换输出转换
vueToAny.generate()

进阶使用

console.log(vueToAny)

// 插件/中间件(可选),方便分别对 [ 转译前/后 ] 的数据进行中间处理

// 处理转译前的 [ ssrRender ] 数据
vueToAny.useMiddleware('before', ssrRender => {
  return ssrRender
})

// 处理转译后的 [ targetLangCode ] 数据
vueToAny.useMiddleware('after', resultTemplate => {
  return resultTemplate
})

// 移除指定中间件(注意:如果要想移除指定某个中间件,则之前 use 时需要传入函数名字,而不是一个匿名函数实体)
vueToAny.delMiddleware('after', oldAfterMiddleware)

// 移除所有编译后中间件
vueToAny.delMiddleware('after')

// 移除所有中间件
vueToAny.delMiddleware()

// 添加不同语言版本的转译器(参考:transfers/php.js)
vueToAny.addTransfer({
  'ejs': ejsTransfer
})

// 设置使用哪个语言版本进行工作
vueToAny.setTransfer('ejs')

// 更新实例配置对象
vueToAny.updateOptions({ ...newOptions })

使用限制(待完善)

  1. 所有组件内数据的引用均不受任何限制
  2. 所有组件内引用的服务端数据(即约定通信字段),均不能 [ 包含 ] 任何 js 计算
  3. 暂时不支持组件内部引入包含依赖window对象的js文件或库
  4. 组件内部对外部资源的引用比如遵循下面规则:
    • 引用组件必须用 import 语句引入 .vue结尾的相对路径
    • 引用其他资源必须用require语句
    • 全局组件由于转译器不能识别,所以要自己加一个叫 configblock,以json的形式配置路径

假设以SERVERDATA字段为约定字段:

不支持的形式:

<div v-text="parseMethod(SERVERDATA.xxx.content)"></div>
<div>{{ SERVERDATA.xxx.content + '222' }}</div>
<img class="php-img" :src="SERVERDATA.imgSrc || '22'" alt="a image">
<img class="php-img" :src="SERVERDATA.imgSrc ? SERVERDATA.imgSrc : localImage">
<ul>
  <li class="item" v-for="(item, key) in SERVERDATA.arr.filter(a => !!a.id)">
    <div>{{ item.abc + 'string' }}</div>
  </li>
</ul>

支持的形式:

  • 数据输出
  • 数据绑定
  • 数据翻转 (!PHPDATA.xxx)
  • 基本的计算符 (PHPDATA.xxx === 'xxx') (PHPDATA.xxx < 'xxx')
  • 动态 class、style 在 object || array 形式的时候允许和本地数据混合使用
  • 列表输出
  • 各种嵌套
  • 依赖的组件调用
  • transition-group 里的 v-for 元素(包括组件),要加属性:trnasfer="group"
<div>{{ SERVERDATA.name }}</div>
<input type="text" v-model="SERVERDATA.name.model">
<div v-text="SERVERDATA.arr.content"></div>
<div v-html="SERVERDATA.arr.xxx"></div>
<div v-show="SERVERDATA.isShow">show</div>
<div v-if="SERVERDATA.isRender">show</div>
<div v-else-if="SERVERDATA.isRenderElse">show</div>
<div v-else>not show</div>
<div class="static-class" :class="{ 
  active: SERVERDATA.active, first: localData 
}"></div>
<div class="static-class" :class="[
  SERVERDATA.active ? 'active': '', 'static-class', localFitst ? 'first' : ''
]"></div>
<div class="static-class" :style="{ 
  background: SERVERDATA.bg,
  fontSize: localData.fontSize
}"></div>
<ul>
  <li class="item" v-for="item in SERVERDATA.arr">
    <div>{{ item.abc }}string</div>
  </li>
</ul>
<ul>
  <li class="item" v-for="(item, key) in SERVERDATA.arr">
    <div>{{ item.abc }} - {{ key }}</div>
    <div>嵌套循环</div>
      <div v-for="children in item.childrens">
        {{ children.name }}
      </div>
    </div>
  </li>
</ul>

相关指令

# 安装依赖
npm install

# 进入开发模式
npm run dev

# 构建
npm run build

# 测试脚本
npm run test

目录结构

nuxt.js-blog/
   |
   ├──src/                     * 主程序
   │   │
   │   │──index.js             * 核心类
   │   │
   │   └──transfers            * 不同语言的转译器方法
   │   │
   │   └──solution(render-ast) * 核心解决方案
   │       │
   │       │──build.js         * vue-server-render 的衍生版
   │       │
   │       │──parses.js        * 对 ast 函数的匹配和替换
   │       │
   │       └──generate.js      * ast 抽象 + vm 创建 / 文件分析 / 依赖分析 / 转换执行
   │
   │──test/                    * 测试用的文件
   │   │
   │   ├──dev.js               * 开发脚本
   │   │
   │   └──test.js              * 测试脚本
   │   │
   │   ├──vue                  * 用于测试/开发的 vue 组件
   │   │
   │   └──out                  * 测试/开发输出的 php 脚本
   │
   │──package.json             * 包信息
   │
   │──.babelrc                 * Babel配置
   │
   │──.gitignore               * Git忽略文件配置
   │
   └──.editorconfig            * 编码风格配置