Практика с вами, чтобы начать работу с фронтенд-инжинирингом - очень подробное руководство

внешний интерфейс JavaScript

Эта статья будет разделена на следующие 7 подразделов:

  1. Технический отбор
  2. унифицированная спецификация
  3. контрольная работа
  4. развертывать
  5. монитор
  6. оптимизация производительности
  7. рефакторинг

Некоторые разделы содержат очень подробные практические руководства для всех желающих.

Кроме того, я также написал демо-версию интерфейса на платформеgithubначальство. Эта демонстрация включает проверку js, css и git. Для проверки js и css необходимо установить VSCode. Конкретное руководство будет упомянуто ниже.

Технический отбор

Для фронтенда выбор технологии достаточно прост. Это сделать вопросы с несколькими вариантами ответов, выбрать одну из трех рамок. Лично я считаю, что его можно выбрать по следующим двум характеристикам:

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

Второй момент особенно важен для небольших компаний. Изначально небольшим компаниям не так просто набирать людей, и если вы выберете фреймворк с небольшой долей рынка (например, Angular), вы не увидите несколько резюме...

Библиотека компонентов пользовательского интерфейса проще, и будет использоваться та, у которой больше всего звездочек на github. Звезд много, а значит, есть много людей, которые ими пользуются.Многие люди наступили на них ради вас, спасая от неприятностей.

унифицированная спецификация

спецификация кода

Давайте посмотрим на преимущества Unicode:

  • Канонический код способствует командной работе
  • Стандартизированный код снижает затраты на техническое обслуживание
  • Канонический код упрощает проверку кода
  • Выработайте привычку к спецификации кода, что полезно для роста самих программистов.

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

Как разрабатывать спецификации кода

Рекомендуется найти хорошую спецификацию кода и внести персонализированные изменения в соответствии с потребностями команды.

Вот некоторые спецификации кода js с большим количеством звездочек:

Существует также множество спецификаций кода css, например:

Как проверить спецификации кода

С помощью eslint можно проверить, что символы кода не соответствуют спецификациям, заданным командой.Давайте посмотрим, как настроить eslint для проверки кода.

  1. Скачать зависимости
// eslint-config-airbnb-base 使用 airbnb 代码规范
npm i -D babel-eslint eslint eslint-config-airbnb-base eslint-plugin-import
  1. настроить.eslintrcдокумент
{
    "parserOptions": {
        "ecmaVersion": 2019
    },
    "env": {
        "es6": true,
    },
    "parser": "babel-eslint",
    "extends": "airbnb-base",
}
  1. существуетpackage.jsonизscriptsдобавить эту строку кода"lint": "eslint --ext .js test/ src/". затем выполнитьnpm run lintВы можете начать проверку кода. в кодеtest/ src/Относится к каталогу кодов, который вы хотите проверить, здесь указывает код, который нужно проверитьtest,srcкод в каталоге.

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

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

Используйте css для проверки спецификаций кодаstylelintплагин.

Из-за ограниченного места, пожалуйста, смотрите мою другую статью о том, как настроитьESlint + stylelint + код автоматического форматирования VSCode (2020).

在这里插入图片描述

git спецификация

Спецификация git включает в себя два пункта: спецификацию управления ветвями и спецификацию фиксации git.

управление филиалом

Общий проект делится на основную ветку (мастер) и другие ветки.

Когда член команды хочет разработать новую функцию или исправить ошибку, из основной ветки открывается новая ветка. Например, чтобы изменить проект с рендеринга на стороне клиента на рендеринг на стороне сервера, откройте ветку с именем ssr и объедините ее обратно с основной веткой после разработки.

Если вы изменили ОШИБКУ, вы также можете открыть новую ветку из основной ветки и назвать ее номером ОШИБКИ (но наша маленькая команда слишком хлопотна, чтобы сделать это, если только нет особенно большой ОШИБКИ).

спецификация коммита git

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

Грубо разделен на три части (разделенные пустыми строками):

  1. Строка заголовка: обязательна, опишите основной тип модификации и содержание
  2. Содержание темы: Опишите, почему была сделана модификация, какая модификация была сделана, а также идеи развития и т. д.
  3. Примечания в нижнем колонтитуле: вы можете писать заметки, ссылка на номер ОШИБКИ

