When I first got in touch with Webpack, I was very concerned about its complicated configuration. Later, as I gradually did more projects and understood more, I became familiar with it and deeply understood its benefits.

Whether doing front-end projects, or using Node to do back-end projects, as long as js related, can use WebPack auxiliary construction, it can provide ES6 syntax support, development hot update, deployment code compression, processing of various resource files…… And so on.

Here’s how to configure WebPack in a node framework KOA project.

I. New KOA project

Create a new KOA-Demo folder and initialize the project through NPM. Then create the following directory structure, files, and content:

Project directory

  koa-demo
  |- package.json
+ |- /src
+ |- index.js
Copy the code

src/index.js

// NPM installs koA first
// This is referenced and initialized
const Koa = require('koa')
const app = new Koa()

app.use(async ctx => {
  ctx.body = 'hello koa'
})

// Listen on the port
app.listen(3000)
Copy the code

A simple KOA project is created.

Configure WebPack

To configure WebPack, let’s install all the required dependencies and explain why:

Note: All configurations here are based on WebPack4. Upgrade WebPack5 for official information

npm i -D webpack@4 webpack-cli@3
Install the webpack4 version
# Install WebPack - version 3 This tool is used to run Webpack from the command line

npm i -D clean-webpack-plugin
# Common plugin, automatically clean up the file before packaging

npm i -D webpack-node-externals
Node_modules is not included in the package. All dependencies are not included in the package

npm i -D @babel/core @babel/node @babel/preset-env babel-loader
# Babel-related dependencies required to use ES6 syntax
# @babel/node is removed from babel7. If used in the Node environment, it should be installed separately. It has the advantage of compiling Babel presets and plug-ins before running them

npm i -D terser-webpack-plugin@4
Production environments need to package compressed code

npm i -D webpack-merge
# Separate development configuration and production configuration, they have common configuration, pull out and merge via webpack-merge

npm i -D nodemon
Monitor node.js source code changes and automatically restart the service

npm i -D cross-env
Run scripts that use cross-platform Settings and environment variables

npm i -D npm-run-all
Execute multiple commands simultaneously

npm i -D rimraf
Package rm -rf to delete files and folders. Used to clean up the dist directory
Copy the code

Our KOA project is divided into development time environment and production time environment, the configuration of Webpack is somewhat different in different environments, so we need to create two configuration files, webpack.config.dev.js and webpack.config.prod.js, and they have something in common. So you need a generic configuration file, webpack.config.base.js.

So create a new config folder under the project root directory and put the three WebPack configuration files in it.

Project directory

  koa-demo
+ |- config
+ |- webpack.config.base.js
+ |- webpack.config.dev.js
+ |- webpack.config.prod.js
  |- package.json
  |- /src
    |- index.js
Copy the code

Next configure it in the generic configuration file webpack.config.base.js:

config/webpack.config.base.js

const path = require('path')
const webpack = require('webpack')
const nodeExternals = require('webpack-node-externals')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

const webpackConfig = {
  target: 'node'.// KoA projects run only in node environment, so set it to 'node'
  entry: {
    // Set the entry file
    server: path.join(__dirname, '.. /src/index.js')},output: {
    // Set the packaged file and location
    filename: '[name].bundle.js'.path: path.join(__dirname, '.. /dist')},// devtool: 'inline-source-map',
  module: {
    rules: [{test: /\.js|jsx$/,
        use: {
          loader: 'babel-loader'
        },
        // Try to apply the loader to the minimum number of necessary modules, so set include
        // Only js files in this directory are Babel processed
        include: path.join(__dirname, '.. /src')}}],resolve: {
    // modules: Tells Webpack which directories to search for to match resolution
    modules: [path.join(__dirname, '.. /src/index.js'), 'node_modules'].// Extensions: tells Webpack that these suffix files need to be searched for matches
    extensions: ['.js'.'.json'].alias: {
      // Set the alias to the corresponding directory
      The '@': path.join(__dirname, '.. /src')}},externals: [nodeExternals()], // Exclude dependencies in node_modules from packaging
  plugins: [
    new CleanWebpackPlugin(), // Clear the output directory before packaging
    new webpack.DefinePlugin({
      // Define environment variables to distinguish between development and production environments
      // See the DefinePlugin document for details
      'process.env.NODE_ENV':
        process.env.NODE_ENV === 'production'
          ? JSON.stringify('production')
          : JSON.stringify('development')})],// These options under Node enable code originally written for the Node.js environment to run in other environments, such as browsers
  node: {
    console: true.global: true.process: true.Buffer: true.__filename: true.__dirname: true.setImmediate: true.path: true}}module.exports = webpackConfig
