Gantt Chart
Strategy

How to Create a Simple Gantt Chart Using CSS Grid


A Gantt chart is a handy type of bar chart that is used in project management for showcasing a schedule of tasks. This chart visualizes project activities as cascading horizontal bars, with width depicting the project’s duration. 

As a front-end web designer or developer, you can make use of Gantt charts to manage projects and enhance the productivity within your team.

In this article, I’m going to show you how to create a simple Gantt chart using the CSS Grid Layout system—no external libraries or other fluff, just pure CSS.

You may also like:
Redesigning a Website Using CSS Grid and Flexbox.

You can reference this tutorial to understand how to use the layout system for applying CSS rules.

The chart will show a typical software development life cycle process, from January to December. 

Here is a screenshot of how the Gantt chart will look at the end of this tutorial:

Gantt Chart

Let’s get started!

Step 1: Create a Container div

Let’s start by creating a container div element for the Gantt Chart:

Let’s add some CSS styling to it:

Step 2: Create a Chart div

Let’s create a div inside the overarching container and name it chart. This is where all the remaining actions are going to take place.

Let’s add some CSS styling to it:

Notice that I’ve set the display property of the class to grid. Consequently, all its direct children will automatically become grid items.

Step 3: Create the Chart’s Rows

Let’s start by creating the first row, which will be the heading of the Gantt chart.

Notice that I’ve provided 12 span elements that will transverse the entire row, showing the months of the project’s duration — from January to December.

Here is its CSS:

Notice that I used the grid-template-columns property to specify the width and the number of columns in the grid layout.

Let’s see how it looks in a browser, so far:

browser view

Next, let’s add lines that will run throughout the chart in a box-like style, which helps showcase the duration of each project. 

I also used 12 span elements for creating the lines.

Here is its CSS:

Let’s see the output in a browser:

browser view

Step 4: Add Entry Items

Finally, let’s add the items that illustrate a year-long process of creating some software.

For example, here is how I added the first entry item:

Let me describe what is happening in the code above:

  • First, the encompassing div element has a class of chart-row, which I illustrated earlier.
  •  

  • The div with a class of chart-row-item is used for numbering the entry items on the Gantt chart. Here is its CSS:
  • To show tasks on the Gantt chart, I created an unordered list and styled it to display a horizontal bar, with its length showing the duration of the task. 

Here is the CSS styling for the chart-row-bars class:

  • The entry item is defined in the li tag. Here is its CSS styling:

Notice that I’ve used the grid-column property to specify the duration of the project. 

For example, a property of grid-column: 3/9; like the “Development” entry, spans a task across the grid from March to August.

Here is how the first entry item looks in a browser:

planning in browser

I added the other entries on the chart following the same process as the first entry.

Ultimately, it resulted in a nice-looking Gantt chart, just like the image I showed earlier. 

Wrapping Up

That’s it! You can view the entire code for this tutorial on CodePen:


As you’ve seen, creating a Gantt chart using CSS Grid is not complicated. With this type of chart, you can manage your web development projects effectively and ensure that everyone is on track toward accomplishing the stipulated objectives.

Furthermore, Gantt charts can also be used in other industries to manage projects. For example, if you are selling composting toilets, you can use Gantt charts to showcase the number of sales made over a period of time. 

Of course, I’ve just scratched the surface about the things you can achieve with Gantt charts. 

There are several other tweaks you can make to Gantt charts to suit your specific requirements and project goals. 

For example, you can use them to show the relationship between various tasks and how the completion of one relies on another, show how resources can be allocated for the success of projects, and show clear project requirements that ensure everyone is on the same page.

Do you have any questions or comments?

Please get in touch  and I’ll do my best to respond.

This article first appeared on freeCodeCamp.

Further Reading



Source link

Customer record
Strategy

5 Simple Rules for CRUD Operations


CRUD is probably the first word your CS teacher taught you, and you might be thinking there’s no way anyone could teach you more about it, right? Well, hopefully I will prove you wrong because once you dive as deep into CRUD as I have, interesting ideas manifest themselves.

For those who don’t already know this from before, CRUD is one of those acronyms that we software developers love, and it means Create, Read, Update, and Delete. It is the 4 axioms around which most of our data evolves. If you have all 4 CRUD operations towards your data, your data has an “orthogonal API.”

Your relational database evolves around 4 basic SQL statements: Insert, Select, Update, and Delete. It doesn’t require a rocket scientist to understand that these 4 words are the SQL equivalent of the CRUD acronym. The SQL version of CRUD hence becomes ISUD. HTTP has the same ideas, but yet again under a different name. HTTP REST refers to these same operations as POST, GET, PUT, and DELETE.

You might also want to read: 
CRUD Operation With ASP.NET Core MVC Using ADO.NET and Visual Studio 2017

So the HTTP equivalent of CRUD becomes PGPD. It’s important for you to understand that POST is for creating new data and PUT is for updating existing data at this point. The reason is that according to the HTTP standard, if PUT has been implemented correctly, you should be able to transmit the same payload to your endpoint without resulting in changes to your server’s internal state. Therefore, updating records multiple times with the same payload does not violate how the PUT verb was intended to be used, since no changes are applied, because the same data for your change is simply transferred multiple times, resulting in no actual change of your underlying data.

Secondly, it’s important to visualize the primary keys in your database. All sane databases should have a way to uniquely address one single record in their tables. If one of your tables doesn’t have this trait, it’s theoretically impossible to reference a single record in it, hence you can insert records and you can read them, but without a primary key, you can never delete a single record, and you can never update a single record (safely!). This is because if there is no way to uniquely identify a single record in your table, any update or delete statement, might in theory reference multiple records.

When I created Magic, which you can see in the video below, I realized that it’s all about primary keys — at least from a philosophical point of view. Watch the video first, and then I’ll explain what I mean afterwards.

The only reason I could create Magic was because I was able to extract a common “design pattern” in regards to how I treated my data. This pattern is as follows:

  1. All read endpoints return all columns from your table.
  2. All create endpoints do not take any primary key fields, as long as the primary key has a default value. Only if there is no default value for your primary key will create require the client to supply it.
  3. All update endpoints require everything that the create endpoints require, but in addition, they also require all primary key fields to uniquely identify which record to update.
  4. All delete endpoints require only the primary key fields and nothing else.
  5. Create always returns the primary key(s) for the records it creates unless an explicit primary key was supplied by the client. And update and delete always returns the number of records affected.

Once applied, the above 5 simple rules allowed me to, in a generic fashion, read metadata from my database tables, at which point I’d end up knowing my table’s column names, which columns are primary keys,  which columns have default values, etc. Once I knew this, I could scaffold code 100% generically, which would first wrap my entire database into HTTP REST endpoints, for then to create an Angular frontend client, consuming my backend, resulting in having my computer automagically create my entire app.

At that point, all I needed to do was to allow for configuring my endpoints by allowing the user to select validators, which roles the client needs to exist within to be allowed to execute the endpoint, etc, etc, etc. In a way, what I did was create my own internal “standard” for how to allow the ideas of CRUD to penetrate securely all the way from my relational database through my .Net Core backend, over HTTP as JSON, and into my frontend Angular data grids. This results in something resembling the following, automatically created for me by my computer.

Customer record

Notice how the primary key for my above Customer record is not possible to edit. In fact, in my Datagrid, I don’t even show it by default, but it’s there behind the scenes since it’s a prerequisite for being able to update my records.

Standardization

I often have junior developers asking me what the difference between a junior and a senior developer is. Often, the answer to this is surprising for the junior, since the junior often knows 15 different ways to implement QuickSort in 10 different programming languages, while the senior has long since forgotten this due to not needing it for 25+ years or something. So I will teach you the difference, and it is “the ability to extract common patterns, standardize on them, and think generically.”

In other words, the junior will happily slash away at any problem they are given and try to solve it as fast as possible, while the senior will spend two days thinking about his problem’s common traits before even implementing a single line of code. Once the senior is done of course, not only has he solved the problem at hand, but he has also solved every possible permutation of future similar problems. So while the junior is adding to the cognitive and technical debt of his employer, by producing hundreds of thousands of lines of code that needs to be maintained, the senior developer does not do this because every time he solves one problem, he solves a thousand similar problems.

If you want to become a senior developer, stop solving problems immediately! Find the common traits the current set of problems you’re working with has with other problems, and extract the commonalities. Then, solve that problem. This will oftentimes result in only 5-10 lines of code you need to maintain in the future instead of tens of thousands of lines of code you need to maintain in the future to keep your “previous solutions running.”

I have created software since I was 8 years old, and I am 45 years of age today, which implies I have created software for 37 years. And yes, I still love it! If you want to see how a “super-senior” software developer solves problems, you can start out by downloading Magic.

Further Reading

Automate CRUD Operations and Focus on What Matters

Web Dev Roundup: What the CRUD!



Source link

You've Got SFTP and Database Access – The WordPress.com Blog
Strategy

You’ve Got SFTP and Database Access – The WordPress.com Blog


Power Users Rejoice: You’ve Got SFTP and Database Access

Three new hosting management tools give you direct access to your site’s files and data.

Three new hosting management tools give you direct access to your site’s files and data.

Have a site on a Business or eCommerce plan? Now you have three new ways to customize your WordPress.com site: SFTP access, database access, and PHP version switching give you behind-the-scenes access to the nuts and bolts of your site, which means more freedom and flexibility than ever before. If you’ve ever gotten stuck trying to modify your WordPress.com site because you couldn’t manually upload file changes or delete something from your database, website management is about to get a lot easier.

You’ll find all these features in a new section of your dashboard: Manage > Hosting Configuration.

The Hosting Configuration Screen

SFTP is a secure way to access the files and folders on your WordPress.com site using a program on your local computer like Filezilla. Some custom plugins and themes ask you to create specific folders or add files via SFTP. While many of those tasks can be accomplished with tools already built into your WordPress.com dashboard, folks who like using SFTP can now have SFTP access so they can make these changes directly. Check out our step-by-step guide to get started.

At WordPress.com, we regularly optimize your database so you don’t have to, but there might still be times when you need an efficient way to modify data, like purging all the tables created by a plugin you’ve decided to delete. Accessing your site’s database is an effective way to do this. 

Database access is a powerful tool, so if you’re at all unsure about working with a database, reach out to our Happiness Engineers! If you’re unfamiliar with databases, you can also find information on getting started in our help documentation.

PHP is still one of the key languages used to build the web, and a new version, PHP 7.4, was recently released. WordPress.com sites currently run PHP 7.3, which has been tested extensively across all of WordPress.com, but sites on Business or eCommerce plans can switch to version 7.4 immediately. Learn more.

Since these new tools let you dig into some of the code and data the powers your site, you’ll find a link for our support team right from the dashboard so you can get help if you need it. Have fun getting under the hood, power users!



Source link

Managing User Permissions in a Vue.js App
Strategy

Managing User Permissions in a Vue.js App


In authenticated front-end apps, we often want to change what’s visible to the user depending on their assigned role. For example, a guest user might be able to see a post, but only a registered user or an admin sees a button to edit that post.

Managing permissions in a front-end app can be messy. You may have written code like this before:

if (user.type === ADMIN || user.auth && post.owner === user.id ) {
  ...
}

As an alternative, there’s a neat little library called CASL that helps manage user permissions very simply. Once you’ve defined your permissions with CASL and set an active user, you could change the above example to something like this:

if (abilities.can('update', 'Post')) {
  ...
}

In this article, I’ll demonstrate how to manage permissions in a front-end app with Vue.js and CASL.

Note: you don’t have to have used CASL before to follow this!

CASL Crash Course

CASL allows you to define a set of rules which restrict what resources a given user is allowed to access.

For example, CASL rules can indicate which CRUD operations (Create, Read, Update, and Delete) a user can undertake on a given resource or entity (e.g. a post, a comment, an article, etc).

Let’s say we have a classified ads website with simple “for sale” posts. An obvious set of rules for this app would be:

  • A guest user can view any post.
  • An admin user can view any post, and can update or delete a post.

In CASL, we use AbilityBuilder to define the rules. A new rule is created with a call to can, e.g.

const { AbilityBuilder } = require('casl');

