The continuous acceleration of high-speed rail, the arrival of 5G era, the landing of unmanned supermarket emerging projects…… Are constantly reminding us, to fast, to high performance. So as programmers, how should we use existing technology to create high-performance products, to bring users “flying” general experience?

Challenge the “ongoing tense”

BCRM is an important customer information management platform for enterprise business, carrying the whole process of rights and interests management of existing customers before settling in. Business sector as a guest and talk of important sectors, but because the current BCRM can only PC network access, such as the sales manager to go out to visit customers, it is difficult to immediately get business information and maintain records of the contact information, easily resulting in the loss of business information and the information confirmed, in order to improve the efficiency of business transformation and follow up, decided to ME services, access to the Beijing Improve sales efficiency.

July 28 demand, planned on August 10 online, in only 10 working days, need to complete static development, joint adjustment, testing, online, and front-end static development evaluation has 17 people/day, to complete in such a tight time from 0 to 1 high-performance project construction, our two pressure is really not small ☹.

After a heated discussion, we decided to change the development model and make bold innovations in several aspects to cope with the coming storm. First, we thought of a collaborative model. We wanted to complete the development in the shortest time possible. In addition, brave try front-end emerging technology, make full use of the power of technology to provide continuous power for the project; Finally, in the development of the project, we dig deeply, and on the basis of quickly completing the requirements, we can also ensure the rapid operation of the application.

Let’s describe it in detail 👇!

“Parallel” collaboration

Let’s think back to our daily development process: product demand -> requirements review -> work hours assessed by all parties -> milestones determined -> development -> joint investigation -> test -> launch

There is nothing wrong with following this process for daily iteration requirements, but it is too late to apply it to BCRM where development time is tight. After all, not everything has to be serial, so why not “parallel” these things to save time? In addition, some modules will be developed first and some will be developed later. Therefore, we finally decided to carry out development, joint commissioning and testing synchronously. Among them, the modules that are developed first will be tested first.

Of course, in the case of reasonable human resources, it is not enough to adjust the collaboration process. In order to fundamentally improve development efficiency and application performance, we also need a “new” technology architecture, after all, people do not want to work overtime.

New technology brings new speed

BCRM, as a brand new project, does not have problems such as compatibility with historical versions, so we can boldly adopt a brand new technology stack for development. The first thing that comes to mind is the popular Vue3 + Vite + TypeScript package combo, plus NutUI3 as the most powerful component library, so it’s no wonder our project can’t fly.

High-performance Vue3

Vue3 has received mixed reviews from the industry since its release. Compared with Vue2, it has a great change. Although it requires a certain learning cost to change from Vue2 to Vue3, it has to be admitted that Vue3 has a better performance than Vue2 in terms of performance.

In Vue2, when data changes, it generates a new DOM tree, compares it to the previous DOM tree, finds different nodes and updates it. But the comparison process is a full comparison, which means that each node is comparing with each other. But obviously, there are some nodes that don’t change, so it takes time to compare them.

<div> <p>BCRM Sales assistant </p> <p>{{MSG}}</p> </div>Copy the code

Therefore, in Vue3, this part of content is optimized: When creating the virtual DOM tree, a PatchFlag will be added according to whether the content in the DOM will change. If the content will change, a flag will be marked. Then, when comparing with the last virtual node, only these nodes with flags will be compared to reduce the cost of static tag traversal.

In addition, Vue2 wasted a lot of performance by recreating and rendering elements every time they were updated or not. Again, using the previous code block as an example, MSG creates the previous P node every time it is updated.

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"; export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock("div", null, [_createVNode("p", null, "BCRM Sales Assistant "), _createVNode(" P ", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)])}Copy the code

In Vue3, for elements that do not participate in the update, static promotion will be done and they will only be created once. They can be reused directly during rendering to reduce the overhead caused by re-creation.

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue";

const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "Hello World", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _hoisted_1,
    _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}
Copy the code

Efficient packaging of Vite

At the beginning of the New Year, UVU replied in zhihu’s q&A about the new changes in the front end of 2021 that “many people will abandon Webpack and start using Vite”. From Grunt and Gulp to Webpack, Rollup, Snowpack, and several build frameworks, we can take a look at 👀 to see what features of Vite make us so confident.

Vite is a native ESM-driven Web development build tool developed by Vue authors based on browser native ES Imports in development environment and rollup packaging in production environment. The biggest characteristic is “fast”. After running the Dev command, you only do two things. First, you start a service to host the resource service. The second is to use esbuild to pre-build NPM dependencies. It then sits there until the browser sends an ESM specification module request over HTTP, and Vite starts “compiling” the requested module on demand.