Copy the code

config/webpack.config.dev.js

const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')

// Merge basic configuration with webpack-merge to add development-time configuration
const webpackConfig = merge(baseConfig, {
  mode: 'development'.// Development mode
  devtool: 'eval-source-map'.// Development error can know which line in the source code
  stats: {
    children: false.// Submodule information is not displayed when webpack is packaged
    modules: false // Do not display module information}})module.exports = webpackConfig
Copy the code

config/webpack.config.prod.js

const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
const TerserPlugin = require('terser-webpack-plugin')

// Use webpack-merge to merge basic configurations and add production-time configurations
const webpackConfig = merge(baseConfig, {
  mode: 'production'.// Production mode
  stats: {
    children: false.// Submodule information is not displayed when webpack is packaged
    warnings: false // Warning is not displayed
  },
  optimization: {
    minimizer: [
      // The terser-webpack-plugin can compress code
      Terser-webpack-plugin4 is required in the Webpack4 version
      // There are specific parameters recommended by the official. For details, please check the documentation
      new TerserPlugin({
        terserOptions: {
          warning: true.compress: {
            warnings: false.drop_console: false.// Uncomment console to facilitate debugging sometimes
            dead_code: true.drop_debugger: true
          },
          output: {
            comments: false.// Do not comment
            beautify: false // Display all code in one line without formatting
          },
          mangle: true
        },
        parallel: true.By default, os.cpus().length-1
        sourceMap: false})].// splitChunks are used to avoid repeated dependencies between modules
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons'.chunks: 'initial'.minChunks: 3.enforce: true}}}}})module.exports = webpackConfig
Copy the code

1. Configure Babel to support ES6 syntax

Add the Babel configuration to the.babelrc file in the root directory:

{
  "presets": [["@babel/preset-env",
      {
        "targets": {
          "node": "current"}}]]}Copy the code

Rewrite the code in SRC /index.js:

- const Koa = require('koa')
+ import Koa from 'koa'Const app = new Koa() app.use(async CTX => {ctx.body = 'hello Koa '}) // Listen to app.listen(3000)Copy the code

If we run node SRC /index.js, we will get an error:

import Koa from 'koa'
^^^^^^

SyntaxError: Cannot use import statement outside a module
Copy the code

After installing Babel, node_modules provides a babel-node command that we can use to run our entry files.

npx babel-node src/index.js
Copy the code

Hot updates can be implemented with nodemon installed previously

npx nodemon --exec babel-node src/index.js
Copy the code

Let’s add this script to package.json:

{
  "script": {
    "start": "nodemon --exec babel-node src/index.js"}}Copy the code

This will start the project by running NPM run start in the terminal.

Of course, this is just a simple implementation to support ES6 syntax and develop-time hot update functionality, and is not built with Webpack, which is fine if the project is simple. Starting with WebPack will be described later.

Debug the WebPack configuration file

Run the following command to listen to the Webpack configuration file:

npx node --inspect-brk ./node_modules/.bin/webpack --config config/webpack.config.prod.js --progress
--config specifies the configuration file to listen on
# --progress Prints the webPack compilation process
Copy the code

You can debug in the Chrome browser or in the editor, as described in the Node debugging documentation.

We added this script to package.json for debugging:

{
  "script": {
    "start": "nodemon --exec babel-node src/index.js"."webpack:debug": "node --inspect-brk ./node_modules/.bin/webpack --config config/webpack.config.prod.js --progress"}}Copy the code

3. Build the development time environment with WebPack

Now that we have our development-time environment configured, it’s time to start it up and use the webpack –watch command to listen for changes to the files in the project. If one of the files is updated, the code will be recompiled without having to run the entire build manually.

We also use cross-env to set environment variables to specify whether the current development environment is production.

npx cross-env NODE_ENV=development webpack --watch --config config/webpack.config.dev.js --progress
Copy the code

To execute this command means to set an environment variable NODE_ENV with a value of development. Since we configured it in the configuration file via DefinePlugin, this value is the development environment if it is not production and the corresponding variable is defined. This variable is available in the project file for different logic processing for development and production.

After set up the development of environment variables, through webpack – watch start listening mode, specify the development of the configuration file config/webpack config. Dev. Js file, when the change in the project file, The code will be recompiled and packaged for output to dist/server.bundle.js.

This is not enough. Although the file is recompiled and packaged, we do not listen to the packaged file, so we use Nodemon to listen to the output file:

npx nodemon --inspect ./dist/server.bundle.js
Copy the code

Instead of simply listening for the raw entry file SRC /index.js, we will listen for the generated file packaged with WebPack processing (including Babel conversion, code compression, etc.).

Running these two commands will help us build our development time environment. We configure this command into the package.json script:

{
  "script": {
    "start": "nodemon --exec babel-node src/index.js"."webpack:debug": "node --inspect-brk ./node_modules/.bin/webpack --config config/webpack.config.prod.js --progress"."watch": "cross-env NODE_ENV=development webpack --watch --config config/webpack.config.dev.js --progress"."debug": "nodemon --inspect dist/server.bundle.js"}}Copy the code

However, this is two commands, a terminal console can only listen to one command, if every time the project is started, it is troublesome to open two terminals, so we installed a tool npm-run-all, to implement a script to execute multiple commands at the same time. Continue adding scripts to package.json:

{
  "script": {
    "start": "nodemon --exec babel-node src/index.js"."webpack:debug": "node --inspect-brk ./node_modules/.bin/webpack --config config/webpack.config.prod.js --progress"."watch": "cross-env NODE_ENV=development webpack --watch --config config/webpack.config.dev.js --progress"."debug": "nodemon --inspect dist/server.bundle.js"."start:dist": "npm-run-all -p watch debug"}}Copy the code

NPM -run-all -p watch debug indicates that the watch and debug commands are executed in parallel.

Once configured, we can start the development-time environment directly from the terminal:

npm run start:dist
Copy the code

Then you can write koA project code.

4. Package the project and apply it to production environment

After the project is written, we need to package and deploy to the online production environment. At this time, it is not appropriate to use the NPM run start:dist command, so let’s configure the package command to build the production environment.

npx cross-env NODE_ENV=production webpack --config config/webpack.config.prod.js
Copy the code

And the above command to build the development environment is very similar, but this time we set the environment variable NODE_ENV value for production, and specify the configuration file config/webpack config. Prod. Js file to pack to build.

We configure this command to package.json:

{
  "script": {
    "start": "nodemon --exec babel-node src/index.js"."webpack:debug": "node --inspect-brk ./node_modules/.bin/webpack --config config/webpack.config.prod.js --progress"."watch": "cross-env NODE_ENV=development webpack --watch --config config/webpack.config.dev.js --progress"."debug": "nodemon --inspect dist/server.bundle.js"."start:dist": "npm-run-all -p watch debug"."build": "cross-env NODE_ENV=production webpack --config config/webpack.config.prod.js"}}Copy the code

Run the command:

npm run build
Copy the code

That is to complete the packaging construction of the project in the production environment.

Push the entire project directory (except node_modules) to the online server, run NPM install to install dependencies, and then start the project with node dist/server.bundle.js. The KOA project interface can be accessed via the server address and exposed port.

Note: Clear the dist directory configuration

NPM run build generates a dist directory each time. Sometimes you need to delete the dist directory altogether. In addition to deleting the dist directory on the command line, you can also use rimraf: rm -rf /dist/

NPM install -d rimraf

Use: NPX rimraf dist

We configure it in package.json:

{
  "script": {
    ...
    "build": "cross-env NODE_ENV=production webpack --config config/webpack.config.prod.js",
+ "clean": "rimraf dist"}}Copy the code

You can then clear the dist directory by running this command:

npm run clean
Copy the code

3, Configure the esLint + prettier style

I’m in the habit of writing projects with esLint as required, as follows:

1. Install ESLint

# installation eslint
npm i eslint -D

# esLint initialization
npx eslint --init

Enter the custom selection
# 1. How would you like to use ESLint? To check syntax and find problems
# 2. Which module is used in your project? JavaScript modules
# 3. Which framework to choose? None of these
# 4. Do you want to use TypeScript? N
# 5. Which environment are you running in? Browser, Node
# 6. Which configuration file format do you want? JavaScript

The.eslintrc.js file will be generated locally after the selection is complete
Copy the code

2, Install Prettier

Install configuration dependencies
npm install --save-dev eslint-config-prettier
npm install --save-dev eslint-plugin-prettier
npm install --save-dev --save-exact prettier
Copy the code

Then add the configuration to the.eslintrc.js file:

module.exports = {
  ...
  plugins: ['prettier'].extends: ['eslint:recommended'.'prettier'.'prettier/prettier'].rules: {
    'prettier/prettier': 'error'}}Copy the code

The “. Prettierrc “file can be used to set the rule for prettier.

{
  "semi": false.// Whether to end with a semicolon
  "singleQuote": true.// Whether to use single quotation marks
  "printWidth": 80.// A line of code can display a maximum length of 80
  "endOfLine": "auto"."trailingComma": "none" // Whether an object or array ends with a comma
}
Copy the code

Prettier –write Lint-staged rule prettier –write It would have worked just as well to set the rule directly in a.eslintrc.js file, but prettier’s default rule would not have been available when lint-staged prettier –write was executed.

So we need to create a.prettierrc file for them to recognize.

Add. Eslintignore and. Prettierignore to increase validation efficiency and exclude files that do not need validation rules, such as mode_modules.

If the rule for prettier does not end with a semicolon, the webstorm editor will default a warning: The unterminated statement color is not wrong, but it looks bad. You can set the language in the editor to remove the warning.

Check the code before submitting Git

To improve codeline quality, you can use Husky and Lint-staged checks before Git commits to the repository.

The most important hook is the pre-commit, which allows you to configure what to do before committing your code. For example, run a code check, abort the Git commit if it doesn’t comply with ESLint rules, and let you check for code problems.

Configuring them is simple, starting with installing the two tools.

npm install husky lint-staged --save-dev
Copy the code

Then add configuration to package.json:

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"}},"lint-staged": {
    // Match all js files to execute tasks in sequence
    "*.js": [
      "prettier --write"."eslint"."git add"]}}Copy the code

Git hooks can be accessed from the hooks directory in the.git file. They are a set of hooks that perform git operations. Like the Vue life cycle, commit time triggers a pre-commit hook that does what you need to do before committing. For example, you can execute check code:

{
  "husky": {
    "hooks": {
      "pre-commit": "prettier --write"}}}Copy the code

So it does prettier before it commits. The problem with this, however, is that it will inspect all of your project’s code, not just the one you submitted this time, so lint-staged episodes are needed to solve this problem, and lint-staged commands can be performed in several tasks.

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"}},"lint-staged": {
    // Matches all submitted JS files and executes the tasks in sequence
    "*.js": [
      "prettier --write"."eslint"."git add"].// Matches all the submitted vUE files and executes the tasks in sequence
    "*.vue": [...]
  }
}
Copy the code

Prettier –write *.js the prettier –write *.js command prettier –write *.js the prettier –write *.js command looks for code that does not conform to ESlint Once you’re ok, run git add *.js to add the fixed files (if any) back to the staging area, and then commit.

If any of these tasks fail, the local submission is aborted and an error message is displayed to remind you of the specification code.

With all of the basic configuration above, the build environment is now in place before the KOA project is developed, and it’s just a matter of writing the project code.

Scaffolding can then be built for these configurations, along with the koA family of related base middleware, to reduce the cost of repeatedly building the project base environment.