This article is intended to help those who have been using scaffolding without the concept of engineering friends.

Many of the steps in this article report errors at run time and analyze the configuration that needs to be added from the errors to gain an impression and understanding of the configuration of each line that is added.

React This article will take you on a walk.

Create a directory

mkdir demo && cd demo
npm init
touch index.js
Copy the code

webpack

Install webPack dependencies

yarn add webpack webpack-cli --dev Install webPack dependencies
touch webpack.config.js Create a webapck configuration file
Copy the code

Modify the configuration

Here is a basic WebPack configuration:

const path = require('path');

module.exports = {
  entry: "./index.js".output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'}};Copy the code

Adjust the package. The json

In package.json, add scipts as follows:

"scripts": {
	"build": "webpack"
},
Copy the code

Try a build

Execute on the command line

npm run build
Copy the code

You’ll see the following warning:

WARNING in configuration The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment. You can also set it to ‘none’ to disable any default behavior. Learn more: Webpack.js.org/concepts/mo…

Since the Mode configuration item was added to WebPack after 4.0, it sets production for us by default. However, this warning does not affect build results for now. You can see that the dist directory has generated a file named my-first-webpack.bundle.js.

Now that the basic configuration is complete, React is introduced.

React

Install react dependencies

yarn add react react-dom Install react-related dependencies
Copy the code

use

Before using it, add index.html to dist directory as follows:


      
<html>
  <head>
    <meta charset="UTF-8">
    <title>React</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./my-first-webpack.bundle.js"></script>
  </body>
</html>

Copy the code

React HelloWorld HelloWorld HelloWorld HelloWorld HelloWorld Never mind, code you always know it, stick over!

Change the contents of index.js to the following:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>Hello, world!</h1>.document.getElementById('root'));Copy the code

OK, it looks perfect, execute “build” and see the result.

ERROR in ./index.js 4:16
Module parse failed: Unexpected token (4:16)
You may need an appropriate loader to handle this file type.
| import ReactDOM from 'react-dom';
|
> ReactDOM.render(<div>Hello World</div>,
|   document.getElementById('root'));
Copy the code

An error? You may need an appropriate loader to handle this file type. The programmer is great, and the error message is very detailed, telling us that we need the appropriate loader to process this file. Loader? Don’t ask me, I didn’t do it. Scroll down to the next section of the react-Jsx introduction and scroll down to the bottom. Is there an official suggestion? Keep looking for keywords. Do you see something called Babel? Nani, could JSX have something to do with it? Although this paragraph is recommended for editor Settings, programmers should be curious.

Babel

Google Babel and take a look inside. So I came across a new term, Babel. What does it do? What does it bring us? Check out the front page.

  • ES2015 and later

    Babel supports the latest versions of JavaScript through syntax converters.

  • Polyfill

    Since Babel only converts syntax (such as arrow functions), you can use Babel-Polyfill to support new global variables, such as promises, new native methods.

  • JSX and Flow

    Babel can convert JSX syntax and remove type comments.

  • pluggable

    Babel is built outside of plug-ins. You can use existing plug-ins or write your own to compose your own transformation pipeline.

  • adjustable

    Source Map support makes it easy to debug compiled code.

After reading the introduction of the home page, whether and I have the same exclamation: good thing ah! If it’s a good thing, use it. From the webpack configuration option, you can see that the two keywords that appeared just now come from Babel, loader, and Emma.

yarn add babel-loader babel-core babel-preset-env babel-polyfill babel-preset-react --dev 
Is this wave installation longer? Because we just saw es2015, higher syntax, Polyfill, JSX loaded
Copy the code

throughconfigconfiguration

Modify the webpack.config.js configuration as follows:

const path = require('path');

