使用TypeScript编写react-native(高效版)



  • TypeScript作为JavaScript的一个富类型扩展语言,深受代码风格严谨的前端开发者欢迎。但在react-native下,因为packager的配置困难,使用TypeScript一直是个麻烦的选择。网上的大部分方案,甚至微软的官方方案都是启动两个进程,一个进程将typescript编译成javascript,另一个进程则是RN默认的packager。

    实际上自从RN的packager独立之后,也多了很多配置的可能性。因此我们也能更高效的在React Native工程中使用TypeScript了。现在我们来尝试进行这样的配置。

    初始化RN工程

    如果你已经有一个RN工程了,可以跳过这一步。

    按照RN中文网的入门文档安装所需的软件,然后初始化项目:

    react-native init MyProject
    cd MyProject
    

    安装TypeScript相关依赖

    yarn add @types/react @types/react-native
    yarn add --dev typescript source-map
    

    配置tsconfig.json

    您可以从您之前的TS项目中复制这个文件,也可以使用tsc初始化(具体参考TypeScript的教程)。注意务必设置"jsx":"react"
    注意多余的注释可能会不兼容,需要移除。

    {
      "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "sourceMap": true,
        "jsx": "react",
        "strict": true,
        "noImplicitAny": true
      },
      "exclude": [
        "node_modules"
      ]
    }
    

    编写一个用于翻译TS代码的transformer

    新建utils/ts-transformer.js,添加如下代码:

    const ts = require('typescript');
    const origin = require("metro-bundler/src/transformer").transform;
    const SourceMap = require('source-map');
    
    function decodeSourceMap(map) {
      const smc = new SourceMap.SourceMapConsumer(map);
      const ret = [];
      smc.eachMapping(m => {
        if (m.name) {
          ret.push([m.generatedLine, m.generatedColumn, m.originalLine, m.originalColumn, m.name]);
        } else {
          ret.push([m.generatedLine, m.generatedColumn, m.originalLine, m.originalColumn]);
        }
      });
      return ret;
    }
    
    exports.transform = function(options) {
      if (/\.tsx?$/.test(options.filename)) {
        const out = ts.transpileModule(options.src, Object.assign({
          fileName: options.filename,
        }, require('../tsconfig.json')));
    
        //var smc = new SourceMapConsumer();
        const map = JSON.parse(out.sourceMapText);
    
        return {
          code: out.outputText,
          filename: options.filename,
          map: options.generateSourceMaps ? map : decodeSourceMap(map),
        };
      }
      return origin(options);
    };
    

    这段代码的作用是根据文件扩展名,ts/tsx选择TypeScript编译器,否则还使用react-native自带的编译器(babel)

    配置rn-cli.config.js,使用自定义的transfomer

    'use strict';
    
    const path = require('path');
    const blacklist = require('metro-bundler/src/blacklist');
    
    const config = {
      getBlacklistRE() {
        return blacklist([
          /\.idea[\/\\].*/,
        ]);
      },
    
      getSourceExts() {
        return ['js', 'json', 'ts', 'tsx'];
      },
    
      getTransformModulePath(foo){
        return require.resolve('./utils/ts-transformer');
      }
    };
    
    module.exports = config;
    

    如果你之前已经有这个文件,注意检查其中的getSourceExtsgetTransformModulePath即可。

    最后修改你的源代码

    入口index.android.jsindex.ios.js的文件名请不要改变,你可以新建src文件夹,在其中建立index.tsx,然后把index.android.jsindex.ios.js的内容都改成

    import './src';
    

    然后在index.tsx里重新编写你的入口代码。

    注意:引用react的方式必须是这样:

    import * as React from 'react';
    import { Component } from 'react';
    

    而初始代码中的

    import React, { Component } from 'react';
    

    会导致错误。

    自由的TS吧,少年

    接下来你可以在工程中自由的使用ts/tsx来编写TypeScript代码啦。


登录后回复