Code address github.com/Maricaya/ad… , please star ~ for help

background

When people first started react, they used scaffolding like create-React-app to quickly create applications. When the basic configuration does not meet our special needs (such as using SASS), we modify the eject WebPack configuration file.

Faced with a variety of configuration items are confused, do not know how to quickly modify. In fact, there is no skill in learning project construction and configuration, but experience (routine).

This article will show you how to build a React project manually using WebPack.

tool

  1. Yarn
  2. webpack 4
  3. Environment: webpack-dev-server 3
  4. Language: TypeScript 3
  5. Editor: WebStorm/VScode, etc

steps

NPM initialization

npm init -y 
Copy the code

This step creates package.json

Install WebPack, TypeScript, and React

Go to the Webpack website and find the install command.

yarn add webpack webpack-cli typescript --dev
yarn add react react-dom 
yarn add @types/react @types/react-dom --dev
Copy the code

New lib/index. The TSX

Write a few lines of TypeScript code in this file.

console.log(123);
Copy the code

Index.tsx will not run in the browser, so we need Webpack to make index.js executable for the browser.

webpack.config.js

Create webpack.config.js in the root directory.

format

The following configuration is written inside the Module.exports object.

module.exports = {}
Copy the code

mode

Mode is a new option added to WebPack 4 and has two optional values: production and development. Mode cannot be omitted. We first configure the development environment, and then modify it if necessary.

mode: 'development'
Copy the code

entry

Packaged entry files.

entry: { 
  <key>: <value>
}
Copy the code

I cover only the object form here, because this is the most complete entry configuration, and the other forms are simplifications of it.

Each property pair in the object represents an entry file. Therefore, when configuring multiple pages, it must be an entry in this form.

Key can be a string variable, corresponding to the name of the output file; It can also be a path, such as ‘a/b/c’, which corresponds to the output path of the file. A full explanation can be found in this article.

entry: { 
  index: './lib/index.tsx' //./ In the current directory
}
Copy the code

resolve

When webPack starts, it triggers to find all dependent modules from the configured entry module. Resolve configs how WebPack finds the corresponding files for the modules.

When an import statement does not have a file suffix, WebPack automatically adds the suffix to try to access the existence of the file. The resolve.extensions are used to configure the list of suffixes used during the attempt.

resolve: {
  extensions: ['.ts'.'.tsx'.'.js'.'.jsx']}Copy the code

module.rules

tsx

Next configure the loader that interprets the TSX. So how do you know which loader is the most suitable?

The answer is there is no standard loader, just try it one by one.

After trying out every configuration method available online, I found one that I thought was best: awesome-typescript-loader.

The installation

yarn add awesome-typescript-loader --dev
Copy the code
module: {
   rules: [{test: /\.tsx? $/, 
        loader: 'awesome-typescript-loader'}}]Copy the code

scss

Then configure the module of SCSS translation, need to use three loaders: style-loader, CSS-loader, sas-loader

yarn add --dev style-loader css-loader sass-loader
Copy the code

The loader principle in Webpack is that a loader only does one thing

  • Sass-loader translates icon. SCSS to icon.css
  • CSS -loader icon. CSS -> object
  • Style – the loader object – ><style>css</style>
{
  test: /\.s([ac])ss$/,
  use: [
    'style-loader'.'css-loader'.'sass-loader']}Copy the code

After configuring the input and intermediate loader, let’s configure the output.

output

  • Path Output address

I thought it was possible to write the absolute path __dirname + ‘./dist’, but on Windows I got an error. This is because paths are represented differently on different operating systems.

- Windows: ` __dirname + '\ \ dist `, MAC & Linux: ` __dirname +'. / dist `.Copy the code

So, we directly use the node.js provided method path.resolve(__dirname, ‘dist’), which connects the two directories based on the type of operating system.

  • library: 'adorable-react'

Library is the name of the module that specifies when the user uses require. When used, the command is require(” ador-react “).

  • LibraryTarget indicates what the output format of the library is, the output module specification. There are several options umD, AMD, commonJS2.

The general choice is’ UMD ‘, a scheme that works under all module definitions.

