Location>code7788 >text

Refactoring Case: Migrating Pure HTML/JS Projects to Webpack

Popularity:66 ℃/2024-10-20 20:41:06

We've learned a lot about Webpack, but it's not easy to become fully proficient at it. A great way to learn is by practicing with real projects. Once we have a good enough understanding of Webpack's configuration, we can try to refactor some projects. This time I chose aPure HTML/JS for PC projectThe project is located on GitHub, and many thanks to the contributors to the project.

Two pages were chosen for the refactoring case: the home page and the shopping cart page.

The project directory is clearly structured, with the root directory containing each HTML page, and CSS, JS and IMG folders at the same level. Each HTML page has its own CSS and business JS files.

Initialize the npm project

First, create a new empty folder and run thenpm init -y command to initialize the project. Next, create the src folder in the root directory of the project and replace the Copy the file to the src directory and use it as a basis for refactoring. Open the file, you can see that CSS, images and JS resources have been introduced to the page. Then move the CSS, IMG and JS folders to the src directory as well.

Subsequently, we observe in the file<link> cap (a poem)<script> tags, which are used to load external CSS files and JavaScript files, respectively. In order to adapt your project to Webpack's modular packaging mechanism, you can add a new tag to the A location at the same directory level creates a new Documentation. In this new file, we will use a modularized approach to import the data that would have been imported via the<link> tag and the CSS files introduced by the<script> JavaScript file loaded by the tag.

For those directly embedded in the<script> The script code inside the tag, such as the one mentioned in the figureflexslider function, we leave it as it is for now, without changes.

import "./css/";
import "./css/";

import './js/jquery-1.12.'
import './js/';
import './js/';
import './js/';

Initializing webpack

Using Commandsnpm install webpack --save to installWebpackand create file to define the basic configuration. This is a multi-entry project since the original project contains multiple HTML pages.

const path = require("path");
 = {
  mode: "development",
  entry: {
    index: "./src/",
  },
  output: {
    filename: "[name].[hash:8].js",
    path: (__dirname, "./dist"),
  },
};

exist Add"build": "webpack" Command.

Handling css, images

Webpack Handling CSS and image resources is not supported by default. To handle CSS resources, you can use thecss-loader cap (a poem)style-loaderand image resources can be accessed through Webpack 5's built-in feature, theasset module to deal with.

First, install the dependencies needed to process CSS:

npm install css-loader style-loader --save

Here we use thecss-loader to parse the CSS file and pass thestyle-loader Insert them into the DOM as inline styles. Initially, we can create inline styles this way, and then we can think about extracting the CSS resources for further optimization.

 = {
  module: {
    rules: [
      { test: /\.css$/, use: ["style-loader", "css-loader"] },
      { test: /\.(jpg|jpeg|png|gif|svg)/i, type: "asset" },
    ],
  },
}

Processing html

utilizationhtml-webpack-plugin The plug-in is based on the Create compressed HTML files and bring in compiled JS files.

const HtmlWebpackPlugin = require("html-webpack-plugin");

 = {
 plugins: [
    new HtmlWebpackPlugin({
      template: "./src/",
      filename: "",
    }),
  ],
}

Photo Resources

asset module It is mainly used to handle image resources that are introduced in CSS files through background images or other means. However, it can't do anything about resources introduced directly through tags in HTML pages.

As you can see, the paths of these images are all img/. Since the compiled files are located in the dist folder, there is no img directory in the dist folder. Therefore, we can use thecopy-webpack-plugin Copy the img folder from the src directory to the dist directory.

const CopyPlugin = require("copy-webpack-plugin");

 = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "./src/img",
          to: "./img",
        },
      ],
    }),
  ],
};

In this way, when we execute thenpm run build When we try to create a new file from the However, when we try to retrieve them from the When you open the page, you find the page error message$ is undefined and defined at the bottom of the pageflexslider method did not take effect.

ProvidePlugin $ Symbol

