如何把 小程序 转换为 VUE

创建时间.png 2025-09-12
标签.png JavaScript
阅读量.png 29

背景

在开发中,会遇到这样的场景,我们开发的小程序,想在 PC 上预览效果,或者,低代码平台脱出来的小程序代码,想预览效果。

这些过程,都涉及到一个核心关键步骤,将 小程序代码转换为 VUE 代码。

本文将从几个方面讲讲转换的核心思路

核心方向

转换方向

要完成整个工作,我们需要从这个这四个方面分别做代码转换

具体方案

wxml -> Template

我们先分析 wxml 和 vue template 的语法区别

语法 wxml template
数据绑定 <view>{{ message }}</view> <div>{{ message }}</div>
列表 1. 默认字段<br> <br><view wx:for="{{array}}"><br> {{index}}: {{item.message}}<br></view><br>2. 自定义自定<br> <br><view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName"><br> {{idx}}: {{itemName.message}}<br></view> <li v-for="(item, index) in items"><br> {{ parentMessage }} - {{ index }} - {{ item.message }}<br></li>
条件 <view wx:if="{{length > 5}}"> 1 </view><br><view wx:elif="{{length > 2}}"> 2 </view><br><view wx:else> 3 </view> <div v-if="type === 'A'"> A</div><br><div v-else-if="type === 'B'">B<br></div><br><div v-else-if="type === 'C'">C</div><br><div v-else> Not A/B/C</div>
事件绑定 bindtap="" @click=""

通过分析,我们可以将以上的差异点逐个转化,过程还是比较容易的。

转换过程,我们使用到 cheerio 这个HTML解析工具。

以if语句为例

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

我们的核心逻辑 是 将标签属性 wx:if -> v-if wx:elif -> v-else-if`wx:else -> v-else`

使用 cheerio 转换代码如下

const cheerio = require('cheerio');

/**
 * 预处理HTML,转义可能干扰解析的特殊字符
 * @param {string} html - 原始HTML字符串
 * @returns {string} 处理后的HTML字符串
 */
