Освободите руки — синтаксис vue автоматически преобразуется в машинописный

Vue.js

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

Когда я обычно пишу код, если я чувствую, что определенный кусок бизнес-кода был написан кем-то другим ранее, то, учитывая приоритет бизнеса, пока код других людей не слишком плохо написан, я обычно копирую чужой код. код сначала, чтобы спасти себя написать его снова Затем я столкнулся с проблемой, большинство текущих front-end проектов компанииvue, не ранееtsПозднее это утверждение постепенно вводилось в новые проекты.ts, поэтому новый проект используетvue-ts, а старый код, который вы вообще хотите скопировать, не импортируетсяtsДа, конечно, они совместимы, но для меня, у которого есть небольшая чистота кода, я все равно не хочу видеть один и тот же код проекта, смешанный сtsи неtsЕсть два способа написания, поэтому, пока есть время, я постараюсь вручную преобразовать старый код вtsнормативный

Сложности не так уж и много, но каждый приходится крутить вручную.Покрутив много, я вдруг впал в глубокую задумку, мне кажетсяrepeat myselfАх, я не мог этого вынести, поэтому я решил написать автоматическийvue-jsПревратиться вvue-tsИнструмент

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

@babel

относится кjsПреобразование синтаксиса, первое, что приходит на ум, этоbabelсейчас,babelуже обеспечила богатоеjsИнструменты анализа грамматики и анти-анализа

@babel/parser

@babel/parserотвечает за разборjsграмматическое средство, которое можно понимать какjsсинтаксис переводится какast, что удобно разработчикам для настройки обработки, черезpluginsдля поддержки различныхjsсинтаксис, напримерes6,es7,ts,flow,jsxили даже какой-то лабораторный синтаксис (experimental language proposals)Ждать

Например:

const code = 'const a = 1'
const ast = require("@babel/parser").parse(code)

преобразованныйastявляется объектом, структура данных описываетconst a = 1это выражение

к этомуastПроходя, вы можете получить все проанализированные в данный моментjsграмматическая информация, которая, естественно, также может быть изменена

@babel/generator

Если есть анализ, есть и антианализ.@babel/generatorдля@babel/parserпроанализированоastПреобразование обратно в строковую формуjsкод

const code = 'const a = 1;'
const ast = require("@babel/parser").parse(code)
const codeStr = require('@babel/generator').default(ast).code
code === codeStr // => true

разное

в общем@babel/parser,@babel/generatorа также@babel/traverseПоявятся и будут использоваться вместе, первые два были введены ранее, что касается@babel/traverse, основная функция которого заключается в@babel/parserСгенерированоastДля обхода предусмотрены некоторые методы, чтобы уберечь разработчиков от самостоятельных суждений.

Однако программа, которую я здесь написал, бесполезна, поскольку не требует слишком детального анализа.@babel/traverseЭта вещь, по моей волеastвыполнить операцию обхода

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

Операции, описанные ниже в этой статье, в основном относятся к@babel/parserпреобразованныйast,так же как@babel/generatorв проанализированной строке кода

props

vueОфициальный сайт дляpropsвводится вprops

следовательноpropsСпецификации соответствуют следующие способы написания:

export default {
  props: ['size', 'myMessage'],
  props: {
    a: Number,
    b: [Number, String],
    c: 'defaultValue',
    d: {
      type: [Number, String]
    }
    e: {
      type: Number,
      default: 0,
      required: true,
      validator: function (value) {
        return value >= 0
      }
    }
  }
}

Вышеуказанное преобразуется вtsСоответствует следующим образом:

export default class YourComponent extends Vue {
  @Prop() readonly size: any | undefined
  @Prop() readonly myMessage: any | undefined
  @Prop({ type: Number }) readonly a: number | undefined
  @Prop([Number, String]) readonly b: number | string | undefined
  @Prop() readonly c!: any
  @Prop({ type: [Number, String] }) readonly d: number | string | undefined
  @Prop({ type: Number, default: 0, required: true, validator: function (value) {
    return value >= 0
  } }) readonly e!: number
}

ok, то это легко сделать, во-первыхpropsТолько тип значенияArray<string>и объекты этих двух типов

тип массива

Array<string>Типы просты в обращении, просто шаблон преобразования:

@Prop() readonly propsName: any | undefined

просто пройтиArray<string>Типprops, тогда ставьpropsNameПросто замените его реальным значением

тип объекта

Шаблон преобразования типа объекта добавляет некоторые строки в шаблон типа массива, в основном@PropПараметры:

