概念,TypeScript + NPM + Webpack

  • TypeScript是微软开发的开源语言,是Javascript的一个超集,设计用来开发大型应用,可以编译为不同版本的Javascript以便不同规格的浏览器执行;当前的TypeScript 3.3版本编译器tsc可以编译到从ES3/ES5/ES6/ES2016/ES2017/ESNext的各种版本,使用静态类型,提供完整的现代语言特性比如接口/泛型/命名空间/类/模块/lambda函数/元组/异步等等,在编写复杂前端应用时能极大的降低时间成本。

  • NPM(Node Package Manager ),是在世界上最大规模的包管理系统,也是Nodejs指定的javascript包管理器,虽然NPM绝不只是做javascript包发行和管理;

  • webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

最近在一个编辑器的项目中,应用Typescript开发,总体上感受非常好,但作为一个前端新手,也踩了不少坑,主要是一些配置和工具设置的问题,简单做个记录:

环境配置

比较简单,略过:

  • IDE 我觉得webstorm比较好用一些,vscode也可以
  • node.js环境安装
  • npm安装Typescript
  • 设置webstorm使用所安装的Typescript版本

用npm init初始化package.json

基本默认一路回车的配置就能用,我这里因为用到了一些node.js的库,所以在编译前端项目的时候要增加browser的配置:

1
2
3
4
5
6
  "browser": {
    "jsdom": false,
    "jsdom/lib/jsdom/living/generated/utils": false,
    "jsdom/lib/jsdom/utils": false,
    "xmldom": false
  },

TypeScript配置文件tsconfig.json

tsconfig.json是tsc编译器的配置文件,当然也可以不使用配置文件,使用tsc命令行参数;以下配置的要点是:

  • 使用commonjs的模块化规范:因为项目要使用npm作为包管理器。
  • 目标代码版本是 es5
  • 使用的lib是es6和dom,当然也可以不指定,在不指定的时候默认由target的版本来决定。
  • 输出sourceMap便于调试

更多可参考 typescript handbook compiler-options 章节:

https://www.typescriptlang.org/docs/handbook/compiler-options.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "exclude": ["node_modules"],
  "compilerOptions": {
    "module"         : "commonjs",
    "outDir"         : "./dist",
    "target"         : "es5",
    "lib"            : [
      "es6",
      "dom"
    ],
    "sourceMap": true
  }
}

其实tsconfig.json中很多都是有默认值的,比如exclude,默认就包含了 node_modules,但习惯还是写清楚一些,避免因为版本导致的配置默认值差异;

引入Webpack,配置文件webpack.config.js

做稍微大一些的项目,免不了要模块化,把逻辑拆分到多个源文件中,而且还会用到一些第三方的软件包,而我们希望做好的产品在发行时尽可能的简单,谁都不希望在页面引用几十个javascript文件,而且还要注意引用的顺序吧,Webpack就能把项目按照你想要的方式,打包成一个或者几个包,而且做好代码压缩甚至混淆,以下配置的目的:

  • 将依赖的包(node_modules目录中),以及所写的项目源码(src目录中),打包到一个dist/posteditor.bundle.js中;
  • 将调用posteditor的DEMO代码main.ts单独打包到dist/main.bundle.js;
  • 页面中只需要引用输出的两个bundle文件即可。
  • 更多参考webpack的文档:

https://webpack.js.org/configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const path = require('path');

module.exports = {
    entry: {
        index: './main.ts'
    },
    output: {
        filename: "[name].bundle.js",
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        modules: [path.resolve('./src'),
            'node_modules'],
        extensions: ['.tsx', '.ts', '.js']
    },
    optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]|[\\/]src[\\/]/,
                    name: "posteditor",
                    chunks: "initial"
                }
            }
        }
    }
};

开发时可以用webpack -d打包,此时输出的bundle不压缩,便于调试;

发布时用webpack -p打包,输出的bundle尺寸较小。

感想

因为一开始很多基本概念没有搞清楚,比如npm、nodejs、webpack、module以及ES不同版本的差异,总是发现磕磕绊绊,最后还是把各种QuickStart读了一遍才恍然大悟;

欲速则不达,前端开发的工具链由于历史沿革的原因各种藤蔓交织;不过始终最明确最简单的解决问题方法,还是去老老实实理解概念、读官方文档,而不是去google错误信息期待得到一个捷径。