export function(type) {
  AbilityBuilder.define(can => {
    switch(type) {
      case 'guest':
        can('read', 'Post');
        break;
      case 'admin':
        can('read', 'Post');
        can(['update', 'delete'], 'Post');
        break;
      // Add more roles here
    }
  }
};

Now you can control your app based on checks to the rules you defined, e.g.:

import defineAbilitiesFor from './abilities';

let currentUser = {
  id: 999,
  name: "Julie"
  type: "registered",
};

let abilities = defineAbilitiesFor(currentUser.type);

Vue.component({
  template: `<div v-if="showPost">{{ post }}<div>
             <div v-else>Please log in</div>
            `,
  props: [ 'post' ],
  computed: {
    showPost() {
      return abilities.can('read', 'Post');
    }
  }
});

You can find out more about CASL by checking the official docs.

Demo Project

As a demonstration, I’ve made a simple server/client app which shows classified ad posts. The permission rules for this app are: a user can read any post or create a new post, but can only update or delete a post if it’s a post they created.

I’ve used Vue.js with CASL to make these rules easy to implement and scale upon, in case other operations or entities are added in the future.

I’ll now take you through the steps of setting up this app. If you’d like to see the finished code, check out this GitHub repo.

Defining User Permissions

Let’s define our user permissions in a file resources/ability.js. One cool thing about CASL is that it is environment agnostic, meaning it can be used in either Node or the browser.

We’ll make our permission definition a CommonJS module to ensure compatibility with Node (Webpack can transform the module for use in the client).

resources/ability.js

const casl = require('casl');

module.exports = function defineAbilitiesFor(user) {
  return casl.AbilityBuilder.define(
    { subjectName: item => item.type }, 
    can => {
      can(['read', 'create'], 'Post');
      can(['update', 'delete'], 'Post', { user: user });
    }
  );
};

Let’s break down that code a bit:

Looking at the second argument to the define method, we define permission rules by making calls to can. The first argument of this method is the CRUD operation(s) you want to allow, the second is the resources/entity, in this case, Post.

Notice that in the second can function call, we pass a third argument; an object. This is used to test if the user property of the entity matches a user object we’ll provide when making the test. If we didn’t do this, any post could be updated or deleted by any user, not just the owner.

resources/ability.js

...
casl.AbilityBuilder.define(
  ...
  can => {
    can(['read', 'create'], 'Post');
    can(['update', 'delete'], 'Post', { user: user });
  }
);

When CASL checks an entity to determine permission, it needs to know the type of entity it’s looking at. One way of doing this is to pass an object with a function property subjectName as the first argument of the define method. This function will return the type of entity.

We’ll implement this by returning the type property on our entities. We’ll need to make sure this property is present when we define our Post objects in a moment.

resources/ability.js

...
casl.AbilityBuilder.define(
  { subjectName: item => item.type }, 
  ...
);

Finally, we wrap our ability definition in a function which allows us to pass in a user object any time we want to test permissions. This will be better understood when we use it in the main app, below.

resources/ability.js

const casl = require('casl');

module.exports = function defineAbilitiesFor(user) {
  ...
};

Accessing Permission Rules in Vue

We now want to be able to test an object in our front-end app to see what CRUD operations the user is allowed to perform on it. We’ll need to provide access to the CASL rules within our Vue components. Here’s how:

  1. Import Vue and the abilities plugin. This plugin adds CASL to the Vue prototype, allowing us to call it from within components.
  2. Import our rule set into the Vue app (i.e. resources/abilities.js).
  3. Define the current user. In a real app, we’d get this user data from the server. For our example, we’ll simply hard-code it.
  4. Remember, the abilities module exports a function, which we’ll call defineAbilitiesFor. We pass the user object to this function. Now, any time we test an object, we can see what permissions are available for the current user.
  5. Add the abilities plugin, allowing us to make tests within a component like this.$can(...).

src/main.js

import Vue from 'vue';
import abilitiesPlugin from './ability-plugin';

const defineAbilitiesFor = require('../resources/ability');
let user = { id: 1, name: 'George' };
let ability = defineAbilitiesFor(user.id);
Vue.use(abilitiesPlugin, ability);

Post Entity

Objects representing classified ad posts will be used by our app. They may be retrieved from a database and then passed to the front-end by the server, for example.

There are two properties our Post entity must have:

  1. The type property. CASL will use the subjectName callback defined in abilities.js to check what kind of entity is being tested
  2. The user property. This is the owner of the post. Remember, a user only has update and delete permissions if they own the post. In main.js, we already told CASL who the current user is with defineAbilitiesFor(user.id). All CASL needs to do now is check if the user’s ID matches the user property.
let posts = [
  {
    type: 'Post',
    user: 1,
    content: '1 used cat, good condition'
  },
  {
    type: 'Post',
    user: 2,
    content: 'Second-hand bathroom wallpaper'
  }
];

Given these two post objects, our current user, George, who has ID 1, will have update/delete permissions on the first post, but not the second.

Testing User Permission on an Object

Posts are displayed in our app via a component called Post. Take a look at the code first, then we’ll break it down below:

src/components/Post.vue

<template>
  <div class="post">
    <div class="content">
      {{ post.content }} 
      <br/><small>posted by {{ username }}</small>
    </div>
    <button @click="del">Delete</button>
  </div>
</template>
<script>
  import axios from 'axios';

  export default {
    props: ['post', 'username'],
    methods: {
      del() {
        if (this.$can('delete', this.post)) {
          ...
        } else {
          this.$emit('err', 'Only the owner of a post can delete it!');
        }
      }
    }
  }
</script>
<style lang="scss">...</style>

When the user clicks the Delete button, the click is captured and the del handler method is called.

We then use CASL to check if the current user has permission for this operation via this.$can('delete', post). If they do have permission, we can take some action. If not, an error message (ex: “Only the owner of a post can delete it!”) could be shown.

Server-Side Testing

In a real application, after a user deletes a post in the front-end, we’d use AJAX to send the delete instruction to an API, e.g.:

src/components/Post.vue

if (this.$can('delete', post)) {
  axios.get(`/delete/${post.id}`, ).then(res => {
    ...  
  });
}

We’d then put the CASL test logic on the server since the server shouldn’t trust a CRUD operation from the client:

server.js

app.get("/delete/:id", (req, res) => {
  let postId = parseInt(req.params.id);
  let post = posts.find(post => post.id === postId);
  if (ability.can('delete', post)) {
    posts = posts.filter(cur => cur !== post);
    res.json({ success: true });
  } else {
    res.json({ success: false });
  }
});

Since CASL is isomorphic, the ability object on the server can be imported from abilities.js, saving us having to duplicate any code!

Wrap Up

With that, we have a really nice way of managing user permissions in a simple Vue app.

I believe this.$can('delete', post) is much more elegant than:

if (user.id === post.user && post.type === 'Post') {
  ...
}

This is not only more difficult to read, but, also, there’s an implicit rule here, i.e. that a post can be deleted by a user. This rule will undoubtedly be used elsewhere in our app, and should really be abstracted. This is what CASL can do for us.

Thanks to Sergii Stotskyi, creator of CASL, for assistance with this article.



Source link

Image title
Strategy

How to Not Screw Up UX in a Single-Page Application


If there were a Hippocratic oath for web developers, it would surely include a promise that any modification to a web page will provide a net improvement to User Experience.

And yet, there are many sites that have multi-megabyte code bundles, break native browser features like page history or make users wait too long before showing page content. Most often the root cause of these infractions is a poor or unnecessary implementation of the single-page application (SPA) architecture.

In this article, we’ll look at how SPAs are designed and the common pitfalls that detract from user experience.

Single-Page Application Architecture

Most websites are broken up into pages in order to make the information they contain easier to consume. The traditional architecture is to give each page a unique URL. To navigate to a page, the browser sends a GET request to the page’s URL. The server will send back the page and the browser will unload the existing page and load the new one.

For the average internet connection, the navigation process will likely take a few seconds, during which time the user must wait for the new page to load.

Image title

With JavaScript and web APIs like XMLHttpRequest, a different model is possible: the browser can load an initial page, but navigating to new pages will not require the browser to unload the page and load a new one. Instead, the page content can be loaded from an API asynchronously with AJAX and then written into the current page with JavaScript.

From a user’s perspective, such a website would appear to have pages just like any other, but from a technical perspective, this site really only has one page. Hence the name, single-page application.

Image title

Routers

A router library is the engine of the SPA architecture. It will mimic browser navigation through JavaScript and various web APIs so that the user gets an experience similar to that of a traditional multi-page app.

Routers will typically include functionality to:

  • Handle navigation actions from within the page.
  • Match parts of the application to URLs.
  • Manage the address bar.
  • Manage the browser history.
  • Manage scrollbar behavior.

Improving UX

The intention of the single-page application architecture is to improve UX, and it does so in the following ways:

  1. SPAs can provide a more continuous experience for the user, as navigation no longer requires a page refresh. Data for new pages must still be retrieved, and will, therefore, create some small disruption to the user’s flow, but this disruption is minimized since the data retrieval can be done asynchronously and JavaScript can continue to run.
  2. Once the SPA has loaded, navigation between pages is quicker because SPAs will reuse page elements and therefore won’t need to keep downloading the same repeated markup. However, a router library will need to be added to your JavaScript bundle, so keep this in mind when doing the accounting.

Pitfalls

Ironically, single-page applications can harm UX if certain pitfalls aren’t avoided:

  1. SPAs break native navigation functionality, e.g. scroll position, history, back button, etc. Once a router has hijacked page navigation, these features may not work as expected. An SPA must restore the functionality with JavaScript and web APIs like history.pushState. Most good router libraries will help you do this, but there will still be some manual implementation required.
  2. SPAs have a large initial download size. Since the router and multi-purpose page elements must be downloaded first for the app to work, SPAs require an upfront download before they run. Build tools like Webpack can help by lazy-loading any code not needed before the first render.
  3. SPAs will need custom loading states and errors messages. Browsers give visual cues that a page is being loaded, and a web server can return a 404 page. The result of an AJAX request, on the other hand, is hidden from the user by design. SPAs must find a way to let users know if the app has successfully responded to their actions or not.
  4. With a naive implementation of the SPA architecture, page content may not be included in the initial page download, which means a user may have to wait for JavaScript to run and AJAX calls to complete. Server-side rendering or prerendering is a solution to this but often requires a complex setup.

Conclusion

The purpose of the SPA architecture is to provide superior user experience, but unless proper care is taken, it can have the opposite effect!

Here are the key things to keep in mind if you choose the SPA architecture:

  • Configure your router software so native navigation features aren’t broken.
  • Employ build tool features like code-splitting and lazy-loading to ensure the initial code bundle isn’t too big.
  • Implement loading states and error messages so that the user knows the page is responding to their actions.
  • Use prerendering or server-side rendering to ensure your SPA shows content as early as possible.

Above all, make sure you have budgeted for the extra work required for building, testing. and maintaining an SPA.



Source link

Image title
Strategy

Jargon-Free Webpack Intro for Vue.js Users


For many developers, Vue.js is the first front-end JavaScript framework they’ve learned. If that’s true for you, you probably haven’t had a reason to learn Webpack yet. But as you continue with Vue, you’ll see Webpack popping up over and over again in the documentation, example projects and in relation to tools like Vue CLI.

Webpack promises great things for Vue users:

  • An automated development process that makes coding a breeze.
  • A world of handy development features like Vue’s beloved Single-File Components.
  • Optimizations for your code to make it fast and lean.

But the range of possibilities of Webpack is also why it’s so intimidating at first. It appears to do so many things that it’s hard to grasp what it really is.

The Webpack guides have become increasingly better at explaining Webpack, but a certain amount of background knowledge is still assumed. In this article, I’ll attempt to give you that background without the jargon that may otherwise leave you confused.

Example Project

Webpack helps you develop JavaScript applications, so to discuss it we should have a simple example project in mind. The following is what I’ll be referring to throughout this article:

app.js

const app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  }  
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Vue.js Project</title>
</head>
<body>
  <div id="app">{{ message }}</div>
  <script type="text/javascript" src="vue.js"></script>
  <script type="text/javascript" src="app.js"></script>
</body>
</html>

Dependencies

The story of Webpack begins with some observations about how JavaScript dependencies are managed in an HTML document. By “dependencies” I mean third-party libraries like Vue, jQuery or Lodash, or even other script files from your own code base.

In fact, there’s no real way of managing dependencies in an HTML document other than to ensure any shared functions and variables have global scope and that scripts are loaded in the right order.

For example, since vue.js defines a global Vue object and is loaded first, we’re able to use the Vue object in our app.js script. If either of those conditions was not met, the script would break. Consider the following where we attempt to use Vue before it has loaded:

<script>
  console.log(Vue);
  // Uncaught ReferenceError: Vue is not defined
</script>
<script type="text/javascript" src="vue.js"></script>

In a complex web application, this system is insufficient because:

  • Global variables introduce possibilities of naming collisions.
  • Script loading order is fragile and can be easily broken as the app grows.
  • Performance optimizations, like loading scripts asynchronously, cannot be utilized.

Modules

A solution to the dependency management problem is to use a module system where code is modularized and imported into other scripts. Over the years, there have been several different JavaScript module systems developed, but ES modules is the system that is now being incorporated into the JavaScript standard.

Here’s a simple ES modules example:

moduleA.js

export default function(value) {
  return value * 2;
}

moduleB.js

import multiplyByTwo from './moduleA';
console.log(multiplyBy2(2));
// 4

Could we make Vue.js a module and avoid the problems discussed? Yes! The Vue.js script file that’s used directly in a browser is just one of the available builds of Vue. There is also an ES module build named vue.esm.browser.js which can be used in our example project like this:

app.js

import Vue from './vue.esm.browser.js';

const app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  }  
});

