Author Archives: toni

Bundle splitting using react-loadable

branch-name:  
Click To Copy

 

So far we created one bundle that loads the code for all components, no matter if we are going to render them or not. if for example  we just open one page with only one component we don’t need to load the code for all other components that are not on the page. It makes the load process slower, especially with a big site with many components.

Let’s fix this and split the bundle into chunks that contain the code only for the components that are on the page that the user opened, and load dynamically other components when the user navigates to a different sections in the site.

Install react-loadable

This is as simple as adding ‘react-loadable’ component.

yarn add react-loadable

Implementing react-loadabe

Before modifying PageLayout to use react-loadable, let’s create helper component called Loading that will show a spinning bar while the components are loading.

Create ‘Loading’ helper component

create a loading component folder mkdir ./src/components/Loading with index file with these contents:

./src/components/Loading/index.js

import React from 'react';

const styles = require('./styles.scss');

/**
 * HELPER COMPONENT TO DISPLAY LOADING ... AND HANDLE ERRORS WHEN COMPONENTS LOADS DYNAMICALLY 
 * @param {} props 
 */
const Loading = (props) => {
  if (props.error) {
    return (<div className={styles.wrapper}>
              <h3>Error loading component!</h3> 
              <p><b>{props.error.message}</b></p>
              <p>{props.error.stack}</p>
              <div><button onClick={ props.retry }>Retry</button></div>
            </div>);
  } else {
    return <div>Loading...</div>;
  }
} 

export default Loading;

and the styling file:

./src/components/Loading/styles.scss

.wrapper {
  background:rgb(141, 141, 172);
  padding-bottom: 10px;
  h3 {
    background: red;
    text-align: center;
  }
  div{
    text-align: center;
  }
}

 

and replace <div>Loading…</div> with the new component.

./src/containers/PageLayout/index.js

import React, { Component } from 'react';
import ComponentList from './ComponentList';
import Loading from '../../components/Loading';
import query from './query';
import { graphql } from 'react-apollo';

const styles = require('./styles.scss');

class PageLayout extends Component {

    constructor(props) {
      super(props);      
    } 
  
