React &&webpack&&引入typescript

585 阅读6分钟

typescript 简介

官方介绍

TypeScript是一种由微软开发自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发。

TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作。TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性。 TypeScript支持为已存在的JavaScript库添加类型信息的头文件,扩展了它对于流行的库如jQuery,MongoDB,Node.js和 D3.js的好处。

typescript 特点介绍

  1. JavaScript + 类型声明
function getRandomArray(): number[] {
    // 定义数字类型的数组 arrayA
    const arrayA: number[] = [1, 2, 3, 4, 5, 6, 7, 8]; 
    // 定义数字类型的数组 arrayB
    const arrayB: number[] = new Array<number>();
    let index: number; // 从0到9循环
    for (let i = 0; i < 10; i++) {
        // 生成一个比arrayA长度小的随机数,作为下标
        index = Math.floor(Math.random() * arrayA.length); // 取出arrayA指定下标的值放入arrayB
        arrayB.push(arrayA[index]);
    }
    console.log('已随机生成一个数组', arrayB);
    return arrayB;
}

代码中,根据数组A,生成随机数组。这里面除了方法和变量的定义时后面跟了一个":number[]"、":number"外,完全就是一段标准的JavaScript代码。这就是TypeScript的第一个特点:在JavaScript的基础上进行了扩展,增加了类型,同时完全兼容JavaScript语法

  1. JavaScript + 面向对象
// 用户实体类
class User {
    // 姓名 字符串类型
    private name: string; // 年龄 数字类型

    private age: number; // 带参数的构造方法 传入参数为 姓名,年龄

    public constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    // 获取姓名
    public getName(): string {
        return this.name;
    }

    // 获取年龄
    public getAge(): number {
        return this.age;
    }
}

这段代码是用TypeScript声明的一个实体类.这就是TypeScript的第二个特点:这就是TypeScript的第二个特点:在JavaScript的基础上添加了基于类的面向对象编程。 支持未来的ES6甚至ES7,最近的更新都与此有关。在TypeScript中,你可以直接使用ES 6的最新特性,在编译时它会自动编译到ES 3或ES 5。

为什么要用typescript

解决JavaScript的“痛点”:弱类型和没有命名空间,导致很难模块化,不适合开发大型程序。另外它还提供了一些语法糖来帮助大家更方便地实践面向对象的编程。

1.编译时的强类型

TypeScript设计了一套类型机制来保证编译时的强类型判断。

最简单的,你可以申明变量的类型,那么任何其他类型的赋值将会引起编译错误。

例如

var foo: string;
foo = true; // error: Cannot convert 'boolean' to string

TypeScript会对赋值的变量进行类型推断

var bar = 0;
bar = ''; // error: Cannot convert 'string' to 'number'

强类型还有一个最大好处就是智能提示,例如你可以知道当前变量具有哪些属性和方法

2. 静态类型

二者的主要区别为:TypeScript是静态类型,js是动态类型

但这不意味着两者差距多大,只是类型检查的时机不同而已,TS和js根本上的差别就这一点,然而其意义却举足轻重

静态类型检能将调试从运行期提前到编码期,诸如类型检查、越界检查这样的功能才能真正发挥作用。TypeScript的开发体验远远超过以往纯JavaScript的开发体验,无需运行程序即可修复潜在bug

3.语法糖

TypeScript可以实现类,接口,枚举,泛型,方法重载等,用简洁的语法丰富了JavaScript的使用。

4.已有的类库可以很方便的使用 TS可以重用现有的JavaScript代码,调用流行的JavaScript库

项目目录

+-- dist/                                  ---打包的静态文件目录
+-- node_modules/                           ---npm下载文件目录
+-- public/
|   --- index.html							---首页入口html文件
+-- logs/                                 ---日志存放目录
+-- server/                                 ---node服务代码目录
+-- webpack                                 --- webapck 配置
+-- src/                                    ---核心代码目录
|   +-- apis                               ---http请求存放目录
|   |    --- index.js
|   +-- components                      ---各式各样的组件存放目录
|   |    +-- xxxx                      ---组件
|   +-- pages                           ---页面存放位置
|   |    +-- xxxx                      ---页面
|   --- app.js                              ---组件入口文件
|   --- index.js                            ---项目的整体js入口文件,包括路由配置等
--- .eslintrc                           ---自定义eslint配置文件,js 与ts 共存检查
--- tsconfig.json                       --- ts 编译配置文件
--- package.json

安装依赖

sudo cnpm i typescript ts-loader -D

ts-loader可以让Webpack使用TypeScript的标准配置文件tsconfig.json编译TypeScript代码

