Typescript and Webpack

Typescript and Webpack

叨逼叨

JS生态在近几年经历了不可阻挡的爆发式增长, 一大堆新技术名词的出现让前端程序员有点不胜其烦. 每当你觉得自己差不多跟上潮流的时候, 总有一波大牛出来带节奏, 因为他们发现了比所谓的最佳实践更好的解决方案. 这种不稳定性让大部分前端程序员陷入困惑, 甚至有点无所适从.

The main problem is that things are moving so fast — best practices change every month as someone comes out with a new approach to a problem.

推荐一篇2016年里做前端是怎样一种体验, 看完我默默的说了一声**.

面对这个处境, 我个人认为只能是先挑一个系列的技术栈弄精通, 其他的后面再说, 毕竟谁都没有那么多精力来折腾. 什么叫一个系列的技术栈? 比如以Angular为核心, 衍生出来的生态圈子, Webpack(构建工具), Typescript(开发语言), RxJs(数据流), NgRx(状态管理), Ionic(移动端)等等. 讲真, 这一套大保健下来不是开玩笑的.

到位前为止, 大部分框架随着版本趋于稳定都提供了官方的命令行工具(angular-cli, ionic-cli, vue-cli, create-react-app), 一个命令可以帮助我们快速生成项目结构, 完成很多工具的配置与兼容. 甚至涵盖到模块, 编译, 打包, 测试等等环节. 虽然命令行工具简化了我们的工作, 但是抱着折腾的精神, 我还是要尝试自己搭建TypescriptWebpack的开发环境.

挽起袖子

全局安装依赖包, webpack命令没有提供类似 -init这样的参数, 所以安装了一个webpack-init的包辅助生成webpack.config.js, 自己手动写文件的话不需要安装这个包.

npm install -g typescript tslint webpack webpack-init webpack-dev-server

开始干

新建项目

// 创建目录tw
mkdir tw

// 进入目录tw
cd tw

// 新建文件index.ts inc.ts index.html
touch index.ts inc.ts index.html

生成配置文件

// 生成tsconfig.json
tsc --init

// 生成tslint.json
tslint --init

// 生成package.json
npm init

// 生成webpack.config.js
webpack-init

执行上边的命令, 有需要用户反馈的按情况选择或者直接回车就好了, 具体的配置我们会再修改. 到这一步我们该有的文件就都齐活了. What? So Easy?

ls

//目录结构如下
.
├── inc.ts
├── index.ts
├── package.json
├── tsconfig.json
├── tslint.json
└── webpack.config.js

编辑业务文件

index.ts

import { LOG } from "./inc";
LOG("i am a function from inc.ts");

inc.ts

export const LOG = console.log.bind(console);

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Typescript & Webpack</title>
  </head>
  <body></body>
</html>

打包文件

  1. 安装本地依赖包
npm install ts-loader html-webpack-plugin --save-dev
npm install typescript webpack --save-dev
  1. 修改webpack.config.js
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './index.ts',
  output: {
    //The output directory as an absolute path.
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  resolve: {
      // Add '.ts' and '.tsx' as a resolvable extension.
      extensions: ['.ts', '.tsx', '.js']
  },
  module: {
    loaders: [
      { test: /\.tsx?$/, loader: 'ts-loader', exclude: /(node_modules)/, }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
        template: path.resolve(path.resolve(__dirname), 'index.html'),
        inject: 'body'
    }),
  ]
}

上面的配置指定了输入, 输出, 需要处理的后缀类型, loader, plugin

  1. 编译并执行
webpack
cd dist && open index.html

运行webpack, 在当前目录下生成一个dist文件夹(在上一步output.path中配置), 并将我们的输出文件写在这个目录下.

.
├── dist
│   ├── bundle.js
│   └── index.html
├── inc.ts
├── index.html
├── index.ts
├── node_modules
├── package.json
├── tsconfig.json
├── tslint.json
└── webpack.config.js

在浏览器打开dist/index.html, 观察console的输出

i am a function from inc.ts

调试

每次修改代码都得重新编译, 实在太麻烦, 下面提供两种解决方案.

  1. 启用webpack的watch模式, 监听文件的变化, 并自动进行编译.
webpack -w
  1. webpack-dev-server
    在webpack.config.js中plugins后继续添加devServer的配置如下, 和第一种方式相比, webpack-dev-server要更加强大, 提供了一个web server并且可以live-reload
devServer: {
    contentBase: './dist',
},

上述的配置告诉webpack-dev-server在./dist目录启动一个服务localhost:8080, 地址和端口当然都是可配置的.
试一下在命令行运行

webpack-dev-server --open

浏览器自动打开并且访问localhost:8080, 然后修改你的ts文件, 在浏览器的console会看到以下信息, 然后浏览器就自动刷新了. 怎么样, 超赞的.