    render() {
      if(!this.props.data.getPageByUrl) {
        return (<Loading />);
      }  

...

Adding react-loadable.

`react-loadable’ works the same way like ‘import’ but will load required components on demand and won’t pre-load the components until it’s time to render them.

./src/containers/PageLayout/ComponentList/index.js

import Loadable from 'react-loadable';
import Loading from '../../../components/Loading';

/* Components */

const Header = Loadable({
  loader: () => import ('../../../components/Header'),
  loading: Loading
});

const Home = Loadable({
  loader: () => import ('../../../components/Home'),
  loading: Loading
});

const About = Loadable({
  loader: () => import ('../../../components/About'),
  loading: Loading
});

const Greetings = Loadable({
  loader: () => import ('../../../components/Greetings'),
  loading: Loading
});

const DogsCatalog = Loadable({
  loader: () => import ('../../../containers/DogsCatalog'),
  loading: Loading
});

export default {
  Home: Home,
  About: About,
  Greetings: Greetings,
  DogsCatalogWithRedux: Gallery,
  Header: Header
}

if you run the code you will notice that it will fail with this error message

`… Support for the experimental syntax ‘dynamicImport’ isn’t currently enabled (7:17)`

This is happening since Babel doesn’t have the appropriate plug-in to transpile dynamic imports. Let’s fix this by installing ‘plugin-syntax-dynamic-import’.

yarn add @babel/plugin-syntax-dynamic-import --dev

then add the plug-in into .babelrc file (line 6)

{
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react"
    ],
    "plugins": ["@babel/plugin-syntax-dynamic-import"]   
  }

Now open the dev console, switch to the net tab and filter by .js and navigate to http://localhost:8080/home You will notice that there will be three bundles loaded: main-bundle.js, dist0-bundle.js and dist1-bundle.js the numbers could very but don’t worry about this as long as you see 3 bundles loaded.These bundles have the code for each component: Header component and the Home compoent. Navigate to the “about” section and you will see another bundle loading: dist2-bundle.js.

Now you have bundle split and loaded whenever the component is about to render. If you navigate back, you will see that the bundle won’t be reloaded.

Now the bundle splitting is done.

Fixing source mapping.

Let’s put debugger breakpoint into DogsComponent and take a look at the file source:

./src/containers/DogsCatalog/index.js

/* eslint-disable no-debugger */
import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import DogDetails from './DogDetails';
import query from './query';
const styles = require('./styles.scss');
class DogsCatalog extends Component {
  constructor(props) {
    super(props);
    this.state = {
        breed: "labrador"
    }
  }
  handleClick(breedType) {
    debugger;
    this.setState({
        breed: breedType
    });
  }
...

 

Give it a try and check the location and you will see something like this:http://localhost:8080/dist0-bundle.js which is not quite understandable.

Let’s fix this. Open base Webpack file, and replace `devtool:'source-map' with devtool: 'cheap-module-eval-source-map' in order to have source mapping working after bundle splitting.

If we want even better source mapping with preserved column # as well we could use devtool: eval-source-map which will give the most detailed information for debugging but will make the building process slower.

There are a good amount of Dev tool config parameters, some are suitable only for development since they make bundle rebuild faster some for production only.

[table id=2 /]

For detailed information about all options visit WebPack dev tool page.

Make sure that the debugger statement is removed before we continue to the next chapter.

 

branch-name:  
Click To Copy

 

Adding dynamic page layout loaded from GraphQL

branch-name:  
Click To Copy

 

Having page layout hardcoded works fine but it doesn’t let us dynamically modify it without changing the code. What about if we store the page layout in mongoDB, then request it through GraphQL and create component that could dynamically load the page components.

Luckily for us we already have page layout set up in GraphQL which for the homepage looks like this:

module.exports = [
    {
      id: 'home',
      url: '/home',
      layout: [ 
        {
          span: 12,
          components: [
            {
              name: "Header"
            }
          ]
        },
        {
          span: 12,
          components:[
            {
              name: "Home"
            }
          ] 
        }                       
      ]
    }
  ]

It simply shows that the page layout has a header and Home component. We are going to build component that will interpret this and will render a webpage with the requested components. This way we will have the flexibility to:

  • dynamically change page layout by adding and removing components without code change
  • create new pages on the fly without code change.

And if you want to explore all other layouts you could look into GraphQL project under ./src/models/mock_data/pages

Creating PageLayout component to load our modules.

Let’s change App/index.js remove all other routes and redirect everything to the new component that we are going to create (line 21).

./src/components/App/index.js

import React, { Component } from 'react';
import PageLayout from '../../containers/PageLayout';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import styles from './styles.scss';
export default class App extends Component {
  render() {
    const GRAPHQL_URL = 'http://localhost:4001/graphql';
    const client = new ApolloClient({
      link: new HttpLink({ uri:  GRAPHQL_URL }),
      cache: new InMemoryCache()
    });  
    return (
      <div className={styles.appWrapper}>
        <ApolloProvider client={client}>
          <Router>
            <Switch>
            <Route exact path="*" component={PageLayout} />  
            </Switch>
          </Router>
        </ApolloProvider>
      </div>        
    );
  }
}

Now let’s continue with creating the component itself.

./src/containers/PageLayout/index.js

import React, { Component } from 'react';
import query from './query';
import { graphql } from 'react-apollo';

class PageLayout extends Component {

    constructor(props) {
      super(props);      
    } 
  
    render() {
        console.log(this.props.data.getPageByUrl);
        return (
        <div>
            this is my page layout!
        </div>
        );
    }
}

export default graphql(query, {
    options(props) {
      return {
        variables: {
          url: props.history.location.pathname
        },
      };
    },
  })(PageLayout);

./src/containers/PageLayout/query.js

import gql from 'graphql-tag';

const query = gql`
query getPageByUrl($url: String) 
{
  getPageByUrl(url: $url) {
    id
    url
    layout {
      span
      components {
        name
      }        
    }
  }
}
`
export default query;

and when you run the project and look at the browser console you will see the layout structure that we printed out in index.js (line 12)

This basically describes which components and in what order should be rendered for the ‘home’ page.

Hook PageLayot component to render required HTML components.

Create ComponentList sub component with the following contents:

./src/containers/PageLayout/ComponentList/index.js

import Header from '../../../components/Header';
import Home from '../../../components/Home';
import About from '../../../components/About';
import Greetings from '../../../components/Greetings';
import Gallery from '../../../containers/DogsCatalog';

export default {
  Home: Home,
  About: About,
  Greetings: Greetings
}

This component simply provide a list of all components that we are going to render in our website.

Now modify PageLayout component to add the list and render the HTML components.

./src/containers/PageLayout/index.js

import React, { Component } from 'react';
import ComponentList from './ComponentList';
import query from './query';
import { graphql } from 'react-apollo';

const styles = require('./styles.scss');

class PageLayout extends Component {

    constructor(props) {
      super(props);      
    } 
  
    render() {
      if(!this.props.data.getPageByUrl) {
        return(<p>loading ...</p>);
      }     
      
      const allLayout = this.props.data.getPageByUrl.layout.map((layoutList) => {
        const layout = layoutList.components.map((component, id , components) => {
          const componentName = component.name;        
          const ChildComponent = ComponentList[componentName];
          if(typeof ChildComponent === 'undefined') {
            return(
              <div key='{id}' className={styles.error}>Can't find {componentName} component!</div>
            );
          }
          return (
              <ChildComponent key={componentName} />
          );
        });
        return layout;
      });
      return(allLayout);
    }
}

export default graphql(query, {
    options(props) {
      return {
        variables: {
          url: props.history.location.pathname
        },
      };
    },
  })(PageLayout);

 what we just did:
– we added the ComponentList which returns all components on our website
– (Line 19) we are getting the layout for this particular url that contains all components, then we loop through all components (line 20) and create an instances for all of them.
– If the component cannot be found it will show red error message (lines 23-27)

Also add the css which will colorize the error message in red:

./src/containers/PageLayout/styles.scss

.error {
  background: red;
  color: white;
}

navigate to http://localhost:8080/home and you are probably going to see this screen:

This is actually made intentional, to demonstrate how our error handler works If it can’t find particular component.

Let’s fix this. Add the highlighted lines with the missing components.

./src/containers/PageLayout/ComponentList/index.js

import Header from '../../../components/Header';
import Home from '../../../components/Home';
import About from '../../../components/About';
import Greetings from '../../../components/Greetings';
import Gallery from '../../../containers/DogsCatalog';

export default {
  Home: Home,
  About: About,
  Greetings: Greetings,
  DogsCatalogWithRedux: DogsCatalog,
  Header: Header
}

Navigate again to http://localhost:8080/home and you should have SPA with 4 pages.

 

branch-name:  
Click To Copy

 

 

 

Listen for DOM changes using Mutation Observer

If we ever needed to listen for DOM events, one of the first options that comes to our mind might be to use setTimeout and periodically check the contents of the tag that we are interested in. Like in this example:

function myTest() {
    setTimeout(function() {
        console.log("Beep ...");
        if(document.getElementById('root').innerHTML == 'Loading ...') {
            myTest();
        }
        else {
            // do something 
            alert("READY!");
        }
    }, 1000);
}

But this is intense JavaScript process, which could be done using more elegant way with the help of Mutation Observer. 

Using Mutation Observer we could subscribe for different types of DOM events and trigger function on change.

example code:

<html>
  <body>    
    <div id="root">loading ...</div>

    <script>
      setTimeout(function() {
        document.getElementById('root').innerHTML = "My content is loaded!";
      }, 3000);
    </script>

    <script>
      var targetNode = document.getElementById('root');
      var config = { attributes: true, childList: true, subtree: true, characterData:true };
      var observer = new MutationObserver(function() {
        alert("#root div changed!");  
        // stop observing
        observer.disconnect();      
      });

      observer.observe(targetNode, config);
    </script>
  </body>
</html>

 what is happening here:
– created a div tag with id=’root’ and added ‘Loading …’ text.
– created MutationObserver (line 14) to listen for DOM changes in ‘root’ folder.
– specified which DOM events will trigger the function in config param (line 13)

– after 3 sec. setTimeout event fires and changes the content of the ‘root’ div, triggering DOM change event which will cause the function set in MutationObserver (line 14) to be executed.
– after showing the alert we could optionally disconnect and unsubscribe for DOM change events for this tag.

Here are some of the most used config properties

Mutation observer init properties:

At a minimum, one of childListattributes, and/or characterData must be true when you call observe(). Otherwise, a TypeError exception will be thrown.

attributeFilter Optional
An array of specific attribute names to be monitored. If this property isn’t included, changes to all attributes cause mutation notifications. No default value.
attributeOldValue Optional
Set to true to record the previous value of any attribute that changes when monitoring the node or nodes for attribute changes; see Monitoring attribute values in MutationObserver for details on watching for attribute changes and value recording. No default value.
attributes Optional
Set to true to watch for changes to the value of attributes on the node or nodes being monitored. The default value is false.
characterData Optional
Set to true to monitor the specified target node or subtree for changes to the character data contained within the node or nodes. No default value.
characterDataOldValue Optional
Set to true to record the previous value of a node’s text whenever the text changes on nodes being monitored. For details and an example, see Monitoring text content changes in MutationObserver. No default value.
childList Optional
Set to true to monitor the target node (and, if subtree is true, its descendants) for the addition or removal of new child nodes. The default is false.
subtree Optional
Set to true to extend monitoring to the entire subtree of nodes rooted at target. All of the other MutationObserverInit properties are then extended to all of the nodes in the subtree instead of applying solely to the target node. The default value is false.

Adding navigation in Single Page App

The concept of Single Page App is that all modules will be dynamically rendered while user navigates the page, without reloading. Express routing which we added is providing this functionality for us, so let’s use it.

Adding navigation.

Let’s get started. Edit index.html and add the highlighted line so we will have some marker on the page that will indicate if the page reloads.

./index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Babel Webpack Boilerplate</title>
    </head>
    <body>
        <h3>SPA</h3>
        <div id="root"></div>
        <script type="text/javascript" src="dist/main-bundle.js"></script>
    </body>
</html>

 

Create header menu for navigation.

Let’s create new navigation component and call it Header component. Create new folder ./src/components/Header and add:

./src/components/Header/index.js

import React from 'react';
import { Link } from 'react-router-dom';

const styles = require('./styles.scss');

const Header = ( {title} ) => (
  <div>
    <h2>{ title }</h2>
    <div className={styles.wrapper}>
      <ul>
        <li><Link to='/'>HOME</Link></li>
        <li><Link to='/about'>ABOUT</Link></li>
        <li><Link to='/greetings'>GREETINGS</Link></li>   
        <li><Link to='/gallery'>GALLERY</Link></li>      
      </ul>
    </div>
  </div>
);

export default Header;

also add the CSS

./src/components/Header/styles.scss

.wrapper {
  
  ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    border: 1px solid #e7e7e7;
    background-color: #f3f3f3;
  }
  li {
    float: left;
  }

  li a {
    display: block;
    color: #666;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
  }

  li a:hover:not(.active) {
      background-color: #ddd;
  }

  li a.active {
      color: white;
    
  }
}

Add header menu in our components.

Home component

We added home as a first link but we actually don’t have Home component, so let’s create a simple one and add header navigation there.

create ./src/components/Home folder and add:

./src/components/Home/index.js

import React from 'react';
import Header from '../Header';

const styles = require('./styles.scss');

const Home = () => (
  <div>
    <Header />
    <div className={styles.wrapper}>This is my home section.</div>
  </div>
)

export default Home;

and the css:

./src/components/Home/styles.js

.wrapper {
  background: rgb(141, 141, 172);
  color: white;
  text-align: center;
}

About component.

do the same for About component.

./src/components/About/index.js

import React from 'react';
import Header from '../Header';

const styles = require('./styles.scss');

const About = () => (
  <div>
    <Header />
    <div className={styles.wrapper}>This is the About section.</div>
  </div>
)

export default About;

./src/components/Home/styles.scss

.wrapper {
  background: rgb(141, 141, 172);
  color: white;
  text-align: center;
}

DogsCatalog container component.

and DogsCatalog container component

./src/containers/DogsCatalog/index.js

/* eslint-disable no-debugger */

import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import DogDetails from './DogDetails';
import Header from '../../components/Header';

import query from './query';

const styles = require('./styles.scss');


class DogsCatalog extends Component {

