tamuraです。

最近、周りがReactなのでReactをやってみます。
で、TypeScriptのほうが書きやすいんではないか?と思い、TypeScriptでやってみます。


はじめに

https://www.typescriptlang.org/docs/handbook/react-&-webpack.html

これをやっていきます。

package.json作成まで

ディレクトリを作ってpackage.jsonを定義します。

$ mkdir webpack-demo
$ cd webpack-demo
$ mkdir src
$ cd src
$ mkdir components
$ cd ..
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (webpack-demo) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Users/shingo/prog/webpack-demo/package.json:

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) yes

webpackインストール

$ npm install -g webpack

出力内容はとても長いので割愛します。

ライブラリのダウンロード

react, react-domを入れていきます。 typescript用のファイルも一緒です。

$ npm install --save react react-dom @types/react @types/react-dom
webpack-demo@1.0.0 /Users/shingo/prog/webpack-demo
├── @types/react@16.0.28 
├─┬ @types/react-dom@16.0.3 
│ └── @types/node@8.0.57 
├─┬ react@16.2.0 
│ ├─┬ fbjs@0.8.16 
│ │ ├── core-js@1.2.7 
│ │ ├─┬ isomorphic-fetch@2.2.1 
│ │ │ ├─┬ node-fetch@1.7.3 
│ │ │ │ ├─┬ encoding@0.1.12 
│ │ │ │ │ └── iconv-lite@0.4.19 
│ │ │ │ └── is-stream@1.1.0 
│ │ │ └── whatwg-fetch@2.0.3 
│ │ ├─┬ promise@7.3.1 
│ │ │ └── asap@2.0.6 
│ │ ├── setimmediate@1.0.5 
│ │ └── ua-parser-js@0.7.17 
│ ├─┬ loose-envify@1.3.1 
│ │ └── js-tokens@3.0.2 
│ ├── object-assign@4.1.1 
│ └── prop-types@15.6.0 
└── react-dom@16.2.0 

npm WARN webpack-demo@1.0.0 No description
npm WARN webpack-demo@1.0.0 No repository field.
$

最後の警告は無視して問題ありません。

開発用ツールのダウンロード

開発時に使うものを落としていきます。 ちょっと長いです。

$ npm install --save-dev typescript awesome-typescript-loader source-map-loader
webpack-demo@1.0.0 /Users/shingo/prog/webpack-demo
├─┬ awesome-typescript-loader@3.4.1 
│ ├── colors@1.1.2 
│ ├─┬ enhanced-resolve@3.3.0 
│ │ ├── graceful-fs@4.1.11 
│ │ ├─┬ memory-fs@0.4.1 
│ │ │ ├─┬ errno@0.1.5 
│ │ │ │ └── prr@1.0.1 
│ │ │ └─┬ readable-stream@2.3.3 
│ │ │   ├── core-util-is@1.0.2 
│ │ │   ├── inherits@2.0.3 
│ │ │   ├── isarray@1.0.0 
│ │ │   ├── process-nextick-args@1.0.7 
│ │ │   ├── safe-buffer@5.1.1 
│ │ │   ├── string_decoder@1.0.3 
│ │ │   └── util-deprecate@1.0.2 
│ │ └── tapable@0.2.8 
│ ├─┬ loader-utils@1.1.0 
│ │ ├── big.js@3.2.0 
│ │ ├── emojis-list@2.1.0 
│ │ └── json5@0.5.1 
│ ├── lodash@4.17.4 
│ ├─┬ micromatch@3.1.4 
│ │ ├── arr-diff@4.0.0 
│ │ ├── array-unique@0.3.2 
│ │ ├─┬ braces@2.3.0 
│ │ │ ├── arr-flatten@1.1.0 
│ │ │ ├─┬ fill-range@4.0.0 
│ │ │ │ ├─┬ is-number@3.0.0 
│ │ │ │ │ └─┬ kind-of@3.2.2 
│ │ │ │ │   └── is-buffer@1.1.6 
│ │ │ │ ├── repeat-string@1.6.1 
│ │ │ │ └── to-regex-range@2.1.1 
│ │ │ ├── isobject@3.0.1 
│ │ │ ├── repeat-element@1.1.2 
│ │ │ ├─┬ snapdragon-node@2.1.1 
│ │ │ │ └─┬ snapdragon-util@3.0.1 
│ │ │ │   └── kind-of@3.2.2 
│ │ │ └─┬ split-string@3.1.0 
│ │ │   └─┬ extend-shallow@3.0.1 
│ │ │     └─┬ is-extendable@1.0.1 
│ │ │       └── is-plain-object@2.0.4 
│ │ ├─┬ define-property@1.0.0 
│ │ │ └─┬ is-descriptor@1.0.1 
│ │ │   ├─┬ is-accessor-descriptor@0.1.6 
│ │ │   │ └── kind-of@3.2.2 
│ │ │   ├─┬ is-data-descriptor@0.1.4 
│ │ │   │ └── kind-of@3.2.2 
│ │ │   └── kind-of@5.1.0 
│ │ ├─┬ extend-shallow@2.0.1 
│ │ │ └── is-extendable@0.1.1 
│ │ ├─┬ extglob@2.0.2 
│ │ │ └─┬ expand-brackets@2.1.4 
│ │ │   ├─┬ define-property@0.2.5 
│ │ │   │ └─┬ is-descriptor@0.1.6 
│ │ │   │   └── kind-of@5.1.0 
│ │ │   └── posix-character-classes@0.1.1 
│ │ ├─┬ fragment-cache@0.2.1 
│ │ │ └── map-cache@0.2.2 
│ │ ├── kind-of@6.0.2 
│ │ ├─┬ nanomatch@1.2.6 
│ │ │ ├── is-odd@1.0.0 
│ │ │ └── kind-of@5.1.0 
│ │ ├── object.pick@1.3.0 
│ │ ├── regex-not@1.0.0 
│ │ ├─┬ snapdragon@0.8.1 
│ │ │ ├─┬ base@0.11.2 
│ │ │ │ ├─┬ cache-base@1.0.1 
│ │ │ │ │ ├─┬ collection-visit@1.0.0 
│ │ │ │ │ │ ├── map-visit@1.0.0 
│ │ │ │ │ │ └── object-visit@1.0.1 
│ │ │ │ │ ├── get-value@2.0.6 
│ │ │ │ │ ├─┬ has-value@1.0.0 
│ │ │ │ │ │ └─┬ has-values@1.0.0 
│ │ │ │ │ │   └── kind-of@4.0.0 
│ │ │ │ │ ├── set-value@2.0.0 
│ │ │ │ │ ├─┬ to-object-path@0.3.0 
│ │ │ │ │ │ └── kind-of@3.2.2 
│ │ │ │ │ ├─┬ union-value@1.0.0 
│ │ │ │ │ │ └── set-value@0.4.3 
│ │ │ │ │ └─┬ unset-value@1.0.0 
│ │ │ │ │   └─┬ has-value@0.3.1 
│ │ │ │ │     ├── has-values@0.1.4 
│ │ │ │ │     └── isobject@2.1.0 
│ │ │ │ ├─┬ class-utils@0.3.5 
│ │ │ │ │ ├── arr-union@3.1.0 
│ │ │ │ │ ├─┬ define-property@0.2.5 
│ │ │ │ │ │ └─┬ is-descriptor@0.1.6 
│ │ │ │ │ │   └── kind-of@5.1.0 
│ │ │ │ │ └─┬ static-extend@0.1.2 
│ │ │ │ │   ├─┬ define-property@0.2.5 
│ │ │ │ │   │ └─┬ is-descriptor@0.1.6 
│ │ │ │ │   │   └── kind-of@5.1.0 
│ │ │ │ │   └─┬ object-copy@0.1.0 
│ │ │ │ │     ├── copy-descriptor@0.1.1 
│ │ │ │ │     ├─┬ define-property@0.2.5 
│ │ │ │ │     │ └─┬ is-descriptor@0.1.6 
│ │ │ │ │     │   └── kind-of@5.1.0 
│ │ │ │ │     └── kind-of@3.2.2 
│ │ │ │ ├── component-emitter@1.2.1 
│ │ │ │ ├─┬ mixin-deep@1.3.0 
│ │ │ │ │ ├── for-in@1.0.2 
│ │ │ │ │ └── is-extendable@1.0.1 
│ │ │ │ └── pascalcase@0.1.1 
│ │ │ ├─┬ debug@2.6.9 
│ │ │ │ └── ms@2.0.0 
│ │ │ ├─┬ define-property@0.2.5 
│ │ │ │ └─┬ is-descriptor@0.1.6 
│ │ │ │   └── kind-of@5.1.0 
│ │ │ ├─┬ source-map-resolve@0.5.1 
│ │ │ │ ├── atob@2.0.3 
│ │ │ │ ├── decode-uri-component@0.2.0 
│ │ │ │ ├── resolve-url@0.2.1 
│ │ │ │ ├── source-map-url@0.4.0 
│ │ │ │ └── urix@0.1.0 
│ │ │ └─┬ use@2.0.2 
│ │ │   ├─┬ define-property@0.2.5 
│ │ │   │ └─┬ is-descriptor@0.1.6 
│ │ │   │   └── kind-of@5.1.0 
│ │ │   └─┬ lazy-cache@2.0.2 
│ │ │     └── set-getter@0.1.0 
│ │ └─┬ to-regex@3.0.1 
│ │   └─┬ define-property@0.2.5 
│ │     └─┬ is-descriptor@0.1.6 
│ │       └── kind-of@5.1.0 
│ ├─┬ mkdirp@0.5.1 
│ │ └── minimist@0.0.8 
│ └─┬ source-map-support@0.4.18 
│   └── source-map@0.5.7 
├─┬ source-map-loader@0.2.3 
│ ├── async@2.6.0 
│ ├── loader-utils@0.2.17 
│ └── source-map@0.6.1 
└── typescript@2.6.2 