module.exports = {
  entry: "./index.js".output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader"}}};Copy the code

create.babelrcThe configuration file

touch .babelrc # to create babelrc
Copy the code

Paste the following into. Babelrc:

{
  "presets": ["env"."react"]}Copy the code

Babel-env and babel-preset-react are used, and polyfill is used for preset. Go ahead and read the wave file. Let’s use polyfill and modify the configuration in webpack.config.js as follows:

const path = require('path');

module.exports = {
  entry: ["babel-polyfill"."./index.js"].output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader"}}};Copy the code

Run the NPM run build command to see what happens. Sure enough, it’s compiled.

You think the project is just finished? NO, this is just the beginning!

react-router

What is SPA, single page application? What is a single page app? With only one HTML, common JS CSS is referenced only once, rendering the application through a partial refresh.

Follow the steps to get started quickly.

The installation

yarn add react-router-dom
Copy the code

use

Now you can copy/paste any of the examples into src/App.js. Here’s the basic one:

Here’s how to use create-react-app. We didn’t use create-react-app, but we built it ourselves. Create your own SRC and app.js.

mkdir src && touch src/App.js
Copy the code

Upload the official 🌰 to app.js:

import React from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'

const Home = (a)= > (
  <div>
    <h2>Home</h2>
  </div>
)

const About = (a)= > (
  <div>
    <h2>About</h2>
  </div>
)

const Topic = ({ match }) = > (
  <div>
    <h3>{match.params.topicId}</h3>
  </div>
)

const Topics = ({ match }) = >( <div> <h2>Topics</h2> <ul> <li> <Link to={`${match.url}/rendering`}> Rendering with React </Link> </li> <li> <Link to={`${match.url}/components`}> Components </Link> </li> <li> <Link to={`${match.url}/props-v-state`}> Props v. State </Link> </li> </ul> <Route path={`${match.path}/:topicId`} component={Topic}/> <Route exact path={match.path} render={()  => ( <h3>Please select a topic.</h3> )}/> </div> ) const BasicExample = () => ( <Router> <div> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/topics">Topics</Link></li> </ul> <hr/> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> <Route path="/topics" component={Topics}/> </div> </Router> ) export default BasicExampleCopy the code

Next reference in index.js:

import React from 'react'
import ReactDOM from 'react-dom';
import App from './src/App';

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

Run NPM run build and see. No error. Open dist/index.html in your browser. Click on the link and nothing happens? What’s going on? Don’t panic, open the console and see:

Uncaught DOMException: Failed to execute ‘pushState’ on ‘History’: A history state object with URL ‘file:///’ cannot be created in a document with origin ‘null’ and URL

What about emmmm? Don’t ask me, I didn’t do it… Since Chrome doesn’t work, take a look at Safari. If it doesn’t work, take a look at the error message.

[Error] SecurityError: Blocked attempt to use history.pushState() to change session history URL from file:///demo/dist/index.html to file:///about. Paths and fragments must match for a sandboxed document.

It seems that Safari’s error is a little more friendly, and we can clearly see that it’s trying to change the link. Do you think it’s reasonable to do that for security reasons? Won’t you be able to access any file on your computer with an HTML? Emmm, sure enough, doesn’t make sense. Paths and fragments must match for a sandboxed document.

So let’s build a sandbox environment.

webpack-dev-server

Configuration Reference Link

The installation

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

Modify the configuration

  • Add scripts to package.json:

    "dev": "webpack-dev-server"
    Copy the code
  • In the webpack.config.js root node, add:

      devServer: {
        contentBase: path.join(__dirname, 'dist'),
        compress: true.port: 9000
      }
    Copy the code
  • Execute srcripts just added above:

    npm run dev
    Copy the code
  • Open the link localhost:9000 in your browser

The function is fine, the style is unbearable, right? To change!

  1. Create the app. CSS file in SRC and add the following content:

    .container {
      list-style: none;
    }
    Copy the code
  2. In app.js, add the following code:

    import './app.css';
    Copy the code
  3. Apply styles to ul from BasicExample:

    <ul className="container">
    	<li><Link to="/">Home</Link></li>
    	<li><Link to="/about">About</Link></li>
    	<li><Link to="/topics">Topics</Link></li>
    </ul>
    Copy the code

At this point, you’ll find a familiar mistake.

You may need an appropriate loader to handle this file type.

This time we can quickly locate the lack of csS-related loaders.

Again withloadermeet

Although the webpack documentation is always so late, but some basic things, still can learn from it. Now that we have encountered loader once again, let’s get to the bottom of what loader is and what it provides for us.

Loader is used to convert the module source code. Loader allows you to preprocess files when importing or “loading” modules. As such, Loader is similar to “tasks” in other build tools and provides a powerful way to handle the front-end build steps. Loader can convert files from different languages (such as TypeScript) to JavaScript, or convert inline images to data urls. Loader even allows you to import CSS files directly into JavaScript modules!

If you need to import files, you need to use the loader. In that case, one at a time.

css-loader

The installation

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

configuration

Modify the rules under module in webpack.config.js, just like adding babel-loader, add the following configuration:

  {
    test: /\.css$/,
    use: [
      'style-loader'.'css-loader']}Copy the code

One more word about CSS-loader: when you don’t want to pollute global CSS, you want to use it in the following ways:

import styles from 'app.css';

<div className={styles.container} />
Copy the code

Please use CSS Modules, webpack configuration CSS module is very simple:

'css-loader? modules&localIdentName=[name]-[hash:base64:5]'.Copy the code

Adjust csS-Loader to the above content.

file-loader

Handle all kinds of ICONS and pictures

The installation

yarn add file-loader --dev
Copy the code

configuration

Modify rules under module in webpack.config.js and add the following configuration:

{
	test: /\.(png|svg|jpg|gif)$/,
	use: [
	  'file-loader']}Copy the code

Font files are still handled by file-loader.

{
	test: /\.(woff|woff2|eot|ttf|otf)$/,
	use: [
	  'file-loader']}Copy the code

Loader is temporarily added here, remember at this time? In order for WebPack to process the corresponding files, there must be a corresponding loader.

Continue to refresh to see our demo project, it works.

I met the Plugin

Loader configuration finished, continue to press the document to see what we can learn.

Plug-ins are the backbone of WebPack. Webpack itself is also built on top of the same plugin system you use in your WebPack configuration!

Plug-ins are designed to solve other things that loader cannot implement.

html-webpack-plugin

The admin output has this paragraph:

What happens if we change the name of one of our entry points, or even add a new name? The generated package will be renamed in a build, but our index.html file will still reference the old name. We use HtmlWebpackPlugin to solve this problem.

OK, understand its purpose, useful, pack!

The installation

yarn add html-webpack-plugin --dev

Copy the code

configuration

Add plugins to the root node of webpack.config.js:

  / / into HTML - webpack - the plugin
  const HtmlWebpackPlugin = require('html-webpack-plugin');
Copy the code
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Output Management'})].Copy the code

Restart NPM run dev and you will notice that the page is blank. Oh, my God, isn’t this a pit? Open the console and take a look at Target Container is not a DOM element. Take a look at the Elements TAB and say, gee. It seems that our

has magically disappeared. Looking at the document again, I found this sentence:

If you want to learn more about all the features and options that the HtmlWebpackPlugin provides, you should familiarize yourself with the HtmlWebpackPlugin repository. Webpack require path to the template. Please see the docs for details

For templates, let’s remove the dist/index.html file, put it in the root directory of the project, and then modify the configuration in webpack.config.js:

new HtmlWebpackPlugin({
  title: 'Demo'.template: './index.html'
})
Copy the code

Let’s restart the service, NPM run dev

Only one instance of babel-polyfill is allowed. What’s wrong with that? We clearly only have one entry, why cited many times? Delete it and restart it. And you’re done.

clean-webpack-plugin

You may have noticed that our /dist folder is quite cluttered due to the legacy of past guides and code examples. Webpack generates files and places them in the /dist folder, but WebPack has no way of keeping track of which files are actually used in the project.

Still useful, still loaded!

The installation

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

configuration

Modify webpack.config.js to introduce CleanWebpackPlugin:

const CleanWebpackPlugin = require('clean-webpack-plugin');
Copy the code

Add the plugins:

new CleanWebpackPlugin(['dist']),
Copy the code

Distinguish between production and development environments

Why distinguish between production and development environments?

The build goals for development and production environments are very different. In a development environment, we need a powerful Source Map and Localhost Server with live reloading or hot Module replacement capabilities. In the production environment, we shifted our focus to smaller bundles, lighter source maps, and more optimized resources to improve load times. To follow logical separation, we generally recommend writing separate WebPack configurations for each environment.

Split the WebPack configuration

Follow the official tutorial to split it. In this case, I recommend creating a config directory and placing the configuration in config. Therefore, our configuration file should be as follows:

config/webpack.common.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  entry: ["babel-polyfill"."./index.js"].output: {
    path: path.resolve(__dirname, '.. /dist'),
    filename: '[name].[contenthash:12].js'
  },
  module: {
    rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader? cacheDirectory" 
      },
      {
        test: /\.css$/.use: [
          'style-loader'."css-loader"] {},test: /\.(png|svg|jpg|gif)$/.use: [
          'file-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/.use: [
          'file-loader']]}},plugins: [
    new CleanWebpackPlugin(["dist"] and {root: path.resolve(__dirname, ".. /"),}),new HtmlWebpackPlugin({
      title: 'Demo'.template: './index.html'}})];Copy the code

