This article is not about React technology per se, but how to maintain a React project.

The text will probably cover some basics of Webpack, but if you don’t already know Webpack, you can take a look at my previous article Webpack Crash Course.

Scaffolding, in programming, means tools that can quickly build the skeleton of a project. For example, most React projects have SRC directory, public directory, webpack configuration file, Babel configuration, etc. The SRC directory usually contains components directory, reducers directory, etc. Every time you create a new project, you have to manually create these fixed file directories, which are tedious and cumbersome. Scaffolding is there to help you do the repetitive work of one-click generation of the main directory structure, installing dependencies, and so on. Yeoman is a famous scaffold tool.

When you enter a company to work on a React project, all you need to do is to develop specific components, execute commands to start the project, run it, debug it, and release a package to go live. You probably don’t think about why the directory structure is the way it is or what so many configuration files are for (I used to be like that). Create-react-app (CRA) and react-starter-Kit (RSK) are the scaffolding tools for the React project. I will analyze the functions of different files one by one according to their documentation and my personal experience.

This knowledge doesn’t just apply to React projects. What’s behind the files are the tools, and what’s behind the tools are the problems to be solved. Different companies and different teams may use different tools, and there will be new technologies or frameworks in the future, but these problem-solving ideas can also be reused. Or they can be a good textbook for when you need to start a React project without relying on scaffolding.

Since CRA is Facebook’s official scaffolding tool, we’ll start with the CRA as the main clue. Its User Guide is the most extensive, and most of this article is taken from this document, so please feel free to comment if you don’t understand it properly. React-starter-kit will also be included.

This article series will be divided into two parts. In the previous part, we covered some basic configurations for a regular project setup, while in the next part, we will cover advanced configurations that cover the back-end functionality of the development environment as well as testing and deployment.

The last bit of crap I want to stress is that any scaffold generated project structure is for reference only. The actual organization and use of tools depends on the actual situation.

Project directory

Let’s start with the basic directory folder

There are two very important directories in CRA: SRC and public:

  • src: This is where your scripts and style source files are stored. All files that you need to package or compile by Webpack must and must only be placed in this folder (in turn Webpack will only go to this folder to find the files that need to be packaged). Examples include TypeScript, LESS, SASS, Stylus source code, etc., images that you need to reference in a component, SVG resources, etc. (we’ll talk about styles and images referenced in a component later). In a wordsrcThe directory, as its name implies (SRC = source), contains the source code of the program.

Of course, the subdirectories of the SRC directory are not so fastidiously named and organized. If you’re developing a Redux project, you’ll naturally have components, Reducers, Actions, etc. It’s even ok to have separate folders for container Component and Stateless Component in components.

Finally, the SRC directory entry is SRC /index.js, so you can look at the contents of index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();Copy the code

Quite simply, it renders the entry component to the page and registers the Service worker.

  • public: publicIt is usually used to store resources accessible to users, such as packaged scripts, images, and HTML files. But there’s more to it than that. From the RSK project we can see that there’s more in the public folderrobots.txt,humans.txt,crossdomain.xml,favicon.ico, etc.

Although the public directory does not hold components, the public directory also has an entry, /index.html, which is the page that the user accesses under the root of the domain name. CRA specifies that only resources in the public folder can be used by index.html. HTML also references static resources in a unique way, not through relative or absolute paths, but through global variable references. We’ll talk about this later in the resource usage section.

The public folder is also sometimes named assets or even Resources, and that doesn’t matter. If you are more disciplined, you can create a subfolder dist in public to store scripts and styles that distribute (dist is actually a distribute abbreviation), or a Build folder to store scripts that are built during development

SRC and public are the two most important folders. These are the only two folders in CRA. We can also look at the folders in RSK:

  • docs: For storing development-related documents (in Markdown format)
  • tools: Directory for storing tool scripts. “Utility scripts” are the scripts that are used to do the specified work, and in the folder you will see examples such asbuild.js,deploy.js,copy.jsAnd so on. Even if you don’t show the content of these scripts, it’s easy to tell what they do by looking at their file names, which is build, deploy, copy files, and so on. This part of the script can also be passednpmCommand execution, more on that later.
  • src/serverIf your project is developed as a full stack of Javascript, you can include server-side code as wellsrcIn the
  • test: Stores test scripts

Various configuration files