Of course, there are many performance optimizations worth mentioning in Vite 👇

Webpack is not the standard answer, front-end build tools can have some new gameplay:

  • “Packing” is not the goal, “running” is. In 2021, let the browser do what it can do, be a lazy programmer monkey!
  • A flexible framework, for the author, can mean a spiraling out of control amount of development; This can mean high learning costs for users, as well as repeated arguments about whether Spaces are better than tabs. Well, a set of tools built into a variety of industry “best practices” without much room for customization can actually increase productivity in some cases 👏

Powerful component library NutUI3

To do a good job, he must sharpen his tools. A good front-end engineer, in addition to constantly improve their own capabilities, but also know how to use tools. A proper component library can save a lot of development time.

NutUI3, a jd.com lightweight mobile Vue component library, uses the Vite2. X + Vue3 + TypeScript architecture, which is in line with the BCRM application stack. Vue3 has made so many performance optimizations over Vue2, and NutUI3 has also made the following optimizations to keep up with business requirements:

  • Using combined API Composition syntax reconstruction, clear structure, modular function
  • Component emits events are extracted separately to enhance code readability
  • Refactoring mount class components using Teleport’s new feature
  • .

So what does NutUI3 do for us in BCRM?

  1. Introduce point-to-point services on demand

It is not possible to use all of the components in the component library during development, and if you import all of them into a project, you end up with a large package file, so use NutUI3’s on-demand loading capabilities at this point.

Since NutUI3 uses the Vite build tool, and Vite itself already imports the component library on demand, we simply import styles on demand. Importing styles on demand isn’t complicated either, just using the on-demand plugin vite-plugin-style-import provided by Vite.

First, install the plug-in

npm install vite-plugin-style-import --save-dev
Copy the code

Add the configuration in vite.config.ts

import vue from '@vitejs/plugin-vue'
import styleImport from 'vite-plugin-style-import';
export default {
  plugins: [
    ... ...
    styleImport({
      libs: [
        {
          libraryName: '@nutui/nutui',
          libraryNameChangeCase: 'pascalCase',
          resolveStyle: (name) => {
            return `@nutui/nutui/dist/packages/${name}/index.scss`
          }
        }
      ],
    }),
  ]
};
Copy the code

After configuration is complete, remember to restart yo, otherwise it will not take effect. Next, we can introduce components

import { Button } from "@nutui/nutui";
Copy the code

Through the above code snippet, not only the Button component is introduced on demand, but also the Button component style is introduced on demand, without the need to introduce a separate style, is not very convenient.

  1. Component application

The BCRM project uses 10+ components including Uploader, Datepicker, Infiniteloading, Popup and so on. NutUI3 provides great convenience for the construction of the project.

Here I have to praise the Calendar component. The time selection and time interval selection functions involved in the development can be perfectly covered. Not only support to select a single time, time interval, but also set the time selection interval, reset to a specified date, really not too useful.

The Calendar component in NutUI3 not only fully meets the business requirements, but also allows developers to correct minor bugs encountered during the use of NutUI3 without affecting the development schedule. It’s hard to imagine when the project would have gone live without NutUI3 and developing Calender components yourself.

The open source Vue3 mobile component library is already weak, and NutUI3 is so good that there is no reason not to use it.

Performance improvement

Performance improvements, a classic topic in the tech world, are a headache for countless programmers, who often spend a lot of time on projects that don’t achieve the desired results. However, the performance optimization in BCRM relies on Vue3, and the results are significant.

In BCRM development, due to the tight time and heavy task, the priority of performance optimization is reduced again and again, which is probably a common problem in most project development. Near the launch, visual perception made us put performance optimization on the agenda and carried out in-depth optimization.

Promise.all improves rendering speed

The purpose of improving the page rendering speed is to make the page “Duang” load all at once, rather than spinning for a long time, generally more than 5 seconds, the user’s favorable rating will decline.

Guess how many interfaces the following page requests when it first loads:

The answer is 23. Everything you can see is provided by the interface, and you need to call more than one interface to get a field. Take the opportunity stage field as an example:

  • According to the opportunity ID to get the details of the opportunity, get the opportunity stage K value, this value is a number, not the text description to show

  • With the value of k just obtained, the request interface retrieves the corresponding literal description

The same is true of business opportunities.

  • Get business opportunity details according to business opportunity ID, get business opportunity source K value

  • With the value of k just obtained, the request interface retrieves the corresponding literal description

