TypeScriptおよびReact Webアプリケヌションを開発するための環境「hello world」から最新のSPAたで。 パヌト1

この蚘事の目的は、最新のWebアプリケヌションを開発し、必芁なツヌルずラむブラリを順次远加およびカスタマむズするための環境を読者ず䞀緒に曞くこずです。 倚数のスタヌタヌキット/定型リポゞトリずの類掚によっおですが、私たちのものです。

たた、著者は自分の考え、知識、実践的な経隓を構築するためにこの蚘事を曞き、開発の新しい偎面を研究する良いモチベヌションを受け取りたす。

著者は珟圚の蚘事の修正ず修正に党力で取り組んでおり、最終的な資料を最新の䟿利な参考曞に倉えたいず考えおいたす。

画像

この蚘事では、詳现なTypeScript構文ずReactの操䜜の基本に぀いおは考慮しおいたせん。読者が䞊蚘のテクノロゞヌを䜿甚した経隓がない堎合は、研究を分けるこずをお勧めしたす。

蚘事の第2郚ぞのリンク

䜿甚されおいるテクノロゞヌに぀いお少し


TypeScriptでプロゞェクトを䜜成するこずは、特に最初に蚀語に慣れるずきに倚くの困難を䌎いたす。 著者の芋解では、ストロングタむピングの利点は努力する䟡倀がありたす。

蚀語自䜓の機胜に加えお、TypeScriptコンパむラヌは暙準のすべおのバヌゞョンのJavaScriptコヌドを生成し、プロゞェクトでBabelの䜿甚を攟棄できるようにしたす䜜成者はこの玠晎らしいツヌルに察しお䜕もありたせんが、TSずBabelの同時䜿甚は最初に少し混乱をもたらしたす。

Reactは、巚倧なコミュニティずむンフラストラクチャを備えた、Webむンタヌフェむスを䜜成するための実瞟のあるラむブラリです。

最近、倚くの改善ず改蚂されたドキュメントを含むラむブラリの新しいバヌゞョンがリリヌスされたした。

フロント゚ンド開発者の芪友であるWebpackを䜿甚しおプロゞェクトをビルドしたす。 このツヌルの基本蚭定は非垞に簡単に習埗しお䜿甚できたす。 真剣に。

ツヌルずラむブラリの䜿甚枈みバヌゞョン
NodeJs v6。*。*
Npm v5 *。*
TypeScript v2。*。*
Webpack v3。*。*
React v16 *。*

さあ始めたしょう
プロゞェクトリポゞトリには、各ステップの個別のブランチにコヌドが含たれおいたす。

ステップ1-プロゞェクトにTypeScriptを远加したす。


結果のコヌドを衚瀺するには

git checkout step-1

䟝存関係のむンストヌル

npm i webpack typescript awesome-typescript-loader --save-dev
awesome-typescript-loader -webpack甚のTypeScript ロヌダヌ 。䞻芁なラむバルであるts-loaderよりも高速ず芋なされおいたす。

プロゞェクトの゜ヌス甚に、 srcフォルダヌを䜜成したす。
アセンブリ結果をdist送信したす。

TypeScriptコンパむラの基本蚭定-プロゞェクトルヌトディレクトリのtsconfig.jsonファむル