Script order isn’t a problem now since the compiler knows it has to wait until vue.esm.browser.js is available before it can run. Global variables aren’t needed either because modules are referenced by their file name.

The problem is that ES modules are not consistently supported in browsers. In fact, until a few months ago, it wasn’t supported in any browser.

If we want to use ES modules or any other JavaScript module system, we’ll need to resolve the code into something that can be reliably used in a browser. Enter Webpack.

Bundling

Webpack is a Node.js tool that runs offline in your development environment. Webpack is able to resolve JavaScript modules into browser-friendly code in a process called “bundling.”

Bundling begins with an “entry file.” Webpack analyzes the entry file to find any dependencies. In the example project, app.js is the entry file and has just one dependency, Vue.js. In most projects, there will be many more.

Webpack then analyzes the dependencies to find any dependencies that they might have. This process continues until all dependencies of the project are found.

The result is a graph of dependencies. For the example project, the graph includes app.js, vue.js and a few other dependencies required by Webpack.

Image title

Webpack uses this graph as a blueprint for bundling all the code into a single browser-friendly file.

In the example project, the bundle file will replace the individual script files vue.js and app.js in the HTML document:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Vue.js Project</title>
</head>
<body>
  <div id="app">{{ message }}</div>
  <script type="text/javascript" src="bundle.js"></script>
</body>
</html>

Loaders

Webpack provides a reliable solution to the JavaScript dependency management problem. From this foundation, other powerful emerge, e.g. loaders.

Loaders allow Webpack to transform a file before it’s bundled. For example, the Webpack Babel loader transforms next-generation JavaScript syntax like ES2015 into standard ES5. This allows developers to write their code using modern features but still provide support in older browsers.

For example, in app.js we use the ES2015 const, which isn’t supported by IE10:

app.js

const app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  }  
});

If the Webpack Babel loader is used const will be transformed to var before it’s added to the bundle:

bundle.js

...

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  }  
});

...

There are many other loaders including:

  • CSS loader
  • Sass loader
  • TypeScript loader
  • Vue Loader (for single-file components)

Running Webpack

Webpack uses a declarative config file to describe each aspect of the build. These config files are notoriously long and difficult to follow, but for a simple project you should be able to get the gist:

webpack.config.js

module.exports = {
  // Entry file
  entry: './app.js',
  output: {
    // Output bundle
    filename: 'bundle.js'
  }, 
  module: {
    rules: [
      {
        // For .js files...
        test: /.js$/,
        use: {
          // Use the Babel loader
          loader: 'babel-loader'
        }
      }
    ]
  },
  resolve: {
    alias: {
      // Ensure the right Vue build is used
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
};

With the config file created, Webpack can then be run with a CLI command:

$ webpack

As Webpack runs, it outputs statistics about the build in the terminal. Many of these stats won’t have meaning right now, but you can at least see that this build took about three seconds to complete and the output file, bundle.js, is 299KB.

Image title

Next Step

This article hasn’t gotten you very far in learning Webpack, but the goal was to give you the background I think is missing from the official docs and other guides.

The big takeaway I hope you’ve gotten is that Webpack is first and foremost a module bundler. All of Webpack’s other features emerge from this basic model.

For the next step, I recommend you go through the Concepts section of the Webpack Docs:

https://webpack.js.org/concepts/

Good luck!



Source link

Lazy loading components
Strategy

Build a Lazy-Load Router With Vue.js


Dynamic module importing is one of the latest JavaScript features to hit the major browsers. The main use case for this feature is lazy-loading modules to allow content to be delivered when it is needed, rather than all at once.

In this article, I’ll demonstrate how you can build a lazy-load router with Vue.js in just a few lines of code. This will work natively in browsers that have implemented dynamic module imports, but I’ll also include a fallback for older browsers.

Lazy loading components

Grab the finished code here on GitHub.

You may also like:
Lazy Loading ES2015 Modules in the Browser.

Static JavaScript Module Imports

If you’re using the latest version of any major browser, you can perform static import/export natively now. For Vue.js, this means you can export a component definition from a file like this:

BooksPage.js

And import it into your app like this:

app.js

Making component JavaScript modules allows you to organize your app such that every “page” is in a different file. This is nothing new if you use Vue.js single-file components, but with native support now, this architecture can be achieved without Webpack or Babel.

I cover importing Vue.js components as JavaScript modules more thoroughly in a recent article Vue.js Single-File JavaScript Components In The Browser.

Dynamic JavaScript Module Imports

If your components represent pages, it’d be better to fetch the module files on demand so that a user doesn’t have to download pages they don’t visit. Static imports are resolved at compile time, however. This means you can’t put import BooksPage from './BooksPage'; inside an if statement to conditionally load it. Instead, your static imports will start downloading as soon as the script that loads them runs.

That’s where dynamic imports come in. These can be determined at runtime, meaning you can conditionally load a JavaScript module, and therefore pages, on demand. Note that dynamic imports return a Promise which resolves the module content.

Note: component is a built-in “meta” component that accepts a component definition through the prop is. Like all Vue components, this can either be a component definition, or a Promise that resolves the component definition.

This code will work natively in the browser if you’re using the latest Safari or Chrome Canary, with other browsers soon to follow.

Creating a Tiny Lazy-Load Router

With that theory in mind, let’s make our lazy-load router. In your app template, create some anchor tags where the href is the path to the component module for that page. Listen to the click event on these elements, prevent the redirect, and instead trigger a method, navigate.

index.html

We’ll define the navigate method in the Vue instance and it will accept the click event as an argument. Use the value of the link’s href, i.e. event.target.pathname, to dynamically import the required page component module and assign it to the local page state property. This is dynamically bound to the component component.

app.js

Note that versions of Vue.js before 2.5.0 need to include a then callback to correctly resolve the module defintion.

That’s it! If you run this in a browser that supports dynamic imports you’ll see this:

Example output

Fallback

What about users who don’t have the latest version of Safari or Chrome? They’ll need a fallback. Let’s create one with Webpack.

Firstly, we’ll have to modify the navigate method a bit. Webpack’s implementation of import() requires it to know ahead of time which files it may need to dynamically load. You don’t need to specify the exact module name, just make sure the value you provide to import() is a resolvable file or directory.

To do this, we’ll change the dynamic file name so it specifies the pages directory and extracts the filename from the href i.e. ./pages/${event.target.pathname.split("https://dzone.com/").pop()}. At compile-time, Webpack is smart enough to know this means “some file in the pages directory” and will process any JavaScript file in this directory.

Secondly, we need to put the comment /* webpackChunkName: "pages/[request]" */ in the function so that Webpack knows to extract this chunk to a separate file. Otherwise, Webpack will bundle all the page component modules in one file and the benefits of lazy-loading will be forfeit.

app.js

Webpack Config

You can use this simple Webpack config which has two notable features:

  1. Specifies a chunkFilename output property. This ensures the page component modules are named correctly in the Webpack output.
  2. Transpiles the JavaScript with Babel. You’ll need the plugin syntax-dynamic-import for Babel to recognize the dynamic import statement.

Run Webpack with that config and you’ll get build files including:

  • All the JavaScript module files transpiled to CommonJS modules.
  • The main bundle will include Webpack’s implementation of dynamic import.

Webpack dynamic import

Checking for Dynamic Import Support

How do you tell the browser to use the fallback? As far as I know, there’s no specific way to check for browser support for import(). My strategy is to use an inline script in the body of the document which creates a new script tag and will conditionally change the source (either the main script or the fallback) depending on support for import().

Note that the fallback script is treated as a normal JavaScript script, whereas the main script is treated as a module.

Conclusion

It’s great if we can use the native implementations of JavaScript module imports as optimizations like lazy-loading can be done with a smaller file size and much simpler implementation. For example, Webpack’s implementation of import() requires it to know ahead of time which files it may need to dynamically load, the native implementation does not.

In practice, features like this will need to be progressive enhancements and will, therefore, require Webpack as a fallback, which reintroduces the same complications. Oh well, that’s web dev for you.

Further Reading



Source link

recursive_components_01.png
Strategy

Build A Collapsible Tree Menu With Vue.js Recursive Componen…


A recursive component in Vue.js is one which invokes itself e.g.:

Vue.component('recursive-component', {
  template: `<!--Invoking myself!-->
             <recursive-component></recursive-component>`
});

Recursive components are useful for displaying comments on a blog, nested menus, or basically anything where the parent and child are the same, albeit with different content. For example:

recursive_components_01.png

To give you a demonstration of how to use recursive components effectively, I’ll go through the steps of building an expandable/contractable tree menu.

Data Structure

A tree of recursive UI components will be the visual representation of some recursive data structure. In this tutorial, we’ll use a tree structure where each node is an object with:

  1. A label property.
  2. If it has children, a nodes property, which is an array of one or more nodes.

Like all tree structures, it must have a single root node but can be infinitely deep.

let tree = {
  label: 'root',
  nodes: [
    {
      label: 'item1',
      nodes: [
        {
          label: 'item1.1'
        },
        {
          label: 'item1.2',
          nodes: [
            {
              label: 'item1.2.1'
            }
          ]
        }
      ]
    }, 
    {
      label: 'item2'  
    }
  ]
}

Recursive Component

Let’s make a recursive component to display our data structure called TreeMenu. All it does is display the current node’s label and invokes itself to display any children.

TreeMenu.vue

<template>
  <div class="tree-menu">
    <div>{{ label }}</div>
    <tree-menu 
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label"
    >
    </tree-menu>
  </div>
</template>
<script>
  export default { 
    props: [ 'label', 'nodes' ],
    name: 'tree-menu'
  }
</script>

If you’re using a component recursively you must either register it globally with Vue.component, or, give it a name property. Otherwise, any children of the component will not be able to resolve further invocations and you’ll get an undefined component error.

Base Case

As with any recursive function, you need a base case to end recursion, otherwise rendering will continue indefinitely and you’ll end up with a stack overflow.

In our tree menu, we want to stop the recursion whenever we reach a node that has no children. You could do this with a v-if, but our v-for will implicitly do it for us; if the nodes array is undefined no further tree-menu components will be invoked.

<template>
  <div class="tree-menu">
    ...
    <!--If `nodes` is undefined this will not render-->
    <tree-menu v-for="node in nodes"></tree-menu>
</template>

Usage

How do we now use this component? To begin with, we declare a Vue instance which has the data structure as a data property and registers the TreeMenu component.

app.js

import TreeMenu from './TreeMenu.vue'

let tree = {
  ...
}

new Vue({
  el: '#app',
  data: {
    tree
  },
  components: {
    TreeMenu
  }
})

Remember that our data structure has a single root node. To begin the recursion we invoke the TreeMenu component in our main template, using the rootnodesproperties for the props:

index.html

<div id="app">
  <tree-menu :label="tree.label" :nodes="tree.nodes"></tree-menu>
</div>

Here’s how it looks so far:

recursive_components_02.png

Indentation

It’d be nice to visually identify the “depth” of a child component so the user gets a sense of the structure of the data from the UI. Let’s increasingly indent each tier of children to achieve this.

recursive_components_03.png

This is implemented by adding a depth prop to TreeMenu. We’ll use this value to dynamically bind inline style with a transform: translate CSS rule to each node’s label, thus creating the indentation.

<template>
  <div class="tree-menu">
    <div :style="indent">{{ label }}</div>
    <tree-menu 
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label"
      :depth="depth + 1"
    >
    </tree-menu>
  </div>
</template>
<script>
  export default { 
    props: [ 'label', 'nodes', 'depth' ],
    name: 'tree-menu',
    computed: {
      indent() {
        return { transform: `translate(${this.depth * 50}px)` }
      }
    }
  }
</script>

The depth prop will start at zero in the main template. In the component template above you can see that this value will be incremented each time it is passed to any child nodes.

<div id="app">
  <tree-menu 
    :label="tree.label" 
    :nodes="tree.nodes"
    :depth="0"
  ></tree-menu>
</div>

Remember to v-bind the depth value to ensure it’s a JavaScript number rather than a string.

Expansion/Contraction

Since recursive data structures can be large, a good UI trick for displaying them is to hide all but the root node so the user can expand/contract nodes as needed.

To do this, we’ll add a local state property showChildren. If false, child nodes will not be rendered. This value should be toggled by clicking the node, so we’ll need a click event listener method toggleChildren to manage this.

<template>
  <div class="tree-menu">
    <div :style="indent" @click="toggleChildren">{{ label }}</div>
    <tree-menu 
      v-if="showChildren"
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label"
      :depth="depth + 1"
    >
    </tree-menu>
  </div>
</template>
<script>
  export default { 
    props: [ 'label', 'nodes', 'depth' ],
    data() {
      return { showChildren: false }
    },
    name: 'tree-menu',
    computed: {
      indent() {
        return { transform: `translate(${this.depth * 50}px)` }
      }
    },
    methods: {
      toggleChildren() {
        this.showChildren = !this.showChildren;
      }
    }
  }
</script>

Wrap Up

With that, we’ve got a working tree menu. As a nice finishing touch, you can add a plus/minus icon to make the UI even more obvious. I did this with Font Awesome and a computed property based on showChildren.

Inspect the CodePen to see how I implemented it.

recursive_components_04.png



Source link

Image title
Strategy

3 Code Splitting Patterns For Vue.js and Webpack


Code splitting a single page app is a great way to improve its initial loading speed. Since a user doesn’t have to download all the code in one hit, they’ll be able to see and interact with the page sooner. This will improve UX, especially on mobile, and it’s a win for SEO, as Google penalizes slow loading sites.

Last week I wrote about how to code split a Vue.js app with Webpack. Long story short, anything you wrap in a single file component can easily be code split, as Webpack can create a split point when it imports a module, and Vue is happy to load a component asynchronously.

I believe the hardest part of code splitting is not getting it to work, but knowing where and when to do it. I’d go as far as to say that code splitting needs to be an architectural consideration when designing your app.

In this article I’ll present three patterns for code splitting a Vue.js single page app:

  • By page
  • By page fold
  • By condition

Image title

1. By Page

Splitting your code by page is an obvious place to start. Take this simple app, which has three pages:

Image title

If we make sure each page is represented by its own single file component, e.g. Home.vue, About.vue, and Contact.vue, then we can use Webpack’s dynamic import function to split each into a separate build file. Then, when the user visits a different page, Webpack will asynchronously load the requested page’s file.

This is trivial to implement if you’re using vue-router, as your pages will already need to be in separate components.

routes.js

const Home = () => import(/* webpackChunkName: "home" */ './Home.vue');
const About = () => import(/* webpackChunkName: "about" */ './About.vue');
const Contact = () => import(/* webpackChunkName: "contact" */ './Contact.vue');

const routes = [
  { path: "https://dzone.com/", name: 'home', component: Home },
  { path: "https://dzone.com/about", name: 'about', component: About },
  { path: '/contact', name: 'contact', component: Contact }
];

Take a look at stats generated when we build this code. Each page is in its own file, but also note there’s a main bundle file called build_main.js, which contains any common code as well as the logic for asynchronously loading the other files. It will need to be loaded no matter what route the user visits.

Image title

Now let’s say I load the Contact page from the URL http://localhost:8080/#/contact. When I check the Network tab I see the following files have loaded:

Image title

Notice that the initiator of build_main.js is (index). This means that index.html requested the script, which is what we’d expect. But the initiator of build_1.js is bootstrap_a877…. This is the Weback script that is responsible for asynchronously loading files. This script is added to the build automatically when you use Webpack’s dynamic import function. The important point is that build_1.js did not block the initial page load.

2. Below the Fold

Below the “fold” is any part of the page that is not visible in the viewport when the page initially loads. You can asynchronously load this content since the user will usually take a second or two to read above the fold before they scroll down, especially on the first time they visit the site.

Image title

In this example app, I consider the fold line to be just below the masthead. So let’s include the nav bar and the masthead on the initial page load, but anything below that can be loaded afterward. I’ll now create a component called BelowFold and abstract the relevant markup into that:

Home.vue

<template>
  <div>
    <div class="jumbotron">
        <h1>Jumbotron heading</h1>
        ...
    </div>

    <below-fold></below-fold>

    <!--All the code below here has been put into-->
    <!--into the above component-->
    <!--<div class="row marketing">
      <div class="col-lg-6">
        <h4>Subheading</h4>
        <p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>
        ...
      </div>
      ...
    </div>-->

  </div>
</template>
<script>

  const BelowFold = () => import(
    /* webpackChunkName: "below-fold" */ './BelowFold.vue'
  );

  export default {
    ...
    components: {
        BelowFold
    }
  }
</script>

BelowFold.vue

<template>
  <div class="row marketing">
    <div class="col-lg-6">
      <h4>Subheading</h4>
      <p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>
      ...
    </div>
    ...
  </div>
</template>

We will now see the below-fold chunk in its own separate file when we bundle the code:

Image title

Note: the below-fold chunk is very small (1.36kB) and it seems hardly worth bothering to split this out. But that’s only because this is a demo app with very little content. In a real app, the majority of the page is likely to be below the fold, so there might be a ton of code there including CSS and JS files for any sub components.

3. Conditional Content

Another good candidate for code splitting is anything that is shown conditionally. For example, a modal window, tabs, drop-down menus, etc.

This app has a modal window that opens when you press the “Sign up today” button:

Image title

As before, we just move the modal code into its own single file component:

Home.vue

<template>
  <div>
    <div class="jumbotron">...</div>

    <below-fold></below-fold>

    <home-modal v-if="show" :show="show"></home-modal>
  </div>
</template>
<script>

  const BelowFold = () => import(
    /* webpackChunkName: "below-fold" */ './BelowFold.vue'
  );
  const HomeModal = () => import(
    /* webpackChunkName: "modal" */ './HomeModal.vue'
  );

  export default {
    data() {
      return {
        show: false
      }
    },
    components: {
      HomeModal,
      BelowFold
    }
  }
</script>

HomeModal.vue

<template>
    <modal v-model="show" effect="fade">...</modal>
</template>
<script>
  import Modal from 'vue-strap/src/Modal.vue';

  export default {
    props: ['show'],
    components: {
        Modal
    }
  }
</script>

Notice that I’ve put a v-if on the modal. The boolean show controls the opening/closing of the modal, but it will also conditionally render the modal component itself. Since on page load it’s false, the code will only get downloaded when the modal is opened.

This is cool because if a user never opens the modal, they never have to download the code. The only downside is that it has a small UX cost: the user has to wait after they press the button for the file to download.

After we build again, here’s what our output looks like now:

Image title

Another ~5KB we don’t have to load up front.

Conclusion

Those are three ideas for architecting an app for code splitting. I’m sure there are other ways to do it if you use your imagination!



Source link

Image title
Strategy

4 Ways To Boost Your Vue.js App With Webpack


Webpack is an essential tool for developing Vue.js single page applications. It makes your development workflow much simpler by managing complex build steps and can optimize your app’s size and performance.

In this article, I’ll explain four ways that Webpack can enhance your Vue app, including:

  1. Single file components.
  2. Optimizing the Vue build.
  3. Browser cache management.
  4. Code splitting.

What About Vue-CLI?

If you’re using a template to build your app from vue-cli, a pre-made Webpack config is provided. They’re well optimized and there are no improvements I can suggest!

But since they work so well out of the box, you probably don’t have much idea of what they’re really doing, right? Consider this article an overview of the Webpack config used in the vue-cli templates, as they include the same optimizations I’m discussing here.

1. Single File Components

One of Vue’s idiosyncratic features is the use of HTML for component templates. These come with an intrinsic problem, though: either your HTML markup needs to be in an awkward JavaScript string, or your template and component definition will need to be in separate files, making them hard to work with.

Vue has an elegant solution called Single File Components (SFCs) that include the template, component definition, and CSS all in one neat .vue file:

MyComponent.vue

<template>
  <div id="my-component">...</div>
</template>
<script>
  export default {...}
</script>
<style>
  #my-component {...}
</style>

SFCs are made possible by the vue-loader Webpack plugin. This loader splits up the SFCs language blocks and pipes each to an appropriate loader, e.g. the script block goes to babel-loader, while the template block goes to Vue’s own vue-template-loader which transforms the template into a JavaScript render function.

The final output of vue-loader is a JavaScript module ready for inclusion in your Webpack bundle.

A typical configuration for vue-loader is as follows:

module: {
  rules: [
    {
      test: /.vue$/,
      loader: 'vue-loader',
      options: {
        loaders: {
          // Override the default loaders
        }
      }
    },
  ]
}

2. Optimizing the Vue build

Runtime-Only Build

If you’re only using render functions in your Vue app*, and no HTML templates, you don’t need Vue’s template compiler. You can reduce your bundle size by omitting the compiler from the Webpack build.

* Remember that single file component templates are pre-compiled in development to render functions!

There is a runtime-only build of the Vue.js library that includes all the features of Vue.js except the template compiler, called vue.runtime.js. It’s about 20KB smaller than the full build so it’s worth using if you can.

The runtime-only build is used by default, so every time you use import vue from 'vue'; in your project that’s what you’ll get. You can change to a different build, though, by using the alias configuration option:

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js' // Use the full build
  }
},

