This is my second entry in the beginner’s column. It was written a long time ago, but it’s worth reading as a beginner’s entry

npm init

The front-end infrastructure is now dependent on the large base environment of NPM. This paper also explains on this basis. Before eating, ensure that Node and NPM have been successfully installed

Find a nice place to go, press NPM init on the command line, type in some random introduction as prompted, and the following file is generated. The content of the file may not be the same, but the likeness is ok

{
  "name": "buildreactwithwebpack"."version": "1.0.0"."description": "Build React from scratch. Use webpack+ React family bucket."main": "index.js"."scripts": {
    "build": "cross-env NODE_ENV=production webpack --config webpack/webpack.config.js "."test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git"."url": "git+https://github.com/starunaway/BuildReactWithWebpack.git"
  },
  "author": "starunaway"."license": "MIT"."bugs": {
    "url": "https://github.com/starunaway/BuildReactWithWebpack/issues"
  },
  "homepage": "https://github.com/starunaway/BuildReactWithWebpack#readme"
}
Copy the code

Why use Webpack

First, look at the original front-end development process

Create an HTML file in the project directory. As follows:

<! DOCTYPEhtml>
<html>
  <head>
    <title>start</title>
    <script src="https://unpkg.com/[email protected]"></script>
  </head>
  <body>
    <script src="./src/index.js"></script>
  </body>
</html>
Copy the code

As you can see in the index.html file, you’ve introduced the moment library and a native-written index.js file

Create a SRC folder in the project directory and create an index.js file under SRC. The index.js file contains the following contents and displays the current time. (Don’t ask why you have to import a library to display the time, if you ask I’m lazy)

function component() {
  var element = document.createElement('div');
  element.innerHTML = 'hello webpack';
  element.innerHTML += moment().format('YYYY-MM-DD HH:mm:ss');
  return element;
}

document.body.appendChild(component());
Copy the code

Ps: This part of the content is webpack official website, simple brainless easy to understand, directly borrowed

If you need to import a common package, you need to introduce the file path on the network in the script tag. If the file is local, import the local relative path. This is fine if the number of files is small, but what about tens or hundreds of files?

  1. How do I know when each dependency package is used?
  2. How do I know which file is actually being used?
  3. How about ensuring that files are introduced in the correct order?

Use WebPack for packaging for the first time

Let’s give WebPack a try. Trust Webpack, it will help us manage it all. Create the dist folder in the project directory and create the index.html file under dist

<! DOCTYPEhtml>
<html>
  <head>
    <title>hello</title>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>
Copy the code

Now use NPM i-s moment to install the moment package. Insert moment at the top of the SRC /index.js file

import moment from 'moment';

// The following is the same as before
Copy the code

Are you a little excited to start using Webpack?

NPM I -d webpack webpack-CLI Webpack-cli is required after Webpack 4.0

Next add a start command to package.json: NPX webpack

Ps: NPX is the module that calls the project’s internal installation, mainly at the command line. I could have just written webpack here.

