preface

It has been a long time since the last article. The company has been dispatched to BOE for a business trip these two weeks to be responsible for the front-end work of the settling project team. During this period of time, I set up a front-end project from scratch. This time, I had more time and thought more. I have often participated in front-end projects in the past, but the time given is relatively tight, so many problems are ignored. This time just on the previous optimization, and summed up some experience points want to give you. If you have better ideas, welcome to leave a message.

Tips:

  • This project is from the perspective of PC front-end project, mobile front-end project is not fully applicable. That’s something you guys need to be aware of.

  • The project has been maintained in different directions, and the corresponding direction of each branch can be checked in CONTRIBUTING. Md

Project description

The address of the project: https://github.com/ruichengping/react-webpack-pc

The project can be constructed with the scaffolding tool ASuna – CLI written by myself. The address of the scaffolding tool I wrote is as follows:

https://github.com/ruichengping/asuna-cli


This is the directory structure of the sample project, which we’ll examine one by one **

build

This file mainly contains related files packaged with WebPack.

  • Build.js —- webpack package scripts for building packages for production environments
  • Check-versions. js —- checks whether the node and NPM versions in the current packaging environment meet requirements
  • Utils.js —- webpack packs some of the tool libraries required
  • Webpack.base.conf.js —- Some basic configurations of Webpack, on which different environments’ Webpack configurations are based
  • Webpack.dev.conf.js —- WebPack configuration for the development environment
  • Webpack.prod.conf. js —- WebPack configuration in the production environment

The webpack configuration of this project was modified on vue-CLI project and can be used in React project construction. At present, only the development environment and production environment are two environments. Some companies may have multiple environments, and the WebPack configuration varies in each environment. In this case, you can create a Webpack configuration with the file name format of Webpack. < Environment name >.conf.js. In webpack.base.conf.js, there are some basic configurations, such as rules, input, output, etc. Generally, these are almost the same in each environment. Some differences can be merged by using webpack-merge plug-in. Generally speaking, two WebPack configurations, development and production, are sufficient for most projects.

config

This is where the configuration parameters needed for webPack in different environments are stored.

  • Dev.env.js —- exposes the environment variable NODE_ENV in the development environment
  • Index.js —- stores configuration parameters of different environments
  • Prod.env. js —- Exposes the environment variable NODE_ENV in the production environment

If you need to add another environment, you can create a file named “< environment name >.env.js” and expose the environment variable NODE_ENV, then import it in index.js and set the parameters.

mock

This is used to mock interfaces, which may not be used by many companies and I rarely mock at work. Here’s a look at my mock interface idea. I chose mockJS plus JSON-server. For the specific use of both, you can check its official documentation.

  • API —- stores data corresponding to different apis
  • Index. js —- json-server main file
  • Json —- Route mapping

Package. json I configured a script as follows:

 "mock": "json-server mock/index.js --port 3000 --routes mock/routes.json"
Copy the code

The console performs “NPM run mock”.

src

api

url.js

export default {
  fetchUserInfo:{
    method:'get',
    url:'/api/user'
  },
  fetchAuthorInfo:{
    method:'get',
    url:'/api/author'
  },
  fetchUserList:{
    method:'get',
    url:'/api/userList'}}Copy the code

index.js

import _ from 'lodash'
import http from '@/utils/http'
import API_URL from './url';

function mapUrlObjToFuncObj(urlObj){
  const API = {};
  _.keys(urlObj).forEach((key)=>{
    const item = urlObj[key]
    API[key]=function(params){
      return http[item.method](item.url,params)
    }
  });
  return API;
}

function mapUrlObjToStrObj(urlObj){
  const Url = {};
  _.keys(urlObj).forEach((key)=>{
    const item = urlObj[key]
    Url[key]=item.url
  });
  return Url;
}

export const API = mapUrlObjToFuncObj(API_URL);
export const URL = mapUrlObjToStrObj(API_URL);
Copy the code

Here we use to place the API interface address, for the subsequent interface maintenance, we will not directly write the interface address in the process of use, but encapsulate the interface request into a method. Through the unified maintenance of the interface, we can perform operations such as modifying the address of the interface, modifying the request method, adding the interface, etc., without looking around the whole project, as long as the maintenance of url.js exposed objects can be done. The usage method is as follows:

import {API} from '@/api'FetchUserInfo (params). Then (response=>{//response =>... })Copy the code

assets

Here we will put the required picture resources of the project, these picture resources are generally ICONS, are relatively small. Webpack will convert it to BASE64 for use. If you don’t want to use it this way, you can store image resources in the static directory.

components

This is where the common components used throughout the project are stored. Create a component folder named component name and create index. JSX and style. SCSS files in this folder. For example, a HelloWorld component would look like this.

HelloWorld

  • index.jsx
  • Style.scss // Store the style of the component

index.js

import React from 'react';
import './style.scss';
class HelloWorld extends React.PureComponent{
  render() {return (
      <h4 className="u-text">Hello World</h4>
    )
  }
}
export default HelloWorld;
Copy the code

style.scss

.u-text{
  color: red;
}
Copy the code

layouts

This is where the layout files are stored. This is how I defined the layout file. I had some pages that were identical in parts during development. Earlier, you might have added and implemented this function in a React component. However, the downside of this is that your routes cannot be managed uniformly and are scattered among components, causing many problems for subsequent maintenance. To solve this, I chose to use props. Children with tags nested. Here’s an example:

React basicLayout.jsx

import React from 'react';
class BasicLayout extends React.PureComponent{
    render(){
        const {children} = this.props;
        return(< div > < div > next to Lao wang today trip: < / div > < div > {children} < / div > < / div >)}}export default BasicLayout;
Copy the code

Once defined, we can use it like this:

import React from 'react';
import BasicLayout from '
      
       '
      
class Work extends React.PureComponent{
    render() {return(<BasicLayout> <div> Today next door Lao Wang is more tired, do not work! </div> <BasicLayout> ) } }export default BasicLayout;
Copy the code

Finally, the dom structure is as follows:

<div> <div> Next door Lao Wang today's schedule: </div> <div> Today next door Lao Wang is more tired, do not work! </div> </div> </div>Copy the code

This way we can make many pages like the following based on BasicLayout.

The < div > < div > trip next to Lao wang today: < / div > < div > / / < different content > < / div > < / div >Copy the code

Using this method, we can write all the routes together. Some people feel that it is troublesome to write BasicLayout every time. Is there a better way to use it?

pages

All page-level components are stored here, and routes corresponding to the React-router need to be mapped one by one. Each page is a folder, the file name is the page name, each page should contain the following files:

  • Components —- holds components unique to the current page
  • Redux —- holds three files actions.js, actiontypes. js, and reducer.js, which should only be relevant to this page
  • Index.jsx entry file for the —- page
  • SCSS —- page required style specific code can be viewed by git Clone project, here will not be posted.

scss

This is where the common SCSS files are stored, comparing some common function classes, @mixin, @function, etc.

store

Here are four files:

  • actions.js
  • actionTypes.js
  • reducer.js
  • index.js

We know that each page has its own actions. Js, actiontypes. js, reducer.js, but this is global, and index.js exposes the store, which is then imported into main.js.

import {createStore,combineReducers,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import API from '@/api';
import user from './reducer';
import author from '@/pages/PageOne/redux/reducer';
const rootReducer = combineReducers({
    user,
    author
  });
const store=createStore(
  rootReducer,
  applyMiddleware(thunk.withExtraArgument({
    API
  }))
)
export default store;
Copy the code

One small detail here is that redux-thunk can carry extra objects or methods, in this case, I carry API objects. When we need to use API objects in actions.js, we don’t need to import them. Here’s a comparison:

Before the change

import * as actionTypes from './actionTypes';
import API from '.. /api';

export const fecthUserName=(params)=> async (dispatch,getState)=>{
  const response =await API.fetchUserInfo(params);
  const {success,data} = response;
  if(success){
    dispatch({
      type:actionTypes.CHANGE_USER_NAME, payload:data }); }}Copy the code

The modified

import * as actionTypes from './actionTypes';

export const fecthUserName=(params)=> async (dispatch,getState,{API})=>{
  const response =await API.fetchUserInfo(params);
  const {success,data} = response;
  if(success){
    dispatch({
      type:actionTypes.CHANGE_USER_NAME, payload:data }); }}Copy the code

utils

This will store some of its own wrapped JS tool files. For example, I wrapped an HTTP.js file based on AXIos in my project to simplify the operation of AXIos.

router/index.js

Here we configure to prevent de-registration of routes and render route tags in app.js.

import Loadable from 'react-loadable';
import createHistory from 'history/createBrowserHistory';
import BasicLayout from '@/layouts/BasicLayout';
import NavTwoLayout from '@/layouts/NavTwoLayout';
import Loading from '@/components/Loading';
import NotFound from '@/pages/Exception/404';


const Home = Loadable({loader: () => import('@/pages/Home'),loading: Loading});
const Teachers = Loadable({loader: () => import('@/pages/Teachers'),loading: Loading});

export const history = createHistory();

export const routes = [
  {
    path:'/',
    redirect:'/navone/home'
  },
  {
    path:'/navone',
    redirect:'/navone/home',
    children:[{
      path:'/home',
      layout:BasicLayout,
      component:Home
    }]
  },
  {
    path:'/navtwo',
    redirect:'/navtwo/teachers',
    children:[{
      path:'/teachers',
      layout:NavTwoLayout,
      component:Teachers
    }]
  },
  {
    path:The '*',
    component:NotFound
  }
]
Copy the code

App.js

Here is the code to render the route tag according to the route configuration:

import React from 'react';
import {Router} from 'react-router-dom';
import {Switch, Route ,Redirect} from 'react-router';
import {history,routes} from '@/router';



function getRouterByRoutes(routes){
  const renderedRoutesList = [];
  const renderRoutes = (routes,parentPath)=>{
    Array.isArray(routes)&&routes.forEach((route)=>{
      const {path,redirect,children,layout,component} = route;
      if(redirect){
        renderedRoutesList.push(<Redirect key={`${parentPath}${path}`} exact from={path} to={`${parentPath}${redirect}`}} / >)if(component){ renderedRoutesList.push( layout? <Route key={`${parentPath}${path}`} 
            exact path={`${parentPath}${path}`}
            render={(props)=>React.createElement(layout,props,React.createElement(component,props))} />:
          <Route 
              key={`${parentPath}${path}`} 
              exact 
              path={`${parentPath}${path}`} 
              component={component}/>)
      }
      if(Array.isArray(children)&&children.length>0){
        renderRoutes(children,path)
      }
    });
  }  
  renderRoutes(routes,' ')
  return renderedRoutesList;
 
}
class App extends React.PureComponent{
  render() {return (
      <Router history= {history}>
        <Switch>
          {getRouterByRoutes(routes)}
        </Switch>
      </Router>
    )
  }
}
export default App;
Copy the code

We’re also going to focus on layouts for all of the content we’re going to skip in our layouts layouts. Here I selected the Render property in.

main.js

React-dom render is the render method under the react-DOM.

import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import store from '@/store';
import App from '@/App';
import '@/scss/reset.scss';
import '@/scss/base.scss';


render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('app'))Copy the code

static

This is a static resource directory that typically houses third-party tool libraries. There are two main considerations for this directory:

  • Some third-party libraries do not have the NPM package. We cannot add NPM install or YARN Add
  • Some of the larger third-party libraries will affect our packaging speed, so we can pull it out and introduce it as a script

In fact, the best way to third-party tool library is CDN, but some companies just do not have, have no choice but to do so. The third library you added is available in the “/static/*” directory on the current server.

templates

Here are the template files required by the PageReducer and component-level build. The PageReducer provides two templates (integrated with Reducer) and PageSample (not integrated with Reducer), while the component provides only one template ComSample. Page and component-level construction can only be built with ASuna – CLI. Currently, the project has integrated ASuna – CLI. Package. json writes two scripts: NPM run newPage (page build) and NPM Run newComponent (component build). Development can choose according to actual needs to build, asuna cli can go to https://github.com/ruichengping/asuna-cli to check the specific use.

Other documents

  • .babelrc —- Babel transformation configuration file
  • . Gitignore —- Files to be ignored for git operations
  • .postcsrc. Js —- Postcss configuration file
  • Index.html —- template index.html, from which Webpack will generate a new index.html, used with html-webpack-plugin
  • Package. json —- household name
  • Readme. md —- Project description
  • Theme. Js —- The theme color configuration file of ant-Design. For details, see Ant-Design
  • Asuna. Config. js —- asuna- CLI configuration file
  • Yarn. lock —- Specifies the version of the locked package

conclusion

This is just a summary of a personal enterprise React project. Of course, there are some shortcomings, and I will update it if I have some good ideas in the process of work. Welcome to Star attention! If you also have a good idea to welcome the message exchange, I hope this humble article can give you some inspiration.