Stripping Out Warnings and Error Messages in Production

Another way to reduce your Vue.js build size is to remove any error messages and warnings in production. These bloat your output bundle size with unnecessary code and also incur a runtime cost you’re best to avoid.

If you inspect the Vue source code you’ll see that warning blocks are conditional on the value of an environment variable process.env.NODE_ENV e.g.:

if (process.env.NODE_ENV !== 'production') {
  warn(("Error in " + info + ": "" + (err.toString()) + """), vm);
}

If process.env.NODE_ENV is set to production then such warning blocks can be automatically stripped out of the code by a minifier during the build process.

You can use the DefinePlugin to set the value of process.env.NODE_ENV, and the UglifyJsPlugin to minify the code and strip out the unused blocks:

if (process.env.NODE_ENV === 'production') {
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin()
  ])
}

3. Browser Cache Management

A user’s browser will cache your site’s files so that they’ll only download if the browser does not already have a local copy, or if the local copy has expired.

If all your code is in one file, then a tiny change would mean the whole file would need to be re-downloaded. Ideally, you want your users to download as little as possible, so it’d be smart to separate your app’s rarely changing code from its frequently changing code.

Vendor File

The Common Chunks plugin can decouple your vendor code (e.g. dependencies like the Vue.js library that are unlikely to change very often) from your application code (code that may change on every deployment).

