Qualified Front End Twelfth Bomb-TypeScript+Large Project

Keywords: Vue axios TypeScript JSON

Write before

TypeScript has been around for a long time and is used by many large companies to develop large projects.Last month, I was also following up on a large operations and maintenance project for the group.

The tasks to be done in a project are roughly divided into the following modules

  • One-stop management platform
  • Scale operation and maintenance capacity
  • Planning Platform
  • Patrol Platform
  • Full-link manometry, etc.

Every module has a lot of things to do, because it involves the business of the company, I will not list the specific things to do here, but the overall size of the project is still large.

1. Selection of Types

After doing some technical research, it combines the magnitude of development and maintenance costs after the project.Ultimately, my colleagues and I reached a consensus on the technical selection, which was Vue's latest family bucket + TypeScript.

So the question is, why do you have to use TypeScript for large projects, not ES6 or 7?

I'm not saying no, but I personally prefer to use TypeScript in some large projects that I collaborate on.Here's a list of my own thoughts after I've done my research

  1. First, TypeScript has a type system and is a superset of JavaScript.What JavaScript can do, it can do.JavaScript can't do it, it can do it too.

  2. Secondly, TypeScript is mature, there is a lot of relevant information on the market, and most libraries and frameworks read it well.

  3. Then, on the premise of guaranteeing excellent performance, it is still in the active development and improvement, and new features will be added constantly

  4. JavaScript is a weak type and has no namespace, making it difficult to modularize, making it less convenient for large collaborative projects

  5. Editors such as vscode and ws are friendly to TypeScript support

  6. TypeScript supports component and business type checks better, such as

    // Define Enumeration
    const enum StateEnum {
      TO_BE_DONE = 0,
      DOING = 1,
      DONE = 2
    }
    
    // Define the item interface
    interface SrvItem {
      val: string,
      key: string
    }
    
    // Define service interfaces
    interface SrvType {
      name: string,
      key: string,
      state?: StateEnum,
      item: Array<SrvItem>
    }
    
    // Then define the initial value (if not by type, error is inevitable)
    const types: SrvType = {
      name: '',
      key: '',
      item: []
    }
    

    With an editor, if you don't follow a defined type, the editor itself will give you an error instead of waiting until you compile

  7. Command space + interface declarations make type checking easier and prevent code irregularities

    For example, you define the return type of Ajax in an ajax.d.ts file

    declare namespace Ajax {
      // axios returns data
      export interface AxiosResponse {
        data: AjaxResponse
      }
    
      // Request Interface Data
      export interface AjaxResponse {
        code: number,
        data: object | null | Array<any>,
        message: string
      }
    }
    

    Then it can be used when requested

    this.axiosRequest({ key: 'idc' }).then((res: Ajax.AjaxResponse) => {
      console.log(res)
    })
    
  8. Generics can be used to create reusable components.For example, if you want to create a generic method where the parameter type is the same as the return value type

    function foo<T> (arg: T): T {
      return arg
    }
    let output = foo('string') // type of output will be 'string'
    

    For example, you want to use generics to lock the types used in your code

    interface GenericInterface<T> {
      (arg: T): T
    }
    
    function foo<T> (arg: T): T {
      return arg
    }
    
    // Locking myFoo can only pass in parameters of type number, passing other types of parameters will cause errors
    let myFoo: GenericInterface<number> = foo
    myFoo(123)
    

In summary, there are many other benefits to using TypeScript, so I won't list them here. Interested partners can look up the information themselves.

2. Infrastructure Construction

1. Initialization structure

I'm using the latest version of scaffolding vue-cli 3 for project initialization, with the following initialization options

The resulting directory structure is as follows

├── public                          // Static Page
├── src                             // home directory
    ├── assets                      // Static Resources
    ├── components                  // assembly
    ├── views                       // page
    ├── App.vue                     // Page Main Entry
    ├── main.ts                     // Script main entry
    ├── registerServiceWorker.ts    // PWA Configuration
    ├── router.ts                   // Route
    ├── shims-tsx.d.ts              // Related tsx module injection
    ├── shims-vue.d.ts              // Vue Module Injection
    └── store.ts                    // vuex configuration