тип: тип фиксации

  • подвиг: новые функции, новые функции
  • исправить: исправить ошибку
  • perf: изменения кода для повышения производительности
  • рефакторинг: рефакторинг кода (рефакторинг, модификация кода без влияния на внутреннее поведение и функции кода)
  • документы: Изменения в документации
  • стиль: модификация формата кода, обратите внимание, что это не модификация css (например, модификация точки с запятой)
  • test: добавлять и изменять тестовые случаи
  • build: влияет на сборку проекта или модификацию зависимостей
  • revert: вернуть последнюю фиксацию
  • ci: изменение файла, связанное с непрерывной интеграцией
  • рутинная работа: другие модификации (модификации, не относящиеся к вышеперечисленным типам)
  • релиз: выпустить новую версию
  • рабочий процесс: изменение файла, связанное с рабочим процессом
  1. scope: область затронутой фиксации, например: маршрут, компонент, утилиты, сборка...
  2. тема: обзор коммитов
  3. тело: зафиксируйте конкретное содержимое модификации, которое можно разделить на несколько строк.
  4. нижний колонтитул: некоторые примечания, обычно ссылка на КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ или исправленную ошибку.

Пример

исправить (исправить ОШИБКУ)

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

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

// 示例1
fix(global):修复checkbox不能复选的问题
// 示例2 下面圆括号里的 common 为通用管理的名称
fix(common): 修复字体过小的BUG,将通用管理下所有页面的默认字体大小修改为 14px
// 示例3
fix: value.length -> values.length
подвиг (добавление новых функций или новых страниц)
feat: 添加网站主页静态页面

这是一个示例,假设对点检任务静态页面进行了一些描述。
 
这里是备注,可以是放BUG链接或者一些重要性的东西。
работа (другие модификации)

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

chore: 将表格中的查看详情改为详情

Другие типы коммитов аналогичны приведенным выше трем примерам, поэтому я не буду о них говорить.

Проверить спецификацию фиксации git

Проверьте спецификацию фиксации git, в основном через gitpre-commitфункция крючка. Конечно, вам также потребуется загрузить вспомогательный инструмент, который поможет вам проверить.

скачать вспомогательные средства

npm i -D husky

существуетpackage.jsonдобавьте следующий код

"husky": {
  "hooks": {
    "pre-commit": "npm run lint",
    "commit-msg": "node script/verify-commit.js",
    "pre-push": "npm test"
  }
}

Затем создайте новую папку в корневом каталоге вашего проекта.script, и создайте новый файл нижеverify-commit.js, введите следующий код:

const msgPath = process.env.HUSKY_GIT_PARAMS
const msg = require('fs')
.readFileSync(msgPath, 'utf-8')
.trim()

const commitRE = /^(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|release|workflow)(\(.+\))?: .{1,50}/

if (!commitRE.test(msg)) {
    console.log()
    console.error(`
        不合法的 commit 消息格式。
        请查看 git commit 提交规范:https://github.com/woai3c/Front-end-articles/blob/master/git%20commit%20style.md
    `)

    process.exit(1)
}

Теперь, чтобы объяснить значение каждого хука:

  1. "pre-commit": "npm run lint",существуетgit commitперед казньюnpm run lintПроверьте формат кода.
  2. "commit-msg": "node script/verify-commit.js",существуетgit commitВыполнить скрипт, когдаverify-commit.jsПодтвердите сообщение фиксации. Если он не соответствует формату, определенному в скрипте, будет сообщено об ошибке.
  3. "pre-push": "npm test", после выполненияgit pushПеред отправкой кода в удаленный репозиторий выполнитеnpm testпровести тестирование. Если тест не пройден, отправка не будет выполнена.

Спецификация проекта

В основном это то, как организованы и названы файлы проекта.

Возьмите наш проект Vue в качестве примера.

├─public
├─src
├─test

Проект содержит public (общедоступные ресурсы, которые не будут обрабатываться webpack), src (исходный код) и test (тестовый код).Каталог src может быть разделен.

├─api (接口)
├─assets (静态资源)
├─components (公共组件)
├─styles (公共样式)
├─router (路由)
├─store (vuex 全局数据)
├─utils (工具函数)
└─views (页面)

Разделяйте имена файлов знаком -, если они слишком длинные.

Спецификация пользовательского интерфейса

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

Преимущества создания спецификаций пользовательского интерфейса:

  • Единые стандарты пользовательского интерфейса страницы для экономии времени на разработку пользовательского интерфейса
  • Повысить эффективность фронтенд-разработки

контрольная работа

Тестирование — неотъемлемая часть начального инженерного строительства, его роль — поиск ошибок, и чем раньше они будут обнаружены, тем ниже стоимость. И его более важная роль в будущем, а не в настоящем.

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