@Prop({ type: typeV, default: defaultV, required: requiredV, validator: validatorV }) readonly propsName: typeV

propsКаждое свойство этого большого объекта являетсяpropsName, это нормально, тогдаpropsNameСоответствующее значение, которое может бытьtype,typeв один тип (например,Number) и типизированные массивы (например,[Number, String]); может быть объектом, атрибуты которого не менее0вплоть до4, если этот объект имеет свойство с именемtypeатрибут, то значение этого атрибута также должно определять единый тип и массив типов, а другие атрибуты могут напрямую принимать исходное значение.

несмотря ни на чтоpropsЯвляется ли значение свойства объекта объектом илиtype, все нужно обработатьtype, поэтому специализированная ручкаtypeМетодыhandlerType

Таким образом, если этоtype,ноhandlerTypeОбработайте его напрямую; если это объект, пройдитесь по свойствам объекта и обнаружите, что свойстваtype, затем позвонитеhandlerTypeдля обработки, в противном случае он непосредственно используется как@Propпараметры могут быть

data

vueОфициальный сайт дляdataвводится вdata

dataТип может бытьObjectилиFunction, то есть допустимы следующие способы написания:

export default {
  data: {
    a: 1
  },
  data () {
    return {
      a: 1
    }
  },
  data: function () {
    return {
      a: 1
    }
  }
}

Вышеупомянутое преобразуется вtsСоответствует следующим образом:

export default class YourComponent extends Vue {
  a: number = 1
}

Так вот тут очень понятно, то есть братьdataВозвращает каждое свойство объекта значения, какclassсвойства, кажется, что вы можете преобразовать его

но,dataНа самом деле, вы также можете написать:

export default {
  data () {
    const originA = 1
    return {
      a: originA
    }
  }
}

когдаdataдаFunctionтипа, когдаreturnРанее вы также можете запустить фрагмент кода, результат которого может повлиять наdataзначение

Такой способ написания не редкость, поэтому игнорировать его нельзя, но как с этим боротьсяreturnпредыдущий код? Мой подход заключается вreturnвставьте предыдущий кодcreatedв функции жизненного цикла и вcreatedПосле этих кодов вdataпереназначить значение Например, для приведенного выше кода преобразование вts, ты можешь это сделать:

export default class YourComponent extends Vue {
  a: any = null
  created () {
    const originA = 1
    this.a = originA
  }
}

Итак, это снова касаетсяdataправильноcreatedДанные были изменены, здесь вы можете рассмотреть возможность принудительной обработки в первую очередьdata, но я глянул.На самом деле тут не сложно написать два куска логики,поэтому порядок обработки строго не оговариваю.

model

vueОфициальный сайт дляmodelвводится вmodel

modelпроцитировано вpropsЗначение, такmodelиспользовать на самом деле требуетpropsкооператив

export default {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: {
      type: Boolean
    }
  }
}

Вышеупомянутое преобразуется вtsСоответствует следующим образом:

export default class YourComponent extends Vue {
  @Model('change', { type: Boolean }) readonly checked!: boolean
}

видимый,@Modelесть заявлениеpropsфункция, в@Modelобъявлено вprops, Нет необходимости в@PropОн снова объявлен в файле , поэтому порядок обработки я упорядочил здесь.model, переработкаprops, и обрабатываетpropsкогдаmodelбыло объявлено вpropsотфильтровывать

Конечно, можно и не заниматься этим специально сначалаmodelпереработкаprops, пока обработкаmodelпора судить, рассматривался ли он раньшеprops, по результатам сделать соответствующий поток обработки, но это немного хлопотно, нужно основываться наpropsСтоит ли иметь дело с двумя частями логики или нет, эти две части логики больше, чем приведенные выше.dataвлияниеcreatedЭто сложнее, поэтому я разберусь с этим прямо здесь, чтобы не утруждать себя

computed

vueОфициальный сайт дляmodelвводится вcomputed

Последующийcomputedнаписание правильное

export default {
  computed: {
    a () { return true },
    b: function () { return true },
    d: {
      get () { return true },
      set: function (v) { console.log(v) }
    }
  }
}

vue-property-decoratorне дает конкретногоcomputedмодификатор, потому чтоES6изget/setСам синтаксис может заменитьcomputedВышеупомянутое преобразуется вtsСоответствует следующим образом:

export default class YourComponent extends Vue {
  get a () { return true }
  get b () { return true },
  get d (){ return true },
  set d (v) { console.log(v) }
}

