[Sleact]ゆったりパンツクローンコード-プロジェクト設定

36946 ワード


基礎科目は趙賢英のゆったりしたズボンのクローン授業です.

プロジェクトの設定


最近はフロント開発の半分が配置されているようです.そのために設置するものが多く、入る時間も長い.このような時間を短縮するためにCRAを使用することができるが、プロジェクトの規模がますます大きくなり、ますます複雑になると、最終的にはその設定手順を理解し、カスタマイズする必要がある.

npm init

npm initによってpackage.jsonファイルを生成することができる.
package.json
{
  "name": "sleact",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "dev": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack serve --env development",
    "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Shin Wonse",
  "license": "MIT",
  "dependencies": {
    ...
  },
  "devDependencies": {
    ...
  },
  "description": ""
}
package.jsonは、プロジェクト情報と依存性(dependencies)を管理するドキュメントであり、作成されたpackage.jsonドキュメントは、どこでも同じ開発環境を作成することができます.name程度のオプションのみが初期設定で重要です.

tsconfig.json


tsconfig.json
{
  "compilerOptions": {
    // import * as React from 'react' 대신 
    // import React from 'react' 로 쓸 수 있다.
    "esModuleInterop": true, 
    "sourceMap": true, // 에러난 위치를 찾기 편하다.
    "lib": ["ES2020", "DOM"], // ES 최신문법과 DOM
    "jsx": "react", // jsx가 react 말고도 다른 프로그래밍에서 쓰이는 것을 방지
    "module": "esnext", // 최신 모듈을 사용하겠다 (import, export).
    "moduleResolution": "Node",
    "target": "es5",
    "strict": true, // 엄격한 타입 적용
    "resolveJsonModule": true,
    "baseUrl": ".",
    "paths": { // import A from ../../../../hello.js 대신
      "@hooks/*": ["hooks/*"],
      "@components/*": ["components/*"],
      "@layouts/*": ["layouts/*"],
      "@pages/*": ["pages/*"],
      "@utils/*": ["utils/*"],
      "@typings/*": ["typings/*"]
    }
  }
}
TypeScriptを使用しているので、tsconfig.jsonファイルが必要です.ここで、私が初めて見たオプションは"paths"オプションで、雑然と見えるimportプロセスをうまく変換することができます.このオプションは実際に私の卒業プロジェクトの再構築コースに適用されます.

きれいとeslint


.eslintrc
{
  "extends": ["plugin:prettier/recommended", "react-app"]
}
prettierの推薦に従ってやります.
.prettierrc
{
  "printWidth": 120,
  "tabWidth": 2,
  "singleQuote": true,
  "trailingComma": "all",
  "semi": true
}
Customができることはそんなに多くありません.

webpack


webpack.config.ts
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import webpack from 'webpack';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const isDevelopment = process.env.NODE_ENV !== 'production';

const config: webpack.Configuration = {
  name: 'sleact',
  mode: isDevelopment ? 'development' : 'production',
  devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    alias: {
      '@hooks': path.resolve(__dirname, 'hooks'),
      '@components': path.resolve(__dirname, 'components'),
      '@layouts': path.resolve(__dirname, 'layouts'),
      '@pages': path.resolve(__dirname, 'pages'),
      '@utils': path.resolve(__dirname, 'utils'),
      '@typings': path.resolve(__dirname, 'typings'),
    },
  },
  entry: {
    app: './client',
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                targets: { browsers: ['last 2 chrome versions'] },
                debug: isDevelopment,
              },
            ],
            '@babel/preset-react',
            '@babel/preset-typescript',
          ],
          env: {
            development: {
              plugins: [['@emotion', { sourceMap: true }], require.resolve('react-refresh/babel')],
            },
            production: {
              plugins: ['@emotion'],
            },
          },
        },
        exclude: path.join(__dirname, 'node_modules'),
      },
      {
        test: /\.css?$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      async: false,
      // eslint: {
      //   files: "./src/**/*",
      // },
    }),
    new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }),
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/dist/',
  },
  devServer: {
    historyApiFallback: true, // react router
    port: 3090,
    publicPath: '/dist/',
    proxy: {
      '/api/': {
        target: 'http://localhost:3095',
        changeOrigin: true,
      },
    },
  },
};

if (isDevelopment && config.plugins) {
  config.plugins.push(new webpack.HotModuleReplacementPlugin());
  config.plugins.push(new ReactRefreshWebpackPlugin());
  config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'server', openAnalyzer: true }));
}
if (!isDevelopment && config.plugins) {
  config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true }));
  config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' }));
}

export default config;

プロジェクト構造



上記の項目構造を有する.

index.html

<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>슬리액</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        overflow: initial !important;
      }
      body {
        font-size: 15px;
        line-height: 1.46668;
        font-weight: 400;
        font-variant-ligatures: common-ligatures;
        -moz-osx-font-smoothing: grayscale;
        -webkit-font-smoothing: antialiased;
      }
      * {
        box-sizing: border-box;
      }
    </style>
    <link
      rel="stylesheet"
      href="https://a.slack-edge.com/bv1-9/client-boot-styles.dc0a11f.css?cacheKey=gantry-1613184053"
      crossorigin="anonymous"
    />
    <link rel="shortcut icon" href="https://a.slack-edge.com/cebaa/img/ico/favicon.ico" />
    <link
      href="https://a.slack-edge.com/bv1-9/slack-icons-v2-16ca3a7.woff2"
      rel="preload"
      as="font"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/app.js"></script>
  </body>
</html>
多くの場合、htmlは無視されていますが、実は非常に重要なファイルです.SEOにとっても重要であり、グーグルはこのファイルに汎用cssを書くことを提案した.このファイルを実行してからJavaScriptファイルが実行されるため、パフォーマンスに差があります.

client.tsx

import React from 'react';
import { render } from 'react-dom';
import App from '@layouts/App';
import { BrowserRouter } from 'react-router-dom';

render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.querySelector('#app'),
);

// pages - 서비스 페이지
// components - 짜잘 컴포넌트
// layouts - 공통 레이아웃
サービスの起点となるファイル.

layouts/App.tsx

import React from 'react';
import loadable from '@loadable/component';
import { Switch, Route, Redirect } from 'react-router';

const LogIn = loadable(() => import('@pages/LogIn'));
const SignUp = loadable(() => import('@pages/SignUp'));

const App = () => {
  return (
    <Switch>
      <Redirect exact path="/" to="/login" />
      <Route path="/login" component={LogIn} />
      <Route path="/signup" component={SignUp} />
    </Switch>
  );
};

export default App;
loadableライブラリでコード剥離を行います.トップページはログインページに設定されており、会員入力以外のurlはログインページにリダイレクトされます.