Написание тестов также позволяет модифицировать код без психологической нагрузки: вам не нужно постоянно думать о проблеме, есть ли здесь проблема? Это вызовет ошибку? И когда вы пишете тест, вам не нужно об этом беспокоиться.

Чаще всего во фронтенде используется модульное тестирование (в основном сквозное тестирование, которым я пользуюсь очень мало и с которым не знаком), здесь я сосредоточусь на объяснении.

модульный тест

Модульный тест — это тест функции, компонента или класса, и его степень детализации относительно невелика.

Как это должно быть написано?

  1. Напишите тесты на правильность, т.е. правильный ввод должен иметь нормальные результаты.
  2. Пишите тесты на основе исключений, т.е. неправильный ввод должен быть неправильным результатом.

проверить функцию

Например, функция, которая принимает абсолютное значениеabs(),входить1,2, результат должен совпадать с вводом; ввод-1,-2, результат должен быть противоположен вводу. Если вы введете нечисловое значение, например."abc", что должно вызывать TypeError.

проверить класс

Предположим, что есть такой класс:

class Math {
    abs() {

    }

    sqrt() {

    }

    pow() {

    }
    ...
}

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

протестировать компонент

Тестирование компонентов затруднено, потому что многие компоненты связаны с манипуляциями с DOM.

Например, компонент загружаемого изображения имеет метод преобразования изображения в код base64.Как его протестировать? Общие тесты выполняются в среде узла, а в среде узла нет объектов DOM.

Давайте сначала рассмотрим процесс загрузки изображений:

  1. нажмите<input type="file" />, выберите Загрузка изображения.
  2. курокinputизchangeсобытие, получитьfileобъект.
  3. использоватьFileReaderПреобразуйте изображение в код base64.

Процесс аналогичен следующему коду:

document.querySelector('input').onchange = function fileChangeHandler(e) {
    const file = e.target.files[0]
    const reader = new FileReader()
    reader.onload = (res) => {
        const fileResult = res.target.result
        console.log(fileResult) // 输出 base64 码
    }

    reader.readAsDataURL(file)
}

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

document.querySelector('input').onchange = function fileChangeHandler(e) {
    const file = e.target.files[0]
    tobase64(file)
}

function tobase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = (res) => {
            const fileResult = res.target.result
            resolve(fileResult) // 输出 base64 码
        }

        reader.readAsDataURL(file)
    })
}

Как видите, объект события window появляется в приведенном выше коде.event,FileReader. То есть, пока мы можем предоставить эти два объекта, мы можем запустить его в любой среде. Итак, мы можем добавить эти два объекта в тестовую среду:

// 重写 File
window.File = function () {}

// 重写 FileReader
window.FileReader = function () {
    this.readAsDataURL = function () {
        this.onload
            && this.onload({
                target: {
                    result: fileData,
                },
            })
    }
}

Тогда тест можно написать так:

// 提前写好文件内容
const fileData = 'data:image/test'

// 提供一个假的 file 对象给 tobase64() 函数
function test() {
    const file = new File()
    const event = { target: { files: [file] } }
    file.type = 'image/png'
    file.name = 'test.png'
    file.size = 1024

    it('file content', (done) => {
        tobase64(file).then(base64 => {
            expect(base64).toEqual(fileData) // 'data:image/test'
            done()
        })
    })
}

// 执行测试
test()

С помощью этого хака мы реализуем тесты для компонентов, которые включают манипуляции с DOM. мойvue-upload-imgsБиблиотека представляет собой модульный тест, написанный таким образом, если вам интересно, вы можете узнать об этом.

TDD-разработка через тестирование

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

Первоначальный замысел TDD хорош, но если ваши требования часто меняются (вы поняли идею), это не очень хорошо. Очень вероятно, что вы меняете тестовый код каждый день, но бизнес-код практически не изменился. Так что до сих пор, за свою более чем трехлетнюю карьеру программиста, я ни разу не пробовал TDD-разработку.

Хотя среда настолько сложна, вы должны попробовать TDD, если можете. Например, когда вы отвечаете за проект и не заняты, вы можете использовать этот метод для написания тестовых случаев.

Рекомендации по тестовой среде

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

развертывать

До того, как я научился деплоить автоматически, я деплоил проект так:

  1. выполнить тестnpm run test.
  2. Построить проектnpm run build.
  3. Поместите упакованные файлы на статический сервер.

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

Автоматическое развертывание (также известное как Continuous Deployment, CD для английской аббревиатуры) обычно имеет два метода запуска:

  1. опрос.
  2. мониторwebhookсобытие.

голосование

Опрос предназначен для создания программного обеспечения для автоматического выполнения операций упаковки и развертывания через регулярные промежутки времени.

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

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

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