It is not difficult to find that after obtaining the detailed data of business opportunities, there is no connection between the text description of the stage of obtaining business opportunities and the process of obtaining the text description of the source of business opportunities.

As in the case above, 22 of the 23 interface requests are unrelated and do not need to be sequenced. But JS is single-threaded and can only do one thing at a time, even if it is a request that can be executed simultaneously. All merges unrelated requests, wraps them into a new Promise instance, and returns the request result as an array to reduce page rendering time.

query.getDetail({ coSn: str }).then((res) => { state.detail = res as object; let asksMap = new Map(); asksMap.set("projectInfo", query.getProjectByCoSn({ coSn: str })); asksMap.set("applyLabel", query.coApplyLabel({ coSn: str })); asksMap.set("coTrajectory",query.coTrajectory({ coSn: str, pageSize: 100 })); . . asksMap.set("planList",planService.queryPage({coId: state.detail.id,pageNo: 1,pageSize: 50})); Promise.all([...asksMap.values()]) .then( (results) => { results.forEach((result, i) => { state[[...asksMap.keys()][i]] = result; }); }, () => {} ) .catch((error) => {}); })Copy the code

Async /await rendering speed upgrade again

The page rendering speed was significantly improved with the optimizations above, but async/await caught our attention during the optimizations. Async /await saves JavaScript developers from the callback function dilemma, but with the high use of async/await, a new async/await dilemma is born.

When doing asynchronous JavaScript programming, it is common to write multiple statements one after another and annotate an await before each function call. Most of the time, a statement doesn’t depend on its predecessor, but you still have to wait for the previous statement to complete. Take a look at the following code snippet

async function orderItems() {
   const items = await getCartItems()    // async call
   const noOfItems = items.length
   for(var i = 0; i < noOfItems; i++) {
     await sendRequest(items[i])    // async call
   }
 }
Copy the code

Match this code with a business scenario that takes the items in the shopping cart and orders each item. At first glance, this code looks fine, but it has some serious pitfalls.

In the for loop, you must wait for the sendRequest() function to complete before continuing with the next loop. In fact, there is no relationship between the processing of each commodity, there is no need to wait. You want to send requests as quickly as possible and then wait for all those requests to complete. If you have thousands or tens of thousands of items in your shopping cart, you can’t escape a page crash, which can be very difficult to troubleshoot.

Some performance optimizations won’t necessarily help the situation, and they won’t necessarily lead to extreme situations, but keep your code robust and have a good code development habit.

Vuex joins forces with Map

Business opportunity stage, business opportunity source and other fields run through the whole BCRM business opportunity project. Although it is interface acquisition, it is static in terms of project dimension, which is most common in project development. In this case, the front end typically requests the interface only once during project initialization, using the Vuex store as a constant.

let result = await service.getDict({kind: `${type}`});
if (result) {
  let obj = result as any;
  store.commit(type, obj);
}
Copy the code

But the format of the data returned by the interface gives us a bit of trouble

Obj = [{k:1,v:' in '}, {k:2,v:' in '},]Copy the code

As mentioned in the above section, if you want to know the v corresponding to the k value returned by the back end to the front end, you need to loop through the objects stored in Vuex every time, which increases the code quantity virtually and also increases the probability of errors. Therefore, the obtained results are transformed and stored in Map form.

let result = await service.getDict({kind: `${type}`});
if (result) {
  let obj = result as any;

  let target = new Map();
  obj.forEach((item: DataVal) => {
    target.set(item.k, item);
  });
  const key: { [props: string]: string } = {
    co_sources: "setManageSources",
    ......
  };
  store.commit(key[type], target);
}
Copy the code

After this transformation, usage can be simplified to

const { state } = useStore(key);
const value = state.manageSources.get(k).v
Copy the code

Isn’t that much clearer all of a sudden. At a small scale, such code optimizations have little impact on performance, but when they do, they can make a huge difference.

conclusion

The process of completing a project from zero to one in a short period of time was painful, but as a member of the NutUI3 development team, IT was exciting to see NutUI3 on every page in the BCRM project.

Vue3 2021Q1 Vue3 has been officially released, and more and more developers have begun to use Vue3 as a development language. NutUI3 follows the new technology with a Vue3 based version NutUI3.0 released in q2 2021. nutui At present, five projects in our group have been developed using NutUI3, and a total of more than 10 projects in the group have been developed and put into operation using NutUI3. NutUI3 is a good example of improving component coverage and stability. Welcome to build, look forward to PR!