├── tests                           // test case
├── .postcssrc.js                   // postcss configuration
├── package.json                    // rely on
├── tsconfig.json                   // ts Configuration
└── tslint.json                     // tslint configuration

2. Reconstructed structure

Obviously these are not able to meet the normal business development, so I have made a version of infrastructure renovation here.After the renovation, the project structure is as follows

├── public                          // Static Page
├── scripts                         // Related script configuration
├── src                             // home directory
    ├── assets                      // Static Resources
    ├── filters                     // filter
    ├── lib                         // Global Plugins
    ├── router                      // Routing Configuration
    ├── store                       // vuex configuration
    ├── styles                      // style
    ├── types                       // Global Injection
    ├── utils                       // Tool methods (axios encapsulation, global methods, etc.)
    ├── views                       // page
    ├── App.vue                     // Page Main Entry
    ├── main.ts                     // Script main entry
    ├── registerServiceWorker.ts    // PWA Configuration
├── tests                           // test case
├── .editorconfig                   // Edit related configurations
├── .npmrc                          // npm source configuration
├── .postcssrc.js                   // postcss configuration
├── babel.config.js                 // Press record
├── cypress.json                    // e2e plugins
├── f2eci.json                      // Deploy related configurations
├── package.json                    // rely on
├── README.md                       // Project readme
├── tsconfig.json                   // ts Configuration
├── tslint.json                     // tslint configuration
└── vue.config.js                   // webpack configuration

3. Module Renovation

Next, I'll introduce some module changes in the project

i. Routing Lazy Loading

This uses webpack's on-demand loading import, putting the same module's stuff in the same chunk and writing in router/index.ts

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    { path: '/', name: 'home', component: () => import(/* webpackChunkName: "home" */ 'views/home/index.vue') }
  ]
})

ii, axios packaging

Write axios-related configurations in utils/config.ts (only a small part is listed, please ask the small partners to configure themselves according to their own business)

import http from 'http'
import https from 'https'
import qs from 'qs'
import { AxiosResponse, AxiosRequestConfig } from 'axios'

const axiosConfig: AxiosRequestConfig = {
  baseURL: '/',
  // Data processing after request
  transformResponse: [function (data: AxiosResponse) {
    return data
  }],
  // Query Object Serialization Function
  paramsSerializer: function (params: any) {
    return qs.stringify(params)
  },
  // Timeout Settings s
  timeout: 30000,
  // Is there a Token across domains
  withCredentials: true,
  responseType: 'json',
  // xsrf settings
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  // Maximum number of forwards, for node.js
  maxRedirects: 5,
  // Maximum Response Data Size
  maxContentLength: 2000,
  // Custom Error Status Code Range
  validateStatus: function (status: number) {
    return status >= 200 && status < 300
  },
  // For node.js
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true })
}

export default axiosConfig

Next, we need to do some global interception in utils/api.ts. Here I've unified the cancellation of duplicate requests in interceptors. If your business doesn't need it, please do it yourself

import axios from 'axios'
import config from './config'

// Cancel duplicate request
let pending: Array<{
  url: string,
  cancel: Function
}> = []
const cancelToken = axios.CancelToken
const removePending = (config) => {
  for (let p in pending) {
    let item: any = p
    let list: any = pending[p]
    // Execute function body when current request exists in array
    if (list.url === config.url + '&' + config.method) {
      // Perform Cancellation
      list.cancel()
      // Remove records from an array
      pending.splice(item, 1)
    }
  }
}

const service = axios.create(config)