мониторwebhookсобытие

Функция ловушки веб-перехватчика заключается в том, чтобы настроить ее в программном обеспечении сборки для прослушивания определенного события (обычно для прослушиванияpushсобытие), когда событие инициируется, определенный сценарий выполняется автоматически.

НапримерGithub Actions, имеет эту функцию.

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

Развертывание автоматизации фронтенд-проекта — очень подробное руководство (Jenkins, Github Actions), учебник был предоставлен. Если вы найдете его полезным после прочтения, не забудьте поставить лайк, большое спасибо.

монитор

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

мониторинг производительности

Общее использование мониторинга производительностиwindow.performanceдля сбора данных.

Интерфейс производительности может получать информацию о производительности на текущей странице.Он является частью API времени высокого разрешения, а также интегрирует API временной шкалы производительности, API синхронизации навигации, API синхронизации пользователя и API синхронизации ресурсов.

Свойства этого APItiming, который содержит время начала и окончания каждого этапа загрузки страницы.

在这里插入图片描述 在这里插入图片描述

Для простоты пониманияtimingЗначение каждого атрибута я нашел в сети на ZhihutimingВступление, которое я написал (забыл имя, потом не найду, извините), перепечатайте здесь.

timing: {
        // 同一个浏览器上一个页面卸载(unload)结束时的时间戳。如果没有上一个页面,这个值会和fetchStart相同。
	navigationStart: 1543806782096,

	// 上一个页面unload事件抛出时的时间戳。如果没有上一个页面,这个值会返回0。
	unloadEventStart: 1543806782523,

	// 和 unloadEventStart 相对应,unload事件处理完成时的时间戳。如果没有上一个页面,这个值会返回0。
	unloadEventEnd: 1543806782523,

	// 第一个HTTP重定向开始时的时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0。
	redirectStart: 0,

	// 最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的时间戳。
	// 如果没有重定向,或者重定向中的一个不同源,这个值会返回0. 
	redirectEnd: 0,

	// 浏览器准备好使用HTTP请求来获取(fetch)文档的时间戳。这个时间点会在检查任何应用缓存之前。
	fetchStart: 1543806782096,

	// DNS 域名查询开始的UNIX时间戳。
        //如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和fetchStart一致。
	domainLookupStart: 1543806782096,

	// DNS 域名查询完成的时间.
	//如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
	domainLookupEnd: 1543806782096,

	// HTTP(TCP) 域名查询结束的时间戳。
        //如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 fetchStart一致。
	connectStart: 1543806782099,

	// HTTP(TCP) 返回浏览器与服务器之间的连接建立时的时间戳。
        // 如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。
	connectEnd: 1543806782227,

	// HTTPS 返回浏览器与服务器开始安全链接的握手时的时间戳。如果当前网页不要求安全连接,则返回0。
	secureConnectionStart: 1543806782162,

	// 返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的时间戳。
	requestStart: 1543806782241,

	// 返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的时间戳。
        //如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。
	responseStart: 1543806782516,

	// 返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时
        //(如果在此之前HTTP连接已经关闭,则返回关闭时)的时间戳。
	responseEnd: 1543806782537,

	// 当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的时间戳。
	domLoading: 1543806782573,

	// 当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的时间戳。
	domInteractive: 1543806783203,

	// 当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的时间戳。
	domContentLoadedEventStart: 1543806783203,

	// 当所有需要立即执行的脚本已经被执行(不论执行顺序)时的时间戳。
	domContentLoadedEventEnd: 1543806783216,

	// 当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的时间戳
	domComplete: 1543806783796,

	// load事件被发送时的时间戳。如果这个事件还未被发送,它的值将会是0。
	loadEventStart: 1543806783796,

	// 当load事件结束,即加载事件完成时的时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.
	loadEventEnd: 1543806783802
}

С помощью приведенных выше данных мы можем получить несколько полезных времен

// 重定向耗时
redirect: timing.redirectEnd - timing.redirectStart,
// DOM 渲染耗时
dom: timing.domComplete - timing.domLoading,
// 页面加载耗时
load: timing.loadEventEnd - timing.navigationStart,
// 页面卸载耗时
unload: timing.unloadEventEnd - timing.unloadEventStart,
// 请求耗时
request: timing.responseEnd - timing.requestStart,
// 获取性能信息时当前时间
time: new Date().getTime(),

Еще одно важное время –время белого экрана, что означает время с момента ввода URL-адреса до момента, когда страница начинает отображать содержимое.

Поместите следующий скрипт в</head>Время белого экрана можно получить спереди.