config/webpack.dev.js:

const merge  = require('webpack-merge');
const common = require('./webpack.common');
const path = require('path');

module.exports = merge(common, {
  mode: 'development'.devtool: 'inline-source-map'.devServer: {
    contentBase: path.resolve(__dirname, '.. /dist'),
    compress: true.port: 9000}});Copy the code

The config/webpack. Prod. Js:

const merge  = require('webpack-merge');
const common = require('./webpack.common');

module.exports = merge(common, {
  mode: 'production'});Copy the code

Also, scripts in package.json should be adjusted accordingly:

"build": "webpack --config config/webpack.prod.js",
"dev": "webpack-dev-server --config config/webpack.dev.js"
Copy the code

About separating CSS

Extract -text-webpack-plugin【4.0 deprecated 】

Tips: This is a Plugin from start to quit. If you are interested, you can continue with the Plugin. If not, please skip to the next section.

It moves all the *.css references in entry chunks to separate CSS files.

The installation
yarn add extract-text-webpack-plugin --dev
Copy the code
configuration

Paste in the configuration as shown in 🌰 and change it to webpack.config.js:

const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  mode: "production".entry: ["babel-polyfill"."./index.js"].output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader? cacheDirectory" 
      },
      {
        test: /\.css$/.use: ExtractTextPlugin.extract({
          fallback: "style-loader".use: "css-loader"})}, {test: /\.(png|svg|jpg|gif)$/.use: [
          'file-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/.use: [
          'file-loader']]}},plugins: [
    new ExtractTextPlugin("styles.css")],devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true.port: 9000}};Copy the code

