Mastering Webpack - Article II

Kinanee Samson - Jan 15 '21 - - Dev Community

Webpack for Beginners Series - Article 2

Familiarize yourself with webpack, learn how to modularize and compile your css and scss, and manage other assets.

In our previous article we we introduced our self to webpack, how it works and why it is so important, we also saw how to use webpack from the command line, then we went further and got into using a configuration file with webpack to bundle our modules, if you missed that article you can find it here and then you can come back to this one.

you can find the complete code for this article on this repository

our directory structure should look like this


bundle-app----------------------package.json
                |-------------package.lock.json
                |
                |-------------dist/---------index.html
                |           
                |
                |-------------src/---------index.js
                |                   |------hero.js
                |
                |-------------webpack.config.js


Enter fullscreen mode Exit fullscreen mode

We will be moving exactly where we left of and our code base should look like this


//webpack.config.js
const path = require('path') //we require the path core module to help us resolve a directory

//this will hold our configuration object
module.exports = {
    //we first specify an entry script to webpack
    entry: './src/index.js',
    //next is our output file and directory
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}

Enter fullscreen mode Exit fullscreen mode

As our projects grows larger we will need to add styles to our page, and it is fine if we do it the traditional way, however we could also use webpack to handle our styling and that we would not be worried about compiling less, scss e.t.c webpack would do that for us automatically and then it will scan the rules we write and it will only load styles that our app needs and only that, this is a cool thing because that way our css is also optimized increasing the performance of our app. lets see a use case. Open up your terminal in the root folder (bundle-app) and then type npm i css-loader style-loader --save-dev and hit enter, this command installs the css loader for us and we can use it in our configuration file to tell webpack to take care of our css for us, our webpack.config.js should look this now:


//webpack.config.js
const path = require('path') //we require the path core module to help us resolve a directory

//this will hold our configuration object
module.exports = {
    //we first specify an entry script to webpack
    entry: './src/index.js',
    //next is our output file and directory
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }, //new property added
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: ['style-loader','css-loader']
            }
        ]
    }
}

Enter fullscreen mode Exit fullscreen mode

We will add some few styles to our page in a new file, so our directory structure should look like this;


bundle-app----------------------package.json
                |-------------package.lock.json
                |
                |-------------dist/---------index.html
                |           
                |
                |-------------src/---------index.js
                |                   |------hero.js
                |                   |---style.css
                |
                |
                |-------------webpack.config.js


Enter fullscreen mode Exit fullscreen mode

You can add any basic style rules you want targeting certain elements in our page with either a class or id property,


.hero-list{
    list-syle: none;
    padding: 0;
}
.hero-item{
    padding: 8px 12px;
    text-transform: capitalize;
    border-bottom: 1px solid #efef4f
}
Enter fullscreen mode Exit fullscreen mode

Import the style.css in our index.js file and add the classes to the html template, our index.js file should look like this now;


//index.js
import Hero from './hero.js' 

import './style.css'

// create an html list object
const ul = document.createElement('ul')
--ul.classList.add('hero-list') //new addition 
// create a div tag
const div = document.createElement('div')
// we will loop through hero object and append a new li tag to the div for each property
for(let key in Hero){
    let li = document.createElement('li')
    li.textContent = Hero[key]
----li.classList.add('hero-item') //new addition
    ul.appendChild(li)
}
//append the list to the div and then the div to the body of the document
div.appendChild(ul)
document.querySelector('body').appendChild(div)

Enter fullscreen mode Exit fullscreen mode

once that is done you can run npx webpack --watch from the command line and this compile our code and watch it for changes by passing in the --watch flag if you run serve up the page you should the hero property looking much better. Now this example might be too simple but think about it, we styled the ul and all the li tags by just adding two classes, our code is cleaner and easier to maintain, we could do go one step and do conditional styling, we could also import our css file as a css module and that way we get access to the different classes and add them to the element, I also said that we can use webpack to process sass or less files, i will be focusing on scss but the approach is just the same and all you have to do is install the loader for the variant of css you writing in your code and we will discuss more about loaders in a bit. So let's change up our directory structure, we create a new scss file that will hold the styles and then we import it inside of our index.js file and add the classnames to the elements that we want to style, our directory structure should now look like this;


bundle-app----------------------package.json
                |-------------package.lock.json
                |
                |-------------dist/---------index.html
                |           
                |
                |-------------src/---------index.js
                |                   |------hero.js
                |                   |------style.scss
                |
                |
                |-------------webpack.config.js


Enter fullscreen mode Exit fullscreen mode

So we now have to install the sass loader and sass in our project and then modify our webpack.config.js file so it knows that we are processing scss, open your terminal in the 'bundle-app' directory and enter the following commands on your terminal npm i sass sass-loader --save-dev and hit enter and this installs the packages for us, next thing is to open your webpack.config.js file and it make the following changes to it;


//webpack.config.js
const path = require('path') //we require the path core module to help us resolve a directory