<script>
    whiteScreen = new Date() - performance.timing.navigationStart
</script>

Через это несколько раз вы можете узнать, какова производительность загрузки первого экрана страницы.

Кроме того, поwindow.performance.getEntriesByType('resource')В этом методе мы также можем получить время загрузки связанных ресурсов (js, css, img...), и он вернет все ресурсы, загруженные в данный момент на странице.

在这里插入图片描述

Как правило, он включает следующие виды

  • sciprt
  • link
  • img
  • css
  • fetch
  • other
  • xmlhttprequest

Нам нужна только следующая информация

// 资源的名称
name: item.name,
// 资源加载耗时
duration: item.duration.toFixed(2),
// 资源大小
size: item.transferSize,
// 资源所用协议
protocol: item.nextHopProtocol,

Теперь напишите несколько строк кода для сбора этих данных.

// 收集性能信息
const getPerformance = () => {
    if (!window.performance) return
    const timing = window.performance.timing
    const performance = {
        // 重定向耗时
        redirect: timing.redirectEnd - timing.redirectStart,
        // 白屏时间
        whiteScreen: whiteScreen,
        // DOM 渲染耗时
        dom: timing.domComplete - timing.domLoading,
        // 页面加载耗时
        load: timing.loadEventEnd - timing.navigationStart,
        // 页面卸载耗时
        unload: timing.unloadEventEnd - timing.unloadEventStart,
        // 请求耗时
        request: timing.responseEnd - timing.requestStart,
        // 获取性能信息时当前时间
        time: new Date().getTime(),
    }

    return performance
}

// 获取资源信息
const getResources = () => {
    if (!window.performance) return
    const data = window.performance.getEntriesByType('resource')
    const resource = {
        xmlhttprequest: [],
        css: [],
        other: [],
        script: [],
        img: [],
        link: [],
        fetch: [],
        // 获取资源信息时当前时间
        time: new Date().getTime(),
    }

    data.forEach(item => {
        const arry = resource[item.initiatorType]
        arry && arry.push({
            // 资源的名称
            name: item.name,
            // 资源加载耗时
            duration: item.duration.toFixed(2),
            // 资源大小
            size: item.transferSize,
            // 资源所用协议
            protocol: item.nextHopProtocol,
        })
    })

    return resource
}

резюме

Интерпретируя информацию о производительности и ресурсах, мы можем судить о том, что загрузка страницы происходит медленно по следующим причинам:

  1. Слишком много ресурсов
  2. Скорость интернета слишком низкая
  3. Слишком много элементов DOM

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

Мониторинг ошибок

Теперь можно отловить три типа ошибок.

  1. Ошибка загрузки ресурса, черезaddEventListener('error', callback, true)Перехватывайте ошибки сбоя загрузки ресурсов на этапе захвата.
  2. js ошибка выполнения, пройтиwindow.onerrorОтловить js-ошибки.
  3. обещание ошибка, пройтиaddEventListener('unhandledrejection', callback)Перехватывать ошибки обещаний, но нет такой информации, как количество строк и столбцов, в которых произошли ошибки, и вручную можно выдать только релевантную информацию об ошибках.

Мы можем создать переменную массива ошибокerrorsПри возникновении ошибки добавьте соответствующую информацию об ошибке в массив, а затем сообщите об этом единообразно на определенном этапе.Подробности см. в коде.

// 捕获资源加载失败错误 js css img...
addEventListener('error', e => {
    const target = e.target
    if (target != window) {
        monitor.errors.push({
            type: target.localName,
            url: target.src || target.href,
            msg: (target.src || target.href) + ' is load error',
            // 错误发生的时间
            time: new Date().getTime(),
        })
    }
}, true)

// 监听 js 错误
window.onerror = function(msg, url, row, col, error) {
    monitor.errors.push({
        type: 'javascript',
        row: row,
        col: col,
        msg: error && error.stack? error.stack : msg,
        url: url,
        // 错误发生的时间
        time: new Date().getTime(),
    })
}

// 监听 promise 错误 缺点是获取不到行数数据
addEventListener('unhandledrejection', e => {
    monitor.errors.push({
        type: 'promise',
        msg: (e.reason && e.reason.msg) || e.reason || '',
        // 错误发生的时间
        time: new Date().getTime(),
    })
})

резюме

Благодаря сбору ошибок вы можете понять тип и количество ошибок веб-сайта, чтобы вы могли внести соответствующие коррективы, чтобы уменьшить количество ошибок. Полный код и DEMO см. в другой моей статье.Внешний интерфейс и мониторинг ошибокВ конце вы можете скопировать код (HTML-файл), чтобы протестировать его локально.