When you restart the service, you will notice an error? WTF? After all this work, it still doesn’t work? At this point we need to note that it must have worked at one time or we can’t put it on the document, which shows that the WebPack document is behind. In this case, since we are currently using webpack 4.x version, at this time, first go to the Github of ExtractTextWebpackPlugin to search if you have thought of issue, the keyword webpack 4. See an issue.

@vasivas don’t use extract-text-webpack-plugin for extract CSS, please use github.com/webpack-con…

If there is such an operation, take a look at the Mini-CSs-extract-plugin.

About Webpack, here is the guide, this article is not to explain webpack, more about the part of Webpack, you can see: @flower pants written article:

Using webpack4 with proper posture (part 1)

Using webpack4 with proper posture

We ended up with the following separation of CSS: webpack.common.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: ["babel-polyfill"."./index.js"].output: {
    path: path.resolve(__dirname, '.. /dist'),
    filename: '[name].[contenthash:12].js'
  },
  module: {
    rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader? cacheDirectory" 
      },
      {
        test: /\.css$/.use: [
          MiniCssExtractPlugin.loader,
          'css-loader? modules&localIdentName=[name]-[hash:base64:5]',]}, {test: /\.(png|svg|jpg|gif)$/.use: [
          'file-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/.use: [
          'file-loader']]}},plugins: [
    new CleanWebpackPlugin(["dist"] and {root: path.resolve(__dirname, ".. /"),}),new HtmlWebpackPlugin({
      title: 'Demo'.template: './index.html'})]};Copy the code