More and more tools are being invented to assist our development, but different tools need to be configured differently for different projects. So there are all kinds of configuration files that might exist in our project files. You may not be able to use all of these tools and configuration files, but at least you won’t be unfamiliar with them after you look at them, and they may come in handy later in the troubleshooting process.

The following configuration files are taken from RSK scaffolding (the first time you see a scaffold generating so many files for you that you have never seen them before you will be scared, I think so anyway).

  • .editorconfig: tells the editor the code specification for the project. One problem that may be involved in team development is that different students may use different development tools and habits, some use WebStorm, some use Visual Studio Code. So it’s possible to indent 2 Spaces in your editor and 4 Spaces in his editor. This configuration file is used to store a uniform style specification, telling the editor to use two Spaces, not to allow empty string endings, and so on. Please refer toeditorconfig.org
  • .eslintrc.jsConfiguration file for the ESLint tool. Eslint is a professional tool for checking THE syntax and format of JS. Most editors should be integrated or installed as plug-ins. This configuration file tells ESLint which files can be ignored, which rules can be ignored, which files fit which rules, and so on. Please refer to:Eslint.org/docs/user-g…
  • .stylelintrc.jsSimilarly, stylelint is a tool for checking the syntax of style files. The configuration file provides detailed configuration of the checking rules. Please refer to:Stylelint. IO/user guide /…
  • .flowconfig: flowFacebook has launched an open source tool for typing JavaScript syntax. This file is the configuration file for the toolFlow.org/en/docs/con…
  • .env: Environment variables are unavoidable when starting a project, the most famous of which are environment variablesNODE_ENVFor example, tell the program to use the production environment:NODE_ENV=production. We all know that you can specify environment variables in the form of command-line arguments when executing a command line, for exampleNODE_ENV=production node app.js, and then read the environment variables indirectly from the program by reading the command line parameters. And bydotenvModule, we can put environment variables in.envUnified management unified access in the environment.
  • .travis.ymlContinuous integration toolstravis-ciGithub Marketplace is available for more configurationDocs.travis-ci.com/user/custom…
  • circle.ymlContinuous integration toolscircleciGithub Marketplace is available for more configurationCircleci.com/docs/1.0/co…
  • jest.config.js: Config file for Jest, Facebook’s testing toolFacebook. Making. IO/jest/docs/e…
  • jsdoc.config.json: jsDocIs a tool that generates documentation based on the function annotations in the file. This file is the configuration file of the tool, for more informationUsejsdoc.org/about-confi…
  • DockerfileDocker container configuration file (sorry Docker I am really not familiar, there is no good to add)

In addition, there are some other documents that you might need, like

  • CHANGELOG.md: Version update logs
  • CONTRIBUTEING.md: How can you contribute to the project

Tool script

Back in my day, the front-end development process was simple: manually create a static page and import the script you needed to get started. Today, however, not only has the way of introducing scripts changed, but the debugging process, packaging process, and distribution process have become complex and professional, all of which are dependent on NodeJS scripts. The benefits of scripting are reusable, automated, and batch processing.

Development needs to use script processing link is very much, such as less to compile CSS, script compilation, compression, splicing, compression pictures and so on. This can be done by a Webpack or Gulp or Grunt. But these third-party libraries are not a panacea, and they also rely on plugins in their ecosystem. In this complex dependency situation, errors can often occur. Why not use Gulp or Grunt again? See Why We Should Stop using Grunt & Gulp. The so-called tool of water, the script of iron

NPM scripts are stored in the scripts field in the package.json file

NPM commands we can talk about in a separate article at some point, but back to scaffolding, there are only four NPM commands used in CRA

  • npm start: Start the app in development mode. By default, port 3000 is used. After starting the app, enter it in the browserhttp://localhost:3000You can access it, and the page will refresh automatically if your application changes
  • npm test: Runs the application test script
  • npm run build: Compile and package the application for production, packaged tobuildfolder
  • npm run eject: If you look carefully at the files in the CRA directory, you will find none.babelFile, nowebpack.config.jsSimilar file. Because all the scaffolding takes care of you. All inreact-scriptsIn the library. So you seepackage.jsonIn the filenpm startWhat’s actually running isreact-scripts start. When you are not satisfied with the scaffolding for your preset configuration, you can use itejectThe command exposes the configuration (e.gstartOrders, andwebpack.config.dev.js), so you can fully customize these configurations. Notice that this operation is irreversible. And when you’re done running it, you’ll seepackage.jsonIn thescriptsthestartThe command tonode scripts/start.js