  constructor(props) {
    super(props);
    this.state = {
        breed: "labrador"
    }
  }

  handleClick(breedType) {
    this.setState({
        breed: breedType
    });
  }

  render() {  
    if(typeof this.props.data.getDogsList == 'undefined') {
      return(
        <div>Loading ... </div>
      );
    }
    return(
      <div>
        <Header />
        <div className={styles.Wrapper}>
          <p>Dogs catalog</p>
          <div className={styles.Buttons}>
            {this.props.data.getDogsList.map( (dog) => {
              return (<button key={dog.id} onClick={ () => { this.handleClick(dog.breed) } }>{dog.breed}</button>);
            })}          
          </div>
          <DogDetails breed={this.state.breed} />
        </div>
      </div>
    );
  }
}


export default graphql(query, {})(DogsCatalog);

Greetings component.

./src/components/Greetings/index.js

import React from 'react';
import Header from '../Header';

const styles = require('./styles.scss');

const Greetings = (props) => (
  <div>
    <Header />
    <div className={styles.wrapper}><h2>Hello  {props.user}</h2></div>
  </div>
)



export default Greetings;

Set up the routes.

./src/components/app.index.js

import React, { Component } from 'react';
import Greetings from '../greetings';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import About from '../about';
import Home from '../Home';
import { ApolloProvider, graphql } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import DogsCatalog from '../../containers/DogsCatalog';
import fetch from 'unfetch';

export default class App extends Component {

  render() {

    const GRAPHQL_URL = 'http://localhost:4001/graphql';
    const client = new ApolloClient({
      link: new HttpLink({ uri:  GRAPHQL_URL, fetch: fetch }),
      cache: new InMemoryCache()
    });  

    return (
      <ApolloProvider client={client}>
        <Router>
          <Switch>
            <Route exact path="/" component={Home} />    
            <Route exact path="/About" component={About} />                      
            <Route exact path="/greetings" component={Greetings} />
            <Route exact path="/gallery" component={DogsCatalog} />
          </Switch>
        </Router>
      </ApolloProvider>
    );
  }
}

Basically we told Express which component to render for each route.

Give it a try and fire up the server using your preferred config.

At this point you will have neat SPA (Single Page App) that dynamically renders the components without ever reloading the page.

 

Adding Apollo provider and introduction to making queries to GraphQL

branch-name:  
Click To Copy

Running GraphQL server to connect to.

 First let’s have GraphQL server running so we could connect to.
How to do this:

  • Install MongoDB because our version of GraphQL is using it and we will need a real database in the future tutorials. So let’s do it now. If you are on MAC and have Homebrew installed this is as simple as executing brew install mongodb from the command line.
  •  Download an run GraphQL setup as well. This will install Database settings that we will need in MogoDB.

yarn setup

then run the server:

yarn start

Now if you  point your browser to http://localhost:4001/graphql you will be able to see GraphQL GUI and you could execute queries there, but this is not the subject of this tutorial. If you are interested of how to set up GraphQL with Express server you could visit this article.

Creating ‘Smart’ component.

The concept of ‘smart’ and ‘dumb’ components is simple: We separate the data fetching from presentational layer by dividing the components into ‘presentational’ which only deal with how to show the data, and ‘container’ components, which only work is to fetch the data from some source. In our case this will be Apollo provider.  (read more …)

Installing dependencies.

yarn add graphql graphql-tag react-apollo apollo-client apollo-link-http apollo-cache-inmemory