//this will hold our configuration object
module.exports = {
    //we first specify an entry script to webpack
    entry: './src/index.js',
    //next is our output file and directory
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }, //new property added
    module: {
        rules: [
            {
                test: /\.scss$/i, //new addition
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader' //we add the scss-loader new addition
                ]
            }
        ]
    }
}

Enter fullscreen mode Exit fullscreen mode

Next thing is to edit our index.js file so we import the style.scss file to it, your index.js file should now look like this;


//index.js
import Hero from './hero.js' 

---- import './style.scss' //new addition

// create an html list object
const ul = document.createElement('ul')
--ul.className = 'hero-list'
// create a div tag
const div = document.createElement('div')
// we will loop through hero object and append a new li tag to the div for each property
for(let key in Hero){
    let li = document.createElement('li')
    li.textContent = Hero[key]
----li.className = 'hero-item' 
    ul.appendChild(li)
}
//append the list to the div and then the div to the body of the document
div.appendChild(ul)
document.querySelector('body').appendChild(div)

Enter fullscreen mode Exit fullscreen mode

Since we are in watch mode, webpack will automatically compile our file for us and we should see no difference however, now you can use some of the features that sass provides and your styles will be processed and added to the page, we can modularize our css such that there is no class conflicting another, i.e scoped scss however we will look at that later. Lets us now examine our webpack.config.js file and talk about the module property we added to it, we add the module property when we want to tell webpack to help us handle some resource that our code uses, the module property takes in a rules object that is an array and inside this rules property we specify the different rules for each resource that we want webpack to handle for us, each rule is an object and each rule must contain at least 2 properties;

  • test property which is a regex that test's for the file extension we want webpack to handle for us and,
  • A use property which is an array of the different loaders we want to use to process the resources that have file extensions that passes the test.

We are just processing css and scss that is why in our test property we used the regex /\.css$/i to target all files that have the extension .css same thing for the scss file, we use /\.scss$/i to target all files that have the extension .scss. In our use array we added the different loaders that we installed to it, first we add the style-loader, then the css-loader and if we are using sass we add the sass-loader. We could still process css and sass file together, all we have to do is pass in different object to the rules array, one for css and the other for scss, don't forget to import them in your code too.
Loaders allow us to make transformation to the different files we import into our code, there is a loader for almost all files that developers work with, so you should definitely check out their website to know more about the different loaders available, however we will use a few of them as we progress through this series.

Assset Management

We can use webpack to handle assets that our code depends on like images, fonts and icons, we will look at how we can use webpack to handle our images, first we need to add the actual image we want webpack to handle for us, so just add an image file to the src folder, and it should be looking like this when you are done;


bundle-app----------------------package.json
                |-------------package.lock.json
                |
                |-------------dist/---------index.html
                |           
                |
                |-------------src/---------index.js
                |                   |------hero.js
                |                   |------style.scss
                |                   |------image.png
                |
                |
                |-------------webpack.config.js

Enter fullscreen mode Exit fullscreen mode

we will need to edit our webpack.config.js file so it we tell webpack that it should handle our image for us, our config file should look like this;


//webpack.config.js
const path = require('path') //we require the path core module to help us resolve a directory

//this will hold our configuration object
module.exports = {
    //we first specify an entry script to webpack
    entry: './src/index.js',
    //next is our output file and directory
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }, //new property added
    module: {
        rules: [
            {
                test: /\.scss$/i, 
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader' 
                ]
            },
            //new addition
            {
                test: /\.(png|jpg|jpeg|svg|gif)$/i,
                type: 'asset/resource'
            }
        ]
    }
}

Enter fullscreen mode Exit fullscreen mode

We dont actually use any loaders for assets we just pass in a type property to the rule for the asset and it should have a value of 'asset/resource', in our test we use regex to test for any file that has an extension of either jpg, png, svg, jpeg or gif, so we can import the image to our index.js file and we can add it to our code, our index.js file should now look like this; if you are not familiar with regex, check out this series


//index.js
import Hero from './hero.js' 
---import Image from './image.jpg' //new addition
import './style.scss' 

// create an html list object
const ul = document.createElement('ul')

//create an image element and append the image to the src of the img element
--let img = document.createElement('img') //new addition
---img.src = Image //new addition


ul.className = 'hero-list'
// create a div tag
const div = document.createElement('div')
// we will loop through hero object and append a new li tag to the div for each property
for(let key in Hero){
    let li = document.createElement('li')
    li.textContent = Hero[key]
li.className = 'hero-item'
    ul.appendChild(li)
}
//append the list to the div and then the div to the body of the document
div.appendChild(ul)
--div.appendChild(img) //new addition
document.querySelector('body').appendChild(div)

Enter fullscreen mode Exit fullscreen mode

Webpack will auto compile this for us since we are in watch mode and we should now see whatever image we added to our directory inside our webpage. You should try and do more of this to become more familiar with it, that is for it for this article, i know i said that we will compile typescript however i just thought that it is good to know how to use webpack to handle our assets, next article will look at the typescript and jsx compilation so stay tuned and have a nice day

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .