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をブラウザに表示させます。
できました!