Pwa has been maintaining a Shanghai metro line map, and its main feature is “Offline First”. However, since the code is implemented through native JS, I was not a big fan of using frameworks before, and did not want to have any preference for frameworks. But later on, as the amount of code increases, the code really gets messy and it becomes very difficult to expand new features. As a result, a complete refactoring of the application took nearly two weeks. Website address: https://neal1991.github.io/subway-shanghai

To prepare

Do your homework first. Given a choice between Vue and React, I prefer the latter. Based on create-React-app setup, CRP provides you with a development environment right out of the box, so you don’t have to configure WebPack yourself, so you don’t have to be a WebPack configuration engineer.

On the other hand, we also need some data, including site information, route path, text description and so on. Based on previous applications, information can be retrieved in a small piece of code. For this purpose, to retrieve the attributes of our previous site in an SVG diagram, normal sites use the circle element, and to retrieve its attributes:


     
  1. const circles = document.querySelectorAll('circle');

  2. let result = [];

  3. circles.forEach(circle => {

  4.  let ele = {

  5.    cx: circle.cx,

  6.    cy: circle.cy,

  7.    sroke: circle.stroke,

  8.    id: circle.id

  9.  };

  10.  result.push(ele);

  11. })

  12. const str = JSON.stringify(result);

Copy the code

From this code we can obtain the general SVG site information, as well as relay station information, line path information, site and line label information. In addition, we need to obtain schedule information, bathroom location information, accessible elevator information and entrance and exit information for each stop. Here is to write some crawlers to the official website to crawl and do some data processing, again not a repeat.

design

Once the data is ready, it’s time to design the application. First, split the component:

Component structure

Interpret the entire Map as a Map component and divide it into four sub-components:

  • Label: text information on the map, including subway station name and line name

  • Station: subway Station, including regular stations and transfer stations

  • It’s a subway Line

  • InfoCard: a component with the most complex status, including schedule information, bathroom location information, entrance and exit information, and accessible elevator information

This is a rough partition of components that may contain more elements, such as InfoCard, which has nested InfoCard => TimeSheet => TimesheetTable.

Component communication and state management

The biggest difficulty with native development is probably this one. I didn’t plan to use a global state management library like Redux because the component hierarchy wasn’t particularly complex. Communication between major components is parent-child communication and sibling communication. The communication between parent and child components is simple. The state of the parent component is the props of the child component, and the parent component can communicate with the parent component. Sibling components are slightly more complex and communicate by sharing the state of their parent. In this scenario, I click on the site, hoping to pop up an information prompt window. This is the communication between the Station component and the InfoCard component, which is shared through the Map component. Clicking on the Station component triggers an event that updates the Map component state via a callback, as well as the InfoCard component. At the same time, in order to achieve this, click other areas to close the message prompt window, we monitor the Map component, listen to the bubbling of events to achieve efficient closing, of course, in order to avoid some unnecessary bubbling, we also need to prevent the bubbling of events in some event processing.

InfoCard is one of the most complex components, because it contains several ICONS, status information switching, and the need to update the message window when switching between different sites. Attention should be paid to the initialization of information in the information prompt window when clicking on the information for the first time, and different information will be displayed when switching different ICONS, such as toilet information or entrance and exit information, as well as updating the corresponding timetable when switching different lines. These transitions of states need to be noted. Another point worth a question is, when switching between different sites of the state, if I am looking at the toilet information of a site, I click on another site, this time the pop-up information prompt window should be schedule information or toilet information? My choice is still the bathroom information, and I have maintained this state, which logically seems to be a better user experience. Specific implementation of the code details are not explained, which can contain more details, welcome to use experience.

Performance optimization

The above development benefited from previous maintenance, so the reconstruction process was relatively fast. After getting familiar with the use of React, the reconstruction was completed. However, after going live, Performan scored zero on lighthouse. The first screen rendering and interactivity score is 0. Let’s analyze it first. Because the entire application is rendered in JS, and the core is SVG. Here are a few things to note:

  • The code imports JSON directly, causing js to be too bulky

  • All components are loaded at render time

