Seamlessly Migrate Taro 2.x to 3.x with Babel AST Transformations

This article explains how to upgrade Taro 2.x projects to Taro 3.x by converting source code into an AST using Babel, programmatically editing imports, configs, routers, and styles, and then generating the updated code, complete with practical code examples and migration tips.

Huolala Tech
Huolala Tech
Huolala Tech
Seamlessly Migrate Taro 2.x to 3.x with Babel AST Transformations

Background

Taro is an open‑source cross‑platform framework that supports React, Vue, Nerv and many mini‑program environments. Although Taro 3.x has been stable for over two years, many projects still use 2.x, and a simple find‑and‑replace upgrade often leads to case‑sensitivity errors, ESLint failures, and other issues.

To achieve a more reliable migration, we convert the source code into an AST with babel, edit the tree, and generate new code.

AST and Babel Quick Start

AST (Abstract Syntax Tree) represents the syntactic structure of source code as a tree. Because the AST type system is large, we use AST Explorer to inspect nodes. babel is a toolchain that transforms modern ECMAScript syntax into backward‑compatible JavaScript. The Babel packages used in this article are:

@babel/parser – parses TypeScript/JavaScript into an AST.

@babel/traverse – walks the AST and allows modifications.

@babel/types – creates new node types.

@babel/generator – generates source code from an AST.

import * as parser from '@babel/parser';

const code = `function square(n) {
  return n * n;
}`;
// Parse to AST
const ast = parser.parse(code);

Using @babel/traverse we can define a Visitor to edit the tree:

declare function traverse<T>(
  node: Node,
  handlers: TraversalHandler<T> | TraversalHandlers<T>,
  state?: T,
): void;

Below is an example that changes a function name:

import traverse from '@babel/traverse';
import generate from '@babel/generator';

traverse(ast, {
  FunctionDeclaration(path) {
    if (path.node.id) {
      path.node.id.name = 'x';
    }
  },
});

const resultCode = generate(ast).code;

Migration Practice

API Migration

Import Statements

In Taro 2.x, imports come from @tarojs/taro. In 3.x, React APIs must be imported from react. We locate ImportDeclaration nodes whose source.value equals '@tarojs/taro', separate React‑related specifiers, and rewrite the import list.

traverse(ast, {
  ImportDeclaration(path) {
    if (path.node.source.value === TARO_TOKEN) {
      const specifiers = [];
      path.node.specifiers.forEach(specifier => {
        if (!REACT_IMPORTS.includes(specifier.local.name)) {
          specifiers.push(specifier);
        } else {
          reactSpecifiers.push(specifier);
        }
      });
      path.node.specifiers = specifiers;
    }
  },
});

We also replace Taro.FC and Taro.useState with their React equivalents by handling TSQualifiedName and MemberExpression nodes.

traverse(ast, {
  TSQualifiedName(path) {
    if (REACT_IMPORTS.includes(path.node.right.name)) {
      path.node.left.name = REACT_SPACE;
    }
  },
  MemberExpression(path) {
    const node = path.node;
    if (
      t.isIdentifier(node.property) &&
      REACT_IMPORTS.includes(node.property.name) &&
      t.isIdentifier(node.object) &&
      node.object.name === TARO_SPACE
    ) {
      node.object.name = REACT_SPACE;
    }
  },
});

Config Migration

Class Components

Class‑component configs are stored as ClassProperty nodes. We extract the config property via its key, save the specifiers, and remove the original node.

traverse(ast, {
  ClassProperty(path) {
    const node = path.node;
    pageConfigSpecifiers = node.value.properties;
    path.remove();
  },
});

Function Components

Function‑component configs appear as AssignmentExpression nodes like ComponentName.config = {}. We collect these assignments, then match them with the default‑exported component name to identify the real config and remove the original node.

traverse(ast, {
  AssignmentExpression(path) {
    const node = path.node;
    allConfigSpecifiers[node.left.object.name] = {
      path,
      specifiers: node.right.properties,
    };
  },
  ExportDefaultDeclaration(path) {
    if (!t.isIdentifier(path.node.declaration)) return;
    const findPath = allConfigSpecifiers[path.node.declaration.name];
    if (findPath) {
      pageConfigSpecifiers = findPath.specifiers;
      findPath.path.remove();
    }
  },
});

Router Migration

Replace this.$router (or Taro.useRouter) with getCurrentInstance().router and adjust imports accordingly.

import { getCurrentInstance } from '@tarojs/taro';

class C extends Component {
  current = getCurrentInstance();
  componentWillMount() {
    console.log(this.current.router);
  }
}

function C() {
  const { router } = getCurrentInstance();
}

Style Migration

In Taro 3.x the concepts of externalClasses and addGlobalClass are removed. We delete any static class properties whose key matches these style‑config keys.

traverse(ast, {
  ClassProperty(path) {
    const node = path.node;
    if (node.static && STYLE_CONFIG_KEYS.includes(node.key.name)) {
      path.remove();
    }
  },
});

Code Generation

After all AST edits, we generate the final source code and write it to a file.

const code = generate(ast).code;
fs.writeFileSync(filePath, code);

Conclusion

By leveraging Babel we successfully migrated a Taro 2.x codebase to Taro 3.x, handling API, config, router, and style changes. The process deepened our understanding of AST manipulation and demonstrated Babel’s powerful, versatile capabilities.

References

[1]

Upgrade guide: https://taro-docs.jd.com/docs/migration [2] AST Explorer: https://astexplorer.net/ [3] Babel: https://www.babeljs.cn/docs [4] @babel/parser: https://www.babeljs.cn/docs/babel-parser [5] @babel/traverse: https://www.babeljs.cn/docs/babel-traverse [6] @babel/types: https://www.babeljs.cn/docs/babel-types [7] @babel/generator: https://www.babeljs.cn/docs/babel-generator [8] Component style docs: https://taro-docs.jd.com/docs/component-style [9] Full demo code: https://github.com/mvpleung/taro2-taro3

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendmigrationJavaScriptASTbabelTaro
Huolala Tech
Written by

Huolala Tech

Technology reshapes logistics

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.