Webpack. Prod. Js:

const merge  = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = merge(common, {
  mode: 'production'.optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true.parallel: true.sourceMap: true 
      }),
      new OptimizeCSSAssetsPlugin({})  // use OptimizeCSSAssetsPlugin]},plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:12].css'.chunkFilename: '[name].[contenthash:12].css'  // use contenthash *]}}));Copy the code

Webpack. Dev. Js:

const merge  = require('webpack-merge');
const common = require('./webpack.common');
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = merge(common, {
  mode: 'development'.devtool: 'inline-source-map'.devServer: {
    contentBase: path.resolve(__dirname, '.. /dist'),
    compress: true.port: 9000
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css'.chunkFilename: '[id].css',})});Copy the code

The separation

Still see above @ flower underpants article, cent of very delicate. Here we simply separate:

Change it in webpack.common.js:

const merge  = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = merge(common, {
  mode: 'production'.optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true.parallel: true.sourceMap: true 
      }),
      new OptimizeCSSAssetsPlugin({})  // use OptimizeCSSAssetsPlugin].runtimeChunk: {
      name: "manifest"
    },
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/.name: "vendors".priority: - 20.chunks: "all"}}}},plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:12].css'.chunkFilename: '[name].[contenthash:12].css'  // use contenthash *]}}));Copy the code

OK, it seems perfect.

React, Babel, and WebPack are all in place, so it’s time for the next tool to standardize your code for collaboration with your team.

Eslint

website

The installation

npm install eslint -g Install ESLint globally
Copy the code

Initialize the

eslint --init
Copy the code

Choose the Default Settings

? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Airbnb (https://github.com/airbnb/javascript)
? Do you use React? Yes
? What format do you want your config file to be in? JavaScript
Checking peerDependencies of eslint-config-airbnb@latest
? The style guide "airbnb"Requires eslint@^4.19.1. You are currently using [email protected]. Do You want to downgrade? YesCopy the code

Choose Airbnb directly here.

Adjust the configuration

After the initial installation, we found that our previous app.js had an error, and we needed to adjust the esLint-related configuration rules to make it more suitable for our intended use: open the.eslintrc.js file and adjust it as follows:

module.exports = {
  "extends": "airbnb"."plugins": ["react"."jsx-a11y"."import"]."rules": {
    "import/no-extraneous-dependencies": "off"."react/jsx-filename-extension": [1, { "extensions": [".js".".jsx"]}],"react/prop-types": 0}};Copy the code

There are more ways to use ESLint that you need to explore personally.

The introduction ofant-design

website

The installation

yarn add antd
Copy the code

According to the need to load

If you don’t want to refer to CSS every time, you can use babel-plugin-import here:

yarn add babel-plugin-import --dev
Copy the code

Babelrc file modified to:

{
  "presets": ["env"."react"]."plugins": [["import", { "libraryName": "antd"."libraryDirectory": "es"."style": "css"}}]]Copy the code

The trial

In app.js, make a reference:


import { DatePicker } from 'antd';

const Home = (a)= > (
  <div>
    <h2>
      <DatePicker />
    </h2>
  </div>
);
Copy the code

After refreshing, we find that the components are displayed, but the styles are not in effect. Continue to find the solution: www.jianshu.com/p/603a61471… The elder brother wrote clearly, it turns out that our rules configuration still has some defects, according to the content adjustment is as follows:

  {
    test: /\.css$/.exclude: /node_modules/.use: [
      MiniCssExtractPlugin.loader,
      'css-loader? modules&localIdentName=[name]-[hash:base64:5]',]}, {// antD style processing
    test: /\.css$/.exclude: /src/.use: [{loader: 'style-loader' },
      {
        loader: 'css-loader'.options: {
          importLoaders: 1,},},],},Copy the code

After the adjustment, restart webPack.

The last

This article is only the introduction, so that you have a certain understanding of front-end engineering, more need to explore according to the keywords themselves.

I made last week based on Baidu map packaging is now open source, welcome to join together to do things:

github

website