TypeScript + крупномасштабный бой проекта

TypeScript Vue.js
TypeScript + крупномасштабный бой проекта

написать впереди

TypeScript давно отсутствует, и многие крупные компании и крупные проекты также используют его для разработки. В прошлом месяце я также официально руководил крупномасштабным проектом по эксплуатации и техническому обслуживанию группы.

Проект, который нужно сделать, примерно разделен на следующие основные модули.

  • Единая платформа управления
  • Широкие возможности эксплуатации и обслуживания
  • Платформа плана
  • Инспекционная платформа
  • Стресс-тест полной ссылки и т. д.

В каждом модуле очень много дел, так как это связано с бизнесом компании, я не буду здесь перечислять некоторые конкретные дела, но в любом случае общий масштаб проекта все равно очень велик.

1. О выборе

После проведения некоторых технических исследований в сочетании с уровнем затрат на разработку и обслуживание после завершения проекта. В конце концов, мои коллеги и я пришли к одному и тому же выводу по техническому выбору, и окончательный выбор был последним семейным ведром Vue + TypeScript.

Так вот вопрос, зачем в крупных проектах использовать TypeScript, ES6 и 7 не хватает?

На самом деле я не говорил «нет», но лично я предпочитаю использовать TypeScript в некоторых крупномасштабных проектах совместной разработки. Вот некоторые из моих мыслей после проведения исследования

  1. Во-первых, TypeScript имеет систему типов и является надмножеством JavaScript. Что может JavaScript, он может. Чего JavaScript не может, он может.

  2. Во-вторых, TypeScript относительно зрел, и на рынке есть много материалов по теме, и большинство библиотек и фреймворков также хорошо читают и поддерживают TypeScript.

  3. Затем, обеспечивая отличную предпосылку, он активно развивается, чтобы улучшить, продолжать добавлять новые функции.

  4. JavaScript — слабый тип и не имеет пространства имен, что затрудняет модульность, что не очень удобно в больших совместных проектах.

  5. Такие редакторы, как vscode и ws, поддерживают TypeScript.

  6. TypeScript лучше поддерживает проверку компонентов и бизнес-типа, например

    // 定义枚举
    const enum StateEnum {
      TO_BE_DONE = 0,
      DOING = 1,
      DONE = 2
    }
    
    // 定义 item 接口
    interface SrvItem {
      val: string,
      key: string
    }
    
    // 定义服务接口
    interface SrvType {
      name: string,
      key: string,
      state?: StateEnum,
      item: Array<SrvItem>
    }
    
    // 然后定义初始值(如果不按照类型来,报错肯定是避免不了的)
    const types: SrvType = {
      name: '',
      key: '',
      item: []
    }
    

    Сотрудничайте с редактором.Если вы не будете следовать определенному типу, редактор сам выдаст вам ошибку, вместо того, чтобы ждать, пока компиляция сообщит об ошибке.

  7. Объявление пространства имен + интерфейса более удобно для проверки типов и предотвращает ошибки в коде.

    Например, вы определяете возвращаемый тип ajax в файле ajax.d.ts.

    declare namespace Ajax {
      // axios 返回数据
      export interface AxiosResponse {
        data: AjaxResponse
      }
    
      // 请求接口数据
      export interface AjaxResponse {
        code: number,
        data: object | null | Array<any>,
        message: string
      }
    }
    

    Затем можно использовать, когда требуется

    this.axiosRequest({ key: 'idc' }).then((res: Ajax.AjaxResponse) => {
      console.log(res)
    })
    
  8. Обобщения можно использовать для создания повторно используемых компонентов. Например, вы хотите создать универсальный метод, тип параметра которого совпадает с типом возвращаемого значения.

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

    Другой пример: вы хотите использовать дженерики для блокировки типов, используемых в коде.

    interface GenericInterface<T> {
      (arg: T): T
    }
    
    function foo<T> (arg: T): T {
      return arg
    }
    
    // 锁定 myFoo 只能传入 number 类型的参数,传其他类型的参数则会报错
    let myFoo: GenericInterface<number> = foo
    myFoo(123)
    

Короче говоря, плюсов от использования TypeScript еще много, я не буду их здесь перечислять, заинтересованные друзья могут сами проверить информацию

2. Инфраструктура

1. Инициализировать структуру