Помимо,computedНа самом деле стрелочные функции тоже поддерживаются:

export default {
  computed: {
    e: () => { return true }
  }
}

ноclassграмматическийget/setСтрелочные функции не поддерживаются, поэтому преобразовать их непросто, а стрелочные функции изменятся.thisуказывая на, иcomputedРасчетный токvueсвойства экземпляра, поэтому обычно не рекомендуется использоватьcomputedИспользуйте стрелочные функции в стрелочной функции, хотя вы можете получить текущуюvueПример, но это немного лишнее подозрение, поэтому обработку стрелочных функций здесь пропускаю, только в энкаунтереcomputedдает вам подсказку, когда на функциях стрелки

watch

vueОфициальный сайт дляwatchвводится вwatch

Все нижеперечисленное является законнымwatchПишу:

export default {
  watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
    // 方法名
    b: 'someMethod',
    // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
    c: {
      handler: function (val, oldVal) { /* ... */ },
      deep: true
    },
    // 该回调将会在侦听开始之后被立即调用
    d: {
      handler: 'someMethod',
      immediate: true
    },
    e: [
      'handle1',
      function handle2 (val, oldVal) { /* ... */ },
      {
        handler: function handle3 (val, oldVal) { /* ... */ },
        immediate: true
      }
    ],
    // watch vm.e.f's value: {g: 5}
    'e.f': function (val, oldVal) { /* ... */ }
  }
}

Вышеупомянутое преобразуется вtsСоответствует следующим образом:

export default class YourComponent extends Vue {
  @Watch('a')
  onAChanged(val: any, oldVal: any) {}
  @Watch('b')
  onBChanged (val: any, oldVal: any) {
    this.someMethod(val, oldVal)
  }
  @Watch('c', { deep: true })
  onCChanged (val: any, oldVal: any) {}
  @Watch('d', { deep: true })
  onDChanged (val: any, oldVal: any) {}
  @Watch('e')
  onE1Changed (val: any, oldVal: any) {}
  @Watch('e')
  onE2Changed (val: any, oldVal: any) {}
  @Watch('e', { immediate: true })
  onE3Changed (val: any, oldVal: any) {}
  @Watch('e.f')
  onEFChanged (val: any, oldVal: any) {}
}

Способов написания еще много, так что ветка суждения однозначно незаменима

watchКаждое свойство подwatchизvueЗначение ответа, значения этих свойств могут быть строками, функциями, объектами и массивами, всего четыре типа

Среди них строковый тип эквивалентен вызову текущегоvueМетод в экземпляре типа функции заключается в вызове этой функции, что относительно просто; Для типов объектов он имеет три свойства:handler,deep,immediate, все три атрибута являются необязательными, гдеhandlerЗначением является функция или строка, значениями двух других свойств являютсяbooleanТипы; Для типа массива каждый элемент массива фактически эквивалентен агрегации типа строки, типа функции и типа объекта, поэтому на самом деле ему нужно иметь дело только с этими тремя типами, тип массива напрямую пересекает элементы массива, каждый элемент массива Тип должен быть в пределах этих трех типов, и соответствующий метод обработки может быть вызван в соответствии с типом.

Это основная часть, кроме этого есть еще что учестьhandlerФорма функции, допустимы следующие типы функций:

export default {
  watch: {
    a: function {},
    b () {},
    c: () => {},
    d: async function {},
    e: async () => {}
  }
}

не только вwatchВнутри какие-то другиеvueсвойства экземпляра, такие какcreated,computedИ т. д., пока могут появляться функции, эти способы написания нужно учитывать Конечно, помимо этого, естьGeneratorфункцию, но я ее здесь не рассматриваю, есть лучшеasync/awaitесть, почему бы не использоватьGenerator

methods

vueметоды экземпляра, какmethodsСвойства этого объекта существуют, и каждый метод является функцией, поэтому вам нужно изменить только исходныйmethodsВсе нижеприведенные методы вынесены и преобразованы вclassметод, не работает Однако следует отметить, что существует множество способов написания функций, которые также могут поддерживатьasync/await, эти сочинения нужно учитывать

lifeCycle

vueСуществует множество функций-ловушек жизненного цикла, а также некоторые сторонние функции-ловушки, такие какvue-router:

const vueLifeCycle = ['beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'activated', 'deactivated', 'beforeDestroy', 'destroyed', 'errorCaptured', 'beforeRouteEnter', 'beforeRouteUpdate', 'beforeRouteLeave']

Эти хуки-функции на самом деле являются функциями, сmethodsобрабатывается так же

component

Это относительно просто, преобразовать его, а затем сшить

export default {
  components: {
    a: A,
    B
  },
}

Вышеупомянутое преобразуется вtsСоответствует следующим образом:

@Component({
  components: {
    a: A,
    B
  }
})
export default class TransVue2TS extends Vue {}

Итак, оригиналcomponentsВсе атрибуты карты могут быть сопоставлены один раз

mixins

vueОфициальный сайт дляmixinsвводится вmixins

Его тип значенияArray<Object>

export default {
  mixins: [A, B]
}

Вышеупомянутое преобразуется вtsСоответствует следующим образом:

export default class TransVue2TS extends Mixins(A, B) {}

оригинальныйextends Vueизменить наextends Mixins,а такжеMixinsПараметр является исходнымmixinsвсе элементы массива

provide && inject

Когда я думал о том, как справиться с этими двумя, я посмотрел наvueОфициальный сайт, официальный сайт говорит об этих двоих следующее:

Предоставлять и внедрять в основном обеспечивают варианты использования для библиотек плагинов/компонентов более высокого порядка. Его не рекомендуется использовать непосредственно в коде приложения.

А в этом абзаце он специально помечен красным восклицательным знаком, прямо скажем, вам не рекомендуется использовать его в бизнес-коде, потому что он не способствует отслеживанию данных, вы можете использовать зрелый код.vueBusилиvuexВместо этого эта штука вообще не используется.Я тоже написал эту программу конвертации для конвертации бизнес-кодов, поэтому с этими двумя я не имел дело.Если вы найдете эти два атрибута в коде, вам будет предложено разобраться с ними вручную.

emit && ref

И то, и другое — просто синтаксическая сахароподобная вещь, которую можно опустить.

обработка файлов

Вышеупомянутое для.vueЛогика детальной обработки файлов, если вы действительно хотите получить доступ к обработке реальных файлов и даже папок, естественно читать и обновлять файлы, что включает в себяnodeСодержимое файла обработки, но оно не сложное, поэтому больше ничего не скажу.

пакет нпм

После того, как код написан, для упрощения процесса использования я упаковываю его вnpmзагрузить пакет вnpmЕсли вы хотите использовать его, вам нужно только загрузить этот пакет и ввести команду в командной строке.

npm i transvue2ts -g

После установки по умолчанию следуетvue-cliто же, путь этой библиотеки будет записан в системуpath, его можно использовать напрямую, открыв инструмент командной строки, и он поддерживает преобразование уха как в один файл, так и в файловый каталог.transvue2tsЭто команда библиотеки, а вторым параметром является файл (папка), который необходимо обработать.полный полный путьНапример: иметь дело сE:\project\testA\src\test.vueдокумент:

transvue2ts E:\project\testA\src\test.vue
=>
输出路径:E:\project\testA\src\testTs.vue

иметь дело сE:\project\testA\srcвсе под папку.vueдокумент:

transvue2ts E:\project\testA\src
=>
输出路径:E:\project\testA\srcTs

Для одного файла должно быть.vueВ конце преобразованный файл будет выведен в каталог того же уровня, имя файла будет исходным именем файла +Ts,Напримерindex.vue => indexTs.vue; Для каталога файлов программа будет рекурсивно перемещаться по каталогу файлов, чтобы найти все файлы в этой папке..vueФайлы преобразуются, и преобразованные файлы будут перемещены в новую папку в каталоге того же уровня в соответствии с исходной структурой каталогов, например/src => /srcTs

Суммировать

Этот процесс конвертации выглядит очень хлопотно, но на самом деле он состоит из трех шагов:

  • Перечислите все, что нужно преобразоватьvue-jsГрамматика и ее разновидности
  • перечислитьjs-tsСвязь отображения преобразования между грамматиками
  • Написать код преобразования синтаксиса

По сути эта программа является переводчиком, который переводитvue-jsграмматика переводится какvue-tsГрамматика, сложность в том, что вам нужно найти сопоставление всех грамматик между ними и знать, как с этим бороться, так что на самом деле большая часть этого — ручная работа.

Пока вы понимаете рутину, в чем заключается изменение?vueПеременаwepy,илиreactПереход на мини-программы WeChat по сути одинаков.Они все переводчики,и все они физический труд,но некоторые из них очень легкие,то есть передвинуть несколько кирпичей,а какой-то физический труд сложнее и требует мозгов