Дизайн фреймворка гораздо менее прост, чем все думают, это не значит, что завершена только разработка функции, и вещь готова, если ею можно пользоваться, в ней еще много знаний. Например, какие артефакты сборки должны предоставлять пользователям наша платформа? Каков модульный формат продукта? Должно ли быть напечатано правильное предупреждающее сообщение, когда пользователь не использует инфраструктуру ожидаемым образом, чтобы улучшить процесс разработки и позволить пользователю быстро найти проблему? В чем разница между сборкой для разработки и сборкой для производства? Замена горячего модуля (HMR: замена горячего модуля) требует поддержки на уровне фреймворка. Другой вопрос заключается в том, что когда ваша структура предоставляет несколько функций, и пользователям нужны только некоторые из них, могут ли пользователи закрыть другие функции, чтобы уменьшить размер пакета ресурсов? Все эти вопросы будут обсуждаться в этом разделе.
Этот раздел требует от вас некоторого опыта работы с распространенными инструментами упаковки модулей, особенно с rollup.js и webpack. Неважно, использовали ли вы или поняли только одну из них, потому что многие концепции на самом деле похожи. Если вы не пользуетесь никакими инструментами для упаковки модулей, вам нужно выяснить это самостоятельно, хотя бы после того, как у вас будет предварительное понимание, лучше будет прочитать этот раздел.
Улучшить пользовательский опыт разработки
Один из показателей, позволяющих определить, достаточно ли хорош фреймворк, — это посмотреть, каков его опыт разработки.В качестве примера возьмем Vue3:
createApp(App).mount('#not-exist')
Когда мы создаем приложение VUE и пытаемся подключить его к несуществующему узлу DOM:
warn
Из этого сообщения мы знаем, что монтирование не удалось, и объясняем причину сбоя: Vue не может найти соответствующий элемент DOM в соответствии с предоставленным нами селектором (возвращаетnull), поскольку наличие этой информации позволяет нам четко и быстро понять и определить проблему, вы можете себе представить, что если Vue не выполняет никакой внутренней обработки, он, вероятно, получит сообщение об ошибке на уровне JS, например:Uncaught TypeError: Cannot read property 'xxx' of null, но с этой информацией трудно понять, в чем проблема.
Таким образом, в процессе проектирования и разработки фреймворка крайне важно предоставлять дружественные предупреждающие сообщения.Если это не будет сделано хорошо, пользователи могут получить частые жалобы. Всегда предоставляя дружественные предупреждения, вы можете не только быстро помочь пользователям найти проблемы и сэкономить время пользователей, но также создать хорошую репутацию для фреймворка и заставить пользователей думать, что вы очень профессиональны.
В исходном коде Vue часто можно увидетьwarn()Вызов функции, например, информация на картинке выше состоит из этого предложения warn()Вызов функции печатает:
warn(
`Failed to mount app: mount target selector "${container}" returned null.`
)
дляwarn()Для функций, поскольку он должен предоставлять как можно больше полезной информации, он должен собирать информацию о стеке компонентов компонента, который в настоящее время имеет ошибку, поэтому, если вы посмотрите на исходный код, вы обнаружите, что это немного сложно. , а на самом деле он, наконец, вызывает функцию console.warn().
Для опыта разработки, помимо предоставления необходимой предупреждающей информации, есть много других аспектов, которые можно использовать в качестве отправной точки, что может еще больше улучшить опыт разработки для пользователя. Например, в Vue3, когда мы выводим данные Ref на консоль:
const count = ref(0)
console.log(count)
Откройте консоль для просмотра вывода, как показано на следующем рисунке:
вывод без обработки
Можно обнаружить, что это очень неинтуитивно, конечно, мы можем печатать напрямуюcount.value, это выведет только0, но есть ли способ распечататьcountКогда пора улучшить вывод информации? Конечно, просмотр позволяет нам писать пользовательскиеformatter, тем самым настраивая форму вывода. В исходном коде Vue вы можете найти файл с именемinitCustomFormatterЭта функция используется для инициализации пользовательского форматтера в среде разработки.Взяв хром в качестве примера, мы можем открыть настройки devtool, а затем проверитьConsole -> Enable custom formatters:
Затем обновите браузер и просмотрите консоль, и вы обнаружите, что вывод становится очень интуитивным:
Контролируйте размер кода фреймворка
Размер фрейма также является одним из критериев измерения фрейма.В случае реализации одной и той же функции, конечно, чем меньше кода используется, тем лучше, так и объем будет меньше, и браузер займет меньше время загрузки ресурсов. В настоящее время мы не можем отделаться от мысли, что предоставление более полной предупреждающей информации означает необходимость написания большего количества кода.Разве это не противоречит размеру управляющего кода? Это верно, поэтому мы собираемся выяснить, как это исправить.
Если мы посмотрим на исходный код Vue, мы обнаружим, что каждыйwarn()Вызовы функций будут соответствовать__DEV__Постоянные проверки, такие как:
if (__DEV__ && !res) {
warn(
`Failed to mount app: mount target selector "${container}" returned null.`
)
}
Как видите, предпосылка для печати предупреждающего сообщения:__DEV__Эта константа должна быть истинной, здесь__DEV__Константы — это ключ к достижению цели.
Vue, используя rollup.js, проект, построенный здесь__DEV__Константа фактически передаетсяrollupКонфигурация предопределена, и ее функция аналогична функции в веб-пакете.DefinePluginплагин.
Когда Vue выводит ресурсы, он выводит две версии ресурсов, одна из которых используется в среде разработки, например, vue.global.js, а другая используется в соответствующей ей производственной среде, например: vue.global. prod.js, Мы также можем отличить по имени файла.
Когда Vue создает ресурсы для среды разработки, он__DEV__Для константы установлено значение true, тогда приведенный выше код, выводящий предупреждающее сообщение, эквивалентен следующему:
if (true && !res) {
warn(
`Failed to mount app: mount target selector "${container}" returned null.`
)
}
можно увидеть здесь__DEV__заменено буквальнымtrue, так что этот код определенно существует в среде разработки.
Когда Vue создает ресурсы для производственного использования, он__DEV__Для константы установлено значение false, тогда приведенный выше код, выводящий предупреждающее сообщение, эквивалентен следующему:
if (false && !res) {
warn(
`Failed to mount app: mount target selector "${container}" returned null.`
)
}
можно увидеть__DEV__Константы заменяются литераламиfalse, в это время мы обнаруживаем, что этот код ответвления никогда не будет выполнен, потому что условие суждения всегда ложно, этот код, который никогда не будет выполнен, называется мертвым кодом, он не появится в конечном продукте, при построении ресурсов будет быть удалены, поэтому этот код не будет существовать в vue.global.prod.js.
Таким образом, мы можем предоставлять удобные для пользователя предупреждающие сообщения в среде разработки, не увеличивая размер кода рабочей среды.
Фреймворк для хорошего Tree-Shaking
Выше мы упомянули установку предопределенных констант с помощью инструментов сборки.__DEV__, можно сделать так, чтобы фреймворк не содержал кода для вывода предупредительной информации в продакшн-окружении, чтобы уменьшить количество кода самого фреймворка. Но с точки зрения пользователя этого все же недостаточно, возьмем в качестве примера Vue, мы знаем, что Vue предоставляет встроенные компоненты, такие как<Transition> , если компонент вообще не используется в нашем проекте, то<Transition>Нужно ли включать код компонента в окончательный ресурс сборки нашего проекта? Ответ конечно нет, так как же это сделать? Это должно упомянуть главного героя этого разделаTree-Shaking.
что это такоеTree-ShakingШерстяная ткань? Во фронтенде эта концепция популяризируется с помощью роллапа, который просто называется так называемымTree-ShakingЭто относится к исключению кода, который никогда не будет выполнен, то есть к исключению мертвого кода.Теперь поддерживаются и роллап, и вебпак.Tree-Shaking.
хочу достичьTree-ShakingДолжно быть выполнено одно условие, то есть модуль должен быть модулем ES, поскольку Tree-Shaking опирается на статическую структуру ESM. Давайте посмотрим, как работает Tree-Shaking, на простом примере с использованием свертки.Структура каталогов нашей демонстрации выглядит следующим образом:
├── demo
│ └── package.json
│ └── input.js
│ └── utils.js
Установить первымrollup:
yarn add rollup -D #илиnpm install rollup -D
Вот содержимое файлов input.js и utils.js:
// input.js
import { foo } from './utils.js'
foo()
// utils.js
export function foo(obj) {
obj && obj.foo
}
export function bar(obj) {
obj && obj.bar
}
Код очень простой, мы определяем и экспортируем две функции в файле utils.js, которыеfooа такжеbar, а затем импортировать в input.jsfooфункцию и выполнить, обратите внимание, что мы не импортировалиbarфункция.
Затем мы выполняем следующую команду для использованияrollupКонструкция:
npx rollup input.js -f esm -o bundle.js
Смысл этой команды в том, чтобы войти в файл input.js и вывести модуль ESM, имя выходного файла — bundle.js. После успешного выполнения команды мы открываем bundle.js, чтобы увидеть его содержимое:
// bundle.js
function foo(obj) {
obj && obj.foo
}
foo();
Как видите, он не содержитbarфункция, которая показывает, что Tree-Shaking работает, так как мы не использовали функцию панели, она действует какdead-codeбыл удален. Но если мы посмотрим внимательно, мы обнаружим, чтоfooВыполнение функции бессмысленно, то есть считывается значение объекта, поэтому принципиальной разницы между тем, выполняется оно или нет, нет, поэтому даже если этот код будет удален, это не повлияет на наше приложение, так почему?rollupНе используйте этот код какdead-codeЧто насчет удаления?
Это касается второго ключевого момента Tree-Shaking — побочных эффектов. Если вызов функции имеет побочные эффекты, его нельзя удалить. Что такое побочные эффекты? Проще говоря, побочные эффекты означают, что когда функция вызывается, она оказывает внешнее воздействие, например модифицирует глобальную переменную. В этот момент вы можете спросить, как приведенный выше код может явно читать значение объекта, чтобы иметь побочные эффекты? На самом деле это возможно, подумайте, что, еслиobjОбъект является прокси-объектом, созданным Proxy, поэтому, когда мы читаем свойства объекта, срабатывает геттер, что может иметь побочные эффекты в геттере.Например, мы модифицируем глобальную переменную в геттере. А будут ли побочные эффекты, это можно узнать только когда код реально работает.JS сам по себе динамический язык.Вы хотите статически анализировать какие кодыdead-codeЭто очень сложная вещь, и приведенное выше — всего лишь простой пример.
Просто потому, что статический анализ кода JS сложен, такие вещи, какrollupТакие инструменты предоставят мне механизм, который даст нам возможность явно указатьrollup: "Расслабьтесь, этот код не будет иметь побочных эффектов, можете смело его удалять", тогда как это сделать? Как показано в коде ниже, мы модифицируем файл input.js:
import {foo} from './utils'
/*#__PURE__*/ foo()
Обратите внимание на этот закомментированный код/*#__PURE_*_/, цель этой аннотации - рассказатьrollupдляfoo()Вызов функции не будет иметь побочных эффектов, на нем можно смело выполнять Tree-Shaking.В это время снова выполните команду сборки и проверьте файл bundle.js.Вы обнаружите, что его содержимое пусто, а это значит, что Tree-Shaking Встряхивание эффективно.
Исходя из этого случая, каждый должен понимать, что при написании фреймворка нужно разумно его использовать./*#__PURE_*_/ 注释Если вы ищете исходный код Vue, вы обнаружите, что он много использует эту аннотацию, например, следующее предложение:
export const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS)
Может быть, вы думаете, что это не будет большим психическим бременем на написании кода? На самом деле нет, это потому, что код, который обычно вызывает побочные эффекты, является вызовом верхнего уровня функции в модуле. Что такое вызов верхнего уровня? Как показано в следующем коде:
foo() // 顶级调用
function bar() {
foo() // 函数内调用
}
Видно, что побочные эффекты возможны для вызовов верхнего уровня, но для внутрифункциональных вызовов, пока панель функций не вызывается, тогдаfooВызовы функций, конечно же, не имеют побочных эффектов. Поэтому вы обнаружите, что в исходном коде Vue он в основном используется для некоторых функций верхнего уровня./*#__PURE__*/Аннотированный. Конечно, эта аннотация предназначена не только для функций, ее можно использовать в любом операторе, и эта аннотация не толькоrollupОн может быть распознан веб-пакетом и инструментами сжатия, такими как terser.
Какие артефакты сборки должна выводить платформа
Выше мы упоминали, что Vue будет выводить разные пакеты для среды разработки и производственной среды, такие как vue.global.js для среды разработки, который содержит необходимую предупреждающую информацию, и vue.global.prod.js для производственной среды, Предупреждающие сообщения не включены. Фактически, в дополнение к различию между средами, продукты сборки Vue также будут выводить другие формы продуктов в соответствии с различными сценариями использования.В этом разделе мы обсудим использование этих продуктов и как выводить эти продукты на этапе построения.
Различные типы продуктов должны иметь соответствующие предпосылки спроса, поэтому мы начнем со спроса. Прежде всего, мы хотим, чтобы пользователи могли использовать его прямо на html-странице.<script>Этикетки заносятся в раму и используются:
<body>
<script src="/path/to/vue.js"></script>
<script>
const { createApp } = Vue
// ...
</script>
</body>
Чтобы выполнить это требование, нам нужно вывести ресурс, называемый форматом IIFE.Полное название IIFE — немедленно вызываемое функциональное выражение, которое представляет собой «немедленно вызываемое функциональное выражение», которое можно легко выразить в JS:
(function () {
// ...
}())
如上代码所示,这就是一个立即执行的函数表达式。实际上 vue.globale.js 文件就是 IIFE 形式的资源,大家可以看一下它的代码结构:
var Vue = (function(exports){
// ...
exports.createApp = createApp;
// ...
return exports
}({}))
поэтому, когда мы используем<script>После непосредственного введения тега в файл vue.global.js становится доступной глобальная переменная Vue.
В накопительном пакете мы можем настроитьformat: 'iife'для вывода ресурса такой формы:
// rollup.config.js
const config = {
input: 'input.js',
output: {
file: 'output.js',
format: 'iife' // 指定模块形式
}
}
export default config
Однако с развитием технологий и поддержкой браузеров основные браузеры теперь хорошо поддерживают встроенные модули ESM, поэтому пользователи могут использовать<script>Помимо ссылки на ресурсы в формате IIFE, теги также могут напрямую ссылаться на ресурсы в формате ESM.Например, Vue3 выводит файл vue.esm-browser.js.Пользователи могут напрямую использовать<script>Введение этикетки:
<script type="module" src="/path/to/vue.esm-browser.js"></script>
Для того, чтобы выводить ресурсы в формате ESM, нам нужно настроитьrollupВыходной формат:format: 'esm'.
Вы могли заметить, почему в файле vue.esm-browser.js-browserНа самом деле, для ресурсов в формате ESM Vue также будет выводить файл vue.esm-bundler.js, в котором-browserстал-bundler. Зачем это делать? мы знаем лиrollupещеwebpackПри поиске ресурсов, если поле модуля существует в package.json, оно будет использовано в первую очередь.moduleполе вместо этого указывает на ресурсmainРесурс, на который указывает поле. Мы можем открыть исходный код Vue вpackages/vue/package.jsonВзгляните на файл:
{
"main": "index.js",
"module": "dist/vue.runtime.esm-bundler.js",
}
Поле модуля указывает на файл vue.runtime.esm-bundler.js, что означает, что если ваш проект построен с помощью веб-пакета, ресурс Vue, который вы используете, — это vue.runtime.esm-bundler.js, что означает, что с-bundlerРесурсы ESM со словами используются инструментами упаковки, такими как накопительный пакет или веб-пакет, и используются с-browserСлово ресурс ESM дается непосредственно<script type="module"> использовать.
Так в чем же между ними разница? Тогда это должно быть упомянуто в приведенном выше__DEV__постоянная, при построении для<script> ресурс ESM с пометкой c, если он используется в среде разработки, то__DEV__будет установлено значение true; если он используется в производственной среде, то__DEV__ Константа будет установлена вfalse, который удаляется Tree-Shaking. Но когда мы создаем ресурсы в формате ESM для инструментов упаковки, мы не можем напрямую__DEV__Установите значение true или false, вместо этого используйте(process.env.NODE_ENV !== 'production')заменять_DEV__постоянный. Например, следующий исходный код:
if (__DEV__) {
warn(`useCssModule() is not supported in the global build.`)
}
в с-bundlerСлово ресурс станет:
if ((process.env.NODE_ENV !== 'production')) {
warn(`useCssModule() is not supported in the global build.`)
}
Таким образом, конфигурация веб-пакета на стороне пользователя может определить целевую среду для сборки ресурсов, но конечный результат фактически одинаков, и этот код появится только в среде разработки.
Помимо того, что пользователь может напрямую использовать<script>Теги импортируют ресурсы, мы также хотим, чтобы пользователи могли ссылаться на ресурсы в Node.js с помощью операторов require, например:
const Vue = require('vue')
Почему возникает такая необходимость? Ответ: рендеринг на стороне сервера.При рендеринге на стороне сервера код Vue выполняется в среде Node.js, а не в среде браузера.В среде Node.js формат модуля ресурсов должен бытьCommonJS, именуемый cjs. Чтобы иметь возможность экспортировать ресурсы модуля cjs, мы можем изменить конфигурацию объединения:format: 'cjs'реализовать:
// rollup.config.js
const config = {
input: 'input.js',
output: {
file: 'output.js',
format: 'cjs' // 指定模块形式
}
}
export default config
Переключатель функций
При разработке фреймворка фреймворк будет предоставлять пользователям множество функций (или функций). Например, мы предоставляем пользователям три функции A, B и C. В то же время мы также предоставляем три соответствующих переключателя функций a, b , и c. можно установить, установив a, b, c наtrueа такжеfalseизображать открытие и закрытие, то пользы будет много:
Для закрытых пользователем функций мы можем использовать механизм Tree-Shaking, чтобы исключить их из конечного ресурса.
Этот механизм обеспечивает гибкость конструкции фреймворка.В фреймворк можно добавлять новые функции с помощью переключателей функций, не беспокоясь об увеличении объема пользовательских ресурсов, которые не используют эти функции.В то же время при обновлении фреймворка , мы также можем использовать переключатели функций. Чтобы поддерживать устаревшие API, новые пользователи могут отказаться от использования устаревших API, тем самым минимизируя пользовательские ресурсы.
Итак, как реализовать переключатель функций? На самом деле все очень просто, принцип и указанный выше__DEV__Так же, как и константа, суть в использованииrollupПредопределенный постоянный плагин для достижения этого параграфа Vue3rollupС точки зрения конфигурации:
{
__FEATURE_OPTIONS_API__: isBundlerESMBuild ? `__VUE_OPTIONS_API__` : true,
}
в__FEATURE_OPTIONS_API__похожий на__DEV__Мы можем искать в исходном коде VUE3, вы можете найти много религиотов, таких как следующий код:
// support for 2.x options
if (__FEATURE_OPTIONS_API__) {
currentInstance = instance
pauseTracking()
applyOptions(instance, Component)
resetTracking()
currentInstance = null
}
Когда Vue создает ресурс, если созданный ресурс предназначен для использования упаковщиком (то есть ресурс со словом -bundler ), тогда приведенный выше код в ресурсе станет:
// support for 2.x options
if (__VUE_OPTIONS_API__) { // 这一这里
currentInstance = instance
pauseTracking()
applyOptions(instance, Component)
resetTracking()
currentInstance = null
}
в__VUE_OPTIONS_API__Это переключатель функций, пользователь может установить__VUE_OPTIONS_API__чтобы контролировать, следует ли включать этот код. Обычно пользователи могут использовать плагин webpack.DefinePlugin для достижения:
// webpack.DefinePlugin 插件配置
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: JSON.stringify(true) // 开启特性
})
Наконец, позвольте мне объяснить подробно__VUE_OPTIONS_API__Для чего нужен переключатель Компонент, который мы пишем на Vue2, называется API параметров компонента:
export default {
data() {}, // data 选项
computed: {}, // computed 选项
// 其他选项...
}
А вот в Vue3 для написания кода больше рекомендуется использовать Composition API, например:
export default {
setup() {
const count = ref(0)
const doubleCount = computed(() => count.value * 2) // 相当于 Vue2 中的 computed 选项
}
}
Но для совместимости с Vue2 по-прежнему можно писать код с использованием API параметров в Vue3, но для пользователей, которые четко знают, что они не будут использовать API параметров, они могут выбрать использование API параметров.__VUE_OPTIONS_API__Переключитесь, чтобы отключить эту функцию, чтобы эта часть кода Vue не включалась в конечный ресурс при упаковке, тем самым уменьшая размер ресурса.
обработка ошибок
Обработка ошибок является очень важным звеном в процессе разработки фреймворка.Качество обработки ошибок фреймворком может напрямую определять надежность пользовательского приложения, а также определяет умственную нагрузку пользователя на работу с ошибками при разработке приложения. .
Чтобы дать вам более интуитивное представление о важности обработки ошибок, давайте начнем с небольшого примера. Предположим, мы разрабатываем инструментальный модуль со следующим кодом:
// utils.js
export default {
foo(fn) {
fn && fn()
}
}
Модуль экспортирует объект, гдеfooСвойство представляет собой функцию, которая получает функцию обратного вызова в качестве параметра.При вызове функции foo функция обратного вызова будет выполнена.При использовании на стороне пользователя:
import utils from 'utils.js'
utils.foo(() => {
// ...
})
Давайте подумаем, что если функция обратного вызова, предоставленная пользователем, даст сбой во время выполнения? В настоящее время есть два способа: один — позволить пользователю справиться с этим самостоятельно, что требует, чтобы пользователь делал это сам.try...catch:
import utils from 'utils.js'
utils.foo(() => {
try {
// ...
} catch (e) {
// ...
}
})
Но это дополнительная нагрузка на пользователя, представьте, если бы utils.js не просто предоставлялfooфункцию, но предоставляет десятки или сотни подобных функций, поэтому пользователям необходимо добавлять обработчики ошибок один за другим при их использовании.
Второй способ заключается в том, что мы единообразно обрабатываем ошибки от имени пользователя, как показано в следующем коде:
// utils.js
export default {
foo(fn) {
try {
fn && fn()
} catch(e) {/* ... */}
},
bar(fn) {
try {
fn && fn()
} catch(e) {/* ... */}
},
}
Этот метод на самом деле, мы пишем обработчик ошибок вместо пользователя. На самом деле, мы можем дополнительно инкапсулировать обработчик ошибок в качестве функции, скажем, это называется CallWitherrororhandling:
// utils.js
export default {
foo(fn) {
callWithErrorHandling(fn)
},
bar(fn) {
callWithErrorHandling(fn)
},
}
function callWithErrorHandling(fn) {
try {
fn && fn()
} catch (e) {
console.log(e)
}
}
Вы можете видеть, что код стал намного проще, но простота не является целью Реальная выгода от этого заключается в том, что у нас есть возможность предоставить пользователям унифицированный интерфейс обработки ошибок, как показано в следующем коде:
// utils.js
let handleError = null
export default {
foo(fn) {
callWithErrorHandling(fn)
},
// 用户可以调用该函数注册统一的错误处理函数
resigterErrorHandler(fn) {
handleError = fn
}
}
function callWithErrorHandling(fn) {
try {
fn && fn()
} catch (e) {
// 捕获到的错误传递给用户的错误处理程序
handleError(e)
}
}
мы предоставляемresigterErrorHandlerфункция, с помощью которой пользователь может зарегистрировать обработчик ошибок, а затемcallWithErrorHandlingКогда ошибка перехватывается внутри функции, объект ошибки передается обработчику ошибок, зарегистрированному пользователем.
Таким образом, код на стороне пользователя будет очень лаконичным и надежным:
import utils from 'utils.js'
// 注册错误处理程序
utils.resigterErrorHandler((e) => {
console.log(e)
})
utils.foo(() => {/*...*/})
utils.bar(() => {/*...*/})
В настоящее время возможность обработки ошибок полностью контролируется пользователем.Пользователь может либо проигнорировать ошибку, либо вызвать программу создания отчетов, чтобы сообщить об ошибке в систему мониторинга.
По сути, это принцип обработки ошибок Vue, его можно поискать в исходникахcallWithErrorHandlingфункцию, а в Vue мы также можем зарегистрировать унифицированную функцию обработки ошибок:
import App from 'App.vue'
const app = createApp(App)
app.config.errorHandler = () => {
// 错误处理程序
}
Хорошая поддержка типов Typescript
Typescript — это язык программирования Microsoft с открытым исходным кодом, называемый TS, который является надмножеством JS и может обеспечивать поддержку типов для JS. Сейчас все больше и больше людей и команд используют язык TS в своих проектах.Есть много преимуществ использования TS, таких как код как документация, автоматические подсказки редактора, в определенной степени избежание низкоуровневых ошибок и создание кода ремонтопригоднее.Сильное ожидание. Поэтому полнота поддержки типа TS также стала важным показателем для оценки фреймворка.
Так как же измерить качество поддержки фреймворком типов TS? Здесь есть распространенное заблуждение. Многие студенты думают, что пока они написаны на TS, они дружественны к поддержке типа TS. На самом деле, использование TS для написания фреймворка и дружественность фреймворка к поддержке типа TS — это две вещи, которые не относится. Учитывая, что некоторые учащиеся, возможно, не были знакомы с TS, мы не будем здесь подробно обсуждать, а приведем только простой пример.Следующая функция написана с использованием TS:
function foo(val: any) {
return val
}
Эта функция проста, она принимает один параметрvalи параметр может быть любого типа (any), Функция напрямую использует параметр как возвращаемое значение, а это значит, что тип возвращаемого значения определяется параметром, если онnumberТип возвращаемого значенияnumberвведите, а затем мы можем попробовать использовать эту функцию, как показано на следующем рисунке:
поддержка недружественных типов
вызовfooПри передаче функции мы передаем параметр типа string'str', по предыдущему анализу получаем результатresтакже должен быть строкового типа, однако, когда мы наводим указатель мыши наresНа константе вы можете видеть, что ее типany, это не тот результат, которого мы хотим, для достижения идеального состояния нам нужно толькоfooФункцию можно просто изменить:
function foo<T extends any>(val: T): T {
return val
}
Вам не нужно понимать этот код, давайте посмотрим на текущую производительность:
Дружественный тип изображения
можно увидетьresТип — символьный литерал'str'вместоany, значит наш код работает.
На этом простом примере мы понимаем, что написание кода с помощью TS и дружественная поддержка типов TS — это две разные вещи. Нелегко добиться идеальной поддержки типов TS при написании больших фреймворков. Вы можете проверить это в исходном коде Vue.runtime-core/src/apiDefineComponent.tsфайл, на самом деле всего 3 строки кода во всем файле, который будет запущен в браузере, но когда вы откроете этот файл, вы обнаружите, что в нем почти 200 строк кода.На самом деле, все эти коды относятся к типу support. , видно, что фреймворку требуются значительные усилия для достижения идеальной поддержки типов.
Помимо того, что мы тратим много усилий на вывод типов для достижения лучшей поддержки типов, нам также необходимо рассмотреть поддержку TSX, которую мы подробно обсудим в отдельной статье.
Выше, добро пожаловать, чтобы поделиться и следовать.
Отказ от ответственности: Пожалуйста, указывайте источник при перепечатке.
Добро пожаловать в мой личный кабинет "HcySunYang".