Chapter 1: Environment Setup and Introduction to UmiJS
UmiJS is Ant Financial's underlying frontend framework and an open source project for developing enterprise-class frontend applications. It's a robust framework you can combine with Ant Design to provide everything you need to build a modern user experience.
In this chapter, you will learn how to install and configure a project using UmiJS and Visual Studio Code (VSCode). You'll also understand the folder structure and main files of UmiJS. Then, you'll learn how to set fast navigation between pages using umi history and finally discover Umi UI, a visual option to interact with UmiJS and add components to your project.
We'll cover the following main topics:
- Setting up our environment and configuring UmiJS
- Understanding the UmiJS folder structure and its main files
- Exploring the Umi CLI and adding pages
- Understanding routing and navigation in UmiJS
- Using Umi UI
By the end of this chapter, you'll have learned everything you need to get started with developing your project and you will also know about the fundamental behavior of an UmiJS project and its configurations.
Technical requirements
To complete this chapter's exercises, you just need a computer with any OS (I recommend Ubuntu 20.04 or higher).
You can find the complete project in the Chapter01
folder in the GitHub repository available at the following link:
https://github.com/PacktPublishing/Enterprise-React-Development-with-UmiJs
Setting up our environment and configuring UmiJS
In this section, we'll install and configure VSCode, the EditorConfig extension, and the Prettier extension, and create our first UmiJS project.
Let's begin by installing a source code editor. You can use any editor that supports JavaScript and TypeScript, but I will use VSCode extensively in this book. It's a free editor with an integrated terminal and internal Git control that natively supports JavaScript, TypeScript, Node.js, and many extensions for other languages.
VSCode is available as a Snap package, and you can install it on Ubuntu by running the following command:
$ sudo snap install code ––classic
For Mac users, you can install it using Homebrew on macOS by running the following command:
$ brew install --cask visual-studio-code
If you are using Chocolatey on Windows, you can run the following command:
> choco install vscode
Alternatively, you can download the installer available at https://code.visualstudio.com/.
Important Note
You can find instructions on installing Homebrew on macOS at https://brew.sh/ and installing Chocolatey on Windows at https://chocolatey.org/install. If you are a Windows user, you can install Ubuntu in Windows Subsystem for Linux (WSL) and set up your project using common Linux commands. You can read more about WSL at https://docs.microsoft.com/en-us/windows/wsl/install.
Next, we need to install the dependencies required to develop with UmiJS. First, let's install Node.js by typing and running the following commands in the terminal:
$ sudo apt update $ sudo apt install nodejs -y
The first command updates the mirrors, and the second command installs Node.js with the -y
flag, which skips user confirmation to install.
You can install Node.js using Homebrew on macOS by running the following command:
$ brew install node
If you are using Chocolatey on Windows, you can run the following command:
> choco install nodejs
Alternatively, you can download the installer available at https://nodejs.org/en/.
Node.js has a default package manager named npm, but we will extensively use Yarn instead of npm in this book, so I recommend installing it. You can do that by running the following command in the terminal:
$ npm install -g yarn
This command will install Yarn globally in your system.
With that, we are ready to get started with UmiJS. But first, let's understand UmiJS a bit more and what kinds of problems it can solve.
Introduction to UmiJS and creating your first project
UmiJS is a framework for developing enterprise-class frontend applications. This means Umi provides a set of tools for solving everyday problems faced when building large business applications that need to deliver a modern user experience and must be easy to maintain and modify.
With Umi, you can quickly develop an application with internationalization, permissions, and beautiful interfaces taking advantage of Umi's deep integration with Ant Design.
Another significant advantage of Umi is that there are a variety of published plugins you can add to your project as you need. You can also extend it by developing your own plugins to meet specific solutions.
Now that you know more about Umi, let's create your first project by following these steps:
- Create a new folder for the project and open it in the terminal:
$ mkdir umi-app; cd umi-app
- Create a new project using the
umi-app
template:$ yarn create @umijs/umi-app
- Install the project dependencies by running the following command:
$ yarn
- Start the project by running the following command:
$ yarn start
We now have a project set up! You can open it by typing http://localhost:8000
in the browser and see the result.
Let's do the final configurations to simplify our work by adding code formatting.
Installing the EditorConfig and Prettier extensions
One of the tools UmiJS provides by default in the umi-app
template is EditorConfig, a file format that editors read to define the code style across IDEs and text editors. You'll learn more about code style in Chapter 5, Code Style and Formatting Tools. Some editors and IDEs offer native support to EditorConfig, while in other cases, such as VSCode, you need to install a plugin, so let's install it by following these steps:
- Type the following command and press Enter to install the official extension for EditorConfig support:
ext install EditorConfig.EditorConfig
The umi-app
template comes preinstalled with Prettier, which is preconfigured for formatting the code. You can use it by running the yarn prettier
command. Still, a better option is to let VSCode format it for you when you save changes or paste code blocks.
For that, we need to install the Prettier extension and configure it as the default code formatter. To install and configure the Prettier extension, follow these steps:
- Press Ctrl + P and type the following command, then press Enter to install the official extension for Prettier support:
ext install esbenp.prettier-vscode
- Next, press Ctrl + , to open the VSCode preferences, and in the search field, type
formatter
and press Enter. - Under Editor: Default Formatter, select Prettier - Code formatter.
- Check the Editor: Format On Paste and Editor: Format On Save options, as shown in the following screenshot:
In this section, we learned how to configure our environment, learned more about UmiJS, and created our first project. Now, let's take a closer look at the project structure.
Understanding the UmiJS folder structure and its main files
In this section, you will understand the UmiJS folder structure, and you will add some essential configurations to files and folders.
The project we create based on the umi-app
template generates a set of folders with responsibilities for different parts of the project. Let's see what each one does:
mock
: In this folder, we store our simulated endpoints definitions to generate a mock API that we can interact with while developing the frontend.src
: This is the source folder where all our components are located.src/.umi
: This folder is automatically generated by UmiJS every time the project is compiled and contains its internal configurations.src/pages
: The React components responsible for rendering the pages in response to configured routes are located in this folder.
These are the folders included with the umi-app
template, but there are other essential folders in a UmiJS project, so let's add them.
The first folder we'll add is config
.
Adding config and locales folders
In the root folder of our project, we have a file named .umirc.ts
. This file contains the configuration for Umi and its plugins. When your project is compact, it's a good choice, but as it grows and becomes complex, the configuration file can become hard to maintain. To avoid that, we can break down our configuration into different parts located in the config
folder. Let's do this now by opening your project in VSCode and following these steps:
- In the root directory of your project, create a new folder named
config
.
You can do that by clicking on the icon in the upper-right corner above the folders list.
- Move the
.umirc.ts
file to theconfig
folder and rename itconfig.ts
.
You can rename a file by selecting it and pressing F2.
- In the
config
folder, create a new file namedroutes.ts
. In this file, we'll configure the application's routes.
You can do that by clicking on the icon in the top-right corner, above the folders list.
- Paste this code into the
routes.ts
file and save:export default [ { path: '/', component: '@/pages/index', }, ];
This code defines the root path ('/'
) to render the component index located in the pages
folder.
- Now we can import the
routes.ts
file intoconfig.ts
and add this line to theconfig.ts
file:import routes from './routes';
We can then rewrite the route section to use it as follows:
import { defineConfig } from 'umi'; import routes from './routes'; export default defineConfig({ nodeModulesTransform: { type: 'none', }, routes, fastRefresh: {}, });
Umi also supports internationalization (also known as i18n) through the locale plugin. You'll learn more about this and other helpful Umi plugins in later chapters. To enable internationalization, create a folder named locales
in the src
folder and add the following configuration to the config.ts
file under the config
folder:
config.ts
import { defineConfig } from 'umi'; import routes from './routes'; export default defineConfig({ locale: { default: 'en-US', antd: true, baseNavigator: true, baseSeparator: '-', }, nodeModulesTransform: { type: 'none', }, routes, fastRefresh: {}, });
The locale
configuration properties are as follows:
default
: The default application language.antd
: Enable Ant Design components internationalization.baseNavigator
: Enable browser language detection.baseSeparator
: The separator used in multi-language files localized under thesrc/locales
folder.
Now we can support internationalization by adding multi-language files in the locales
folder. For example, to support the English language, we need to add a file named en-US.js
.
Now, we'll add the app.tsx
file to set configurations at runtime.
Runtime configuration
Umi uses a file named app.tsx
to expand your application's configurations at runtime. This file is useful to configure the initial state using the initial-state plugin and the layout using the layout plugin. The app.tsx
file needs to be located in the src
folder.
Add a file named app.tsx
to the src
folder following the steps demonstrated previously.
At this point, our project structure should look like this:
You'll better understand all these features following the exercises in the upcoming chapters.
Now that you understand the Umi project structure and have added the missing folders and files, let's learn about some useful commands in the Umi command-line interface (CLI).
Exploring the Umi CLI and adding pages
In this section, we'll explore the Umi CLI for automating tasks and use the generate
command to add some pages to your project.
Umi provides a CLI with commands to build, debug, list configurations, and so on. You can use them to automate tasks. Some of these commands are already configured in the umi-app
template as scripts in the package.json
file: yarn start
will execute umi dev
, yarn build
will execute umi build
, and so on.
These are the main commands available:
umi dev
: Compiles the application and starts a development server for debugging.umi build
: Compiles the application bundle in thedist
folder.umi webpack
: This shows the webpack configuration file generated by Umi.umi plugin list
: Lists all Umi plugins in use.umi generate page
: Creates a new page template.Important Note
For more commands, refer to the documentation available at https://umijs.org/docs/cli.
Let's add some pages using the generate page
Umi CLI command. Follow these steps:
- First, delete the files under the
src/pages
folder, then add two pages by running these commands:$ yarn umi g page /Home/index ––typescript ––less $ yarn umi g page /Login/index ––typescript ––less
These commands generate two components under the pages
folder, Login
and Home
, with TypeScript and Less support.
- To access these pages, we need to define routes, so modify your
routes.ts
file to define the created components for new routes:
routes.ts
export default [ { path: '/', component: '@/pages/Login', }, { path: '/home', component: '@/pages/Home', }, ];
- To check the result, start the project by running
yarn start
, then navigate tohttp://localhost:8000/
; you should see the login page. - Navigate to
http://localhost:8000/home
; you should now see the home page.
Now that we have pages set up, we can learn more about Umi routing and navigation using umi history.
Understanding routing and navigation in UmiJS
In this section, you'll understand the Umi routing system and options for configuring routes. You will also learn how to access route parameters and query strings and about navigating between pages.
A Umi project is a single-page application. This means that the entire application remains on the first page served to the browser (index.html
), and all other pages we see when accessing different addresses are components rendered on this same page. Umi does the job of parsing the route and rendering the correct component; we just need to define which component to render when the route matches a specific path. As you may have noticed, we already did that. But there are other configuration options. For example, we can set subroutes to define a standard layout for various pages:
routes.ts
export default [ { path: '/', component: '@/layouts/Header', routes: [ { path: '/login', component: '@/pages/Login' }, { path: '/home', component: '@/pages/Home' }, ], }, ];
The preceding example defines that all routes under '/'
will have a default header, which is a component located in the src/layouts
folder.
The header component should look like this:
import React from 'react'; import styles from './index.less'; export default function (props: { children: React.ReactChild }) { return ( <div className={styles.layout}> <header className={styles.header}> <h1>Umi App</h1> </header> {props.children} </div> ); }
props.children
will receive the components when you access a defined route.
Another option we have is to redirect routes. Consider the following example:
routes.ts
export default [ { path: '/', redirect: '/app/login', }, { path: '/app', component: '@/layouts/Header', routes: [ { path: '/app/login', component: '@/pages/Login' }, { path: '/app/home', component: '@/pages/Home' }, ], }, ];
With this configuration, when you access http://localhost:8000/
, Umi will immediately redirect the page to http://localhost:8000/app/login
.
We can also define whether a path should be exact or not:
{ exact: false, path: '/app/login', component: '@/pages/Login', }
This configuration defines that you can access this page in any path under /app/login
, such as http://localhost:8000/app/login/user
. By default, all paths are exact.
You now understand how the routing system works and the different configuration options we have for routing. Now, you will learn how to access path and query string parameters and about conventional routing and navigating between pages.
Understanding path parameters and query strings
Sometimes we need to identify a resource in the route path. Imagine we have a page in our project that only displays product information. When accessing this page, we need to specify what product to get information from. We can do that by identifying the product ID in the route path:
{ path: '/product/:id', component: '@/pages/Product', },
If the parameter is not mandatory to access the page, you must add the ?
character, like this: /product/:id?
.
To access the product ID, we can use the useParams
hook provided by Umi:
import { useParams } from 'umi'; export default function Page() { const { id } = useParams<{ id: string }>();
You can also receive query string parameters after the route. Query string parameters are key-value pairs in the ?
character sequence in a URL, such as this example: /app/home?code=eyJhbGci
. Here, code
contains the value eyJhbGci
.
We don't have a specific hook to access query string parameter values, but we can easily do that using umi history:
import { history } from 'umi'; export default function Page() { const { query } = history.location; const { code } = query as { code: string };
Now, let's see how you can define parameters when working with conventional routing.
Conventional routing
UmiJS offers an automatic route configuration based on your project structure under the pages
folder. UmiJS will rely on that if it can't find route definitions in the config.ts
or .umirc.ts
files.
If you want to configure a route parameter, you can name the file enclosed in []
, like this: [id].tsx
. If this parameter is not mandatory to access the page, you must add the $
character, like this: [id$].tsx
.
Next, you will see how to navigate between pages.
Navigating between pages
When we need to set navigation between pages, usually, we use the DOM history object and anchor tag. In UmiJS, we have similar options to navigate: umi history and the Link
component.
You can create hyperlinks between pages using the Link
component, as in the following example:
import { Link } from 'umi'; export default function Page() { return ( <div> <Link to="/app/home">Go Home</Link> </div> ); }
You can also set navigation between pages using the push()
umi history command, as in the following example:
import { history } from 'umi'; export default function Page() { const goHome = () => { history.push('/app/home'); }; return ( <div> <button onClick={goHome}></button> </div> ); }
In addition to the push()
command, umi history has the goBack()
command to revert one page in the history stack and goForward()
to advance one page.
We have covered all the essential aspects of the Umi routing system, the different options to configure routes, access path and query string parameters, and navigation between pages.
Before finishing this chapter, I will introduce an exciting feature Umi provides if you prefer to interact with the project visually.
Using Umi UI
Umi UI is a visual extension of Umi to interact with the project. You can run commands to install dependencies, verify and test code, build the project, and add components through a graphical user interface.
Before using Umi UI, we need to add the @umijs/preset-ui
package. You can do that by running the following command:
$ yarn add @umijs/preset-ui -D
Now, when you start the project, you should see the following console log:
Navigate to http://localhost:8000
, and you will notice that the UmiJS logo appears in a bubble in the bottom-right corner. Clicking on this bubble will open Umi UI (you can also access Umi UI at http://localhost:3000
).
Let's see what we can do using Umi UI, beginning with tasks:
- BUILD: This option will create the application bundle in the
dist
folder. You can also click on ENVS to select compilation options, such as CSS compression. - LINT: This option will execute linters in your project. You need to configure the
lint
script to use this option. - TEST: This option will test the project. You need to write tests first.
- INSTALL: This option will install all project dependencies.
The following screenshot shows the Umi UI Task tab:
Next, let's add Ant Design components to our project.
Adding Ant Design components
Ant Design is a design system created by Ant Financial's user experience design team to meet the high demands of enterprise application development and fast changes in these applications. They also created a React UI library of components for building interfaces.
In the Assets tab, we can add Ant Design components to our pages as blocks:
Tip
The Umi UI Assets tab is almost entirely in Chinese at the moment. Still, you can always refer to the Ant Design documentation by clicking on Preview Demo and changing the website language to English.
Let's add a login form to experiment with this feature:
- Navigate to
http://localhost:8000
and open the Umi UI Assets tab. - Click on Add in the form-login box component.
- Now, in the Variable Name field, type
LoginForm
, make sure the package manager client selected is yarn, and click on OK.
Wait until the block is added and we are done. Umi UI will reload the page, and the component is already there!
If you want, you can add some styles to the login page, as follows:
- Add this code to the Login page's
index.less
file:.container { display: flex; flex-direction: column; align-items: center; }
- Add the
container
CSS class to the login component:import React from 'react'; import styles from './index.less'; import LoginForm from './LoginForm'; export default function Page() { return ( <div className={styles.container}> <h1 className={styles.title}> Welcome! Access your account.</h1> <LoginForm /> </div> ); }
The result should look like this:
And that's it! Now you know how to use Umi UI to interact with your project. If you like this option, I recommend experimenting with it by adding more components and styling them to get you used to it.
Summary
In this chapter, you learned how to configure VSCode to work with UmiJS. You learned how to set up a project and organize the UmiJS folder structure. You also learned how to use the Umi CLI to automate tasks and quickly add pages and templates to your project.
You learned that an UmiJS project is a single-page application and about various configurations to define routes in your project. You learned how to access path parameters and query string parameters. You also learned how UmiJS could automatically configure routes based on the folder convention. You learned about navigation using umi history and the link component.
Finally, you learned how to install and use Umi UI to interact with your project. You then learned how to execute tasks using Umi UI and add Ant Design components as blocks in your project.
In the next chapter, you will learn more about Ant Design in a Umi project and how to use it to develop interfaces.