preface

Hello everyone! I’m front-end nameless

background

The React project has been done one after another for many years. To create a new React project, copy the old project directly, delete the old business logic, and modify or subtract a new project framework.

This results in:

  1. Creating a project takes too long
  2. The framework of the repair and subtraction project is unstable
  3. It is not conducive to iterative optimization frameworks
  4. .

So I came up with the idea of building my own React project framework right out of the box.

This article first introduces the functionality of the project template, and then explains how to build and design the project.

The project structure

React - project - the template ├ ─ ─. Babelrc # Babel configuration ├ ─ ─ Webpack # Webpack common configuration directory │ │ ├ ─ ─ the plugins # common plug-in set │ │ ├ ─ ─ resolve # Webpack resolve configuration │ │ ├ ─ ─ utils # webpack tools │ │ ├ ─ ─ variable # webpack variable configuration │ ├ ─ ─ webpack. Base. Js based configuration file # webpack │ ├ ─ ─ Webpack. Dev. Js # webpack development environment configuration file │ └ ─ ─ webpack. Prod. Js # webpack production configuration file ├ ─ ─ yarn. # lock locking NPM package depends on version file ├ ─ ─ Package.json ├─ Postcss.config.js # Automatic Support for CSS3 Style ├── Public # Automatic Support for CSS3 style ├─ Postcss.config.js # Automatic Support for CSS3 style ├── Public # Automatic Support for CSS3 style ├── Readme.md ├─ SRC │ ├─ Assets # │ │ ├── images # │ │ ├─ CSS # │ │ ├─ common.scss │ │ ├─ Core. SCSS # ├─ Core. │ │ │ └ ─ ─ init. SCSS # global initialization CSS │ │ └ ─ ─ js # global js │ ├ ─ ─ common # storage project gm file │ │ ├ ─ ─ Resolution. The ts # layout adapter configuration center │ │ └ ─ ─ Appcontext. ts # ├─ Components # ├─ Config # ├─ Pages # ├─ Typings # │ ├─ Type # │ │ ├─ Type # │ │ ├─ Type # │ │ ├─ Type # │ │ ├─ Type # │ │ ├─ Type # Global App context statement │ │ ├ ─ ─ IRedux. Ts related statement # redux │ │ └ ─ ─ IRouterPage. Ts # routing announcements │ ├ ─ ─ uiLibrary # component library │ ├ ─ ─ routes # routing directory │ │ ├ ─ ─ index. The TSX # routing configuration file entrance │ │ └ ─ ─ RouterUI. The TSX # routing conversion │ ├ ─ ─ # services related to the back-end file directory │ │ ├ ─ ─ # API calls the backend interface definition directory │ │ │ ├ ─ ─ ├. Ts │ │ ├─ Bass Exercises. Ts │ │ ├─ Bass Exercises. Ts │ │ ├─ Bass Exercises │ │ ├─ Heavy Metal Exercises - - Heavy Metal Exercises - - Heavy metal Exercises - - Heavy metal Exercises Action │ ├─ Reducers # Reducers # │ ├─ History # Related │ ├─ index │ ├─ App.tsx # App │ ├─ index. TSX # Project Import file │ ├─ index SCSS └─ tsconfig.json # TSCopy the code

The project address

If you find the React template project structure appealing, check out the source code react-project-template

Quanyj-react-cli, your star is my driving force!

Project introduction

  • The project introduced core. SCSS, which is globally public and can be used directly without requiring every SCSS file @import

  • CSS3 styles are automatically compatible when you build your project, so you don’t need to write your own browser-compatible styles

  • Routes can be configured

  • Connected – React-Router is integrated in the project. Routes are stored in store and interfaces are directly obtained from store

  • Immer is used instead of immutable by default in the project

  • Some common utility functions are configured in the project by default

  • A secondary wrapper was done for AXIOS in the project

  • Use PX directly for the project

  • The project makes extensive use of decorators such as @connect,@context, etc to simplify the code.

Partial code snippet

Page use Cases

Use the decorator Connect to get data from the Store

Use the WithAppContextDecorator to get the data from the AppContext content provider

import React from "react";
import Page from "@/components/Page";
import { UPDATE_USER_ID } from "@/store/actions/user";
import connect from "@/store/connect";
import CPng from "@/assets//images/02.png";
import HomeChild from "./HomeChild";
import { withAppContextDecorators } from '@/common/AppContext';
import "./index.scss";
import { IAppContext } from '@/types/IContext';

interface IHomeProps {
    userId: number;
    updateId: (id: number) = > void;
}

interface IHomeState {
    count: number
}

const mapStateToProps = {
    userId: "user.userId"
};

const mapDispatchToProps = {
    updateId: UPDATE_USER_ID
};

@withAppContextDecorators
@connect(mapStateToProps, mapDispatchToProps)
class Home extends Page<IHomeProps & IAppContext.IHomeState>{
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        };
    }

    componentDidMount = () = > {
        setTimeout(() = > {
            this.props.updateId(5);
        }, 3000)
        console.log(this.props.test());
        this.setState({
            count: 1})}render() {
        const { userId } = this.props;
        const { count } = this.state;
        console.log("this.props==".this.props);
        return (
            <div className='home'>
                <div className="text1">Hello, World! {userId}</div>
                <div className="bg1">{count}</div>
                <img className="img1" src={CPng} ></img>
                <HomeChild></HomeChild>
            </div>); }}export default Home;