Start and test are not. NPM start is a built-in default command, which you can interpret as something like a macro. If you execute NPM start without customizing the start command in package.json, it actually executes Node server.js. More built-in commands refer to docs.npmjs.com/misc/script…

Automatic formatting code

By formatting code, I don’t mean beautifying and formatting compressed code for easy reading. Instead, you force the code to be formatted at the commit stage. So we’re using three extra libraries here

  • husky— Easy to use as NPM scripts to call git hooks.
  • lint-staged: Easy for us to execute NPM scripts for passively written documents
  • prettier: Formats the code

4, The core library, of course, prettier. Prettier herself gives several reasons why formatting code is still necessary, such as forcing formatting to avoid syntax problems associated with PR, and helping new students standardize code while not familiar with it.

Prettier solves how, but husky and Lint-passage still have to solve when, when to format. In CRA, formatting is done in the pre-commit phase, and in a real project you can also do it in the pre-push phase.

The problem Husky solved was to expose the pre-commit hooks. By default if you want to write a pre-commit script, you need to edit your project’s.git/hooks/pre-commit files, which are shell scripts if I remember correctly, and give them execution permissions before executing them.

However, once you have husky installed, you can place scripts that need to be executed during the pre-commit phase directly in the sciPTS field of package.json, for example:

"scripts": {
  "precommit": "eslint"
},Copy the code

