preface

Around 2019, the idea of setting up the React development environment by ourselves was born. So far, many projects of the company have been in use and are relatively stable. Why build your own wheel? At first, I was not satisfied with the scaffolding on the market. In addition, built the wheels for his help, there are some technical learning what others secondary packaging, not used directly at the bottom of the library, and also help yourself learn knowledge again in the system, Vite recently very fire, so use Vite structures, a wave, the nonsense not much said, directly into the body, how to set up their own development environment.

Initialize the

Create a folder and go to:

$ mkdir tristana && cd tristana
Copy the code

Initialization package. Json

$ npm init
Copy the code

Install vite

$ npm install vite vite-plugin-babel-import vite-plugin-imp --save-dev
Copy the code

Create the following directory structures, files, and content:

project

tristana
|- package.json
|- index.html
|- vite.config.js
|- /src
   |- index.js

Copy the code

src/index.js

document.getElementById("root").append("React");
Copy the code

index.html

<! DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>tristana</title> </head> <body> <div id="root"></div> <script type="module" src="/src/index.jsx"></script> <script> window.global = window; </script> </body> </html>Copy the code

vite.config.js

import { defineConfig } from 'vite';

const path = require('path');
export default defineConfig({
    plugins: [
        reactRefresh()
    ]
});
Copy the code

package.json

{/ /... "scripts": { "build": "vite build", }, }Copy the code

Then the root terminal type: NPM run build

Open the index.html in the dist directory in your browser, and if everything works, you should see the following text: ‘React’

Index.html is currently in the dist directory, but it was created manually. Here’s how to generate index.html instead of editing it manually.

Vite core functions

Hot update

$ npm install @vitejs/plugin-react-refresh --save-dev
Copy the code

vite.config.js

import reactRefresh from '@vitejs/plugin-react-refresh';
export default defineConfig({
    // ...
    plugins: [
        reactRefresh(),
    ],
});
Copy the code

alias

vite.config.js

import { defineConfig } from 'vite';
const path = require('path');
export default defineConfig({
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    }
});
Copy the code

Development services

package.json

{/ /... "scripts": { "dev": "vite", }, }Copy the code

The JSX file

Install dependencies

$ npm install @babel/preset-react react react-dom --save-dev
Copy the code

.babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}
Copy the code

src/App.jsx

In the SRC directory, add app. JSX:

import React, { Component } from "react";

class App extends Component {
    render() {
        return (
            <div>
                <h1> Hello, World! </h1>
            </div>
        );
    }
}

export default App;

Copy the code

src/index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App.jsx";
ReactDOM.render(<App />, document.getElementById("root"));
Copy the code

React Router

Install dependencies

$ npm install react-router history --save
Copy the code

src/index.js

import React from "react";
import ReactDOM from "react-dom";
import { Router, Route, Link } from "react-router";
import { createBrowserHistory } from "history";
import App from "./App.jsx";

const About = () => {
    return <>About</>;
};

ReactDOM.render(
    <Router history={createBrowserHistory()}>
        <Route path="/" component={App} />
        <Route path="/about" component={About} />
    </Router>,
    document.getElementById("root")
);

Copy the code

MobX

Install dependencies

$ npm install mobx mobx-react babel-preset-mobx --save
Copy the code

.babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react", "mobx"]
}
Copy the code

src/store.js

Create store.js in the SRC directory

import { observable, action, makeObservable } from "mobx";

class Store {

    constructor() {
        makeObservable(this);
    }

    @observable
    count = 0;

    @action("add")
    add = () => {
        this.count = this.count + 1;
    };

    @action("reduce")
    reduce = () => {
        this.count = this.count - 1;
    };
}
export default new Store();

Copy the code

index.js

import { Provider } from "mobx-react";
import Store from "./store";
// ...
ReactDOM.render(
    <Provider store={Store}>
        <Router history={createBrowserHistory()}>
        <Route path="/" component={App} />
        <Route path="/about" component={About} />
        </Router>
    </Provider>,
    document.getElementById("root")
);

Copy the code

src/App.jsx

import React, { Component } from "react";
import { observer, inject } from "mobx-react";

@inject("store")
@observer
class App extends Component {
    render() {
        return (
            <div>
                <div>{this.props.store.count}</div>
                <button onClick={this.props.store.add}>add</button>
                <button onClick={this.props.store.reduce}>reduce</button>
            </div>
        );
    }
}

export default App;
Copy the code

Ant Design

Install dependencies

$ npm install antd vite-plugin-babel-import vite-plugin-imp --save
Copy the code

vite.config.js

import { defineConfig } from 'vite';
import vitePluginImp from 'vite-plugin-imp';

const path = require('path');
export default defineConfig({
    // ...
    plugins: [
        vitePluginImp({
            libList: [
                {
                    libName: 'antd',
                    libDirectory: 'es',
                    style: name => `antd/es/${name}/style`
                }
            ]
        })
    ],
    css: {
        preprocessorOptions: {
            less: {
                javascriptEnabled: true
            }
        }
    }
});
Copy the code