Copy the code

The routing configuration

Routing is configurable for lazy loading and subcontracting

import React from 'react';

export default {
    routes: [{exact: true.path: '/'.isDynamic: true.component: React.lazy(() = >
                import(/* webpackChunkName: "home",webpackPrefetch: true */ '@/pages/Home'),),}, {exact: true.path: '/page1'.isDynamic: true.component: React.lazy(() = >
                import(/* webpackChunkName: "page1",webpackPrefetch: true */ '@/pages/Page1'),),},],}Copy the code

Self-adaptive component encapsulation

import React from "react";
import Resolutions from "@/common/Resolution";

interfaceIResolutionComProps { WIDTH? :number;
}

export default class ResolutionCom extends React.Component<IResolutionComProps> {
    componentDidMount() {
        this.resize();
        window.onresize = this.resize;
    }
    resize = () = > {
        // Get the size of the design draft
        const design = Resolutions.getDesign();
        const getHtmlFs = () = > {
            return parseFloat(window.getComputedStyle(html, null) ["font-size"]);
        };
        const getScreenWidth = () = > {
            let htmlWidth = 0;
            try {
                let htmlElement = document.documentElement;
                htmlWidth = Math.max(htmlElement.offsetWidth || 0, htmlElement.clientWidth || 0, htmlElement.getBoundingClientRect().width || 0);
                // Failed to read
                if(! htmlWidth || htmlWidth <=0) {
                    if (window.orientation == 180 || window.orientation == 0) {
                        / / vertical screen
                        htmlWidth = window.innerWidth || (window.screen && window.screen.width) || (window.screen && window.screen.availWidth) || 0;
                    } else if (window.orientation == 90 || window.orientation == -90) {
                        / / landscape
                        htmlWidth = window.innerHeight || (window.screen && window.screen.height) || (window.screen && window.screen.availHeight) || 0; }}}catch (e) {
                console.log("Error getting screen width");
            }
            return htmlWidth | 0;
        }
        let html = document.documentElement,
            WIDTH = this.props.WIDTH || design.WIDTH,
            // The first time I came in without setting the HTML tag font size
            screenWidth = getScreenWidth(),
            htmlFs = getHtmlFs(),
            mediaFs = (design.RATIO / WIDTH) * screenWidth; // Get page width /fontSize= design (750) /100=7.5;

        html.style.fontSize = mediaFs + "px"; // Calculate the font size according to the page size

        // This is a special treatment. If you set the font size on an HTC browser and then get the font size, it will be smaller than the desired value

        if(htmlFs ! == mediaFs &&Math.abs(htmlFs - mediaFs) > 2) {
            html.style.fontSize = "100px";
            html.style.fontSize = (100 / getHtmlFs()) * mediaFs + "px"; }};render() {
        return this.props.children; }}Copy the code

Connected -react-router supports immer

import { createHashHistory } from 'history';
import produce from "immer";
import { LOCATION_CHANGE } from 'connected-react-router';
import { IActionParam } from "@/types/IRedux";

let history = createHashHistory();
export default history;

//import { push } from 'connected-react-router';
/ / provides a push, go, goBack to replace, block, goForward method.
//push("/home") || push({pathname:"/home",search:"name=1",hash:"1"})
// History can be divided into two parts: switch and modify the history state: back,forward,go corresponding to the browser back,forward, forward. history.go(2); // Proceed twice

// Push stores the page state in a state object, which can be retrieved when the page is returned via event.state.




// Check connected-react-router connectRouter to enable immer to be used with history
export interface HistoryState {
    location: any.action: any,}const injectQuery = (location) = > {
    if (location && location.query) {
        // Don't inject query if it already exists in history
        return location
    }

    const searchQuery = location && location.search

    if (typeofsearchQuery ! = ='string' || searchQuery.length === 0) {
        return {
            ...location,
            query: {}}}// Ignore the `? ` part of the search string e.g. ? username=codejockie
    const search = searchQuery.substring(1)
    // Split the query string on `&` e.g. ? username=codejockie&name=Kennedy
    const queries = search.split('&')
    // Contruct query
    const query = queries.reduce((acc, currentQuery) = > {
        // Split on `=`, to get key and value
        const [queryKey, queryValue] = currentQuery.split('=')
        return {
            ...acc,
            [queryKey]: queryValue
        }
    }, {})

    return {
        ...location,
        query
    }
}


const initHistoryState: HistoryState = {
    location: injectQuery(history.location),
    action: history.action,
};

/* eslint-disable no-param-reassign */
export const reducer = produce((draft: HistoryState, actionParam: IActionParam) = > {
    if (actionParam.type === LOCATION_CHANGE) {
        const { location, action, isFirstRendering } = actionParam.payload;
        draft.action = action;
        draft.location = injectQuery(location);
        return draft;
    }
    return draft;
}, initHistoryState);



Copy the code

After the language

Your comments are welcome. Project templates are constantly being optimized, like every time! Comments are welcome.