Identify the problem and come up with some solutions. The first is simple, compressing the JSON data to remove unwanted information. Second, a good solution is to load components asynchronously, especially for InfoCard components:

synchronous


     
  1. class InfoCard extends React.Component {

  2.  constructor(props) {

  3. Super (props) {

  4. .

  5.    }

  6.  }

  7. .

  8. }

Copy the code

asynchronous


     
  1. export default function asyncInfoCard (importComp) {

  2.  class InfoCard extends React.Component {

  3.    constructor(props) {

  4.      super(props);

  5.      this.state = {

  6.        component: null

  7.      };

  8.    }

  9.    asyncComponentDidMount() {

  10.      const { default: component } = await importComp();

  11.      this.setState({

  12.        component: component

  13.      })

  14.    }

  15.  }

  16. }

Copy the code

This allows us to transform the synchronous component into a component that loads asynchronously, so that we don’t have to load all the components at once. This allows us to load components asynchronously in the Map:


     
  1. import asyncInfoCard from './InfoCard'

  2. const InfoCard = asyncInfoCard(() => import('./InfoCard')

Copy the code

After its launch, lighthouse’s performance score immediately rose to over 80 points, proving that such improvements are effective. Another point worth noting is the front screen. For historical reasons, the positions of elements in SVG are fixed throughout the image, and the abscissa and ordinate coordinates are already defined, while SVG is set in the middle. When loading on the mobile terminal, the blank area on the left is displayed, giving the user the illusion that the program has not been loaded. In the previous version, we did scroll through the scroll bar, moving the focus of the view to the middle. This time the idea is to implement it through transform:


     
  1. .svg {

  2. transform: translate(-100px, -300px)

  3. }

Copy the code

This enabled an offset of the position of the entire SVG diagram, and with Lighthouse analysis, the performance score dropped to more than 70 points. Let’s see if there’s another way, and then I want to define an arrow animation in the top left corner.


     
  1. <img src="right_arrow.png" alt="right arrow" title="right arrow" class="right-arrow"/>

Copy the code

     
  1. .right-arrow {

  2.  animation: moveright 3s linear infinite;

  3. }

  4. @keyframs moveright {

  5.  0% {

  6.    transform: translateX(2rem);

  7.  }

  8.  50% {

  9.    transform: translateX(3rem);

  10.  }

  11.  100% {

  12.    transform: translateX(5rem);

  13.  }

  14. }

Copy the code

This allows us to create a looping right movement animation that prompts the user to swipe right. After deployment, the practice was abandoned when the performance split-up dropped to zero. At last I decided to use transform:translateX(-200px)translateY(-300px); In addition, translateX does not cause page redrawing or rearrangement, but only layer rearrangement with minimal performance impact.

The deployment of

The current deployment solution takes create-React-app’s official recommendation and uses gh-Pages implementation to upload the build package file to the GH-Pages branch for deployment.

compatibility

It works best on Chrome, which is recommended for Android, and I generally prefer Chrome on my phone. For Safari, the rest of the browsing features don’t seem to be a major problem, and there’s probably no support for adding them to the home screen yet. However, later versions of ios seem to have further support for PWA.

conclusion

I spent two weeks to complete the complete reconstruction of the project. From the commit records of this year, we can see that there was a wave of crazy commit in March, mainly in the first weekend, I spent two days to modify a lot of code, which was made by the state switch of the InfoCard for a long time, and then I made some optimization for performance. The process was painful and I doubted my coding ability. But here’s the takeaway:

  • There is no best language, best frame, only the most appropriate

  • The most elegant implementations are not achieved overnight, they are all tried out

One last dry joke:

The young man asked the zen master, “Excuse me, master, why didn’t the program I wrote get the expected output?” The Zen master replied, “Young man, that’s because your program only works as you write, not as you think…”

Read the original article, see the project source code address, welcome star or PR.

The above

Originally wanted to upload the video, Tencent video real-name authentication. It’s horrible…