  • graphql – query language for APIs created by Facebook.
  • graphql-tag – utilities for parsing GraphQL queries.
  • react-apolllo – utilities that fetch data from GraphQL server and use it in building complex and reactive UIs using the React framework.
  • apollo-client – GraphQL client with integrations for React, Angular, and more.
  • apollo-link-http – interface for modifying control flow of GraphQL requests and fetching GraphQL results
  • apollo-cache-inmemory – Tool for caching GraphQL client with integrations for React, Angular, and more.

yarn add graphql graphql-tag react-apollo apollo-link-http apollo-client apollo-cache-inmemory 

Now if we have all moving peaces in place, we could start building our first ‘connected‘ component, that will fetch data from QgraphQL.

Creating Apollo provider.

Let’s add all necessary components that we will need to connect to GraphQL, and wrap the rotes into Apollo provider .

./src/components/App/index.js

import React, { Component } from 'react';
import Home from '../Home';
import Greetings from '../Greetings';
import About from '../About';
import DogsCatalog from '../../containers/DogsCatalog';
import Header from '../Header';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import styles from './styles.scss';
export default class App extends Component {
  render() {
    const GRAPHQL_URL = 'http://localhost:4001/graphql';
    const client = new ApolloClient({
      link: new HttpLink({ uri:  GRAPHQL_URL }),
      cache: new InMemoryCache()
    });  
    return (
      <div className={styles.appWrapper}>
        <ApolloProvider client={client}>
          <Router>
            <Header />
            <Switch>
              <Route exact path="/home" user="Test" component={Home} />
              <Route exact path="/greetings" user="Test" component={Greetings} />
              <Route exact path="/dogs-catalog" component={DogsCatalog} />              
              <Route exact path="/about" component={About} />
            </Switch>
          </Router>
        </ApolloProvider>
      </div>        
    );
  }
}

 

what we just did:
– added all necessary components that we will need to connect to GraphQL (lines 7 – 10) .
– Import our new Dogs-Catalog component that we are going to build (line 5).
– created Apollo component, and pass link and cache parameters (lines 15-19).
– wrap the routes into Apollo provider.
– add new route to show out new ‘Dogs Catalog’ component.

Also make sure that you are adding navigation section to the Header component.

import React from 'react';
import { Link } from 'react-router-dom';
const styles = require('./styles.scss');
const Header = ( {title} ) => (
  <div>
    <div className={styles.wrapper}>
      <ul>
        <li><Link to='/home'>HOME</Link></li>
        <li><Link to='/greetings'>GREETINGS</Link></li>       
        <li><Link to='/dogs-catalog'>DOGS CATALOG</Link></li>
        <li><Link to='/about'>ABOUT</Link></li>
      </ul>
    </div>
  </div>
);
export default Header;

 

Creating connected component.

We will create a simple component, that will fetch data from GraphQL and will show different dogs breeds depending of which one user will choose.

Create new folder mkdir ./src/containers and add the new index.js for the new ‘Dogs Catalog’  component.

./src/containers/DogsCatalog/index.js

import React from 'react';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';

const GET_DOG = gql`
query 
{
  getDogByBreed(breed: "labrador") {
    id
    breed
    displayImage
  }
}
`

const DogCatalog = () => (
  <Query query={GET_DOG}>
    {({ loading, error, data }) => {
      if (loading) return <div>Loading...</div>;
      if (error) return <div>Error!</div>;

      return (
        <div>
            <span>breed: </span>
            <span>{data.getDogByBreed.breed}</span>
            <br />
            <img src={data.getDogByBreed.displayImage} />
        </div>
      )
    }}
  </Query>
)

export default DogCatalog;

what we just did:
– we created const GET_DOG query and we ‘asked’ GraphQL to getDogByBreed where breed = labrador (for more information of what queries you could execute, you could go to http://localhost:4001/graphql and look at the right side where the schema is defined or read more here)
– we wrapped our new component in Query tag, which passes down three properties: loading, error and data
– we showed the appropriate messages depending of the status of loading and error and on success we rendered the component, showing the returned data

Fire up the server using you prefered method, and  navigate to http://localhost:8080/dogs  and enjoy your new component, showing an image of nice black Labrador!

Refactoring the component.

Move the GraphQL query to a separate file.

Keeping things well organized is very important for maintainability and scaling reasons, so let’s move the query in a separate file.

./src/containers/DogsCatalog/index.js

import React from 'react';
import { graphql } from 'react-apollo';
import query from './query';


const DogCatalog = (props) => {
  if(typeof props.data.getDogByBreed === 'undefined') {
    return (
      <p>Loading ...</p>
    );
  }
  return(
    <div>
        <span>breed: </span>
        <span>{props.data.getDogByBreed.breed}</span>
        <br />
        <img src={props.data.getDogByBreed.displayImage} />
    </div>
  );
}

let breed = 'labrador';

export default graphql(query, {
  options: {
    variables: { 
      breed: breed
    } 
  } 
})(DogCatalog);

Create query.js file with the query.

./src/containers/DogsCatalog/query.js

import gql from 'graphql-tag';

const query = gql`
  query getDogByBreed($breed: String) 
  {
    getDogByBreed(breed: $breed) {
      id
      breed
      displayImage
    }
  }
`
export default query;

 what we just did:
– we moved the query to a separate file query.js
– we include the query (line 3)
– we wrapped the DogsCatalog component into GraphQL component, which will execute the query once the component is mounted, and will pass the result back into props.data .

Adding high order component with dogs breed buttons grid.

What we just created is more like a Dog Details component than Dog catalog, so let’s create a new folder under ./src/containers/DogsCatalog and name it DogDetails, and move  ./src/containers/DogsCatalog/index.js and ./src/containers/DogsCatalog/query.js to the new DogDetails folder.

Then create a new ./src/containers/DogsCatalog/index.js file which will show a gird with buttons, from where users can select which dog breed should be displayed into DogDetails component. Then we will pass the selected breed to the DogDetails component.

./src/containers/DogsCatalog/index.js

import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import DogDetails from './DogDetails';
import query from './query';


class DogsCatalog extends Component {

  constructor(props) {
    super(props);
    this.state = {
        breed: "labrador"
    }
  }

  handleClick(breedType) {
    this.setState({
        breed: breedType
    });
  }

  render() {  
    if(typeof this.props.data.getDogsList == 'undefined') {
      return(
        <div>Loading ... </div>
      );
    }
    return(
      <div>
        <p>Dogs catalog</p>
        <div>
          {this.props.data.getDogsList.map( (dog) => {
            return (<button key={dog.id} onClick={ () => { this.handleClick(dog.breed) } }>{dog.breed}</button>);
          })}          
        </div>
        <DogDetails breed={this.state.breed} />
      </div>
    );
  }
}


export default graphql(query, {})(DogsCatalog);

 

./src/containers/DogsCatalog/query.js

import gql from 'graphql-tag';

const query = gql`
query getDogsList
{
  getDogsList {
    id
    breed
  }
}
`
export default query;

 

./src/containers/DogsCatalog/DogDetails/index.js

import React from 'react';
import { graphql } from 'react-apollo';
import query from './query';


const DogDetails = (props) => {
  if(typeof props.data.getDogByBreed === 'undefined') {
    return (
      <p>Loading ...</p>
    );
  }
  return(
    <div>
        <span>breed: </span>
        <span>{props.data.getDogByBreed.breed}</span>
        <br />
        <img src={props.data.getDogByBreed.displayImage} />
    </div>
  );
}

let breed = 'labrador';

export default graphql(query, {
  justADumFunction(ThePropsValuesPassedIntoTheFunction) {
    return {
      variables: {
        breed: ThePropsValuesPassedIntoTheFunction.breed,
      },
    };
  },
})(DogDetails);

 

./src/containers/DogsCatalog/DogDetails/query.js

import gql from 'graphql-tag';

const query = gql`
  query getDogByBreed($breed: String) 
  {
    getDogByBreed(breed: $breed) {
      id
      breed
      displayImage
    }
  }
`
export default query;

We could have download all dog details and pass it to the DogDetails component and save the second query, but for the purpose of exercising working with queries we are not going to do this. Besides imagine that we might need a lot more data for each breed then it’s not wise to pre-load it since this might take significant amount of time.

 

Beautify the component with SASS.

Let’s add some css.

create new file

./src/containers/DogsCatalog/styles.scss

.Wrapper {
  img {
    width: 100%;
  }
  .Buttons {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    grid-gap: 8px;
    max-width: 1200px;
    width: 100%;
    margin: 0 auto;    
  }

  .Buttons > button {
    background: efefef;
    border-radius: 5px;
    transition: 0.5s;
  }

  .Buttons > button:hover {  
    background: silver;
    color:white;
    cursor: pointer;
  }

}

and edit dogs component adding the css styles:

./src/containers/DogsCatalog/index.js

/* eslint-disable no-debugger */

import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import DogDetails from './DogDetails';
import query from './query';

const styles = require('./styles.scss');


class DogsCatalog extends Component {

  constructor(props) {
    super(props);
    this.state = {
        breed: "labrador"
    }
  }

  handleClick(breedType) {
    this.setState({
        breed: breedType
    });
  }

  render() {  
    if(typeof this.props.data.getDogsList == 'undefined') {
      return(
        <div>Loading ... </div>
      );
    }
    return(
      <div className={styles.Wrapper}>
        <p>Dogs catalog</p>
        <div className={styles.Buttons}>
          {this.props.data.getDogsList.map( (dog) => {
            return (<button key={dog.id} onClick={ () => { this.handleClick(dog.breed) } }>{dog.breed}</button>);
          })}          
        </div>
        <DogDetails breed={this.state.breed} />
      </div>
    );
  }
}


export default graphql(query, {})(DogsCatalog);

Now our component looks much better.

We could have also move the presentational layer to the components instead, but since this is pretty small amount of code, we are not going to complicate it and will keep the presentational layer here for now.

branch-name:  
Click To Copy

 

 

 

Adding routing

branch-name:  
Click To Copy

Set up Express server to route all requests to the default ./index.html 

  • For CLI instance this could be achieved by just passing –history-api-fallback in package.json file.
    Give it a try: fire up the project using the cli settings yarn start-cli and go to http://127.0.0.1:8080/test for example and you will see the same homepage no matter what is the url.

./package.json

"scripts": {
  "start-cli": "webpack-dev-server --hot --history-api-fallback",
  "start-api": "babel-node server-api.js",
  "start-middleware": "babel-node server-middleware.js",
  "clean": "rm -rf ./dist",
  "lint": "eslint .",
  "build-dev": "webpack --mode development",
  "build-prod": "webpack --mode production"
},
  • For server using API config we are adding historyApiFallback: true (line 12) which basically tells the the dev server to fallback to the default file if it can’t find a route.

./server-api.js

/**
 * Runs a webpack-dev-server, using the API.
 */
import WebpackDevServer from 'webpack-dev-server';
import webpack from 'webpack';
import config from './webpack.api.config.js';

const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
  hot: true,
  publicPath: config.output.publicPath,
  historyApiFallback: true
});
server.listen(8080, 'localhost', function() {});
  • For the dev-middleware it a bit different approach since we are no longer using the built in express server in Webpack-dev-server since we have Express server already in place. So the solution is to tell Express server to accept all requests ‘*’ and to server the same index.html for all requests (line 18)

./server-middleware.js

const express = require('express');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpack = require('webpack');
const webpackConfig = require('./webpack.middleware.config.js');
const app = express();
const path = require('path');
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
  hot: true,
  publicPath: webpackConfig.output.publicPath,
}));
app.use(webpackHotMiddleware(compiler, {
  log: console.log,
  path: '/__webpack_hmr',
  heartbeat: 10 * 1000,
}));
app.get('*', function(req, res) {
  res.sendFile(path.join(__dirname, 'index.html'));
});
const server = app.listen(8080, function() {
  const host = server.address().address;
  const port = server.address().port;
  console.log('App is listening at http://%s:%s', host, port);
});