// Add Request Interceptor
service.interceptors.request.use(
  config => {
    removePending(config)
    config.cancelToken = new cancelToken((c) => {
      pending.push({ url: config.url + '&request_type=' + config.method, cancel: c })
    })
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// Return to status judgment (add response interceptor)
service.interceptors.response.use(
  res => {
    removePending(res.config)
    return res
  },
  error => {
    return Promise.reject(error)
  }
)

export default service

For convenience, we also need to define a fixed set of axios return formats, which we can define globally.Write in types/ajax.d.ts file

declare namespace Ajax {
  // axios returns data
  export interface AxiosResponse {
    data: AjaxResponse
  }

  // Request Interface Data
  export interface AjaxResponse {
    code: number,
    data: any,
    message: string
  }
}

Next, we'll put all axios in vuex's actions for unified management

Modular management of iii and vuex

Below the store, a folder represents a module, and the general directory of the store is as follows

├── home                            // home directory
    ├── index.ts                    // vuex state getters mutations action management
    ├── interface.ts                // Interface Management
└── index.ts                        // vuex main entry

Manage interfaces for related modules in home/interface.ts

export interface HomeContent {
  name: string
  m1?: boolean
}
export interface State {
  count: number,
  test1?: Array<HomeContent>
}

Then define the relevant vuex module content at home/index.ts

import request from '@/service'
import { State } from './interface'
import { Commit } from 'vuex'

interface GetTodayWeatherParam {
  city: string
}

const state: State = {
  count: 0,
  test1: []
}

const getters = {
  count: (state: State) => state.count,
  message: (state: State) => state.message
}

const mutations = {
  INCREMENT (state: State, num: number) {
    state.count += num
  }
}

const actions = {
  async getTodayWeather (context: { commit: Commit }, params: GetTodayWeatherParam) {
    return request.get('/api/weatherApi', { params: params })
  }
}

export default {
  state,
  getters,
  mutations,
  actions
}

Then we can use it on the page

<template>
  <div class="home">
    <p>{{ count }}</p>
    <el-button type="default" @click="INCREMENT(2)">INCREMENT</el-button>
    <el-button type="primary" @click="DECREMENT(2)">DECREMENT</el-button>
    <el-input v-model="city" placeholder="Please enter a city" />
    <el-button type="danger" @click="getCityWeather(city)">Get the weather</el-button>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { State, Getter, Mutation, Action } from 'vuex-class'

@Component
export default class Home extends Vue {
  city: string = 'Shanghai'
    
  @Getter('count') count: number
  @Mutation('INCREMENT') INCREMENT: Function
  @Mutation('DECREMENT') DECREMENT: Function
  @Action('getTodayWeather') getTodayWeather: Function

  getCityWeather (city: string) {
    this.getTodayWeather({ city: city }).then((res: Ajax.AjaxResponse) => {
      const { low, high, type } = res.data.forecast[0]
      this.$message.success(`${city}Today: ${type} ${low} - ${high}`)
    })
  }
}
</script>

As for more modifications, I will not introduce them here.The next section describes some of the ways ts are written in the vue file

3. Use of ts in vue

1,vue-property-decorator

Here the single page component is written using vue-property-decorator Library, which is completely dependent on vue-class-component Is also the official library recommended by vue.

In single-page components, it is extremely inconvenient to write props, data, etc. in @Component({}), while vue-property-decorator contains eight decorators to solve this problem, which are

  • @Emit. Specifies the event emit, which can be used with this modifier or directly with this.$emit()
  • @Inject Specifies Dependent Injection)
  • @Mixins mixin injection
  • @model Specifies model
  • @Prop Specifies Prop
  • @Provide Specifies Provide
  • @Watch Specifies Watch
  • @Component export from vue-class-component

Enumerate

import {
  Component, Prop, Watch, Vue
} from 'vue-property-decorator'

@Component
export class MyComponent extends Vue {
  dataA: string = 'test'
    
  @Prop({ default: 0 })
  propA: number

  // watcher
  @Watch('child')
  onChildChanged (val: string, oldVal: string) {}
  @Watch('person', { immediate: true, deep: true })
  onPersonChanged (val: Person, oldVal: Person) {}

  // Other modifiers are detailed in the github address above, which is not explained here
}

After parsing it becomes

export default {
  data () {
    return {
      dataA: 'test'
    }
  },
  props: {
    propA: {
      type: Number,
      default: 0
    }
  },
  watch: {
    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    },
    'person': {
      handler: 'onPersonChanged',
      immediate: true,
      deep: true
    }
  },
  methods: {
    onChildChanged (val, oldVal) {},
    onPersonChanged (val, oldVal) {}
  }
}