Lint-staged versions solve the last kilometer problem, that is, encapsulating scripts that need to be executed during the pre-commit phase, again in package.json configuration, for example:

  "dependencies": {
    // ...
  },
 "lint-staged": {
   "src/**/*.{js,jsx,json,css}": [
     "prettier --single-quote --write"."git add"]},"scripts": {Copy the code

The development of specification

In the era of componentization, everything is a component, even HTML can become a component. In the RSK scaffolding, you’ll even see a component called html.js (which is then rendered back end). We want to solve everything with components, rather than leaving code that needs to be maintained all over the place, even inside the tag. React Helmet


I don’t need to talk about developing and referencing components. It’s the same all over the world, and I’m sure you’re familiar with it.

style

Styles, whether you use Less, Sass, or Stylus, can be compiled to CSS using the corresponding loader in Webpack. It’s the way the style is organized that matters. In traditional projects, styles and scripts are kept separate, in separate folders. But in the React project, we have only one dimension, components, which contain both styles and scripts, in the Components folder. Such as:

components/
|--Button.js
|--Button.lessCopy the code

So in button.js you can reference styles directly

import React, { Component } from 'react';
import './Button.css';Copy the code

Alternatively, you can add all styles to the SRC /index.css style entry and then add the SRC /index.css style entry to the component entry SRC /index.js.

There is some additional work to be done in addition to compiling styles, such as compression, such as adding browser prefixes to certain style attributes. CRA uses Autoprefixer or PostCSS, and of course it’s all integrated into React-Scripts. You can also use NPM scripts independently to monitor style changes and automatically preprocess and “post” style files as they change. This process also works for script files.

There are also many libraries dedicated to optimizing NPM script execution that you could (or should) use in the above process:

  • onchange,watch: to monitor file changes and then execute specific NPM scripts, such as
    "scripts": {..."watch:css": "onchange 'src/scss/*.scss' -- npm run build:css"."watch:js": "onchange 'src/js/*.js' -- npm run build:js",}Copy the code
  • parallelshell: used to execute multiple NPM scripts in parallel, for example
    "scripts": {..."watch:all": "parallelshell 'npm run serve' 'npm run watch:css' 'npm run watch:js'"
    }Copy the code

    Trust that you can see what this code is doing 😛

Add additional resources such as picture fonts

Resources such as images and fonts, like styles, are placed in the same hierarchy as the components that use them, at least in the same components folder, and are introduced in components using the import keyword, for example

import React from 'react';
import logo from './logo.png'; // Tell Webpack this JS file uses this image

console.log(logo); // /logo.84287d09.png

function Header() {
  // Import result is the URL of your image
  return <img src={logo} alt="Logo" />;
}Copy the code

To reduce the number of requests to the page, images less than 10,000 bytes in size return the data URI instead of the actual path. When a project needs to be built (for production), Webpack copies the image resources greater than 10,000 bytes into the final build folder (the directory in CRA is /build/static/ Media) and renames them based on the hash value of the content. So you don’t have to worry about resource changes that won’t take effect because of the browser cache

The document gives three reasons for importing styles and images:

  • Scripts and styles can be compressed and packaged together to reduce additional network requests
  • If a file is missing, an error will be reported at compile time, rather than being presented to the user with a 404 error after going live
  • Rename files based on the hash value of their contents to avoid browser cache problems

If you must quotepublicResources in folders

Not all resources can be referenced in a component, or some third-party libraries do not support React integration. In this case, you need to put resources in a public folder and reference them in HTML. For example:

<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">Copy the code

Then at build time (NPM run build), Webpack replaces %PUBLIC_URL% with the absolute path to the actual public directory.

You can also access the process.env.public_URL variable in the js file to get the absolute path to the public folder

render() {
  // Note: this is an escape hatch and should be used sparingly!
  // Normally we recommend using `import` for getting asset URLs
  // as described in “Adding Images and Fonts” above this section.
  return <img src={process.env.PUBLIC_URL + '/img/logo.png'} / >;
}Copy the code

This approach to accessing resources has the following disadvantages (compared to the import approach, of course) that you need to be aware of:

  • publicThe files in the folder are not processed, including compression or concatenation
  • If a file is missing, no errors are reported at compile time, and the user may receive a 404 request back
  • You may need to handle caching issues manually, such as renaming files when they are modified, or modifying themEtagWait for the cache condition. Please refer to my article for detailsDesigning an Airtight Browser Caching Solution: On Ideas, Details, ServiceWorker, and HTTP/2

However, in some cases you can consider using this method of accessing resources

  • You need to reference additional scripts that are not packaged, such as pace.js, such as the Google Analytics script
  • Some scripts are incompatible with Webpack
  • Thousands of images need to be referenced dynamically
  • File names need to be specified for packaged output files at build time

Add custom environment variables

CRA scaffolding also allows you to add custom environment variables to process.env for global access.

By default, it provides two environment variables to use, one of which is the public folder path PUBLIC_URL used in the previous section. The other is the more familiar NODE_ENV. The latter is a variable representing the current development environment, which is equal to development when you run NPM start; When you run NPM test it will be test; When you run NPM run build, its value is production. You can’t override it manually, and it prevents developers from accidentally packaging a development version and deploying it online. NODE_ENV can also help you debug code specifically, for example if you only want to disable analysis scripts in non-production environments:

if(process.env.NODE_ENV ! = ='production') {
  analytics.disable();
}Copy the code

Of course, you can also add your own environment variables, either through the command line, for example, in Windows set REACT_APP_SECRET_CODE=abcdef&& NPM start. The other option is to use the. Env file (that is, the dotenv library) and put all the environment variables you need in this file:

REACT_APP_SECRET_CODE=abcdefCopy the code

One thing to note is that all custom environment variables need to start with REACT_APP_SECRET_CODE (for reasons I don’t understand: Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name. 5. I don’t quite understand a private key.

Once environment variables are defined, they can be used in files, such as scripts:

render() {
  return (
    <div>
      <small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small>
    </div>
  );
}Copy the code

For example, in HTML:

<title>%REACT_APP_WEBSITE_NAME%</title>Copy the code

Finally, environment variables in different development environments do not have to be stored in. Env files, but can be divided into. Env.development,.env.test,.env.production and other different files, and there is a priority relationship between different files

Editor debugging

Popular ides such as Visual Studio Code and WebStorm support Code debugging within the editor, but you may need to configure environment variables for the editor, or add configuration files, or install plug-ins for the browser. My personal experience with debugging with the editor has not been good, and not all scenarios support debugging. And because both scaffolding and editor debugging start up the back-end environment, there may be areas where conflicts need to be resolved.

For detailed configuration information, refer to the official documentation for both editors.

Finish last

This article is also published in my Zhihu column in the front-end technology Hitchhiker’s Guide, welcome your attention

The resources

  • In a web project, what is the difference between a dist and build directory (traditionally)?
  • Why npm Scripts?
  • npm-build-boilerplate
  • Why we should stop using Grunt & Gulp
  • How to Use npm as a Build Tool
  • kentcdodds/nps
  • Running scripts with npm
  • npm-scripts