отчетность данных

Отчет о производительности

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

window.onload = () => {
    // 在浏览器空闲时间获取性能及资源信息
    // https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback
    if (window.requestIdleCallback) {
        window.requestIdleCallback(() => {
            monitor.performance = getPerformance()
            monitor.resources = getResources()
        })
    } else {
        setTimeout(() => {
            monitor.performance = getPerformance()
            monitor.resources = getResources()
        }, 0)
    }
}

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

Отчет об ошибках

Код, который я предоставил в DEMO, используетerrorsМассив собирает все ошибки, а затем единообразно сообщает о них на определенном этапе (отложенная отчетность). На самом деле, его также можно изменить, чтобы сообщать при возникновении ошибки (мгновенный отчет). Это может избежать проблемы, связанной с тем, что пользователь закрыл веб-страницу и данные об ошибках будут потеряны после того, как отчет о задержке ошибки не будет запущен после завершения сбора.

// 监听 js 错误
window.onerror = function(msg, url, row, col, error) {
    const data = {
        type: 'javascript',
        row: row,
        col: col,
        msg: error && error.stack? error.stack : msg,
        url: url,
        // 错误发生的时间
        time: new Date().getTime(),
    }
    
    // 即时上报
    axios.post({ url: 'xxx', data, })
}

SPA

window.performanceAPI недоработан, когда SPA переключает маршруты,window.performance.timingданные не будут обновлены. Поэтому нам нужно найти другой способ подсчета времени от переключения маршрутов до завершения загрузки. Возьмем в качестве примера Vue. Возможным способом является защита глобального фронта маршрута при переключении маршрутов.beforeEachПолучите время начала в компонентеmountedВыполнить в хукеvm.$nextTick для получения времени завершения рендеринга компонента.

router.beforeEach((to, from, next) => {
	store.commit('setPageLoadedStartTime', new Date())
})
mounted() {
	this.$nextTick(() => {
		this.$store.commit('setPageLoadedTime', new Date() - this.$store.state.pageLoadedStartTime)
	})
}

Мы можем делать больше, чем просто отслеживать производительность и ошибки.

Сбор информации о пользователях

navigator

использоватьwindow.navigatorИнформация об устройстве пользователя, операционной системе, информация о браузере может быть собрана...

УФ (уникальный посетитель)

Относится к физическому лицу, которое получает доступ и просматривает эту веб-страницу через Интернет. Компьютер-клиент, обращающийся к вашему веб-сайту, является посетителем. Один и тот же клиент учитывается только один раз в период с 00:00 до 24:00. Только один UV рассчитывается для нескольких посещений одним и тем же посетителем в один день. Когда пользователь посещает веб-сайт, случайная строка + время и дата могут быть сгенерированы и сохранены локально. Когда запрашивается веб-страница (если она превышает 24 часа в сутки, она будет создана заново), передайте эти параметры серверной части, и серверная часть использует эту информацию для создания отчета статистики UV.

PV (просмотр страницы)

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

время на странице

традиционный сайтКогда пользователь входит на страницу A, время, когда пользователь входит на страницу, передается через фоновый запрос. Через 10 минут пользователь заходит на страницу B. В это время фон может определить, что пользователь оставался на странице A в течение 10 минут по параметрам, включенным в интерфейс.SPAВы можете использовать маршрутизатор, чтобы получить время пребывания пользователя, например, Vue, черезrouter.beforeEach destroyedЭти две функции хука определяют время пребывания пользователя в компоненте маршрутизации.

Глубина просмотра

пройти черезdocument.documentElement.scrollTopАтрибуты и высота экрана могут определить, закончил ли пользователь просмотр содержимого веб-сайта.

источник перехода на страницу

пройти черезdocument.referrerатрибут, вы можете узнать, с какого веб-сайта перешел пользователь.

резюме

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

Руководство по развертыванию внешнего мониторинга

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

Sentry — это инструмент для мониторинга производительности и ошибок, написанный на python.Вы можете использовать услуги, предоставляемые sentry (с небольшим количеством бесплатных функций), или вы можете развернуть службы самостоятельно. Теперь посмотрим, как реализовать мониторинг с помощью сервисов sentry.

Зарегистрировать аккаунт

Открытымhttps://sentry.io/signup/сайт для регистрации.

Выберите проект, я выбрал Vue.

Установить часовые зависимости

После выбора проекта ниже появится руководство по установке конкретных зависимостей sentry.