[WDS] App updated. Recompiling...
14:42:11.346 bundle.js:3710 [WDS] App updated. Recompiling...
14:42:11.720 bundle.js:3710 [WDS] App updated. Reloading...

代码检查

前端很常见各种linter, 帮助我们检查代码的一些风格, 潜在问题或错误, Typescript也不例外, 还记得在之前我们全局安装了tslint吗? 现在我们需要在本地安装一个tslint-loader

npm install --save-dev tslint-loader

修改webpack.config.js, 在module.rules中添加一条, 注意看enfore属性设置为了pre, 相当于1.x版本的preLoaders

{ test: /\.tsx?$/, loader: 'tslint-loader', enforce: 'pre', exclude: /(node_modules)/, },

修改完后, 再次启动服务. 看到命令行有一些warning,

WARNING in ./inc.ts
[2, 62]: block is empty
[1, 5]: Identifier 'DEBUG' is never reassigned; use 'const' instead of 'let'.

单元测试

额, 又是一大块内容, 之前写过一篇搭建基于Karma和Jasmine的前端单元测试,

  1. 安装相关依赖
npm install karma-cli -g
npm install karma jasmine-core karma-jasmine karma-chrome-launcher karma-sourcemap-loader karma-webpack karma-coverage-istanbul-reporter istanbul-instrumenter-loader @types/jasmine --save-dev

我擦嘞, 这么多东西? 是的, 我也不想这样.
和之前相比, 多了karma-coverage-istanbul-reporter istanbul-instrumenter-loader 用来生成测试报告. @types/jasmine jasmine的类型定义, 修改tsconfig.json

"compilerOptions": {
        "types": [
            "jasmine"
        ]
    },
  1. 配置karma
karma init

上面命令帮我们在项目根目录生成karma.conf.js, 修改其内容如下

module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: [ 'spec.bundle.ts' ],
        plugins: [ 'karma-webpack', 'karma-sourcemap-loader', 'karma-chrome-launcher', 'karma-coverage-istanbul-reporter', 'karma-jasmine' ],
        exclude: [],
        preprocessors: {
            'spec.bundle.ts': ['webpack', 'sourcemap']
        },
        mime: { 'text/x-typescript': ['ts', 'tsx'] },
        reporters: ['progress', 'coverage-istanbul'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        singleRun: false,
        concurrency: Infinity,
        coverageIstanbulReporter: {
            reports: ['html', 'text-summary'],
            dir: './coverage',
            fixWebpackSourcePaths: true,
            skipFilesWithNoCoverage: true,
            'report-config': {
                html: {
                    subdir: 'html'
                }
            }
        },
        webpack: {
            resolve: {
                extensions: ['.ts', '.tsx', '.js']
            },
            devtool: 'inline-source-map',
            module: {
                rules: [
                    { test: /\.tsx?$/, loader: 'ts-loader', exclude: /(node_modules)/, },
                    { test: /\.tsx?$/, loader: 'istanbul-instrumenter-loader', enforce: 'post', exclude: [/(node_modules)/, /\.spec\.ts$/] },
                ]
            }
        }
    })
}
  1. 编写测试文件
    创建一个spec.bundle.ts作为我们的测试入口文件, 并把它添加到files配置中去
    spec.bundle.ts
declare function require(moduleName: string): any;
let testsContext = (require as any).context("./test", true, /\.spec\.ts$/);
testsContext.keys().forEach(testsContext);
mkdir test
cd test
touch inc.spec.ts

inc.spec.ts

import { LOG } from "../inc";

describe('inc.ts', () => {
  it('LOG should be defined', () => {
    expect(LOG).toBeDefined();
    expect(toString.apply(LOG)).toEqual('[object Function]');
  });
});

这一步完成后, 我们的目录结构为

.
├── dist
│   ├── bundle.js
│   └── index.html
├── inc.ts
├── index.html
├── index.ts
├── karma.conf.js
├── package.json
├── spec.bundle.ts
├── test
│   └── inc.spec.ts
├── tsconfig.json
├── tslint.json
└── webpack.config.js
  1. 运行测试
karma start

Chrome弹出新窗口, 命令行控制台输出如下.

Chrome 60.0.3112 (Mac OS X 10.12.5): Executed 1 of 1 SUCCESS (0.046 secs / 0.037 secs)

=============================== Coverage summary ===============================
Statements   : 100% ( 4/4 )
Branches     : 100% ( 0/0 )
Functions    : 100% ( 0/0 )
Lines        : 100% ( 4/4 )
================================================================================

根目录下生成了coverage目录, 我们的测试报告就在这里. 直接进入目录打开index.html即可看到测试的具体覆盖率情况. 超赞的

.
├── coverage
│   └── html
│       ├── base.css
│       ├── inc.ts.html
│       ├── index.html
│       ├── prettify.css
│       ├── prettify.js
│       ├── sort-arrow-sprite.png
│       ├── sorter.js
│       └── spec.bundle.ts.html

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容