sudo cnpm i @types/lodash @types/react @types/react-redux @types/react-router-dom -D

使用@types/前缀表示我们额外要获取React和React-DOM的声明文件。 通常当你导入像 "react"这样的路径,它会查看react包;然而,并不是所有的包都包含了声明文件,所以TypeScript还会查看 @types/react包。

添加TypeScript配置文件

我们想将TypeScript文件整合到一起-这包括我们写的源码和必要的声明文件。 我们需要创建一个tsconfig.json文件,它包含了输入文件列表以及编译选项。 在工程根目录下新建文件 tsconfig.json文件,添加以下内容:

{
  "compilerOptions": {
    "types": [
      "node",
      "react",
      "react-redux",
      "react-router-dom",
      "lodash",
    ],
    "typeRoots": [
      "./types"
    ],
    "noEmit": true,
    "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017',
    'ES2018' or 'ESNEXT'. */ /* Specify library files to be included in the compilation. */
    "allowJs": true, /* Allow javascript files to be compiled. */
    "checkJs": false,
    "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    "sourceMap": false, /* Generates corresponding '.map' file. */
    "preserveConstEnums": true, /*Do not erase const enum declarations in generated code*/
    "skipLibCheck": true, /* Skip type checking of all declaration files  */
    "removeComments": false, /* Do not emit comments to output. */
    "strict": true, /* Enable all strict type-checking options. */
    "strictNullChecks": true, /* Enable strict null checks. */
    "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
    "noUnusedLocals": true, /* Report errors on unused locals. */
    "noUnusedParameters": false, /* Report errors on unused parameters. */
    "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "baseUrl": ".", /* Base directory to resolve non-absolute module names. */
    "paths": {
      "@/*": [
        "./src/*"
      ]
    },
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  }
}

配置说明

  1. "types": ["node","react","react-redux","react-router-dom","lodash"], 禁用自动引入@types包,默认所有可见的"@types"包会在编译过程中被包含进来
  2. "typeRoots": ["./types"] 指定要解析的类型定义目录为根目录下的types 文件
  3. "noEmit": true 不需要生成文件
  4. "target": "es6" typescript 直接转译es5,速度不如babel 转译,故配合babel 处理
  5. "allowJs": true 由于js项目引入ts ,故先期兼容js
  6. "checkJs": false 不对引入的js 文件做语法检查
  7. "jsx": "preserve", 不对jsx 语法转译,配合babel 处理
  8. "sourceMap": false开发过程中会延缓编译速度,并且实用性不是很高

webpack 配置文件

const dirSrc = path.resolve('./src');
const dirNodeModule = /node_modules/;

const babelLoader = {
        loader: 'babel-loader',
        options: {
            cacheDirectory: true,
            presets: [
                [
                    '@babel/env',
                    {
                        targets: {
                            browsers: ['last 2 versions', 'ie >= 7', 'node>=6']
                            // 支持每个浏览器最近的两个版本和IE大于等于7以及node 6 以上的版本所需的polyfill和代码转
                        },
                        modules: false,
                        useBuiltIns: 'usage',
                        corejs: 3
                    }
                ],
                '@babel/react'
            ],
            plugins: [
                [
                    'import',
                    {
                        libraryName: 'antd',
                        style: true
                    }
                ],
                [
                    '@babel/plugin-transform-runtime',
                    {
                        corejs: 2
                    }
                ],
                '@babel/transform-modules-commonjs',
                '@babel/proposal-class-properties',
                '@babel/proposal-object-rest-spread'
            ]
        }
    };
    const jsxRule = {
        test: /\.jsx?$/,
        exclude: [dirNodeModule, /dist/],
        use: ['cache-loader', 'thread-loader', babelLoader]
    };

    const tsxRule = {
        test: /\.tsx?$/,
        include: [dirSrc],
        exclude: [dirNodeModule, /dist/],
        use: [
            'cache-loader',
            'thread-loader',
            babelLoader,
            {
                loader: 'ts-loader',
                options: {
                    transpileOnly: true
                }
            }
        ]
    };

添加ts-loader 后webpack 编译速度下降

因为 ts-loader 每次都要跑一边类型检查,awesome-typescript-loader 会另开线程进行类型检查,但是实际使用后,awesome-typescript-loader的编译速度并没有提升很多。所以参考文档,加入了cache-loader 用来缓存编译结果,thread-loader 用来分解loader 压力,同时关闭了ts-loader 的类型检查功能。得益于vs code 支持对ts的语法检测

官方参考文档

TypeScript简介