Primary zhihu column, zhuanlan.zhihu.com/p/55357377 welcome the attention

With 2019 Lunar New Year just around the corner, it’s time to take stock of the team’s technical precipitation over the past year. In the past year, data-related businesses supported by us have made rapid progress. The code volume of two core platform-level products has reached 300,000 lines and 800,000 lines respectively. The number of TS modules has exceeded 1,000, and the number of co-developers has increased to 20+ people. Due to historical reasons, development frameworks are based on both React and Angular. Considering product complexity, personnel shortages, and different technical backgrounds, we have tried a variety of ways to polish the tool system to improve development efficiency. Here are five main ways.

Redux-based state management

It has been nearly six years since React was released in 2013, and the front-end framework has gradually formed a three-way battle between React/Vue/Angular. A few years ago there was a debate about one-way binding versus two-way binding. Now one-way binding has been chosen by all three frameworks, and two-way binding has been reduced to mere syntactic sugar. The differences between frameworks are getting smaller and smaller, and with the maturity of the Ant-Design/Fusion-Design/ Ng-Zorro /ElementUI component libraries, choosing any framework you’re familiar with can be very efficient.

So what’s the core question? We think of it as state management. In simple applications, it is convenient and quick to use State within a component. However, as the complexity of the application increases, data will be scattered among different components, and component communication will become extremely complicated. We have tried the idea of native Redux, Fractal, self-developed Mobx framework, Angular Service, and finally concluded that Redux is still one of the best options for complex application data flow processing.

Fortunately, in addition to the React community, the Vue community has similar Vuex, and the Angular community has NgRx that provides almost the same capabilities, and even NgRx can seamlessly debug state changes using Redux-devTools.

No matter how you optimize, always follow the Three Redux principles:

The principle of methods Questions raised
Single source of truth Component Stateless, data from Store How are stores organized?
State is read-only State can only be changed by triggering an action Action bloated, boilerplate code
Changes are made with pure functions Reducer is a pure function What about side effects, a lot of boilerplate code

We solved these three problems by developing our own iron-Redux library. The following are the thinking behind:

How are actions organized?

  1. Action Types need to be globally unique, so we add a prefix to the action type, which is essentially a namespace concept
  2. In order to pursue experience, the Fetch scene needs to process three states, corresponding to LOADING/SUCCESS/ERROR, which we passedFetchTypesType to automatically generate corresponding to three actions

How to organize Store/Reducer?

  1. Reducer and View do not have to correspond one by one. There are both component tree and state tree in the application, which are organized according to their needs. Bind one or more branches of the state tree to the component tree through connect
  2. Reduce boilerplate code by constructing some default data types. For the data returned by Fetch, we define AsyncTuple to reduce boilerplate code
  3. Clear organizational structure, the first layer is ROOT, the second layer is each page, the third layer is the card in the page, the fourth layer is the card data, so the deepest division is not more than 5 layers

We end up with the following flat state tree. Large but organized, you can access any data quickly and unambiguously.

How to reduce boilerplate code? With native Redux, a common request processing is as follows. It’s very redundant, which is why Redux is criticized by many people

const initialState = {
  loading = true,
  error = false,
  data = []
};

function todoApp(state = initialState, action) {
  switch (action.type) {
    case DATA_LOADING:
      return {
        ...state,
        loading: true,
        error: false
      }
    case DATA_SUCCESS:
      return {
        ...state,
        loading: false,
        data: action.payload
      }
    case DATA_ERROR:
      return {
        ...state,
        loading: false,
        error: true
      }
    default:
      return state
  }
}
Copy the code

After using iron-Redux:

class InitialState { data = new AsyncTuple(true); } function reducer(state = new InitialState(), action) {switch (action.type) {/** omit other actions */ default: return AsyncTuple.handleAll(prefix, state, action); }}Copy the code

2 thirds less code!! I mainly did the following two points:

  1. Introduced the defaultAsyncTupleType, that is{data: [], loading: boolean, error: boolean}Such data structures;
  2. useAsyncTuple.handleAllHandle LOADING/SUCCESS/ERROR actions, handleAll code is very simple, use if to determine the suffix of action. Type, source code in thehere.

React and Angular used to be two frameworks that were hard to reconcile and wasted a lot of our manpower on development. By using lightweight Iron-Redux, we internally reuse almost all of our code except the component layer, completely following the core principles of Redux. Development specifications and toolsets are agreed upon, developers can switch seamlessly, and the additional cost of framework differences is minimized.

Embrace TypeScript

TypeScript is booming right now, with over 50% usage and 90% satisfaction according to 2018 StateofJS, and even Jest is switching from Flow to TS. If you’re not already using it, consider switching. It will definitely improve your project. Over the past year, we have switched from partial TS use to full TS, including our own tool libraries and so on.

The biggest advantage of TS is that it provides powerful static analysis capabilities and, combined with TSLint, allows for stricter checking of code. Traditional EcmaScript doesn’t have static typing, so even ESLint can only do very basic checking, and some typo problems may not be discovered until they are bugged online.

The following figure shows a common 4-tier architecture for a front-end application. After the code and tools fully embrace TS, realize the full link static analysis from the back-end API interface to View components, with perfect code prompt and verification capabilities.

In addition to iron-Redux, we also introduced Pont to implement front-end fetch, which automatically maps the back-end API to the request methods that the front end can call.

Pont implementation Principle: (French: bridge) is our research and development of the front-end take several layers of the framework. The backend API for docking uses Java Swagger, which provides meta information for all apis, including the type format of requests and responses. Pont parses the API meta-information to generate TS fetch functions, which are perfectly typed and mounted under the API module. The final code looks like this:

Pont realizes the following effects:

  1. Automatically match URL and method according to method name, and correspond to PRAMS and Response type perfectly, and can automatically prompt
  2. After the back-end API interface is changed, the associated request of the front end will automatically report an error, and the front end will no longer worry about the backend quietly change the interface unaware
  3. There is no need for front and back interface convention documentation, and code to ensure that the front fetch and back interface definitions are exactly the same

In addition, iron-Redux can receive the Pont interface response data format, and derive the static type definition of the entire Redux state tree, perfect type hint of the data in the Store. The effect is as follows:

As a result, TS makes your code more robust, especially for large projects, where it almost always works and gives you a lot of confidence in refactoring.

3. Return to Sass/Less

In 2015, we started to practice CSS Modules, including the styled Components. In 2019, the CSS-in-JS scheme is still controversial. Although it does solve some inherent problems of CSS language, it also adds a lot of costs. Not friendly to beginners, high cost of global style coverage, complex pseudo-class handling, and poor integration with component libraries such as AntD. At the same time, the Sass/Less community is growing rapidly, especially with the maturity of Stylelint, which can avoid Bad Parts of CSS by means of technical constraints.

  1. Global contamination: The convention is that each style file can have only one top-level class, such as.home-page{ .top-nav {/**/}, .main-content{ /**/ } }. If you have more than one top-level class, you can use the Stylelint Rule to detect and give warnings.
  2. Dependency management is not thorough. Webpack’s CSS-loader is sufficient.
  3. JS and CSS variables are shared. For JS and Sass/Less variable sharing, we figured out our own solution:
// src/styles/variables.js
module.exports = {
  / / the main color
  'primary-color': '#0C4CFF'.// Error color
  'error-color': '#F15533'.// Success color
  'success-color': '#35B34A'};Copy the code
// webpack.config.js
const styleVariables = require('src/styles/variables');

// ...
      {
        test: /\.scss$/.use: [
          'style-loader'.'css-loader? sourceMap&minimize',
          {
            loader: 'sass-loader'.options: {
              data: Object.keys(styleVariables)
                .map(key= > ` \ $${key}: ${styleVariables[key]}; `)
                .join('\n'),
              sourceMap: true.sourceMapContents: true}}}]/ /...
Copy the code

In SCSS files, variables can be referenced directly

// page.scss
.button {
  background: $primary-color;
}
Copy the code

Iv. Development tools cover the whole link

It’s almost impossible to develop a React/Angular/ Vue-level framework in 2019, and there’s no need to reinvent the wheels of Ant-Design/Fusion-Design/Ng-Zorro. Is there no chance?

Of course there are. There are still plenty of opportunities for your own product development process. The following is a routine project development flow chart, any link as long as deep digging, there is room for improvement. If you can eliminate one or more links with a tool, the value is greater.

Taking the [development] part of it alone, there are many extendable scenarios:



kiwi

  1. VS Code plugin Kiwi Linter automatically marks Chinese text in red and replaces it if it already exists
  2. Shell commands fully check out the untranslated documents and submit them to translators in batches
  3. The Codemod script automatically migrates the old internationalization scheme to Kiwi at a very low cost

In addition to the above three points, there are plans to develop a browser plug-in to check for lost copy, and use Husky to automatically translate lost copy before git submission.

In the future, if you only provide one code base, its value will be very limited. You can use the diagram above to develop extensions to enrich the ecosystem. If you are new, it is recommended to learn the principles of compilation and the corresponding extension development specification.

5. Strict and thorough Code Review

In the past year, we conducted a total of 1200+ times of Code Review(CR). Many colleagues were embarrassed to mention MR at the beginning, but later they chased others to Review, and CR became a habit of everyone. Through CR, every line of code in the project has been touched by at least two people, which reduces the majority of low-level errors and improves the quality of the code, which is also one of the fastest ways to help new people grow.


Code Review tips:

  1. No magic
  2. Explicit not implicit
  3. Coverage is more important than depth, and 100% coverage is pursued
  4. Frequency is more important than the sense of ceremony. You can Review other people’s code by taking the bus, squatting in the toilet and opening the mobile phone, and there is no need to organize a special meeting
  5. Keep the granularity as small as possible, one component at a time, and one method at a time, in combination with Git Flow
  6. If there is no problem, merge directly. If there is any problem, please leave a comment and provide action
  7. For the code that needs to be released and can not be reviewed, it can be merged and released, and then Review can be added
  8. It needs to be promoted from top to bottom, with perfect specifications, and Review experience is regularly summarized to enrich the development specifications
  9. CR isn’t just about finding mistakes. When you see good code, don’t be stingy with compliments
  10. The essence is to encourage more communication between developers, learn from each other, and create a culture of technology

conclusion

Of course, the above five points are not the whole story of our technology. In addition, we also practiced mobile terminal development, visual chart /WebGL, Web Worker, GraphQL, performance optimization, etc., but these are still at the technical level, and will be shared to a certain extent in the future.

If you are preparing or developing complex front-end applications and have a team of people with diverse technical backgrounds, follow these 5 tips: Use Redux for clear and predictable state management, work with TypeScript for robustness and maintainability, and revert to simple and convenient CSS with various Lint tools. Constantly polishing their own development tools to ensure efficient development specifications, and strictly and thoroughly implementing Code Review to promote communication and improvement.

Links

  1. Pont:github.com/nefe/pont
  2. Kiwi:github.com/nefe/kiwi
  3. Iron – redux: github.com/nefe/iron-r…
  4. The State of JavaScript 2018

We are still recruiting, please email your resume and reply to your letter. [email protected]