2,vuex-class

vuex-class Is based on Vue,Vuex,vue-class-component Like vue-property-decorator, vuex library also provides four modifiers and namespace, which solves the inconvenience of using vuex in.Vue files.

Cop an official

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const someModule = namespace('path/to/module')

@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @someModule.Getter('foo') moduleGetterFoo

  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux

  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}

Here, the usage of ts in the.vue file is almost described.I also believe that my little buddy saw this and had some understanding of its general grammatical sugar.

3. Some suggestions

  • If you have defined a.d.ts file, restart the service so that your service recognizes the module you defined, and restart vscode so that the editor can also recognize it (really nausea)
  • Set your tsconfig, for example, remember to set strictPropertyInitialization to false, otherwise you must give it an initial value to define a variable.
  • Manage your routing hierarchy, or even the rules won't save you
  • At the business level, do a good job of type detection or enumeration definition, which not only facilitates development, but also quickly locates problems when they arise
  • Use vuex across modules, use rootGetters directly
  • If you need to modify the theme of a component library, separate one file for centralized management, separate one file for each component to change, otherwise compiling speed is worrying
  • Be able to reuse what others in your team have developed, and try not to develop it a second time, otherwise you may not just waste development time, but also code review time

There's a bunch of things like that, but more of them you'll explore yourself.Next, I'll talk about some specifications for teamwork in large projects

4. How to Work in Team

A large project must be accompanied by many people, including not only front-end team cooperation, but also demand exploration (bi) with product classmates, and coordination with back-end classmates, or even need to configure some services by themselves or relying on SRE.

1. Front End Development Specification

Since the project is vue + ts based and multi-person collaboration, development specifications must be necessary to make concurrent development easier.Next, I pull out some of the specifications I made at that time to give my little buddies some reference (just for reference)

i. Page development placement order

  • HTML
  • TypeScript
  • CSS
<template>
</template>

<script lang="ts">
</script>

<style lang="scss">
</style>

ii. CSS rules (use BEM naming rules to avoid style conflicts, do not use scoped)

<template>
  <div class="home">
    <div class="home__count">{{ count }}</div>
    <div class="home__input"></div>
  </div>
</template>

<style lang="scss">
.home {
  text-align: center;
  &__count {}
  &__input {}
}
</style>

TS Context Order in iii. vue File

  • data

  • @Prop

  • @State

  • @Getter

  • @Action

  • @Mutation

  • @Watch

  • Lifecycle hook

    • beforeCreate (top to bottom hooks by lifecycle)

    • created

    • beforeMount

    • mounted

    • beforeUpdate

    • updated

    • activated

    • deactivated

    • beforeDestroy

    • destroyed

    • errorCaptured (last life cycle hook)

  • Routing hook

    • beforeRouteEnter

    • beforeRouteUpdate

    • beforeRouteLeave

  • computed

  • methods

Component references, mixins, filters, etc. are placed in @Component

<script lang="ts">
@Component({
  components: { HelloWorld },
  mixins: [ Emitter ]
})
export default class Home extends Vue {
  city: string = 'Shanghai'

  @Prop({ type: [ Number, String ], default: 16 })
  @State('state') state: StateInterface
  @Getter('count') count: Function
  @Action('getTodayWeather') getTodayWeather: Function
  @Mutation('DECREMENT') DECREMENT: Function
  
  @Watch('count')
  onWatchCount (val: number) {
    console.log('onWatchCount', val)
  }
  
  // computed
  get styles () {}
  
  created () {}
  mounted () {}
  destroyed () {}

  // methods
  getCityWeather (city: string) {}
}
</script>

iv. vuex modular management