Для инициализации проекта я использую последнюю версию скаффолдинга vue-cli 3. Варианты инициализации следующие:

Результирующая структура каталогов выглядит следующим образом

├── public                          // 静态页面
├── src                             // 主目录
    ├── assets                      // 静态资源
    ├── components                  // 组件
    ├── views                       // 页面
    ├── App.vue                     // 页面主入口
    ├── main.ts                     // 脚本主入口
    ├── registerServiceWorker.ts    // PWA 配置
    ├── router.ts                   // 路由
    ├── shims-tsx.d.ts              // 相关 tsx 模块注入
    ├── shims-vue.d.ts              // Vue 模块注入
    └── store.ts                    // vuex 配置
├── tests                           // 测试用例
├── .postcssrc.js                   // postcss 配置
├── package.json                    // 依赖
├── tsconfig.json                   // ts 配置
└── tslint.json                     // tslint 配置

2. Реконструированная структура

Очевидно, что они не могут соответствовать развитию нормального бизнеса, поэтому я сделал вариант преобразования инфраструктуры здесь. После ремонта структура проекта выглядит следующим образом.

├── public                          // 静态页面
├── scripts                         // 相关脚本配置
├── src                             // 主目录
    ├── assets                      // 静态资源
    ├── filters                     // 过滤
    ├── lib                         // 全局插件
    ├── router                      // 路由配置
    ├── store                       // vuex 配置
    ├── styles                      // 样式
    ├── types                       // 全局注入
    ├── utils                       // 工具方法(axios封装,全局方法等)
    ├── views                       // 页面
    ├── App.vue                     // 页面主入口
    ├── main.ts                     // 脚本主入口
    ├── registerServiceWorker.ts    // PWA 配置
├── tests                           // 测试用例
├── .editorconfig                   // 编辑相关配置
├── .npmrc                          // npm 源配置
├── .postcssrc.js                   // postcss 配置
├── babel.config.js                 // preset 记录
├── cypress.json                    // e2e plugins
├── f2eci.json                      // 部署相关配置
├── package.json                    // 依赖
├── README.md                       // 项目 readme
├── tsconfig.json                   // ts 配置
├── tslint.json                     // tslint 配置
└── vue.config.js                   // webpack 配置

3. Модульное преобразование

Далее я представлю трансформацию некоторых модулей в проекте

I. Маршрутизация ленивой загрузки

Импорт нагрузки на посяжению по требованию используется здесь, а вещи того же модуля введены в тот же кусок.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

существуетutils/config.tsнаписать вaxiosСоответствующая конфигурация (перечислена лишь небольшая часть, пожалуйста, настройте ее в соответствии с вашим бизнесом)

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

const axiosConfig: AxiosRequestConfig = {
  baseURL: '/',
  // 请求后的数据处理
  transformResponse: [function (data: AxiosResponse) {
    return data
  }],
  // 查询对象序列化函数
  paramsSerializer: function (params: any) {
    return qs.stringify(params)
  },
  // 超时设置s
  timeout: 30000,
  // 跨域是否带Token
  withCredentials: true,
  responseType: 'json',
  // xsrf 设置
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  // 最多转发数,用于node.js
  maxRedirects: 5,
  // 最大响应数据大小
  maxContentLength: 2000,
  // 自定义错误状态码范围
  validateStatus: function (status: number) {
    return status >= 200 && status < 300
  },
  // 用于node.js
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true })
}

export default axiosConfig

Далее вам необходимоutils/api.tsДелайте какие-то глобальные операции по перехвату в перехватчике, здесь я единообразно обрабатываю отмену повторных запросов в перехватчике, если вашему бизнесу это не нужно, пожалуйста, уберите это сами

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

// 取消重复请求
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]
    // 当前请求在数组中存在时执行函数体
    if (list.url === config.url + '&request_type=' + config.method) {
      // 执行取消操作
      list.cancel()
      // 从数组中移除记录
      pending.splice(item, 1)
    }
  }
}

const service = axios.create(config)

// 添加请求拦截器
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)
  }
)

// 返回状态判断(添加响应拦截器)
service.interceptors.response.use(
  res => {
    removePending(res.config)
    return res
  },
  error => {
    return Promise.reject(error)
  }
)

export default service

Для удобства нам также необходимо определить фиксированный набор форматов, возвращаемых axios, которые мы можем определить глобально. существуетtypes/ajax.d.tsзаписать в файл