So the question is, what is UMD?

To understand what UMD is, we need to take a look at the history of front-end packaging.

There is no package management system at the beginning of the front-end, and all JS code is loaded one by one via script tags. Global variables with the same name in the JS package will affect each other at this stage. This is where packaging tools are needed to limit variables to local scope.

Front-end programmers came up with AMD packaging, and the Node.js community came up with another set of packaging rules, commonJS.

  1. AMD

In the famous require.js scheme, our variables are defined in the define function.

define (function () { 
	// Here is our variable
	var a = 1. })Copy the code

This standard is called AMD Asynchronous Module definition.

  1. commonJS

Around the same time, the Node.js community came up with another set of packaging rules, commonJS.

var a = 1 // Only valid for the current file
module.exports = { ... }
Copy the code

The webpack.config.js we’re writing follows this specification as well.

CommonJS is only used in Node.js, AMD is only used in browsers. As a result, you have to distinguish between code running on the client side and server side.

At this time, someone invented a unified set of rules UMD unified module definition. UMD determines what the current runtime environment is and uses either commonJS or AMD depending on the runtime environment.

So here, we choose the most complete UMD.

Finally, the complete output code is as follows:

const path = require('path')
// mo
output: {
  path: path.resolve(__dirname, 'dist/lib'),
-  library: 'adorable-react'.libraryTarget: 'umd'
}
Copy the code

Configuration tsconfig. Json

Create tsconfig.json in the root directory, which is the TypeScript configuration file.

It mainly contains two parts:

  1. Specify the file to compile
  2. Defining compilation options

This article is mainly about webpack 4.0 configuration, ts configuration file first skip, you can directly copy my warehouse tsconfig.json file.

Those of you who want to know more can read the official documentation and this blog post.

Try packing for the first time

With these basic rules configured, we can try packaging with NPX WebPack.

The packaged file looks like this:

Root directory/dist/lib/index. Js— This is the index.tsx translated file

The root directory /lib/index.d.ts – contains all declared types

The index.d.ts directory is not correct. All *. D. ts generated files should be placed in dist directory.

Json “compilerOptions” to “outDir”: “dist”.

Again, all files are placed in the dist directory, and the first webpack is done.

The only thing Webpack has done so far is translate TS and TSX into JS.

webpack-dev-server

Next we install webpack-dev-server so that the code automatically compilers for hot updates.

yarn add webpack-dev-server --dev
npx webpack-dev-server 
Copy the code

Visit http://localhost:8080/index.js at this point, we can see the compiled files.

Tinkering lib/index. In the TSX files, http://localhost:8080/index.js will automatically update.

So why is Webpack-dev-server compiling so fast? What is hot update?

Speed is fast

The whole process can be simplified as webpack-dev-server starts a small server (called the Bundle Server).

The Bundle Server is a Server that executes these compiled files and makes them accessible to the browser.

The index.js file wrapped by Webpack is transferred to the Bundle Server and stored in memory.

When the user accesses index.tsx, the server throws index.js directly from memory, so compilation is fast.

Hot update

In the application’s development environment, developers can easily modify the code without refreshing the page and visually see the mechanism of change on the page.

html-webpack-plugin

So far, I don’t have an HTML file. In the root directory, create a new HTML entry file called index.html.

The next question is, what is the file path to import JS?

As soon as the file name under Entry changes, we need to manually change the js path. So why not generate paths automatically?

Install html-webpack-plugin, root directory to create index.html file, webpack static files automatically inserted into HTML.

yarn add --dev html-webpack-plugin
Copy the code

Webpack. Config. Js configuration

const HtmlWebpackPlugin = require('html-webpack-plugin')

plugins: [
  new HtmlWebpackPlugin({
    title: "adorable-react"./ / page title
    template: "index.html"})].Copy the code

Mount the index.tsx ReactDom to index.html. Index. In the TSX:

const div = document.createElement('div');

div.innerText = 'div';
document.body.appendChild(div);
Copy the code

externals

If we want to reference a library but don’t want webpack to be packaged, we can do so by configuring externals.

This feature is mainly used when creating an NPM library.