A folder under the store corresponds to a module, each module has an interface for interface management, as mentioned above

v. Routing introducing posture

Route lazy loading, as shown above

vi. File Naming Specification

Lower case words, separated by'-', as shown in Fig.

The noun precedes the verb, as shown in the figure

Same module description comes first, different description comes last

2. Collaboration with Product+Backend, etc.

Remember the following three points:

  1. Be polite to explore (si) and discuss (bi)

  2. Be polite to explore (si) and discuss (bi)

  3. Be very polite to explore (si) and discuss (bi)

I knew there was an answer in the details, so I won't go into it here.Port: What should I do when the front and back ends are separated and the data returned from the back end cannot be written?

3. Human Effectiveness Improvement

Last point, we talked about collaboration at the development level.Here, let's talk about efficiency improvement.

Everyone knows that the most important reason for a project to be able to complete development + debugging + testing + online within a set time limit is the efficiency of everyone.We can't guarantee that everyone is efficient, but we have to ensure that we are developing efficiently.

When we need it, the first thing we have to guarantee is our own understanding of it.Generally, for the old hand, it takes roughly how long it takes to complete the requirement once in mind, while the new hand never has a good understanding of the completion time.

So how can you improve your development efficiency?

  • Split Requirements into Modules
  • Split things in the module into small details again
  • Evaluate the development time of the small details themselves
  • Evaluate the development time of some potential risk points in small details
  • Evaluate tuning time
  • Reserve Test + Repair BUG Time Node
  • Reserve deadline (generally 1 * (1 + 0.2))
  • Arrange your own development nodes in 1D (one day)
  • Document risk causes, risk points and corresponding avoidance plans
  • If you anticipate a delay, provide timely remedies (e.g. overtime)
  • Record the number of BUGs and the corresponding BUG personnel (really not to shake the pot)

summary

This is almost the end of the article.Talk about the selection before the project is started, the infrastructure at the beginning of the project, the use of ts in.vue, and even some things about team cooperation in project development.However, after all, the writing is limited, many points can not be explained, most of them are point-to-point.If you find the article helpful to your little buddies, don't stint on your favorite articles

If you want to know more about your little buddies, please join the despicable group: 731175396

Recruitment stickers

At the end of the article, wave the team:

Meituan Shanghai side recruited ~~ 15.5 salary, 15.5 inch Mac, salary 25K-45K, stock options.

Advanced/experienced front-end development

Operating duty:

  1. Responsible for web front-end architecture design and code implementation
  2. Analysis and discovery of optimizable points in the system to improve reliability and performance
  3. Common Javascript module packaging and performance optimization, updating and maintaining company front-end development component libraries
  4. Research the latest technology in the industry and its application, solve the key problems and technical difficulties in the process of innovation research and development

Job Requirements:

  1. Proficiency in Javascript, H5, Sass/Less, and HTML front-end template engines
  2. Familiar with ECMAScript, CommonJS, Promise, TypeScript and other standards, familiar with Git
  3. Familiarize with object-oriented JavaScript development, participate in or design JS framework or common component development experience
  4. Familiarize yourself with Vue.js or React.js frameworks, research their source implementations, and familiarize yourself with data-driven principles
  5. In-depth study on Javascript engine implementation mechanism and browser rendering performance
  6. Familiarize yourself with Node.js and understand one of the back-end languages like PHP/java/python
  7. Familiarize yourself with front-end building tools such as gulp, webpack, etc., and build project scaffolding to improve development efficiency
  8. Good problem solving, understanding and learning abilities, good collaboration and team spirit
  9. Good self-driving, hands-free, bold in exploring new technologies and applying them

Add-ons:

  1. Familiarize yourself with the Node.js language
  2. Have open source blogs or technical blogs
  3. Technology Community Active
  4. Independent works on Github
  5. Geek control, enthusiastic interest and pursuit of Technology
  6. First-line Internet company experience

Students interested in the above positions are welcome to join the group: 731175396, then contact me

Posted by Ifa on Sat, 11 May 2019 15:58:57 -0700