declare namespace Ajax {
  // axios 返回数据
  export interface AxiosResponse {
    data: AjaxResponse
  }

  // 请求接口数据
  export interface AjaxResponse {
    code: number,
    data: any,
    message: string
  }
}

Далее мы поместим всеaxiosпомещатьvuexизactionsединое управление

iii, модульное управление vuex

Под магазином папка представляет собой модуль, а приблизительный каталог магазина выглядит следующим образом.

├── home                            // 主目录
    ├── index.ts                    // vuex state getters mutations action 管理
    ├── interface.ts                // 接口管理
└── index.ts                        // vuex 主入口

существуетhome/interface.tsИнтерфейсы для управления связанными модулями в

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

затем вhome/index.tsСвязанные с определениемvuexСодержание модуля

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
}

Затем мы можем использовать его на странице

<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="请输入城市" />
    <el-button type="danger" @click="getCityWeather(city)">获取天气</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 = '上海'
    
  @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}今日:${type} ${low} - ${high}`)
    })
  }
}
</script>

Что касается других трансформаций, я не буду их здесь приводить. В следующих подразделах будут представлены некоторые способы написания ts в файле vue.

3. Использование ts в vue

1. vue-свойство-декоратор

Здесь одностраничный компонент написан с использованиемvue-property-decoratorбиблиотека, которая полностью зависит отvue-class-component, которая также является библиотекой, официально рекомендованной vue.

В одном компоненте страницы, в@Component({})написать внутриprops,dataКрайне неудобно звонить, иvue-property-decoratorОн содержит 8 декораторов для решения таких задач, они

  • @EmitУкажите событие для генерации, вы можете использовать этот модификатор, или вы можете использовать его напрямуюthis.$emit()
  • @Injectуказать внедрение зависимостей)
  • @Mixinsинъекция миксина
  • @Modelуказать модель
  • @PropУкажите опору
  • @ProvideУказать Предоставить
  • @WatchУказать часы
  • @Component export from vue-class-component

дать 🌰

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) {}

  // 其他修饰符详情见上面的 github 地址,这里就不一一做说明了
}

После разбора становится

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-класс

vuex-classосновывается наVue,Vuex,vue-class-componentбиблиотека иvue-property-decoratorТочно так же он также предоставляет 4 модификатора и пространства имен, которые устраняют неудобства использования vuex в файлах .vue.

  • @State
  • @Getter
  • @Mutation
  • @Action
  • namespace

скопируйте официальную 🌰

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']
  }
}

На данный момент использование ts в файлах .vue почти такое же. Я также считаю, что мои друзья видели это и имеют определенное представление об общем синтаксическом сахаре.

3. Некоторые предложения

  • если определено.d.tsфайл, пожалуйста, перезапустите службу, чтобы ваша служба могла распознать определенный вами модуль, и перезапустите vscode, чтобы редактор также мог его распознать (действительно отвратительно)
  • установите свойtsconfig, например, не забудьте поставитьstrictPropertyInitializationУстановите значение false, в противном случае вы должны задать ему начальное значение при определении переменной.
  • Обязательно хорошо управляйте уровнем маршрутизации, иначе даже регулярность вас не спасет
  • На бизнес-уровне определение типов или перечисление должно быть выполнено хорошо, что не только облегчает разработку, но и позволяет быстро обнаруживать проблемы при их возникновении.
  • Чтобы использовать vuex в модулях, используйте его напрямую.rootGetters
  • Если вам нужно изменить тему библиотеки компонентов, откройте один файл для централизованного управления и не изменяйте каждый компонент в одном файле, иначе скорость компиляции будет беспокоить.
  • Уметь повторно использовать вещи, разработанные другими людьми в команде, стараться не разрабатывать это во второй раз, иначе потерянное время может быть не только временем разработки, но и временем проверки кода

Есть еще куча подобных, но вам придется узнать больше для себя. Далее я расскажу о некоторых нормах совместной работы в больших проектах.

4. Как сотрудничать в командах

Большой проект должен быть распараллелен многими людьми, это не только сотрудничество команды фронтенда, но и исследование спроса (си) и обсуждение (би) с одноклассниками по продукту, а также совместная отладка с бэком -конец одноклассникам, а то и необходимости самому Или полагаться на SRE для настройки некоторых сервисов.

1. Спецификации фронтенд-разработки

Поскольку проект основан на vue + ts и совместной работе нескольких человек, определенно необходимы спецификации разработки, которые могут упростить параллельную разработку. Ниже я взял некоторые спецификации, которые я сделал в то время, в качестве справки для моих друзей (просто для справки).

I. Порядок разработки страницы

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

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

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

2. Правила CSS (используйте правила именования БЭМ, чтобы избежать конфликтов стилей, не используйте область действия)

<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>

iii. Порядок контекста TS в файле vue

  • data

  • @Prop

  • @State

  • @Getter

  • @Action

  • @Mutation

  • @Watch

  • крючки жизненного цикла

    • beforeCreate (сверху вниз в соответствии с крючками жизненного цикла)

    • created

    • beforeMount

    • mounted

    • beforeUpdate

    • updated

    • activated

    • deactivated

    • beforeDestroy

    • destroyed

    • errorCaptured (последний хук жизненного цикла)

  • крючок маршрутизации

    • beforeRouteEnter

    • beforeRouteUpdate

    • beforeRouteLeave

  • computed

  • methods

Ссылки на компоненты, примеси, фильтры и т. д. размещаются в @Component.

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

  @Prop({ type: [ Number, String ], default: 16 })
  size: number | string
  @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

Папка под магазином соответствует модулю, и каждый модуль имеет интерфейс для управления интерфейсом.Конкретный пример упомянут выше.

v. Поза введения маршрута

Маршрутизация ленивой загрузки, также есть примеры выше

vi. Соглашения об именах файлов

Слова в нижнем регистре, разделенные знаком «-», как показано на рисунке.

Существительные идут первыми, глаголы идут после

Один и тот же модуль описан первым, другие описания идут после

2. Сотрудничайте с продуктом + серверной частью и т. д.

Запомните эти три вещи:

  1. Будьте вежливы, чтобы исследовать (си), чтобы спросить (би)

  2. Будьте вежливы, чтобы исследовать (си), чтобы спросить (би)

  3. Прощупать (си) спросить (би) очень вежливо

Конкретные детали были даны ранее в Zhihu, поэтому я не буду повторять их здесь. Портал:Внешняя и внутренняя части разделены, и данные, возвращаемые серверной частью, не могут быть записаны во внешнюю часть. Что мне делать?

3. Повышение эффективности человека

В предыдущем пункте я говорил о сотрудничестве на уровне разработки. Здесь давайте поговорим о повышении эффективности человека.

Всем известно, что если проект сможет завершить разработку + совместную отладку + тестирование + запуск в заданные сроки, самое главное — это эффективность работы каждого. Мы не можем гарантировать, что все будут очень эффективными, но мы должны гарантировать собственную эффективность разработки.

Когда появляется спрос, первое, что мы должны обеспечить, — это наше собственное понимание спроса. Как правило, ветераны, пройдя требования один раз, будут иметь общее представление о том, сколько времени потребуется для выполнения требований, в то время как новички никогда не будут иметь четкого представления о времени выполнения.

Итак, как повысить эффективность разработки?

  • Разделите требования на модули
  • Снова разделите вещи в модулях на мелкие детали
  • Оцените время разработки самих мелких деталей
  • Время разработки для оценки некоторых возможных точек риска в мелких деталях
  • Оцените время совместной отладки
  • Зарезервировать тест + исправить ошибку
  • Крайний срок бронирования (обычно 1 * (1 + 0,2))
  • Организуйте свои собственные узлы разработки в единицах 1D (один день)
  • Причины риска записи, точки риска и соответствующие планы избегания
  • Если есть подозрение, что оно будет отложено, следует вовремя дать план исправления (например, сверхурочная работа).
  • Запишите количество BUG и соответствующий персонал BUG (на самом деле не для сброса)

Суммировать

Статья почти здесь. Я рассказал об отборе до создания проекта, инфраструктуре на ранней стадии проекта, использовании ts в .vue и даже некоторых вещах о совместной работе в команде при разработке проекта. Но ведь стиль написания ограничен, и многие моменты нельзя объяснить прямолинейно, и большинство из них останавливается на конце. Если вы считаете, что статья будет полезна вашим друзьям, не скупитесь на лайки.

Если вы, ребята, хотите узнать больше, присоединяйтесь к моей группе общения: 731175396