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:
- Creating a project takes too long
- The framework of the repair and subtraction project is unstable
- It is not conducive to iterative optimization frameworks
- .
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.