function sanitizeHtml(html) {
  // 转义可能导致解析问题的特殊字符组合
  return html
    // 处理未闭合的花括号
    .replace(/{{([^}]+)$/, '{{$1}}') // 补全末尾未闭合的}}
    .replace(/^([^{]+)}}/, '{{$1}}') // 补全开头未闭合的{{
    // 转义HTML实体
    .replace(/&(?!amp;|lt;|gt;|quot;|#39;)/g, '&amp;')
    // 处理可能的特殊字符序列
    .replace(/\[\[/g, '&#91;&#91;')
    .replace(/\]\]/g, '&#93;&#93;')
    .replace(/\]\[/g, '&#93;&#91;');
}

/**
 * 将微信小程序的wx:if系列属性转换为Vue的v-if系列属性
 * @param {string} html - 包含wx:if属性的HTML字符串
 * @returns {string} 转换后的HTML字符串
 */
function convertWxIfToVueIf(html) {
  try {
    // 预处理HTML,清理特殊字符
    const sanitizedHtml = sanitizeHtml(html);

    // 加载HTML字符串,使用更兼容的配置
    const $ = cheerio.load(sanitizedHtml, {
      xmlMode: true,
      decodeEntities: false,
      lowerCaseTags: false,
      recognizeSelfClosing: true,
      normalizeWhitespace: false
    });

    // 处理wx:if属性
    $('[wx\:if]').each((i, el) => {
      const $el = $(el);
      const ifValue = $el.attr('wx:if');
      $el.attr('v-if', ifValue);
      $el.removeAttr('wx:if');
    });

    // 处理wx:elif属性
    $('[wx\:elif]').each((i, el) => {
      const $el = $(el);
      const elifValue = $el.attr('wx:elif');
      $el.attr('v-else-if', elifValue);
      $el.removeAttr('wx:elif');
    });

    // 处理wx:else属性
    $('[wx\:else]').each((i, el) => {
      const $el = $(el);
      $el.attr('v-else', '');
      $el.removeAttr('wx:else');
    });

    // 恢复转义的特殊字符
    return $.html()
      .replace(/&#91;&#91;/g, '{{')
      .replace(/&#93;&#93;/g, '}}')
      .replace(/&#93;&#91;/g, '][');

  } catch (error) {
    console.error('转换过程出错:', error);
    // 尝试更严格的错误恢复模式
    try {
      const $ = cheerio.load(html, {
        xmlMode: false,
        decodeEntities: false,
        ignoreParseErrors: true // 忽略解析错误
      });
      return $.html();
    } catch (fatalError) {
      console.error('无法恢复的解析错误:', fatalError);
      return html; // 返回原始内容,避免完全失败
    }
  }
}

// 示例用法
const wxHtml = `
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2 && type[[0]] == 'test'}}"> 2 </view>
<view wx:else> 3 </view>
`;

const vueHtml = convertWxIfToVueIf(wxHtml);
console.log('转换后的Vue模板:');
console.log(vueHtml);

这里有个小优化 wx:elif 作为属性去查询,会报错 throw new Error("Attribute selector didn't terminate");于是将 wx:elif 转化为 wx\:elif再处理。

其余的方法类似,可以使用AI生成,就不一一列举了。

wxs -> script

还是先分析差异点

语法 wxs script
data <br>{<br> data: {<br> array: [1, 2, 3, 4, 5, 1, 2, 3, 4]<br> }<br>}<br> <br>{<br> data() {<br> return {<br> array: [1, 2, 3, 4, 5, 1, 2, 3, 4]<br> }<br> }<br>}<br>
数据赋值 <br>this.setData({<br> myValue: 'leaf'<br> })<br> this.myValue = 'leaf'

先列这些差异点,实际开发中差异点可能更多,我们根据实际问题处理即可

转换JS推荐使用 AST 分析的方法去处理,具体 AST 转化工具可以使用 babel。

以数据赋值为例

this.setData({
  myValue: 'leaf'
})

// 转化为
this.myValue = 'leaf'

转化过程

const babel = require('@babel/core');
const t = require('@babel/types');

// Babel插件:将this.setData({...})转换为直接赋值
const setDataTransformer = {
  visitor: {
    // 访问调用表达式
    CallExpression(path) {
      const node = path.node;

      // 检查是否是this.setData调用
      if (
        t.isMemberExpression(node.callee) &&
        t.isThisExpression(node.callee.object) &&
        t.isIdentifier(node.callee.property, { name: 'setData' }) &&
        node.arguments.length === 1 &&
        t.isObjectExpression(node.arguments[0])
      ) {
        // 获取setData的参数对象
        const dataObject = node.arguments[0];

        // 创建多个赋值表达式语句
        const assignments = dataObject.properties.map(prop => {
          // 处理对象属性
          if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
            const key = prop.key.name;
            return t.expressionStatement(
              t.assignmentExpression(
                '=',
                t.memberExpression(
                  t.thisExpression(),
                  t.identifier(key)
                ),
                prop.value
              )
            );
          }
          // 对于其他情况,返回原属性(不转换)
          return prop;
        });

        // 替换原setData调用为多个赋值语句
        path.replaceWithMultiple(assignments);
      }
    }
  }
};

// 转换函数
function transformCode(code) {
  const result = babel.transform(code, {
    plugins: [setDataTransformer]
  });
  return result.code;
}

// 示例用法
const wxCode = `
// 简单赋值
this.setData({
  myValue: 'leaf',
  count: 100,
  isActive: true
});

// 包含表达式的赋值
this.setData({
  total: this.count + 1,
  message: 'Hello ' + name
});

// 不转换的其他代码
this.otherMethod({
  param1: 'value1'
});
`;

// 执行转换
const transformedCode = transformCode(wxCode);
console.log('转换后的代码:');
console.log(transformedCode);

同上,其他的转换逻辑直接 AI 生成

wxss -> style

微信样式是web样式的子集 因此这部分不用特殊转换,直接复制过来就行

WX API -> Web API

由于 web 端没有 wx 提供的API,如果执行代码,会报错。

解决这个问题,可以在 web 端模拟 wx 的相关 API,比如

// 此处是A页面
wx.navigateTo({
  url: 'B?id=1'
})

可以在web端实现如下

window.wx = {
  navigateTo() {}
}

这里navigateTo不需要真的跳转,只需要能跑通就行

至此整个转换工作已完成

总结

本文主要讲小程序到vue组件的转化思路,提供核心方法和思考链路,很多过程没有写代码,但是可以通过AI 生成。希望整个过程有个抛砖引玉的效果。

原文地址:https://webfem.com/post/wx2vue,转载请注明出处