We know.$ The symbols are actuallyjQuery A global variable provided. The prompt cannot find the$ symbols, meaning that thejQuery 's global variables have not been exposed correctly. To fix this, we can follow the steps below:

First, by installing thejQuery

npm install jquery --save

Next, adjust the introduction method in the file:

// Before changes
import '. /js/jquery-1.12.'

// After
import 'jquery'.

Then, use theProvidePlugin to define the $ mapping relationship:

const webpack = require("webpack");
 = {
  plugins: [
    new ({
      $: "jquery",
      jQuery: "jquery",
    }),
  ],
};

Finally, it will be The bottom of the file is passed through the<script> Tagged calls of theflexslider The function code is moved to the business JS file where it needs to be introduced.

After completing the above steps, perform thenpm run buildThe original The functionality then enables basic refactoring, and more optimization work can follow.

Automatically empties compiled folders

in implementingnpm run build whenWebpack will be based on The rules in thedist directory to generate compiled files. To avoiddist The files generated in the folder are mixed together, and we usually need to manually clean up that directory before each compilation.

To save ourselves the trouble of doing this manually, we can use theclean-webpack-plugin to automatically empty thedist folder. This ensures that every time you build, thedist The directories are clean, thus avoiding interference from old files.

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
 = {
  plugins: [
    new CleanWebpackPlugin()
  ],
};

Extract css file

This results in JS files that are too large and a mix of JS and CSS code that is not clear enough. In a development environment this works because of the speed of compilation, but in a production environment we need to abstract the CSS resources into separate files.

To do this, we can use themini-css-extract-plugin replacestyle-loaderto enable independent packaging of CSS resources.

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

 = {
  module: {
    rules: [
      { test: /\.css$/, use: [, "css-loader"] },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].[hash:8].css",
      chunkFilename: "[name].[hash:8].css",
    }),
  ],
};

In this way, the CSS resources are packaged into a separate file, making the final output more standardized and efficient. As you can see in the figure, the CSS file has been separated.

js and css compression

In the previous configuration, themode be set todevelopment, which facilitates debugging in development mode. However, when the code is released, we need to switch to theproduction Mode. In this mode, theWebpack Automatically compresses resource files to reduce file size.

In addition to changing the mode setting, we can utilize theterser-webpack-plugin cap (a poem)css-minimizer-webpack-plugin separate opinionJavaScript cap (a poem)CSS resources for further compression.

const TerserPlugin = require("terser-webpack-plugin");
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");

 = {
  mode: "production",
  optimization: {
    minimizer: [new TerserPlugin({}), new CssMinimizerWebpackPlugin()],
  },
};

As you can see in the figure, we can see the changes in file size with different mode settings and after using the plugin to compress the code resources. Although the current project has only one page with a small number of HTML, CSS, and JS files, so the effect of code compression may not be particularly significant, but as the size of the project grows, the effect of this compression strategy will be more obvious.

Add development mode

All of the above code changes we made by executing thenpm run build to observe the compiled product. However, when multiple files need to be migrated, using development mode makes it easier to see how all business scenarios are being used in real time.

To accomplish this, we can use thewebpack-dev-server to start a development server. After the installation is complete, add the Add to the filedevServer The configuration of the

 = {
  devServer: {
    open: true,
    compress: true,
    port: 8000,
  },
};

Next, in the file to configure a script command for starting the development server:

"scripts": {
"dev": "webpack serve",
},

In this way, by executing thenpm run dev You can start the development server and automatically open a browser to view the The content of the page. This not only facilitates debugging, but also provides a real-time preview of the effect of code changes.

multi-entry

So far we have only migrated the resources on the home page. Now we will move on to migrate the shopping cart page. Similar to the home page migration, first copy the files into the src directory and look for the CSS and JS resources that were introduced there.

Next, create a file with the required JS and CSS files:

// 
import "./css/";
import "./css/";