We did this for all server configurations, but at the end we could just stick with only one, but for now we demonstrate how to set up all three configurations.

Let’s continue with setting up React router.

Setting up React router.

Install React-router-dom

yarn add react-router-dom

Before we continue, let’s add another component so we have two URLs to navigate to.

mkdir ./src/Components/About

and create a simple component that will display a message.

./src/Components/About/index.js

import React from 'react';


const About = () => (
  <div>
    <div>Wellcome to my tutorial!</div>
  </div>
)

export default About;

Now let’s move to adding the react router.

There are different palaces where we could ‘wrap’ our project components with Express router component. We could do it in ./src/index.js but I personally prefer to keep ./src/index.js as simple as possible and would move all complex logic into ./src/App/index.js 

./src/App/index.js

import React, { Component } from 'react';
import Greetings from '../Greetings';
import About from '../About';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import styles from './styles.scss';
export default class App extends Component {
  render() {
    return (
      <div className={styles.appWrapper}>
        <Router>
          <Switch>
            <Route exact path="/greetings" component={() => <Greetings user="John"/> } />
            <Route exact path="/about" component={About} />
          </Switch>
        </Router>
      </div>        
    );
  }
}

what we just did:
– we added React router and Switch module.
– also we added two routes: ‘/greetings’, and ‘/about’ which will open the corresponding components.
– we wrapped ‘Greetings’ component into an inline function that passes the user parameter, otherwise if you try passing it into the route <Route user="John ... React router will simply ignore it.

Wrapping the component into an inline function to pass parameters will work but is not the best approach. The reason for this is because of performance. According to the official docs…

  “When you use the component props, the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component attribute, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component.”

So what is the better solution? Instead of using component, use the renderprop. render accepts a functional component and that function won’t get unnecessarily remounted like with component. That function will also receive all the same props that component would receive. So you can take those and pass them along to the rendered component.

Let’s fix this:

./src/App/index.js

import React, { Component } from 'react';
import Greetings from '../Greetings';
import About from '../About';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import styles from './styles.scss';
export default class App extends Component {
  render() {
    return (
      <div className={styles.appWrapper}>
        <Router>
          <Switch>
            <Route exact path="/greetings" render={(props) => <Greetings {...props} user="John" />} />
            <Route exact path="/about" component={About} />
          </Switch>
        </Router>
      </div>        
    );
  }
}

Give it a try and navigate to http://localhost:8080/about  and http://localhost:8080/greetings

Great! Now we have React router set up with  two pages opening two different components.

Adding Navigation

Let’s create a component that will serve as a navigation menu mkdir ./src/components/Header

./src/components/Header/index.js

import React from 'react';
import { Link } from 'react-router-dom';

const styles = require('./styles.scss');

const Header = ( {title} ) => (
  <div>
    <div className={styles.wrapper}>
      <ul>
        <li><Link to='/home'>HOME</Link></li>
        <li><Link to='/greetings'>GREETINGS</Link></li>       
        <li><Link to='/about'>ABOUT</Link></li>
      </ul>
    </div>
  </div>
);

export default Header;

and let’s add styles:

./src/components/Header/styles.scss

.wrapper {
  
  ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    border: 1px solid #e7e7e7;
    background-color: #f3f3f3;
  }
  li {
    float: left;
  }

  li a {
    display: block;
    color: #666;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
  }

  li a:hover:not(.active) {
      background-color: #ddd;
  }

  li a.active {
      color: white;
    
  }
}

 

We used React Link component to allow users to navigate through the site.

Now put the component inside the <Router> component.

./src/components/App/index.js

import React, { Component } from 'react';
import Home from '../Home';
import Greetings from '../Greetings';
import About from '../About';
import Header from '../Header';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import styles from './styles.scss';
export default class App extends Component {
  render() {
    return (
      <div className={styles.appWrapper}>
        <Router>
          <Header />
          <Switch>
            <Route exact path="/home" component={Home} />            
            <Route exact path="/greetings" render={(props) => <Greetings {...props} user="John" />} />
            <Route exact path="/about" component={About} />
          </Switch>
        </Router>
      </div>        
    );
  }
}

You probably noticed that we have 2 pages but the navigation has 3 links. Let’s add another component just to have more pages to navigate through, besides we will need this component later in this tutorial  mkdir ./src/components/Home

./src/components/Home/index.js

import React from 'react';

const styles = require('./styles.scss');

const Home = () => (
  <div>
    <div className={styles.wrapper}>This is my home section!</div>
  </div>
)

export default Home;

./src/components/Home/styles.scss

.wrapper {
  background: rgb(141, 141, 172);
  color: white;
  text-align: center;
  font-family: MyFont;
}

Start the server and give it a try!

branch-name:  
Click To Copy

 

 

Three different ways to set up the project

branch-name:  
Click To Copy

 

So far we explored setting our project using  Webpack Dev Server’s Command line Interface (CLI) Let’s explore the other two options and create scripts to run them.

Three ways of setting up Webpack

Edit package.json ‘scripts’ tag and add start script for each setup:

./package.json

  "scripts": {
    "start-cli": "webpack-dev-server --hot",
    "start-api": "babel-node server-api.js",
    "start-middleware": "node server-middleware.js",
    "clean": "rm -rf ./dist",
    "lint": "eslint .",
    "build-dev": "webpack --mode development",
    "build-prod": "webpack --mode production"
  }

What we just did:
– We added ‘start-cli’ script to replace ‘start’ script and to run Webpack-dev-server with Command Line Interface (the setup that we did so far)
– we added ‘start-api’ to run the project using Webpack, and Webpack-dev-server API instead.
– we added ‘startup-middleware’ to use Webpack-Dev-Server middleware.

Command line Interface (CLI)

We already have this set up and we changed the ‘start’ script tag to be ‘start-cli’ instead, but everything else is the same, but to enhance the functionality (like been able to parse scss) we will pass a config file.

./package.json

...
  "scripts": {
    "start-cli": "webpack-dev-server --hot --history-api-fallback --config webpack.cli.config.js",
...

and let’s create the cli config and since it will be the exact copy of webpack.config.js you could just rename webpack.config.js the file to be webpack.cli.config.js

./webpack.cli.config.js

const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',    
    './src/index.js'    
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },

      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      },
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      },
      //File loader used to load fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }                    
    ]
  }
};

Give it a try by running yarn start-cli and everything will work like before.

Webpack Dev Server API

Create server-api.js with the following contents:

./server-api.js

const WebpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.api.config.js');

const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
  hot: true,
  publicPath: config.output.publicPath
});
server.listen(8080, 'localhost', function() {});

What we just did is pretty simple and it’s the same that we did for CLI but just using the APIs insted:
– we added Webpack-dev-server instance (line 1) and then we configured it with ‘hot: true’ param to enagle HMR.
– and ‘publicPath: config.output.publicPath’ which will take the parameter from the config file, and pass it to the dev server.
– we also added Webpack (line 2) which will be used as a compiler for Webpack-dev-server (line 6)

Also we have to pass a few extra parameters to webpack.config.js so let’s create a new config file, and name it:

./webpack.api.config.js

const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',    
    './src/index.js',
    'webpack/hot/dev-server',
    'webpack-dev-server/client?http://localhost:8080/',       
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],   
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },

      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      },
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      },
      //File loader used to load fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }                    
    ]
  }
};

 

The config is almost the same like webpack.config.js with 2 extra parameters into the ”entry’ point:
– we added ‘webpack/hot/dev-server’ in order to activate HMR, and
– webpack-dev-server/client?http://localhost:8080/ specifying the client domain and port.

Also we added a plugins section and loaded HotModuleReplacementPlugin.

Webpack Dev Middleware