For example, we developed a React component library that references React and the React -dom. However, there is no need to bundle React and React -dom together, because users will download it when they use it.

We can externals react/react-dom from our source code.

    externals: {
// How to import configuration
      react: {
// In node.js, import * as React from 'React' is equivalent to const React = require(' React ')
        commonjs: 'react'.commonjs2: 'react'.// use require.js, equivalent to define(["react"], factory);
        amd: 'react'.Var React = (window.react) or (React);
        root: 'React'
      },
      'react-dom': {
        commonjs: 'react-dom'.commonjs2: 'react-dom'.amd: 'react-dom'.root: 'ReactDom'}}Copy the code

Webpack configurations distinguish between development and production modes

At this point, you have completed all the basic configuration of WebPack! Give yourself a round of applause!

The develop and Production modes are both written together. Do you need to manually change them every time you package them?

Don’t worry, Webpack provides us with automatic switching.

Divide the webpack file into develop mode and production mode, and write the common part to webpack.config.js.

Production says webpack.config.prod.js, develop says webpack.config.dev.js.

The full version can be viewed by clicking on the link, which gives the core code: webpack.config.prod.js

const base = require('./webpack.config')
/ / use the Object. The assign
module.exports = Object.assign({}, base,{
  mode: 'production'
})
Copy the code

Configuration package. Json scripts

We configure the command in package.json scripts to run the project directly using YARN XXX.

Go straight to the final command:

{
    "script": {
	"start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js"."build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js"}}Copy the code

Development mode command webpack-dev-server, webpack configuration is webpack.config.dev.js.

Production mode command webpack, webpack configuration is webpack.config.prod.js.

cross-env NODE_ENV=xxx

NODE_ENV=development is the environment variable we write.

You may also be wondering if there is a mode in webpack.dev.config.js. Why write environment variables manually?

Since neither the entry script file processed by Webpack nor the script file referenced by webpack can access the webpack.dev.config.js properties, manually write environment variables to make process.env.node_env accessible to all files.

The cross-env plugin ensures that environment variables are added successfully on all platforms. (Environment variables are written differently on Unix and Windows.)

installation

yarn add --dev cross-env 
Copy the code

Introducing tests (optional)

Jest is the most popular testing framework in the React community

Testing configuration is not the point, you can directly look at the warehouse

Here is a brief configuration:

yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
yarn add --dev ts-jest
Copy the code

Create the babelrc

{ "presets": ["react-app"] }
Copy the code

The configuration scripts test

"test": "cross-env NODE_ENV=test jest --config=jest.config.js --runInBand"
Copy the code

Create a jest. Config. Js

Configure the Enzyme is a JavaScript test utility for React that makes it easier to test the React component.

yarn add --dev enzyme enzyme-adapter-react-16
Copy the code

Create test folder, write configuration, here is not described, interested in direct source ~ ~

Summarize the function of each directory

Finally, the project is complete, and finally, let’s see what each directory does!

Now we know exactly what each file does, what goes wrong, and where to fix it.

. ├ ─ ─. Babelrc/configuration/Babel ├ ─ ─ the README. Md ├ ─ ─ index. The HTML / / homepage ├ ─ ─ jest. Config. Js/configuration/jest ├ ─ ─ dist / / final code ├ ─ ─ lib / / Source code │ ├ ─ ─ __tests__ │ │ └ ─ ─ but unit.and TSX │ ├ ─ ─ index. The SCSS │ └ ─ ─ index. The TSX ├ ─ ─ package. The json ├ ─ ─ the test / / test configuration │ ├ ─ ─ __mocks__ │ │ ├ ─ ─ file - the mock. Js │ │ └ ─ ─ the object - the mock. Js │ └ ─ ─ setupTests. Js ├ ─ ─ tsconfig. Json / / ts configuration ├ ─ ─ Json // Ts Test Config ├─ tslint.json // Code Test Config ├─ webpack.config.dev.js // webpack Config ├─ webpack.config.js └── ├─ ├.config.├.js ├─ ├.config.wheelCopy the code

Congratulations on completing the project construction. Now we can coding happily!