npm WARN webpack-demo@1.0.0 No description
npm WARN webpack-demo@1.0.0 No repository field.

tsconfig.jsonの作成

tsconfig.jsonを作っていきます。

$ ed tsconfig.json
tsconfig.json: No such file or directory
i
{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react"
  },
  "include": [
    "./src/**/*"
  }
}
.
w
209
q

okです。

Reactコンポーネントの作成

Helloコンポーネントを src/components 下に作ります。

$ ed src/components/Hello.tsx
src/components/Hello.tsx: No such file or directory
i
import * as React from "react";

export interface HelloProps { compiler: string; framework: string; }

export const Hello = (props: HelloProps) => <h1>Hello from {props.compiler} and {props.framework}!</h1>;
.
w
208
q

単一の引数を受け取って、React Elementを返す関数はReactのコンポーネントとして扱えるみたいです。

最後の部分はこう書くこともできるそうです。

export class Hello extends React.Component<HelloProps, {}> {
  render() {
    return <h1>Hello from {this.props.compiler} and {this.props.framework}!</h1>;
  }
}

indexの作成

Helloコンポーネントを呼び出す部分(?)を作成していきます。

$ ed src/index.tsx
src/index.tsx: No such file or directory
i
import * as React from "react";
import * as ReactDOM from "react-dom";

import { Hello } from "./components/Hello";

ReactDOM.render(
  <Hello compiler="TypeScript" framework="React" />,
  document.getElementById("example")
);
.
w 
227
q

最後にHTMLを作成します。

$ ed index.html
index.html: No such file or directory
i
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
  </head>
  <body>
    <div id="example"></div>

    <!-- Dependencies -->
    <script src="./node_modules/react/umd/react.development.js"></script>
    <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>

    <!-- Main -->
    <script src="./dist/bundle.js"></script>
  </body>
</html>
.
w
406
q

webpack設定

webpack用の設定を行います。

$ ed webpack.config.js
webpack.config.js: No such file or directory
i
module.exports = {
  entry: "./src/index.tsx",
  output: {
    filename: "bundle.js",
    path: __dirname + "/dist"
  },

  // Enable sourcemaps for debugging webpack's output.
  devtool: "source-map",

  resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: [".ts", ".tsx", ".js", ".json"]
  },

  module: {
    rules: [
      // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
      { test: /\.tsx?$/, loader: "awesome-typescript-loader" },

      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      { enforce: "pre", "test": /\.js$/, loader: "source-map-loader" }
    ]
  },

  // When importing a module whose path matches one of the following, just
  // assume a corresponding global variable exists and use that instead.
  // This is important because it allows us to avoid bundling all of our
  // dependencies, which allows browsers to cache those libraries between builds.
  externals: {
    "react": "React",
    "react-dom": "ReactDOM"
  },
};
.
w
1067
q

ビルド

ビルドします。

$ webpack

[at-loader] Using typescript@2.6.2 from typescript and "tsconfig.json" from /Users/shingo/prog/webpack-demo/tsconfig.json.


[at-loader] Checking started in a separate process...

[at-loader] Ok, 0.583 sec.
Hash: 4e0027c7639f8f63e2fc
Version: webpack 3.10.0
Time: 2020ms
        Asset     Size  Chunks             Chunk Names
    bundle.js  3.38 kB       0  [emitted]  main
bundle.js.map  3.75 kB       0  [emitted]  main
   [1] ./src/index.tsx 332 bytes {0} [built]
    + 3 hidden modules

確認

dist下のindex.htmlをブラウザに表示させます。


24

できました!