src/App.jsx

// ...
import { DatePicker } from "antd";
import "antd/dist/antd.css";

@inject("store")
@observer
class App extends Component {
    render() {
        return (
            <div>
                <DatePicker />
            </div>
        );
    }
}

export default App;

Copy the code

TypeScript

Install dependencies

$ npm install typescript @babel/preset-typescript --save-dev
Copy the code

.babelrc

{
    "presets": [
        // ...
        "@babel/preset-typescript"
    ]
}
Copy the code

tsconfig.json

Add the tsconfig.json file in the root directory:

{
    "compilerOptions": {
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "ES5",
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "allowJs": true,
        "outDir": "./dist/",
        "esModuleInterop": true,
        "noImplicitAny": false,
        "sourceMap": true,
        "module": "esnext",
        "moduleResolution": "node",
        "isolatedModules": true,
        "importHelpers": true,
        "lib": ["esnext", "dom", "dom.iterable"],
        "skipLibCheck": true,
        "jsx": "react",
        "typeRoots": ["node", "node_modules/@types"],
        "rootDirs": ["./src"],
        "baseUrl": "./src"
    },
    "include": ["./src/**/*"],
    "exclude": ["node_modules"]
}

Copy the code

src/App.jsx

Change the file suffix app.jsx -> app.tsx

import React, { Component } from "react";
import { observer, inject } from "mobx-react";
import { DatePicker } from "antd";
import "antd/dist/antd.css";

@inject("store")
@observer
class App extends Component {
    props: any;
    render() {
        return (
            <div>
                <DatePicker />
                <div>{this.props.store.count}</div>
                <button onClick={this.props.store.add}>add</button>
                <button onClick={this.props.store.reduce}>reduce</button>
            </div>
        );
    }
}

export default App;

Copy the code

Code specification

Code verification, code formatting, Git pre-submission verification, Vscode configuration, compile verification

ESLint

Install dependencies

$ npm install @typescript-eslint/parser eslint eslint-plugin-standard @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-promise  --save-dev
Copy the code

.eslintrc.js

In the root directory, add the.eslintrc.js file:

module.exports = {
    extends: ["eslint:recommended", "plugin:react/recommended"],
    env: {
        browser: true,
        commonjs: true,
        es6: true,
    },
    globals: {
        $: true,
        process: true,
        __dirname: true,
    },
    parser: "@typescript-eslint/parser",
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
            modules: true,
        },
        sourceType: "module",
        ecmaVersion: 6,
    },
    plugins: ["react", "standard", "promise", "@typescript-eslint"],
    settings: {
        "import/ignore": ["node_modules"],
        react: {
            version: "latest",
        },
    },
    rules: {
        quotes: [2, "single"],
        "no-console": 0,
        "no-debugger": 1,
        "no-var": 1,
        semi: ["error", "always"],
        "no-irregular-whitespace": 0,
        "no-trailing-spaces": 1,
        "eol-last": 0,
        "no-unused-vars": [
        1,
        {
            vars: "all",
            args: "after-used",
        },
        ],
        "no-case-declarations": 0,
        "no-underscore-dangle": 0,
        "no-alert": 2,
        "no-lone-blocks": 0,
        "no-class-assign": 2,
        "no-cond-assign": 2,
        "no-const-assign": 2,
        "no-delete-var": 2,
        "no-dupe-keys": 2,
        "use-isnan": 2,
        "no-duplicate-case": 2,
        "no-dupe-args": 2,
        "no-empty": 2,
        "no-func-assign": 2,
        "no-invalid-this": 0,
        "no-redeclare": 2,
        "no-spaced-func": 2,
        "no-this-before-super": 0,
        "no-undef": 2,
        "no-return-assign": 0,
        "no-script-url": 2,
        "no-use-before-define": 2,
        "no-extra-boolean-cast": 0,
        "no-unreachable": 1,
        "comma-dangle": 2,
        "no-mixed-spaces-and-tabs": 2,
        "prefer-arrow-callback": 0,
        "arrow-parens": 0,
        "arrow-spacing": 0,
        camelcase: 0,
        "jsx-quotes": [1, "prefer-double"],
        "react/display-name": 0,
        "react/forbid-prop-types": [
        2,
        {
            forbid: ["any"],
        },
        ],
        "react/jsx-boolean-value": 0,
        "react/jsx-closing-bracket-location": 1,
        "react/jsx-curly-spacing": [
        2,
        {
            when: "never",
            children: true,
        },
        ],
        "react/jsx-indent": ["error", 4],
        "react/jsx-key": 2,
        "react/jsx-no-bind": 0,
        "react/jsx-no-duplicate-props": 2,
        "react/jsx-no-literals": 0,
        "react/jsx-no-undef": 1,
        "react/jsx-pascal-case": 0,
        "react/jsx-sort-props": 0,
        "react/jsx-uses-react": 1,
        "react/jsx-uses-vars": 2,
        "react/no-danger": 0,
        "react/no-did-mount-set-state": 0,
        "react/no-did-update-set-state": 0,
        "react/no-direct-mutation-state": 2,
        "react/no-multi-comp": 0,
        "react/no-set-state": 0,
        "react/no-unknown-property": 2,
        "react/prefer-es6-class": 2,
        "react/prop-types": 0,
        "react/react-in-jsx-scope": 2,
        "react/self-closing-comp": 0,
        "react/sort-comp": 0,
        "react/no-array-index-key": 0,
        "react/no-deprecated": 1,
        "react/jsx-equals-spacing": 2,
    },
};