This is the most advanced setup and it uses Express web server with Webpack Dev Middleware. So far we used the builtin Express server from Webpack-dev-server, but now we will use standalone express server. Let’s install it first. And we will need it later not only in development mode so let’s skip ‘–dev’.

yarn add express

yarn add webpack-hot-middleware --dev

create the server file

./server-middleware.js

const express = require('express');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpack = require('webpack');
const webpackConfig = require('./webpack.middleware.config.js');
const app = express();
const path = require('path');

const compiler = webpack(webpackConfig);

app.use(webpackDevMiddleware(compiler, {
  hot: true,
  publicPath: webpackConfig.output.publicPath,
}));

app.use(webpackHotMiddleware(compiler, {
  log: console.log,
  path: '/__webpack_hmr',
  heartbeat: 10 * 1000,
}));

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname, 'index.html'));
});

const server = app.listen(8080, function() {
  const host = server.address().address;
  const port = server.address().port;

  console.log('App is listening at http://%s:%s', host, port);
});

we are using express server with ‘webpack-dev-middleware’,

and the config file

./webpack.middleware.config.js

const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',    
    './src/index.js',
    'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000',     
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],   
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },

      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      },
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      },
      //File loader used to load fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }                    
    ]
  }
};

 

 Give it a try by executing yarn start-middleware and you will see the same result like in the previous two set ups. Site is running and hot module reloader is enabled.

Running server scripts using latest ES syntax

If you noticed we are using `import … from …` syntax everywhere under ./src folder instead of `const module = require(‘…’)since we set up Babel to transpile everything under this folder to the latest ES6 syntax.

But if we try to use this in some of the ./server.api.config.js or ./server.middleware.config.js it will fail since there is nothing to transpile these files to the latest ES6 yet. Fortunately there is very straight forward solution. Let’s use Babel-node to run the scripts instead of node. Like it says in the Babel-node website:

 “babel-node is a CLI that works exactly the same as the Node.js CLI, with the added benefit of compiling with Babel presets and plugins before running it.”

Perfect for our needs. Now all we need to do in order to have the benefit of EC6 in our server scripts is to replace node instance with babel-node and run scripts using babel-node instead.

But first let’s install babel-node

yarn add @babel/node --dev

then edit package.json and replace all instances of node with babel-node

./package.json

...
"scripts": {
    "start-cli": "webpack-dev-server --hot --history-api-fallback --config webpack.cli.config.js",
    "start-api": "babel-node server-api.js",
    "start-middleware": "babel-node server-middleware.js",
    "clean": "rm -rf ./dist",
...

now it should be perfectly safe to start using latest ES6 into our server scripts. We could go ahead and change all server side scripts to use import ... from syntax.

Now we have our project set up to use EC6 on the front-end and the back end as well!

Adding base Webpack config.

In the previous chapter we created three different ways of running our project:

  • Through Webpack-dev-server Command line interface (CLI) which is using `
    yarn start-cli
    which uses: webpack.cli.config.js
  • Through Webpack-dev-server API
    yarn start-api
    which uses: webpack.api.config.js
  • Through Webpack-dev-server middleware
    yarn start-middleware
    which uses: webpack.midleware.config

but if we look at the config files we will notice that most of the parameters are the same. It might make more sense if we create one base config and override only the parameters that are different in these 3 config files. Let’s do this:
– copy the contents of webpack.api.config.js and create a new file called webpack.base.config

./webpack.base.config.js

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',    
    './src/index.js',
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },

      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      },
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      },
      //File loader used to load fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }                    
    ]
  }
};

edit all three config files to look like this:

./webpack.cli.config.js, ./webpack.api.config, ./webpack.midleware.config

let config = require('./webpack.base.config.js');

module.exports = config;

Let’s give it a try and see if HMR still works by edit something like ./src/components/Greetings/index.js for example, and add some extra character inside the H2 tag and observe if the changes will apply

<h2>Hello, {props.user}</h2>

  • Starting with CLI
    yarn start-cli
    looks good
  • Starting with Dev server API
    yarn start-api
    looks good but HMR doesn’t work
  • Starting with Dev middleware
    yarn start-midleware
    also looks good but HMR is broken as well.

Looks like HMR works only for the CLI setup because we are not adding HotModuleReplacement plugin required for the other two set ups. Let’s fix this:

./webpack.api.config

const webpack = require('webpack');
let config = require('./webpack.base.config.js');

config.entry = [
  '@babel/polyfill',    
  './src/index.js',
  'webpack/hot/dev-server',
  'webpack-dev-server/client?http://localhost:8080/',       
];

config.plugins = [
  new webpack.HotModuleReplacementPlugin(),
];

module.exports = config;

./webpack.middeware.config

const webpack = require('webpack');
let config = require('./webpack.base.config.js');

config.entry = [
  '@babel/polyfill',    
  './src/index.js',
  'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000',        
];

config.plugins = [
  new webpack.HotModuleReplacementPlugin(),
];

module.exports = config;

now give it a try and HTR should work just fine.

There is another way to achieve this by using webpack-merge module, but I personally don’t like to add extra module if I could achieve the same result with plain JavaScript.

Adding source map for easy debugging

let’s go to ./src/components/greetings/index.js  and intentionally made an error

function Greetings(props) {
  return(<div className={styles.wrapper}>
          <h2>Hello, {props.user.test}</h2>
        </div>);

 

Run the server and look at the console. You will see something similar as this:

}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h2", null, "Hello ", props.user.test)));

This doesn’t show you where the error actually is in the original file but it shows the error in the bundle. Let’s fix this by adding ‘source-mapping‘ in our webpack.base.config

module.exports = {
  mode: 'development',
  devtool: 'source-map',

 Restart the sever and check the console: now the error points to the original file in ./src/components/greetings/index.js

make sure that before we continue you go and remove .test so the component will not break.

branch-name:  
Click To Copy

 

 

Adding React Library, SASS, Images and font loaders

branch-name:  
Click To Copy

 

React is cool frontend library that updates only the necessary portions of the html. Let’s add it to the project.

Adding React library

yarn add react react-dom

First, let’s keep things more consistent and do some house cleaning. Let’s rename ./src/app.js to ./src/index.js This will be the place where React app will be attached to our HTML and remove ./src/greeting.js

Also change Webpack entering point to reflect app.js filename change:

./webpack.config.js

entry: [
  './src/index.js'
],

Optionally but not necessarily  change the app.js to index.js in the package.json. It’s always a good practice to keep things consistent.

Alternatively you could simply ommit the name of the entrance and Webpack by default will look for src/index.js

Now Let’s do a react app. Remove ./src/app.js and ./src/greeting folder and let’s

Create simple react app

and place it into the components folder that we will create.

mkdir -p src/components/App

and add it’s index file

./src/components/App/index.js

import React, { Component } from 'react';


export default class App extends Component {

  render() {
    return (
      <div>
        <h1>React is running</h1>
      </div>
    );
  }
}

Attach React app to the main html

edit ./src/index.js remove it’s test contents and replace it with this:

./src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

ReactDOM.render(<App/>, document.getElementById('root'));

if (module.hot) {
  module.hot.accept();
}

What we just did:
– we imported React and React dom library. React dom is needed to attach React app to the HTML
– we imported newly created React app
– we attached the app to the root container.
– we also added the HMR activation script.

One last thing before we are ready to test is to add the html container where the react app will be attached.

./index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Babel Webpack Boilerplate</title>
    </head>
    <body>
        <h1>Babel Webpack React Boilerplate</h1>
        <div id="root"></div>
        <script type="text/javascript" src="dist/main-bundle.js"></script>
    </body>
</html>

 

Start the project yarn start

Oh snap! Dev server crashed with error message:
ERROR in ./src/index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: /Users/toninichev/Examples/WebpackReactReduxApolloTutorial/.babelrc: Error while parsing config – JSON5: invalid end of input at 1:1

Why this is happening? We replaced the plain JavaScript form ./src/index.js with JSX syntax that react is using but Babel doesn’t know how to transpile this. Let’s fix it and add in .babelrc config, the react preset plug-in, and install it.

yarn install @babel/preset-react --dev

./.babelrc

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

Right now we need only @babel/preset-react” and @babel/preset/env plug-ins, but in the feature we will have to use more of the advanced ES6 syntax and add more.

Give it another shot and do yarn start

At this point we should have working react app!

Create React component

Let’s re-create ‘greetings’ component but this time making it React component. Create Greetings folder  mkdir -p ./src/components/Greetings folder and add:

./src/components/Greetings/index.js

import React from 'react';

function Greetings(props) {
  return <div>Hello, {props.user}</div>;
}

export default Greetings;

and render the component.

./src/components/App/index.js

import React, { Component } from 'react';
import Greetings from '../Greetings';

export default class App extends Component {

  render() {
    return (
      <div>
        <h1>React is running</h1>
        <Greetings user="John" />
      </div>
    );
  }
}

What we just did:
– in ./src/greetings/index.js we created a new Greetings component that will take property of the user passed from the higher component and show it. Props stands short for properties.
– in ./src/components/app/index.js we are loading the Greetings component and passing the user name as a property to the Greeting component.

Webpack loaders.

Webpack loaders  allow us to bundle any static resource way beyond JavaScript.

Using SCSS and adding SASS loader.

So far so good. We could see our new React component rendering but no styles.
The simplest way to style our components could be to add css-loader and then to inject the css into the DOM using style-loader.

...
    {
      test: /\.(s)?css$/,
      use: [
        'style-loader',
        'css-loader'
      ]
    },
...

 

But it could be really nice if we could add specific styles only applying to this component.

Go to the terminal, and install these modules:

yarn add style-loader --dev
yarn add css-loader --dev
yarn add postcss-loader --dev
yarn add sass-loader --dev
yarn add node-sass --dev
yarn add autoprefixer --dev

Add the highlighted lines in webpack.config.js

./webpack.config.js

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',    
    './src/index.js'
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true

            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      }      
    ]
  }
};

 

 what we just did:
– added sass-loader which will first convert SCSS to plain CSS
– added postcss-loader interprets @import and url() like import/require() and will resolve them.
– added style-loader simply inserts the css into the DOM

Let’s add some styling for our new greetings component:

./src/components/Greetings/styles.scss

.wrapper {
  background: blue;
  h2 {
    color: white;
  }
}

and let’s load it and style the component:

./src/components/Greetings/index.js

import React from 'react';

const styles = require('./styles.scss');

function Greetings(props) {
  return <div className={styles.wrapper}><h2>Hello, {props.user}</h2></div>;
}

export default Greetings;

What we just did:
– we added the new style that will be loaded by JavaScript into the new component (line 3)
– we applied the new style (line 6) by using JSX way of doing this.

If you are not familiar with React and JSX please read the React tutorial first.

Congratulations! Now you have React component that could really be in use!

Adding image loader.

Let’s add url-loader and file-loader and save them as a dev dependencies.

yarn add url-loader file-loader --save

Download a small Home Icon that we are going to add it to our greeting component, and save it into a new folder under ./src folder./src/images/home.png (Make sure that you renamed the file to home.png ). Now let’s add the Webpack config to load images:

./webpack.config.js

import webpack from 'webpack';
import getEnvironmentConstants from './getEnvironmentConstants';

module.exports = {
  mode: 'development',
  devtool: 'eval-source-map',
  entry: [
    '@babel/polyfill',
    './src/index.js',
    'webpack/hot/dev-server',
    'webpack-dev-server/client?http://localhost:8080/',
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  }, 
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.DefinePlugin({ 'process.env' : getEnvironmentConstants() } )
  ],   
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },


      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: false
            }
          }
        ],
      }, 
      
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      }      
      
    ],
  }
};

 what we just did:
– we are testing files for image type (line 64) (png, gpeg or gpg, svg)
– we are using url-loader to load the images.
– url-loader – limit: 8000 means that any image smaller than 8kb will be converted to base64 string and inlined in the bundle. Any other image will be passed to the loader that will pass it as a file.

An now let’s use the image in our Greetings component:

./src/components/Greetings.js

import React from 'react';
const styles = require('./styles.scss');
import homeIcon from '../../images/home.png';

function Greetings(props) {

  return (<div className={styles.wrapper}>
            <img height='75px' width='75px' src={homeIcon} /> 
            <h2>Hello, {props.user}</h2>
          </div>);
}
export default Greetings;

Navigate to the greeting component, and we should see the house image there.

Let’s also check if we can add the image url in the CSS. Remove the image tag from the file above, and add it in the greeting CSS

./src/components/Greetings/styles.scss

.wrapper {
  background-image:url('../../images/home.png');
  height: 500px;
  h2 {
    color: black;
  }
}

navigate to the page again and you will see a big background house in the component.

Adding fonts loader.

Loading fonts is as easy as adding another file-loader as it is shown below.

./webpack.config.js

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',    
    './src/index.js'
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },

      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      },
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      },
      //File loader used to load fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }                    
    ]
  }
};

then we could download some font for example from fonts.google.com or here and add the font in the css using font face:

./src/components/App/styles.scss