You can configure the plugin to check if a dependency is from the node_modules folder, and if so, output it into a separate file vendor.js:

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module) {
    return module.context && module.context.indexOf('node_modules') !== -1;
  }
})

If you do this, you’ll now have two separate files in your build output which will be cached by the browser independently:

<script src="vendor.js" charset="utf-8"></script>
<script src="app.js" charset="utf-8"></script>

Fingerprinting

When a build file changes, how do we bust a browser’s cache?

By default, only when a cached file expires, or when the user manually clears the cache, will the browser request the file again from the server. The file will be re-downloaded if the server indicates the file has changed (otherwise the server returns HTTP 304 Not Modified).

To save an unnecessary server request, we can change a file’s name every time its content changes to force the browser to re-download it. A simple system for doing this is to add a “fingerprint” to the file name by appending a hash, e.g.:

Image title

The Common Chunks plugin emits a “chunkhash” which is updated if the file’s content has changed. Webpack can append this hash to the file names when they’re outputted:

output: {
  filename: '[name].[chunkhash].js'
},

When you do this, you’ll see that your outputted files will have names like app.3b80b7c17398c31e4705.js.

Auto Inject Build Files

Of course, if you add a hash, you’ll have to update the reference to the file in your index file, otherwise, the browser won’t know about it:

<script src="app.3b80b7c17398c31e4705.js"></script>

This would be a hugely tedious task to do manually, so use the HTML Webpack Plugin to do it for you. This plugin can auto inject references to the build files into your HTML file in the bundling process.

Start by removing references to your build file:

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>test-6</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files should go here, but will be auto injected -->
  </body>
</html>

And add the HTML Webpack Plugin to your Webpack config:

new HtmlWebpackPlugin({
  filename: 'index.html'
  template: 'index.html',
  inject: true,
  chunksSortMode: 'dependency'
}),

Now your build files with hashes will automatically be added to your index file. Also, your index.html file will now be included in your bundle output so you may need to tell the web server that its location has changed.

4. Code Splitting

By default, Webpack will output all your app’s code into one large bundle. But if your app has multiple pages, it would be more efficient to split the code so each individual page’s code is in a separate file, and is only loaded when needed.

Webpack has a feature called “code splitting” that does exactly that. Achieving this in Vue.js also requires async components, and is made even easier with Vue Router.

Async Components

Rather than having a definition object as their second argument, async components have a Promise function that resolves the definition object, for example:

Vue.component('async-component', function (resolve, reject) {
  setTimeout(() => {
    resolve({
      // Component definition including props, methods etc.
    });
  }, 1000)
})

Vue will only call the function when the component actually needs to be rendered. It will also cache the result for future re-renders.

If we architect our app so each “page” is a component, and we store the definition on our server, then we’re halfway to achieving code splitting.

Require

To load your async component’s code from the server, use the Webpack require syntax. This will instruct Webpack to bundle async-component in a separate bundle when it builds, and better yet, Webpack will handle the loading of this bundle with AJAX, so your code can be as simple as this:

Vue.component('async-component', function (resolve) {
  require(['./AsyncComponent.vue'], resolve)
});

Lazy Loading

In a Vue.js app, vue-router will typically be the module you use to organize your SPA into multiple pages. Lazy loading is a formalized way to achieve code splitting with Vue and Webpack.

const HomePage = resolve => require(['./HomePage.vue'], resolve);

const rounter = new VueRouter({
  routes: [
    {
      path: "https://dzone.com/",
      name: 'HomePage',
      component: HomePage
    }
  ]
})



Source link