/ /...
    "start": "npx webpack"./ /...
  "devDependencies": {

    "webpack": "^ 4.43.0"."webpack-cli": "^ 3.3.11." ",}/ /...

Copy the code

Run NPM start from the command line and open the dist/index.html file in your browser. Amazingly, it’s the same as before (except for the date).

Use the webPack configuration file

Webpack configuration items have many, many, many, many, many, although you can pass in a command line, but every time you have to write a pile of configuration, how can live up to the natural lazy programmer! So WebPack provides a way to write configuration through configuration files. To have a try

Create the webpack.config.js file in the project directory

const path = require('path');

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

Simple and clear. You know what the word means. As above. Entry is the entry to the package, and output is the path to the file output

Now, replace the script path in the dist/index.html file with index.js and bundle.js. Rerun NPM start to try

Webpack packages resources

Is that all WebPack does? As the front three musketeers, CSS does not support? Of course not! Webpack can also package a variety of resources, including but not limited to CSS/LESS/SCSS, various images, fonts, and data. However, the loader needs to be configured first. To have a try

Create a style.less file in SRC directory. Use less to create a style.less file.

.time {
  color: blue;
}
Copy the code

Add a time style to the element in SRC /index.js and introduce style.less

import moment from 'moment';
// Import files
import './style.less';

function component() {
  var element = document.createElement('div');
  element.innerHTML = 'hello webpack';
  element.innerHTML += moment().format('YYYY-MM-DD HH:mm:ss');

  // Add the class name
  element.classList.add('time');
  return element;
}
document.body.appendChild(component());
Copy the code

Next modify webpack.config.js to level with Entry and create the following


  entry: {},output: {},module: {
    rules: [{test: /\.(less|css)$/,
        use: ['style-loader'.'css-loader'],},],},Copy the code

As the name implies, what are the rules for modules ____? Fills up the topic. More advanced usage, we’ll talk about it later

Use NPM i-d style-loader csS-loader to install both loaders. Re-run NPM start when the installation is complete. Open dist/index.html again and look at the text color

Use multiple entry

For now, we have only introduced a JS file in the HTML file. What if we need to have multiple JS files to import? Do we need to add each file manually? Obviously, this is not realistic. As a lazy programmer, it is impossible to touch something like this.

So how do you automatically add file entry? To make the

Well, now create a new js file SRC /print.js in the SRC directory

export default function printMe() {
  console.log('I get called from print.js! ');
}
Copy the code

Modify SRC /index.js as follows:

import moment from 'moment';
import printMe from './print';
import './style.less';

function component() {
  var element = document.createElement('div');
  var btn = document.createElement('button');

  btn.innerHTML = 'Click me and check the console! ';
  btn.onclick = printMe;

  element.innerHTML = 'hello webpack';
  element.innerHTML += moment().format('YYYY-MM-DD HH:mm:ss');

  element.classList.add('time');
  element.appendChild(btn);

  return element;
}
document.body.appendChild(component());
Copy the code

What is multiple entry? The idea is to include multiple files in index.html at the same time, rather than bundle them together. Why introduce multiple files instead of packing them into one? A simple scenario, the first screen loading speed, at the beginning of the load a lot of temporarily unavailable data, waste

Anyway, how do you use WebPack to do this?

module.exports = {
  entry: {
    app: './src/index.js'.print: './src/print.js',},output: {
    filename: '[name].bundle.js'.path: path.resolve(__dirname, 'dist'),},module: {
    //....}};Copy the code

Simply modify entry and Output in the configuration file. The name of [name] in output.filename is the key value under the entry. As for how to optimize, we will discuss it later when we use it.

Now, modify the dist/index.html file

<! DOCTYPEhtml>
<html>
  <head>
    <title>hello</title>
    <script src="./print.bundle.js"></script>
  </head>
  <body>
    <script src="./app.bundle.js"></script>
  </body>
</html>
Copy the code

Re-run NPM start, open it in your browser, and take a look

Use plug-ins to manage entries

Ever notice that you have to change index.html every time you change a file and need to repackage it? Really troublesome, how can become lazier? That’s where plugins come in. What is a plug-in? Unlike loaders that handle a wide variety of resources, plug-ins are designed to do other things that loaders can’t do. Webpack provides many plug-ins. You can check it out here

Go ahead, open the command line, type NPM I -d html-webpack-plugin with me and modify webpack.config.js

module.exports = {
  entry: {
    app: './src/index.js'.print: './src/print.js',},output: {
    filename: '[name].bundle.js'.path: path.resolve(__dirname, 'dist'),},module: {
    rules: [{test: /\.(less|css)$/,
        use: ['style-loader'.'css-loader'],},],},// Add here
  plugins: [
    new HtmlWebpackPlugin({
      title: 'HtmlWebpackPlugin',})]};Copy the code

Next you can delete the dist folder and run NPM start

Clean up the files before each build

If you are careful, you will find that there is a lot of stuff in the dist folder. How many files are occupied during each build, and if you are in production, what if you accidentally add some useless files that still have an impact? Do I have to manually delete it every time I build? That’s not lazy!

Remember the definition of a plug-in in the previous section? Looking for a plugin to do this for you? Don’t say it. There is

Command line, NPM I -d clean-webpack-plugin go

Modify the webpack. Config. Js,

/ /... Require a bunch of other packages
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = {
  entry: {},
  / /... Other configurations
  plugins: [
    new HtmlWebpackPlugin({
      title: 'HtmlWebpackPlugin',}).// Add this plugin
    new CleanWebpackPlugin(),
  ],
};
Copy the code

Run NPM start again, do you find dist only has the contents packed this time!

Add a map of the original location of the code at development time

When Webpack the source code, it packs the files together, making it difficult to trace the original locations of Errors and warnings in the source code. For example, if three source files (A.js, B.js, and c.js) are packaged into a bundle(bundle.js), and one of the source files contains an error, the stack trace will point directly to bundle.js. After all, I am not god, just a lazy programmer, bugs are certainly exist, and do not want to turn over the code line by line, so, you need to open a new configuration item.

Add a new field to the configuration file

module.exports = {
  / /...
  devtool: 'inline-source-map'};Copy the code

Write a bug and it will now tell you exactly what line it is

Reload in real time using webpack-dev-server

People have been walking on the lazy road further and further. You know, every time I change a file, I have to package it and refresh the web page. How do I get it to refresh itself? This makes use of the new Webpack tool: webpack-dev-server. Webpack-dev-server starts a Node service locally to listen for file changes. Each time a file is modified, it is “packaged” again, but the “packaged” file is stored in memory, and the server creates a socket, each time the file changes, the front end will send a notification, after receiving the notification to reload the page, to achieve the effect of automatic refresh.

Lets go, come with me to become lazy

First, you need NPM i-d webpack-dev-server

Modify package.json startup command

{
  "scripts": {
    "start": "webpack-dev-server --open"}}Copy the code

Configure devServer in webpack.config.js:

module.exports = {
  entry: {
    app: './src/index.js'.print: './src/print.js',},/ /...
  devtool: 'inline-source-map'.devServer: {
    contentBase: './dist',},/ /...
};
Copy the code

Re-execute NPM start, take a look at your browser, make random changes to the code, and save it

Enable hot replacement of the module

Module hot replacement is one of the most useful features webPack provides. It allows all types of modules to be updated at run time without a complete refresh. With webpack 4.x, devServer is now available without a plugin, so you can configure it in webpack.config.js.

module.exports = {
  / /...

  devServer: {
    contentBase: './dist'.hot: true,},/ /...
};
Copy the code

The introduction of the react

Configure React. No more words, just get started. Mostly I’m tired too

To use React, you must install the react dependency NPM i-s react-dom

React uses the JSX syntax, so it needs Babel support. What’s a Babel? The good news is that the author of this article has formed a strategic partnership with Baidu, Google, etc., and you can search on the above sites for free, so you’ll need to install Babel again

npm i -D @babel/core @babel/preset-react babel-loader

We need to use loader to package the resources. In order to avoid errors, we need to paste the complete Webpack configuration

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

module.exports = {
  entry: './src/index.js'.output: {
    filename: '[name].bundle.js'.path: path.resolve(__dirname, 'dist'),},module: {
    rules: [{test: /\.(less|css)$/,
        use: ['style-loader'.'css-loader'],}, {test: /\.(js|jsx)$/,
        use: ['babel-loader'].exclude: /node_modules/,}]},plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',}).new CleanWebpackPlugin(),
  ],
  devtool: 'inline-source-map'.devServer: {
    contentBase: './dist'.hot: true,}};Copy the code

Most of the configuration is the same as the original, the transformation part:

  1. entryThe file has now been changed to a single entry, the reason being that only one entry is needed for the tutorial up to this point
  2. module.rulesThe new added tojsxloader
  3. reactI have to have a root element,HtmlWebpackPluginThe default template didn’t work, so we created one ourselves

Take a look at the template: SRC /index.html

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>React-Manage</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
Copy the code

Change SRC /index.js to be a starting point for React

import React from 'react';
import ReactDom from 'react-dom';
import App from './App.js';

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

Create an App under SRC, go SRC/app.js

import React from 'react';

function App() {
  return <div>this is a react app</div>;
}

export default App;
Copy the code

Now start the project and find that Babel does not work because the configuration of Babel is missing

{
  "presets": ["@babel/preset-react"]}Copy the code

React is now running and ready for development

Configure WebPack by environment

Now we only have one configuration, but actually we need different configurations in different environments, such as code compression in the production environment, and some development tools in the development environment to help us improve development efficiency. So, we need different configurations for different environments.

Follow the principle of non-duplication and keep a common configuration. To merge these configurations together, we will use a tool called Webpack-Merge

NPM i-d webpack-merge cross-env

Create a WebPack folder in the project root directory to store the configuration. First, create webpack.base.config.js to store the common base configuration

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
  entry: './src/index.js'.output: {
    filename: '[name].bundle.js'.path: path.resolve(__dirname, '.. /dist'),},resolve: {
    extensions: ['.js'.'jsx'].mainFiles: ['index'].alias: {
      '@src': path.resolve(__dirname, '.. /src'),}},module: {
    rules: [{test: /\.(less|css)$/,
        use: ['style-loader'.'css-loader'],}, {test: /\.(js|jsx)$/,
        use: ['babel-loader'].exclude: /node_modules/,}]},plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',})]};Copy the code

Next, create the configuration for the development environment. Note that the development environment is configured to do incremental webpack.dev.config.js on top of the base configuration

module.exports = {
  devtool: 'inline-source-map'.devServer: {
    contentBase: './dist'.port: 10086.hot: true.open: true,}};Copy the code

As you can see, the dev environment configuration only has dev-related configuration properties. Similarly, we create the production configuration webpack.prd.config.js

const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = {
  plugins: [new CleanWebpackPlugin()],
};
Copy the code

Finally, use the webpack-merge tool to merge configuration files by environment webpack.config.js

const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config');
const devConfig = require('./webpack.dev.config');
const prdConfig = require('./webpack.prd.config');

module.exports = (env, argv) = > {
  const config = process.env.NODE_ENV === 'development' ? devConfig : prdConfig;
  return merge(baseConfig, config);
};
Copy the code

The file structure is as follows

|-webpack
| |- webpack.config.js
| |- webpack.dev.config.js
| |- webpack.base.config.js
| |- webpack.prd.config.js
|
|-src
| |-- index.js
| |-- index.html
| |-- App.js
|
|- package.json
|- .babelrc
Copy the code