@font-face {
  font-family: 'MyFont';
  src:  url('../../fonts/aclonica/aclonica-regular.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
}

.appWrapper {
  background: rgb(141, 141, 172);
  color: white;
  text-align: center;
  font-family: MyFont;
}

 

Modifying component to use latest ES syntax.

We want to be cool devs and to use the latest technologies so let’s modify our Greeting component to use the latest ES6 syntax and take advantage of Babel that we installed in the previous chapters.

Modify greetings index file as follows:

./src/components/Greetings/index.js

import React from 'react';
import styles from './styles.scss';

function Greetings(props) {
  return(<div className={styles.wrapper}>
          <h2>Hello, {props.user}</h2>
        </div>);
}
export default Greetings;

What we just did:
– (line 2) we refactored the component to use the latest EC6 syntax by replacing require with import
– we removed the image tag since now we are loading the image-background from CSS.

 

branch-name:  
Click To Copy

 

Adding Webpack-dev-server

branch-name:  
Click To Copy

Ading Webpack-dev-server

what is webpackdevserver ? Like it was said  “The webpackdevserver is a little Node.js Express server, which uses the webpackdev-middleware to serve a webpack bundle. It also has a little runtime script, which is connected to the server via Sock.js. The server emits information about the compilation state to the client, which reacts to those events.

Basically webpack-dev-server is handy dev tool that is not only an HTTP server but it could monitor your files for changes and rebuild the bundle and serve it from memory which speeds up the developing process.

Let’s add webpack-dev-server so we could open the project via http. Additionally  Webpack dev server adds a few more benefits like live reloading for the client side code.
Webpack dev server like it’s name suggests should be used only for development and never in production.

yarn add webpack-dev-server --dev

Let’s add a script to execute it through Yarn/NPM. Also, since we have the entry and the output set up in webpack.config.js we can simplify the build-dev and build-prod scripts as well.

  "scripts": {
    "start": "webpack-dev-server",
    "clean": "rm -rf ./dist",
    "lint": "eslint .",
    "build-dev": "webpack --mode development",
    "build-prod": "webpack --mode production"
  }

Let’s give it a try and start the server:

yarn start

and point the browser to http://localhost:8080/

If everything worked fine, you should be able to see the project and the console message.

But bundle file is still served statically from the ./dist` folder instead of bundled dynamically from Webpack-dev-server and serving it from memory,  and you could confirm this by deleting the ./dist folder, and restarting the server. And you will get this error message in the console.

GET http://localhost:8080/dist/main-bundle.js net::ERR_ABORTED 404 (Not Found)

As you see bundle file is not there, instead Webpack-dev-server is serving it from here:http://localhost:8080/main-bundle.js
Point your browser there and you will see the bundle file. Let’s fix this. Open ./webpack.config.js and add the highlighted lines:

./webpack.config.js

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',
    './src/app.js'
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

This instructs Webpack-dev-server to serve the bundle from ./dist location but this time the bundle will be dynamically generated by Webpack-dev-server, instead of statically served from the ./dist location.

The advantage is that the bundle will re-generate on change and the location will be the same as the production location but just served from the memory.
Reload the browser and now the error message in the console is gone, and if you navigate to `http://localhost:8080/dist/main-bundle.js` you will see newly generated bundle.

 Webpack-dev-server is serving the bundle from memory. You could go ahed and delete ./dist folder and reload the server and the main bundle should still be accessible.

Set up Webpack to monitor your project’s folder for changes and rebuild the bundle.

Webpack-dev-server comes with very handy option --watch to monitor for changes, rebuild the bundle and restart the browser.
The god news is that by default the --watch` mode it turned on so if you go and do some changes into any file under ./src folder and save it, you will see in the console that Webpack-dev-server will re-generate the bundles and the browser will restart and reflect the changes.

There are two ways of setting up Webpack-dev-server to reload the page:

  • Iframe mode (page is embedded in an iframe and reloaded on change)
  • Inline mode (a small webpack-dev-server client entry is added to the bundle which refreshes the page on change)

But we could go even further.

Adding Hot Module Replacement plug-in.

Having Webpack-dev-server to reload our page and reflect the changes is cool but we still reload the whole page and components state is reset back to the initial state. We could tell Webpack-dev-server to use hot module replacement plugin (HMR) to re-load only the components that did change, but before doing this, let’s make some real component that will show something in the website instead of the console.

Remove ./src/test.js.

Let’s create greeting component that will simply show a greeting in the webpage.
create folder called ‘greeting’ and file in ./src/greeting/index.js with these contents:

./src/greeting/index.js

function doGreeting(name) {
  return "Hello " + name;
}
module.exports = doGreeting;

edit ./src/app.js to load the new component and attach the result to an HTML div component. We have to also add the highlighted if statement that will enable HMR.

./src/app.js

var greeting = require('./greeting');

var result = greeting("John");
document.querySelector('#root').innerHTML = result;


if (module.hot) {
  module.hot.accept();
}

and edit index.html to have a div component with id=”root” that  will be the entry point for our app and will show the result of our greeting component.

./index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Babel Webpack Boilerplate</title>
    </head>
    <body>
        <div id="root"></div>
        <script type="text/javascript" src="dist/main-bundle.js"></script>
    </body>
</html>

Last step is to tell Webpack-dev-server to use HMR.

./package.json

{
  "name": "babel-webpack-react-boilerplate",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "babel-eslint": "^10.0.1",
    "babel-loader": "^8.0.4",
    "eslint": "^5.7.0",
    "eslint-loader": "^2.1.1",
    "eslint-plugin-react": "^7.11.1",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.9"
  },
  "scripts": {
    "start": "webpack-dev-server --hot",
    "clean": "rm -rf ./dist",
    "lint": "eslint .",
    "build-dev": "webpack --mode development",
    "build-prod": "webpack --mode production"
  }
}

what we just did:
– we added --hot parameter to tell Webpack-dev-server to use HMR.

Go ahead, restart the server and give it a try and now if you change something in ./src folder the browser will reflect the changes without reloading. (you could observe the reload button and it will never show reload). Also if you look at browser’s console you will see the messages from HMR

[HMR] Waiting for update signal from WDS…
client:71 [WDS] Hot Module Replacement enabled.

Now development process could really speed up!

branch-name:  
Click To Copy

 

Setting up SSL host using LetsEncrypt free certificate and set up auto renewal shell script.

1. Obtain SSL certificates from the letsencrypt.org ACME server.Navigate to the location that you would like to add the script. I personally prefer to keep apps and scripts here:
cd ~/Applications

2. Download getssl (this is the auto renewal shell script)
git clone https://github.com/srvrco/getssl.git

4. Do initial setup
 ./getssl -c yourdomain.com

this command will create the following folders and files:

~/.getssl
~/.getssl/getssl.cfg
~/.getssl/yourdomain.com
~/.getssl/yourdomain.com/getssl.cfg

5. Edit ~/.getssl/getssl.cfg There are a couple of things that we have to set up:
– make sure that you switch to use the staging server while testing, since the production one has rate limits.

# The staging server is best for testing
CA="https://acme-staging.api.letsencrypt.org"
# This server issues full certificates, however has rate limits
#CA="https://acme-v01.api.letsencrypt.org"

6.Add the sub domains in ~/.getssl/yourdomain.com/getssl.cfg

# Additional domains - this could be multiple domains / subdomains in a comma separated list
# Note: this is Additional domains - so should not include the primary domain.
SANS="www.mysite.com, other.mysite.com"

– Very important: add the location where the script should put verification file in order to prove that you have ownership over this domain. For more information of what exactly is the prove of ownership procedure you could read https://letsencrypt.org/how-it-works/ but basically the bash script puts a small file into specific server directory and then the letsencrypt server checks id the file is there and ensures that you have control over this domain.
So Make sure that this folder (acme-challenge) is accessible on the web.
What that means is that if you put a test text file (e.e. test.txt) with any random text inside in this location: /webroot/.well-known/acme-challenge and then you open the browser and point to www.mysite.com/.well-known/acme-challenge/test.txt you should be able to see the contents of the file.
Once you did this you could go ahead and edit .getssl file and add the right location.

# Acme Challenge Location. The first line for the domain, the following ones for each additional domain.
# If these start with ssh: then the next variable is assumed to be the hostname and the rest the location.
# An ssh key will be needed to provide you with access to the remote server.
# Optionally, you can specify a different userid for ssh/scp to use on the remote server before the @ sign.
# If left blank, the username on the local server will be used to authenticate against the remote server.
# If these start with ftp: then the next variables are ftpuserid:ftppassword:servername:ACL_location
# These should be of the form "/path/to/your/website/folder/.well-known/acme-challenge"
# where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain.
#ACL=('/var/www/toninichev.com/web/.well-known/acme-challenge'
#     'ssh:server5:/var/www/toninichev.com/web/.well-known/acme-challenge'
#     'ssh:sshuserid@server5:/var/www/toninichev.com/web/.well-known/acme-challenge'
#     'ftp:ftpuserid:ftppassword:toninichev.com:/web/.well-known/acme-challenge')

ACL=('/var/www/html/projects/src/webroot/.well-known/acme-challenge')

 Make sure that you replace /var/www/html/projects/src/ with the actual location on your server.
– one last thing that I did is to use single ACL to make my life easier

#Set USE_SINGLE_ACL="true" to use a single ACL for all checks
USE_SINGLE_ACL="true"

7. Execute the script and create certificate

~/Applications/getssl yourdomain.com

Again make sure that you navigate to the place where you did git clone of the script.

Example of adding certificate to an express server:

var options = {
    key: fs.readFileSync('/Users/toninichev/.getssl/mysite.com.key'),
    cert: fs.readFileSync('/Users/toninichev/.getssl/mysite.com.crt')
    };

/**
 * HTTP Server
 * Gets post requests from app_clients and sends data to the web_clients
 */
var app = https.createServer(options, function (request, response) {
...
});

Example of adding certificate to Apache virtual host:

<VirtualHost *:443>

    ServerName mywebsite.com
    ServerAlias www.mywebsite.com
    ServerAdmin info@mywebsite.com

    SSLEngine on
    SSLCertificateFile /Users/toninichev/.getssl/mywebsite.com/mywebsite.com.crt
    SSLCertificateKeyFile /Users/toninichev/.getssl/mywebsite.com/mywebsite.com.key

    ErrorLog "/private/var/log/apache2/mywebsite.com-error_log"
    CustomLog "/private/var/log/apache2/mywebsite.com-access_log" common

    SetEnv APPLICATION_ENV production
    DocumentRoot "/Users/toninichev/mywebsite/app/webroot"
    <Directory "/Users/toninichev/mywebsite/app/webroot">
        Options Indexes FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>