import 'jquery';
import './js/';
import './js/';
import './js/';

The next configuration is critical. We need to be in Define the multiple entries in the HTML file and generate the appropriate template HTML file for each page. It is important to note here that you must define thechunks attribute, otherwise the resulting HTML page will incorrectly introduce all CSS and JS files.

 = {
  entry: {
    index: "./src/",
    cart: "./src/",
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/",
      filename: "",
      chunks: ["index"],
    }),
    new HtmlWebpackPlugin({
      template: "./src/",
      filename: "",
      chunks: ["cart"],
    }),
  ],
};

After completing the above configuration, execute thenpm run buildIf you want to compile the two pages, you can do so by clicking on the At this point, in the dist folder, click file, you can also access the content of the page without any problems.

Splitting up public resources

Although it is currently possible to compile twoHTML page's resources, but if you view thedist folder of the or file, you will find that it still contains thejQuery of the code.

In order to optimize this, we would like to include a new class likejQuery Duplicate resources like this are referenced as public modules, rather than having them compiled over and over again in different JS files. Here's a detailedsplitChunks Configuration example, which sets thenode_modules The resources in thejQuery Compile into a separate file and compile other third-party libraries into another file.

 = {
  optimization: {
    splitChunks: {
      chunks: "all",
      name: "common",
      cacheGroups: {
        jquery: {
          // Test if the module contains the 'jquery' string.
          test: /[\\/]node_modules[\\\/]jquery[\\\/]/,, // Set the filename.
          // Set the filename
          name: "jquery", // set the filename.
          // The filename can be a function or a string.
          filename: "",
          // Ensure that only jQuery in asynchronously loaded chunks is included.
          priority: 10, // You can set the priority to control the merge order.
          enforce: true, // Force the creation of this chunk even if other rules might ignore it.
        }, }
        vendors: {
          // This cache group is used to handle other third-party libraries
          test: /[\\/]node_modules[\\/]/, name: "vendors", // "\/", "\/", "\/", "\/
          name: "vendors", priority: -10, }
          
          name: "vendors", priority: -10, filename: "",
          chunks: "all", }
        }, }
      }, }, }, }, }, }, }, }, }, }, }, }
    }, }, }
  }, }
}.

Since jQuery is the only resource used in the current project, only thejQuery be packaged separately. As the project grows and resources increase, the splitting rules can be further refined. As can be seen from the results, whenjQuery After being split, the file sizes of both and have been significantly reduced.

Template file ejs

The navigation at the top of the page is usually fixed and identical across pages. In the current project, the same HTML sections are defined by copying. To improve code reusability and maintainability, we can use theEJS(Embedded JavaScript)to pull this same part of the logic out.

First, in thesrc folder to create aejs folder and create a File. Find the definition of theheader code, copy it to the file with the changes (e.g., page titles) passed through the<%= title %> The way it is defined.

Then, in the original HTML page, defineheader The place where the code is introduced file and pass in dynamic variables:

<%=require('. /ejs/')({ title: 'Home' })%>

due toWebpack Inherently not equipped to handleEJS file capability, so we need to install theejs-loader and configure the corresponding processing rules:

 = {
  module: {
    rules: [
      { test: /\.ejs/, loader: "ejs-loader", options: { esModule: false } },
    ],
  },
};

With this configuration, we achieve reuse of public code.

The above steps complete the process of migrating from a pure HTML/JS project to development using Webpack. By using Webpack, we have achieved code segmentation, on-demand loading of resources, and modular development. Plugins such as html-webpack-plugin and clean-webpack-plugin simplify the build process and ensure a clean and optimized output file for every build.

Abstracting repetitive code fragments such as public headers through EJS reduces redundancy, improves code reuse, and makes the code base more concise.

If you're interested in front-end, JavaScript, and engineering, come take a look at my other articles ~ I'll be sharing various learning tips and tricks from time to time. Poke my avatar to explore more fun content together!