В соответствии с подсказкой выполните этот код в своем проекте Vue.npm install --save @sentry/browser @sentry/integrations @sentry/tracing, который устанавливает зависимости, необходимые для sentry.

Скопируйте следующий код в свойmain.js, вставитьnew Vue()До.

import * as Sentry from "@sentry/browser";
import { Vue as VueIntegration } from "@sentry/integrations";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  dsn: "xxxxx", // 这里是你的 dsn 地址,注册完就有
  integrations: [
    new VueIntegration({
      Vue,
      tracing: true,
    }),
    new Integrations.BrowserTracing(),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

Затем нажмите на первом шагеskip this onboardingдля входа на страницу консоли.

Если вы забыли свой DSN, нажмите на строку меню слева, чтобы выбратьSettings -> Projects-> Нажмите на свой проект ->Client Keys(DSN).

Создайте первую ошибку

Выполните оператор печати в вашем проекте Vue.console.log(b).

В это время щелкните элемент проблем на главной странице часового, и вы увидите сообщение об ошибке.b is not defined:

Это сообщение об ошибке содержит конкретную информацию об ошибке, а также ваш IP-адрес, информацию о браузере и т. д.

Но как ни странно, наша консоль браузера не выводила сообщение об ошибке.

Это потому, что он заблокирован часовым, поэтому нам нужно добавить опциюlogErrors: true.

Затем снова проверьте страницу и обнаружите, что консоль также сообщает об ошибке:

загрузить исходную карту

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

Давайте посмотрим, как загрузить исходную карту.

Сначала создайте токен авторизации.

Этот сгенерированный токен будет использоваться позже.

Установитьsentry-cliи@sentry/webpack-plugin:

npm install sentry-cli-binary -g
npm install --save-dev @sentry/webpack-plugin

После установки двух вышеуказанных плагинов создайте один в корневом каталоге проекта..sentryclircфайл (не забудьте.gitignoreДобавьте этот файл, чтобы не раскрывать токен), его содержимое выглядит следующим образом:

[auth]
token=xxx

[defaults]
url=https://sentry.io/
org=woai3c
project=woai3c

Замените xxx только что сгенерированным токеном.

orgназвание вашей организации.

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

Создать в рамках проектаvue.config.jsфайл, заполните следующее:

const SentryWebpackPlugin = require('@sentry/webpack-plugin')

const config = {
    configureWebpack: {
        plugins: [
            new SentryWebpackPlugin({
                include: './dist', // 打包后的目录
                ignore: ['node_modules', 'vue.config.js', 'babel.config.js'],
            }),
        ],
    },
}

// 只在生产环境下上传 sourcemap
module.exports = process.env.NODE_ENV == 'production'? config : {}

После заполнения выполнитьnpm run build, ты можешь видетьsourcemapзагрузить результат.

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

исходная карта не загружена

исходная карта загружена

Видно, что информация об ошибке после загрузки исходной карты более точная.

Переключить китайскую среду и часовой пояс

После выбора Обновить.

мониторинг производительности

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

оптимизация производительности

Оптимизация производительности в основном делится на две категории:

  1. Оптимизация времени загрузки
  2. оптимизация времени выполнения

Например, сжатие файлов и использование CDN — это оптимизация времени загрузки, сокращение операций DOM и использование делегирования событий — оптимизация времени выполнения.

Прежде чем решить проблему, нужно сначала обозначить проблему, иначе никак не начать. Поэтому, прежде чем приступать к оптимизации производительности, лучше всего изучить производительность загрузки и производительность веб-сайта.

Ручная проверка

Проверить производительность загрузки

Производительность загрузки веб-сайта в основном зависит от времени белого экрана и времени первого экрана.

  • Время белого экрана: относится ко времени с момента ввода URL-адреса до момента, когда страница начинает отображать содержимое.
  • Время в верхней части экрана: относится ко времени от ввода URL-адреса до полного отображения страницы.

Поместите следующий скрипт в</head>Время белого экрана можно получить спереди.

<script>
	new Date() - performance.timing.navigationStart
</script>

существуетwindow.onloadвыполнить в случаеnew Date() - performance.timing.navigationStartВы можете получить первое экранное время.

Проверить работоспособность

С помощью инструментов разработчика Chrome мы можем просматривать производительность веб-сайта во время выполнения.

Откройте сайт, нажмите F12, чтобы выбрать исполнение, нажмите на серую точку в левом верхнем углу, она станет красной, чтобы начать запись. В это время вы можете имитировать пользователя для использования веб-сайта.После его использования нажмите «Стоп», после чего вы сможете увидеть отчет о производительности во время работы веб-сайта. Если есть красный блок, значит, есть просадка кадров, если зеленый, значит, FPS хороший.

Кроме того, на вкладке «Производительность» нажатие ESC вызовет маленькое поле. Щелкните три точки слева от маленького поля, чтобы отметить рендеринг.

Эти два параметра: первый — выделить область перерисовки, а второй — отобразить информацию о рендеринге кадра. Отметьте эти два параметра, а затем просмотрите веб-страницу, вы можете увидеть изменения рендеринга вашей веб-страницы в режиме реального времени.

Проверить с помощью инструментов

инструмент мониторинга

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

хромированные инструменты Маяк

Если у вас установлен Chrome версии 52+, нажмите F12, чтобы открыть инструменты разработчика.

Это не только оценит производительность вашего сайта, но и SEO.

Проверяйте веб-приложения с помощью Lighthouse

Как выполнить оптимизацию производительности

В Интернете есть бесчисленное множество статей и книг по оптимизации производительности, но многие правила оптимизации устарели. Поэтому я написал статью об оптимизации производительности.24 совета по оптимизации производительности интерфейса (2020 г.), анализ обобщает 24 предложения по оптимизации производительности, которые настоятельно рекомендуются.

рефакторинг

«Рефакторинг 2»Книга определяет рефакторинг:

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

Рефакторинг и оптимизация производительности имеют сходства и различия.

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

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

Если вы посвятите некоторое время рефакторингу, рекомендуется проводить тестирование сразу после рефакторинга фрагмента кода. Это позволяет избежать слишком большого изменения кода и поиска неправильной точки, когда что-то идет не так.

принципы рефакторинга

  1. Вещей не более трех, и реконструируются три. То есть нельзя писать один и тот же код повторно, и в этом случае нужен рефакторинг.
  2. Если часть кода трудно читать, пришло время подумать о рефакторинге.
  3. Если вы уже понимаете код, но он громоздок или недостаточно хорош, вы также можете провести его рефакторинг.
  4. Слишком длинные функции нуждаются в рефакторинге.
  5. Одна функция лучше всего подходит для одной функции, и если функция вставлена ​​в несколько функций, ее необходимо реорганизовать.

Рефакторинг

существует«Рефакторинг 2»В этой книге представлены сотни методов рефакторинга. Но я думаю, что есть два наиболее часто используемых:

  1. Извлечение повторяющегося кода и инкапсуляция его в функции
  2. Разделение функции, которая слишком длинная или имеет слишком много функций

Извлечение повторяющегося кода и инкапсуляция его в функции

Предположим, есть интерфейс для запроса данных/getUserData?age=17&city=beijing. Все, что теперь нужно сделать, это поставить пользовательские данные:{ age: 17, city: 'beijing' }Преобразуется в форму параметров URL:

let result = ''
const keys = Object.keys(data)  // { age: 17, city: 'beijing' }
keys.forEach(key => {
    result += '&' + key + '=' + data[key]
})

result.substr(1) // age=17&city=beijing

Если нужно преобразовать только этот интерфейс, то не проблема не инкапсулировать его как функцию. Но если есть несколько интерфейсов, которые имеют это требование, вы должны инкапсулировать его как функцию:

function JSON2Params(data) {
    let result = ''
    const keys = Object.keys(data)
    keys.forEach(key => {
        result += '&' + key + '=' + data[key]
    })

    return result.substr(1)
}

Разделение функции, которая слишком длинная или имеет слишком много функций

Предположим, теперь есть функция регистрации, представленная в псевдокоде:

function register(data) {
    // 1. 验证用户数据是否合法
    /**
     * 验证账号
     * 验证密码
     * 验证短信验证码
     * 验证身份证
     * 验证邮箱
     */

    // 2. 如果用户上传了头像,则将用户头像转成 base64 码保存
    /**
     * 新建 FileReader 对象
     * 将图片转换成 base64 码
     */

    // 3. 调用注册接口
    // ...
}

Эта функция содержит три функции: проверка, преобразование и регистрация. Функции проверки и преобразования могут быть извлечены и инкапсулированы в функции по отдельности:

function register(data) {
    // 1. 验证用户数据是否合法
    // verify()

    // 2. 如果用户上传了头像,则将用户头像转成 base64 码保存
    // tobase64()

    // 3. 调用注册接口
    // ...
}

Если вы заинтересованы в рефакторинге, я настоятельно рекомендую прочитать«Рефакторинг 2»Эта книга.

Использованная литература:

Суммировать

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

Более полное руководство см.Позвольте вам начать работу с фронтенд-инжинирингом