Copy the code

.eslintignore

In the root directory, add the.eslintignore file:

src/assets
Copy the code

.vscode

Add the.vscode folder in the root directory, and then add.vscode/settings.json

{
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        "typescript",
        "typescriptreact"
    ]
}

Copy the code

Perttier

Install dependencies

$ npm install prettier --save-dev
Copy the code

prettier.config.js

Add the file prettier.config.js in the root directory:

Module.exports = {// Max 100 characters printWidth: 100, // use 4 Spaces to indent tabWidth: 4, // use useTabs instead of indentation: False, // A semicolon should be used at the end of the line, // singleQuote: true, // The key of the object should be quoted only when necessary: 'as-needed', // JSX uses double quotes instead of single quotes, // trailingComma: 'None ', // braces need spacing at the beginning and end of bracketSpacing: true, // JSX tag Angle brackets need newline jsxBracketSameLine: False, // arrowParens: 'avoid', // Each file is formatted to the full contents of the file rangeStart: 0, rangeEnd: // @prettier requirePragma: false does not automatically insert @prettier insertPragma: False, / / use the default fold line standard proseWrap: 'preserve, / / according to display style decided to don't fold line htmlWhitespaceSensitivity HTML: 'CSS ', // use lf endOfLine: 'lf'};Copy the code

stylelint

Install dependencies

$ npm install stylelint stylelint-config-standard stylelint-config-prettier --save-dev
Copy the code

stylelint.config.js

In the root directory, add the stylelint.config.js file:

module.exports = {
    extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
    ignoreFiles: [
        '**/*.ts',
        '**/*.tsx',
        '**/*.png',
        '**/*.jpg',
        '**/*.jpeg',
        '**/*.gif',
        '**/*.mp3',
        '**/*.json'
    ],
    rules: {
        'at-rule-no-unknown': [
            true,
            {
                ignoreAtRules: ['extends', 'ignores']
            }
        ],
        indentation: 4,
        'number-leading-zero': null,
        'unit-allowed-list': ['em', 'rem', 's', 'px', 'deg', 'all', 'vh', '%'],
        'no-eol-whitespace': [
            true,
            {
                ignore: 'empty-lines'
            }
        ],
        'declaration-block-trailing-semicolon': 'always',
        'selector-pseudo-class-no-unknown': [
            true,
            {
                ignorePseudoClasses: ['global']
            }
        ],
        'block-closing-brace-newline-after': 'always',
        'declaration-block-semicolon-newline-after': 'always',
        'no-descending-specificity': null,
        'selector-list-comma-newline-after': 'always',
        'selector-pseudo-element-colon-notation': 'single'
    }
};

Copy the code

Lint – staged, pre – commit

Install dependencies

$ npm install lint-staged prettier eslint pre-commit --save-dev
Copy the code

package.json

{
    // ...
    "scripts": {
        "lint:tsx": "eslint --ext .tsx src && eslint --ext .ts src",
        "lint:css": "stylelint --aei .less .css src",
        "precommit": "lint-staged",
        "precommit-msg": "echo 'Pre-commit checks...' && exit 0"
    },
    "pre-commit": [
        "precommit",
        "precommit-msg"
    ],
    "lint-staged": {
        "*.{js,jsx,ts,tsx}": [
            "eslint --fix",
            "prettier --write",
            "git add"
        ],
        "*.{css,less}": [
            "stylelint --fix",
            "prettier --write",
            "git add"
        ]
    }
}

Copy the code

eslint-webpack-plugin

Install dependencies

$ npm install eslint-webpack-plugin --save-dev
Copy the code

vite.config.ts

import { defineConfig } from 'vite';
const ESLintPlugin = require('eslint-webpack-plugin');

const path = require('path');
export default defineConfig({
    // ...
    plugins: [
        new ESLintPlugin()
    ]
});
Copy the code

conclusion

In the process of building this, I also encountered a lot of pits and harvested a lot. I hope this tutorial can help more students and pick fewer pits. Based on this project framework can develop a variety of business pages, very smooth, integrated very very many libraries, improve the routing configuration, network request, component click load and so on. The complete tristana can be seen, if you feel good, you can give a Star 🌟, also welcome to the project issues ~