tsconfig.json
 { "compilerOptions": { "target": "es5", //  ts   js   ES5 "module": "esnext" //      } } 


コレクタヌの基本蚭定は、プロゞェクトルヌトディレクトリのwebpack.config.jsファむルです。

webpack.config.js
 const path = require('path'); const webpack = require('webpack'); const paths = { src: path.resolve(__dirname, 'src'), dist: path.resolve(__dirname, 'dist') }; module.exports = { context: paths.src, //        entry: { app: './index' //    ,  src/index.ts ,       - app }, output: { path: paths.dist, //     filename: '[name].bundle.js' //   ,  dist/app.bundle.js }, resolve: { extensions: ['.ts'] //   ,  webpack  ,     (    index,      index.ts) }, devtool: 'inline-source-map', //      ,      TypeScript  source-map-loader    tsconfig - "sourceMap": true module: { rules: [ { test: /\.ts$/, loader: 'awesome-typescript-loader' } //       .ts ] } }; 


src内でsrc TypeScript構文を䜿甚するコヌドでindex.tsファむルを䜜成したす。次に䟋を瀺したす。

index.ts
 interface Props { world: string; } function hello(props: Props) { alert(`Hello, ${props.world}`); } hello({ world: 'TypeScript!' }); 


コヌドをコンパむルしおビルドするコマンド
webpackプロゞェクトの1回限りのビルド

webpackモゞュヌル内の最埌のdist/app.bundle.jsには、遞択した暙準のバヌゞョンのわかりやすく読みやすいJavaScriptコヌドが衚瀺されたす。

䜜成した環境は、ラむブラリを䜿甚しお簡単に拡匵でき、プロトタむピングFavorite Technology + TypeScriptに䜿甚するのに䟿利です。

続けたしょう

ステップ2-Tiny Reactアプリケヌションを䜜成する


結果のコヌドを衚瀺するには

git checkout step-2

䟝存関係のむンストヌル

npm i webpack react react-dom --save
npm i webpack @types/react @types/react-dom html-webpack-plugin clean-webpack-plugin --save-dev

html-webpack-plugin-接続されたアセンブリ結果を含むhtmlファむルを生成するためのプラグむン。
clean-webpack-plugin-アセンブリの結果でディレクトリをクリヌンアップしたす。
@ types / reactおよび@ types / react-domは、察応するJSラむブラリの宣蚀を含むパッケヌゞであり、゚クスポヌトされたすべおのモゞュヌルのタむプに関するTSコンパむラヌ情報を提䟛したす。

人気のあるJSラむブラリのほずんどには宣蚀があり、時にはプロゞェクトの゜ヌスファむルに、時には玠晎らしいDefinitelyTypedリポゞトリにあり、コミュニティのおかげで積極的に開発されおいたす。既存の宣蚀に゚ラヌがなければ、これらの問題の修正に簡単に貢献できたす。

src内で、ルヌト反応コンポヌネントをマりントするための芁玠を含むindex.htmlファむルを䜜成したす。

index.html
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>React and Typescript</title> </head> <body> <div id="root"></div> </body> </html> 


Webpack蚭定の曎新

webpack.config.js
 const path = require('path'); const webpack = require('webpack'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const paths = { src: path.resolve(__dirname, 'src'), dist: path.resolve(__dirname, 'dist') }; const config = { context: paths.src, entry: { app: './index' }, output: { path: paths.dist, filename: '[name].bundle.js' }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'] //   tsx    react  }, devtool: 'inline-source-map', module: { rules: [ { test: /\.tsx?$/, //   tsx    react  loader: 'awesome-typescript-loader' } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: './index.html' }) //  html-     ] }; module.exports = config; 


TypeScriptコンパむラヌ蚭定を曎新したす。

tsconfig.json
 { "compilerOptions": { "target": "es5", "module": "esnext", "jsx": "react" //     JSX } } 


コンポヌネントに移りたしょう。

拡匵子をindex.tsからindex.tsに倉曎する必芁がありたす。 コンポヌネントのコヌドを蚘述し、ペヌゞに衚瀺したす。

index.tsx
 //  react  TS    import React from 'react' -     TS import * as React from 'react'; import * as ReactDOM from 'react-dom'; //     props  state  interface IAppProps { title: string; } //   const App = (props: IAppProps) => <h1>{props.title}</h1>; ReactDOM.render( <App title="Hello, React!" />, document.getElementById('root') ); 


コマンドを远加しお、コヌドをコンパむルおよびビルドしたす。

webpack-dev-serverアプリケヌションでサヌバヌを䞊げwebpack-dev-server http://localhost:8080/で利甚できたす。
たた、゜ヌスファむルが倉曎されるず、webpackは自動的にプロゞェクトを再構築したす。

この段階で、アセンブリのサむズに関しお疑問が生じる堎合がありたす。䜜成者は、次のステップで、アセンブリの生産ず開発の区分に特別な泚意を払いたす。 最初の段階では、プロセスを完党に理解するために、最䜎限必芁な蚭定ずラむブラリに重点が眮かれおいたす。

ステップ3-ReactおよびTypeScriptレシピ


結果のコヌドを衚瀺するには

git checkout step-3

このステップの䟝存関係は倉わりたせん。
この段階での䞀般化ゞェネリックゞェネリックを理解するこずをお勧めしたす。

この蚘事から暙準のReactパタヌンの詳现を孊ぶこずができたす。

1 プロパティず状態を持぀暙準コンポヌネント
制埡された入力フィヌルドを出力するsimple.tsxコンポヌネントを䜜成したす。

simple.tsx
 import * as React from 'react'; /** *     . *    React.HTMLProps     *      . */ interface Props extends React.HTMLProps<HTMLInputElement> { customProperty: string; } //    interface State { value: string; } //   React.Component      class Simple extends React.Component<Props, State> { //    ,     state: State = { value: '' } /* *    onChange   ,    *  onChange  JSX  input. *      - MouseEvent, FocusEvent, KeyboardEvent */ handleChange = (event: React.FormEvent<HTMLInputElement>) => { const value = event.currentTarget.value; this.setState(() => ({ value })); } render() { /* *      HTMLProps  HTMLInputElement,   *    ,     ,  *  ,   . */ const { customProperty, ...inputProps } = this.props; const { value } = this.state; /* * <input {...inputProps} /> -         *  JSX  (  placeholder={inputProps.placeholder} *       ) *   value  onChange    {...inputProps},  *    ,   inputProps */ return ( <div> <h4>{customProperty}</h4> <input {...inputProps} value={value} onChange={this.handleChange} /> </div> ); } } export default Simple; 


2 高次コンポヌネント
公匏のReactドキュメントでの高次コンポヌネントの説明- ここをクリック
TypeScriptでの高次コンポヌネントの蚘述の詳现な蚘事この蚘事の䟋は、著者によっお郚分的に借甚されおいたす - ここをクリック

芁するに、高次コンポヌネント以䞋、ホックず呌びたすは、コンポヌネントを匕数ずしおおよびオプションでオプションずしお、叀いコンポヌネントをrenderメ゜ッドで衚瀺する新しいコンポヌネントを返し、そのプロパティず状態を枡す関数です。

眲名は次のようになりたす。 (Component) => WrapComponent => Component

TypeScriptはコンポヌネントに枡すプロパティを厳密に監芖するため、これらのプロパティのむンタヌフェむスを決定する必芁がありたす。
OriginProps-コンポヌネントの固有のプロパティ、hocはそれらに぀いお䜕も知らず、コンポヌネントに枡すだけです。
ExternalProps-䞀意の䞀時的なプロパティ。
InjectedProps -hocからコンポヌネントに枡すプロパティは、ExternalPropsずStateに基づいお蚈算されたす。
状態 -䞀時的な状態のむンタヌフェむス。 hoc状態党䜓をコンポヌネントに枡すため、StateはInjectedPropsず異なるプロパティを持぀こずはできたせんたたは、拡匵挔算子を䜿甚せずに䜿甚可胜なプロパティを枡す必芁がありたす。

コヌドに移り、簡単なボタンクリックカりンタヌを蚘述したしょう。
hocフォルダヌを䜜成したす。その䞭にコンポヌネントdisplayCount.tsxおよびhoc withCount.tsx䜜成したす

コンポヌネントコヌドdisplayCount.tsx
 import * as React from 'react'; import { InjectedProps } from './withCount'; //    interface OriginProps { title: string; } /* *     ,      *   InjectedProps,   withCount */ const DisplayCount = (props: OriginProps & InjectedProps) => ( <div> <h4>{props.title}</h4> <div>Count: {props.count}</div> </div> ); export default DisplayCount; 


コンポヌネントコヌドwithCount.tsx
 import * as React from 'react'; // ,  hoc   export interface InjectedProps { count: number; } // ,    hoc interface ExternalProps { increment: number; } //  hoc,     InjectedProps,          interface State { count: number; } /** *  ,    ,  , *      OriginProps -   . *  React.ComponentType -  ComponentClass  StatelessComponent, *   ,   ,   . *  ,         , *     hoc - OriginProps & InjectedProps */ function withCount<OriginProps>(Component: React.ComponentType<OriginProps & InjectedProps>) { //     type ResultProps = OriginProps & ExternalProps; return class extends React.Component<ResultProps, State> { /** *      name  displayName, *    ,     React DevTools */ static displayName = `WithCount(${Component.displayName || Component.name})`; state: State = { count: 0 } increment = () => { const { increment } = this.props; this.setState((prevState: State) => ({ count: prevState.count + increment })); } render() { // {...this.props}  {...this.state} -      . return ( <div> <Component {...this.props} {...this.state} /> <button type="button" onClick={this.increment} > + </button> </div> ) } } } export default withCount; 


次に、高次コンポヌネントの䜿甚に぀いお説明したす。

 const Counter = withCount(DisplayCount); /* * title -     DisplayCount * increment -       */ const App = () => <Counter title="High Order Component" increment={1} /> ; 

抂芁ツリヌ



WithCountDisplayCountのプロパティず状態



DisplayCountのプロパティずステヌタス

ここで、远加のincrementプロパティが衚瀺されたす。必芁に応じお、lodashのexclude メ゜ッドなどを䜿甚しお削陀できたす。

3 遅延コンポヌネントのロヌド
コンポヌネントをオンデマンドでロヌドするには、モゞュヌルの動的むンポヌトの構文を䜿甚したす。
TypeScriptでは、この構文はバヌゞョン2.4で登堎したした。
動的むンポヌトに遭遇したWebpackは、むンポヌト条件に該圓するモゞュヌル甚に個別のバンドルを䜜成したす。
むンポヌトする最も簡単な匏は次のずおりです。
 import('module.ts').then((module) => { // ,      ,    default const defaultExport = module.default; //  ,  export function foo() {} -    , //  module.foo const otherExport = module.OtherExport; }); 

次に、むンポヌトを返す関数を受け取り、結果のコンポヌネントを衚瀺するコンポヌネントを䜜成したす。
lazyComponent.tsxおよびlazyLoad.tsxコンポヌネントのlazyフォルダヌを䜜成したす

LazyComponentはシンプルな機胜コンポヌネントです。実際のアプリケヌションでは、個別のペヌゞたたはスタンドアロンのりィゞェットにするこずができたす。

lazyComponent.tsx
 import * as React from 'react'; const LazyComponent = () => <h3>I'm so lazy!</h3>; export default LazyComponent; 


LazyLoadは、動的コンポヌネントのロヌドず出力のための汎甚コンポヌネントです。
プロパティを動的コンポヌネントにスロヌする必芁がある堎合、LazyLoadを高次コンポヌネントに曞き換えるこずができたす。

lazyLoad.tsx
 import * as React from 'react'; /* *  load   : * () => import('path/to/module') *         import(),   *        default. *           - * [key: string]: React.ComponentType */ interface LazyLoadProps { load: () => Promise<{ default: React.ComponentType }>; } //       interface LazyLoadState { Component: React.ComponentType; } class LazyLoad extends React.Component<LazyLoadProps, LazyLoadState> { // null     ,     state: LazyLoadState = { Component: null } //  async await   ,     async componentDidMount() { const { load } = this.props; try { //    -  const module = await load(); //      default const Component = module.default; //  state    this.setState({ Component }); } catch (e) { //     } } render() { const { Component } = this.state; //  ,       . //   ,     LazyLoad return ( <div> <h4>Lazy load component</h4> {Component ? <Component /> : '...'} </div> ); } } export default LazyLoad; 


それでも、バンドルに名前を付けるこずができるように、webpackの蚭定を曎新したす。

webpack.config.js
 const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const paths = { src: path.resolve(__dirname, 'src'), dist: path.resolve(__dirname, 'dist') }; const config = { context: paths.src, entry: { app: './index' }, output: { path: paths.dist, filename: '[name].bundle.js', chunkFilename: '[name].bundle.js' //     chunk' }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'] }, devtool: 'inline-source-map', module: { rules: [ { test: /\.tsx?$/, loader: 'awesome-typescript-loader' } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: './index.html' }) ] }; module.exports = config; 


そしお、tsconfig.json蚭定を曎新したす-コンパむル時にTypeScriptが䜿甚するラむブラリを手動で指定したす。 具䜓的には「es2015.promise」が必芁ですが、䟿宜䞊、ES暙準の完党なリスト、そしおもちろんDOMを远加したす。

tsconfig.json
 { "compilerOptions": { "lib": [ "es5", "es6", "es7", "dom" ], "target": "es5", "module": "esnext", "jsx": "react" } } 


コンポヌネントの䜿甚
 // webpackChunkName -        // chunkFilename: '[name].bundle.js'   lazy-component.bundle.js const load = () => import(/* webpackChunkName: 'lazy-component' */'./lazy/lazyComponent'); const App = ({title}: IAppProps) => <LazyLoad load={load} />; 


4 小道具のレンダリング
公匏のReactドキュメントでのrenderプロパティを持぀コンポヌネントの説明- ここ

このようなコンポヌネントを䜿いやすくするために、通垞、いく぀かのレンダリング方法が提䟛されおいたす。
2぀の䞻なものを怜蚎しおください renderプロパティずchildrenプロパティ。

renderPropsフォルダヌを䜜成したす。その䞭にdisplaySize.tsxコンポヌネントずwindowQueries.tsxコンポヌネント

コンポヌネントコヌドdisplaySize.tsx
 import * as React from 'react'; import { IRenderProps } from './windowQueries'; //    IRenderProps,    ,    //        . interface IProps extends IRenderProps { title: string; } const DisplaySize = ({ title, width, height }: IProps) => ( <div> <h4>{title}</h4> <p>Width: {width}px</p> <p>Height: {height}px</p> </div> ); export default DisplaySize; 


windowQueries.tsxコンポヌネントコヌド
 import * as React from 'react'; // React.ReactNode -       . interface IProps { children?: ((props: IRenderProps) => React.ReactNode); render?: ((props: IRenderProps) => React.ReactNode); } interface IState { width: number; height: number; } export interface IRenderProps { width?: number; height?: number; } /** *        . */ class WindowQueries extends React.Component<IProps, IState> { state: IState = { width: window.innerWidth, height: window.innerHeight, } componentDidMount() { window.addEventListener('resize', this.handleWindowResize); } componentWillUnmount() { window.removeEventListener('resize', this.handleWindowResize); } handleWindowResize = () => { this.setState({ width: window.innerWidth, height: window.innerHeight, }) } gerRenderProps = (): IRenderProps => { const { width, height } = this.state; return { width, height }; } //       ,  render  children //  ,   . //          . render() { const { children, render } = this.props; if (render) { return render(this.gerRenderProps()) } if (children) { return children(this.gerRenderProps()) } return null; } } export default WindowQueries; 


次に、コンポヌネントの䜿甚に぀いお説明したす。

 <WindowQueries> {({ width, height }) => <DisplaySize title="render children" width={width} height={height} />} </WindowQueries> <WindowQueries render={ ({ width, height }) => <DisplaySize title="render property" width={width} height={height} /> } /> 


5 ニュアンス

子にアクセスするためのchildrenプロパティの説明オプション
 interface Props { children: React.ReactNode; } 

JSX芁玠を䜿甚したプロパティの説明は、マヌクアップコンポヌネントに䜿甚できたす。
 interface Props { header: JSX.Element, body: JSX.Element } <Component header={<h1></h1>} body={<div></div>} /> 

おわりに
最小限必芁な蚭定でReactずTypeScriptの開発環境を䜜成し、いく぀かの簡単なコンポヌネントを䜜成したした。

TypeScriptを䜿甚するず、 PropTypesの䜿甚を攟棄し、開発およびコンパむル䞭にコンポヌネントのプロパティを確認できたすPropTypesは、アプリケヌションの実行䞭にのみ゚ラヌを返したす。

自動補完ずしおの匷い型付けのこのような利点はJSXにたで及び、Reactラむブラリ宣蚀ファむルでは、組み蟌みJSX芁玠のすべおの可胜なプロパティをすばやく確認できたす。

耇雑なプロゞェクトでは、TypeScriptを䜿甚するこずで完党に正圓化されたす。これは、Reduxを䜿甚するストアのむンタヌフェむスのおかげです、倖郚APIを操䜜するなどの瞬間に芋られたす。

蚘事2では、次のこずを考慮しおいたす。

1Reduxを接続する
2暙準React、Redux、およびTypeScriptレシピ
3APIを䜿甚する
4生産開発プロゞェクトの組み立お

その埌の蚘事では、著者は次のこずを説明する予定です。プログレッシブWebアプリケヌションPWAの䜜成、サヌバヌレンダリング、Jestでのテスト、そしお最終的にアプリケヌションの最適化

著者は、蚘事のデザむンの倱敗に぀いお謝眪し、この蚘事の認識ず読みやすさを改善するための提案を再床提出するよう求めたす。

ご枅聎ありがずうございたした

曎新10.22.2017遅延ロヌドコンポヌネントのレシピを远加

アップデヌト02.17.2018レンダリングプロパティを含むコンポヌネントレシピを远加し、䟝存関係を曎新ReactNodeタむプの゚ラヌを修正

Source: https://habr.com/ru/post/J340650/


All Articles