〇 Предисловие
Это вторая передовая статья основной статьи, созданной автором после того, как компания обновила структуру деятельности.Учитывая, что эта статья является относительно независимой, она извлекается и пишется отдельно. Сестры и братья»,Геймплей с динамическими компонентами, о котором вы могли не знать 🍉".
Может быть, текст "Извините". Если вы знаете большого парня, не входите. Нет, нет, пожалуйста, жалуйтесь, делайте кирпич, Руи Сибай.
Слава богу Хао за вопрос 😂, не вините ssh, вините меня.
В этой статье могут быть некоторые недочеты, надеюсь, вы сделаете еще замечания и предложения, спасибо 😘.
〇 Фон
Автор раньше работал в рекламном отделе нашей компании.Метод рекламы заключается в отображении страниц активности, таких как скретч-карты и большие вертушки, а затем пользователи участвуют в слое бомбы рекламного купона.
Основная предыстория этой статьи такова: есть отзывы со стороны бизнеса, предполагающие, что мы можем сделать некоторые оптимизации, чтобы улучшить скорость оттока страниц.
Поэтому для ситуации с данными активной страницы мы провели тест. По тестовым данным скорость завершения загрузки некоторых страниц (под профессиональной можно понимать скорость загрузки первого экрана) низкая, но кликабельность входа первого уровня на активной странице в норме.
Эта ситуация немного странная, но опыт подсказывает нам, что после того, как пользователь щелкнет запись верхнего уровня для входа, пользователь может подумать, что белый экран завис, или не может вынести беспокойства ожидания белого экрана из-за долгое ожидание белого экрана Терпение теряется.
Как сократить время белого экрана?
Это сделано для того, чтобы пользователи могли быстрее видеть не «белые», такие как цвет фона фона, фоновое изображение, изображение ключевой части и т. д., которые могут быть связаны с ним.
Я не знаю, использовали ли вы экран-скелет, ниже мы будем использовать возможность, аналогичную экрану-скелету, для решения этой проблемы.
〇 Реализация «схемы скелета»
Скелетный экран в основном обрисовывает структуру DOM с помощью линий, когда подробные элементы страницы не отображаются. Для страницы маркетинговой активности C-стороны нет стандартного скелета, и каждая активность имеет свою структуру, так что же нам делать?
Мы можем добиться аналогичных эффектов с помощью цветов фона и изображений, поэтому мы разработали концепцию «скелетной карты», которая на самом деле представляет собой каркас экрана.
Реализовать идеи
Глядя на действие по открытию красных конвертов, мы обнаружим, что контент, на который обращают внимание пользователи, — это «красные конверты» и цвет фона на картинке.
Мы должны сделать все возможное, чтобы изображение красного конверта "снос" отображалось быстрее.
Ниже приведен скриншот этого действия, через Chrome Dev Tools -> Network -> Disable Cache -> Fast 3G (4G, WIFI слишком быстр для наблюдения) -> right ⚙️ -> Capture ScreenShots. открыть. Вы можете просматривать кадр за кадром.
Наша цель — сделать ключевые кадры, кадр 1,44 с в зеленой рамке на рисунке ниже, можно отобразить раньше.
Как сформировать такую рамку из ключевых картинок🤔? Естественно думать о статической странице. Это не что иное, как визуализация с помощью HTML, CSS и изображений.
Следовательно, необходимо предоставить структуру DOM, предоставить CSS, предоставить изображения и сгенерировать «статическую скелетную диаграмму».
Вышеупомянутое является обычной HTML-разработкой.
🤔️ Но мы не разрабатываем через чистый HTML, как нам получить DOM-структуру Vue-страницы?
Здесь у учащихся могут возникнуть вопросы, зачем нужно брать DOM-структуру Vue отдельно.
Обычно мы монтируем проект Vue под корневым узлом, например #app.
<html>
<head></head>
<body>
<div id="app"></div>
<script src="/cdn/xxx/vue.js"></script>
</body>
</html>
Смонтировав экземпляр Vue в #app.
// index.js
import Entry from './Entry.vue'
new Vue({
render: h => h(Entry)
}).$mount('#app')
Затем идет структура DOM (шаблон), соответствующая реальному компоненту.
// Entry.vue
<template>
<div class="entry">
<img src="/cdn/xx/image.png"/>
<button class="btn" type="button">请点击</button>
</div>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
Из приведенного выше видно, что DOM-структура Vue на самом деле скрыта в файле .vue, и до того, как мы инициализируем и визуализируем Vue, мы можем получить только div #app.
Поэтому нам нужно найти способ получить структуру DOM в компоненте Vue. Как получить его?
Предварительная визуализация DOM
Прежде чем мы начнем, давайте взглянем на эту картинку, на которой показан наш общий процесс, так что не теряйтесь, если у вас есть картинка.
Я не знаю, слышали ли вы о кукловоде, безголовом браузере, что он может сделать? Как правило, мы будем использовать его для запуска целевых онлайн-страниц и получения некоторых данных.
Здесь мы используем его, чтобы помочь нам перехватить структуру DOM Vue. Если мы используем puppetter для перехвата DOM, нам нужно будет сделать несколько шагов, запустить соответствующую страницу в безголовом браузере, а затем дождаться, пока страница отобразит компонент Vue.После завершения рендеринга перехватите структуру DOM под соответствующее #app и сохраните его.
Поскольку шаги несложные, но для этого потребуется много кода, на самом деле в сообществе уже есть большие ребята, которые помогут нам интегрировать эти возможности.prerender-spa-plugin, Это оно.
Используя prerender-spa-plugin, можно легко получить структуру DOM.Его принцип заключается в том, чтобы сначала запустить безголовый браузер, затем выполнить соответствующую маршрутизацию приложения и перехватить структуру DOM на отображаемой странице.
Конкретное использование prerender-spa-plugin здесь подробно обсуждаться не будет, вы можете обратиться к официальной документации.
cheerioЭто инструмент, который помогает нам получать контент, см. официальное объяснение.
Быстрая и гибкая реализация базовой реализации jQuery, разработанная специально для сервера.
Чтобы получить структуру DOM страницы Vue, вам нужно сделать это в два этапа.
- Сначала выполните предварительный рендеринг сборки и выведите ключевую структуру DOM страницы Vue, полученную в результате предварительного рендеринга.
- Затем выполните сборку в обычном режиме и вставьте полученную структуру DOM в исходный DOM страницы.
предварительно визуализированная сборка
Ниже приведена конфигурация предварительного рендеринга для веб-пакета в сборке предварительного рендеринга.
// 预渲染构建的配置
{
plugins: [
// 生成vue dom骨架
new PrerenderSPAPlugin({
// 原文件地址
staticDir: path.join(__dirname, config.localDir),
// 构建生成地址
outputDir: path.join(__dirname, config.prerenderPath),
routes: [ '/' ],
// 获取上下文
postProcess (context) {
// 获取编译后的html内容
const html = context.html
// 使用cheerio选择器
const $ = cheerio.load(html)
// 预渲染删除apple(一般C端页面都有个苹果免责协议,在预渲染的页面是多余的)
$('#apple').remove()
// 截取需要的html片段
const contentHtml = '<div class="app">' + $('.app').html() + '</div>'
// 删除掉一些首屏无关的image图片
context.html = contentHtml.replace(/<img[^>]*>/gi, '')
// 匹配每个标签里的style里的内容
context.html = context.html.replace(/style="[^"]*"/g, function (matched) {
// 如果符合以下布局属性,则保留,这里没考虑margin、padding等
const reg = /(width|height|left|top|bottom|right):[^rem]*rem;/g
const result = matched.match(reg)
if (result) return 'style="' + result.join('') + '"'
return ''
})
// 输出处理好的预渲染内容
return context
}
})]
}
После того, как предварительный рендеринг построен, мы получаем DOM-структуру страницы Vue и начинаем официальное построение.
Формальная сборка
Здесь мы сами пишем плагин для Webpack, как писать плагин для Webpack здесь повторяться не будем. Основная функция — вставить предварительно отрендеренный DOM в официальный DOM.
// 正式构建,利用插件的能力
const path = require('path')
const fs = require('fs')
// 命名插件,也可以直接使用class定义,不是重点
function VueDomPrerenderPlugin (options) {
this._options = options
}
VueDomPrerenderPlugin.prototype.apply = function (compiler) {
const self = this
compiler.hooks.compilation.tap('VueDomPrerenderPlugin', (compilation) => {
// 通过html-webpack-plugin的hook
compilation.plugin(
'html-webpack-plugin-after-html-processing',
(data, cb) => {
// 找到预渲染输出的文件
const prerenderFile = path.join(self._options.preoutDir, 'index.html')
// 把预渲染生成的DOM,插入到正式DOM中。
let htmlContent = data.html.replace('<div id="app"></div>', (matched) => {
const prerenderHtml = fs.readFileSync(prerenderFile, 'utf8')
return '<div id="app" data-server-render="true">' + prerenderHtml + '</div>'
})
}
)
})
}
module.exports = VueDomPrerenderPlugin
Но теперь он статичен, а картинки и цвета фона на странице фиксированы.Как можно динамически задавать картинки, цвета фона и т.д.?
Динамически устанавливаемые данные
HTML и CSS можно использовать только как статические страницы, но JavaScript может, а JS может получать данные и устанавливать их в соответствии с данными, поэтому наши страницы не являются жестко запрограммированными изображениями и цветами.
Сначала получите соответствующие данные изображения и цвета через JS, затем найдите соответствующий заполнитель изображения в структуре DOM и т. д. и установите изображение с помощью JS. Здесь есть три шага.
// H5页面中的代码
let color = window.CFG.color
let bgImage = window.CFG.bgImage
let bgColor = window.CFG.bgColor
// 类似的还有其他数据...
let $text = document.querySelector('.text')
let $bg = document.querySelector('.bg')
// 类似的还有很多...
$text.style.color = color
$bg.style.backgroundImage = 'url(' + bgImage + ')'
$bg.style.backgroundColor = bgColor
// 类似的还有很多...
🤔️ Из-за вышеупомянутого императивного метода JS читабельность настроек стилей плохая, и у нас слишком много шаблонного кода, который относительно навязчив для бизнеса и недружелюбен к разработке. Как это сделать?
уменьшить дублирующийся код
Мы думаем, можем ли мы просто позволить студентам, изучающим разработку, писать код, такой как написание CSS, и код для предустановки некоторых предварительно загруженных изображений, цветов фона и т. д.?
Можно понять, что указанные выше точки абстракции могут превратить императивный код в декларативный код, который легче понять.
грубый процесс.
определить шаблон
Мы подумали о том, можем ли мы использовать возможность шаблонов для предоставления файла типа .tcss, который является файлом, подобным CSS. Вы можете видеть, что мы предоставляем возможности настройки переменных через **{{ }}**.
Разобрать шаблон
Итак, как разобрать такой шаблон, мы передаем возможности узла и регулярных выражений. Вам нужно предоставить часть логики кода, сначала прочитать файл .tcss, а затем заменить шаблон реальным исполняемым кодом.
// node脚本中的代码
// 解析preload.tcss,输出preloadCss、preloadImages
// 某个活动下
const BASE_FOLDER = `./src/pages/activity`
// 上述文件.tcss所在地址
const cssPath = path.join(BASE_FOLDER, 'preload.tcss')
// 获取文件的字符串
const data = fs.readFileSync(cssPath).toString()
// 匹配字符串的开始
let start = 0
// 记录预加载的图片
const preloadImages = []
let preloadCss = data.replace(/{{(\S+)}}/g, function (matched, pattern, offset, string) {
// 当前匹配到的字段的偏移量
let end = offset
// 截取非空字符串
const substring = string.substring(start, end)
// 只有image类型的才会存到预加载数组里
if (substring.indexOf('url') !== -1) {
preloadImages.push(`get('${pattern}')`)
}
// 上一次的终点为这次的起点
start = end
// 把匹配到的例如image、color进行包装
return `' + get('${pattern}') + '`
})
// 统一成\n
preloadCss = preloadCss.replace(/(\r|\n|\r\n)/g, function (matched) {
return `'\n+'`
})
Примерный код приведен выше, который выведет список предварительно загруженных изображений — preloadImages и JS-фрагмент стиля предварительной загрузки Style — preloadCss.
Чтобы сгенерировать JS-фрагмент стиля, вы можете задаться вопросом, как генерируется такой фрагмент кода JS, потому что мы сначала предварительно создали код JS, который может «генерировать CSS» локально через скрипт узла, и, наконец, этот код является страницей. рендеринг время запуска. Почему не чистый CSS, ведь нам нужно динамически получать значения свойств (изображение, цвет и т.д.).
preloadCss примерно такой, как показано на рисунке ниже.
PreloadImages примерно такие, как показано ниже.
Код производственного материала (изображение, CSS)
Для изображения здесь мы решили использовать самый простой новый образ для реализации.
После получения preloadImages и preloadCss мы вызываем общедоступные методы для загрузки изображений и генерации фрагментов стилей.
// node脚本中的代码
const TARGET_PATH = './node_modules/.cache/preload-image/'
const outputFilePath = path.join(TARGET_PATH, 'index.js')
if (!fs.existsSync(TARGET_PATH)) {
fs.mkdirSync(TARGET_PATH, { recursive: true })
}
const preloadCode = `
;(function(win) {
var preloadImages = [${preloadImages.join(',')}];
preloadImage(preloadImages);
var styles = '${preloadCss}';
addPreloadStyle(styles);
})(window);
`
fs.writeFileSync(outputFilePath, preloadCode)
Приведенный выше код объединяется в строку строк, которые в конечном итоге будут встроены в HTML-страницу и будут выполняться при отображении страницы.
Таким образом, мы завершили предзагруженные материалы: подготовка картинок и стилей. Далее нужно нанести препарат на страницу.
Встроенный код на страницу
Набор изображений для предварительной загрузки, стиль JS, встроенный способ проникнуть в HTML.
<html>
<head>
<script src="./node_modules/.cache/preload-image/index.js?__inline"></script>
</head>
</html>
Как видите, что делает __inline?Его эффект заключается в том, чтобы встроить исходящий JS в HTML.
<html>
<head>
<script>
function preloadImage (arr) {
for (let i = 0; i < arr.length; i++) {
const images = []
images[i] = new Image()
images[i].src = arr[i]
}
}
function addPreloadStyle (styles) {
const css = document.createElement('style')
css.type = 'text/css'
if (css.styleSheet) {
css.styleSheet.cssText = styles
} else {
css.appendChild(document.createTextNode(styles))
}
document.getElementsByTagName('head')[0].appendChild(css)
}
</script>
<script>
!function(win){
var preloadImages = [
get("bgImage"),
// 省略了...
];
preloadImage(preloadImages);
var preloadCss = '.tac-app {' +
'background-image: url("'+ get("bgImage")+'");' +
'background-color: '+ get("bgColor")+';' +
'}'
addPreloadStyle(preloadCss)
(window);
</script>
</head>
</html>
«Благополучие: плагин веб-пакета для функции __inline»
Что касается вышеизложенного, как реализована функция __inline, вы можете взглянуть на метод написания этого плагина веб-пакета.
/*
* script标签中含有?__inline标识的Js会被内联到HTML。
*/
const fs = require('fs')
const getScriptAbsolutePath = (matched, reg) => {
const result = matched.match(reg)
const relativePath = result && result[1]
return relativePath
}
class ScriptInlinePlugin {
apply (compiler) {
compiler.hooks.compilation.tap('ScriptInlinePlugin', (compilation) => {
// 由于vuecli3使用的webpack-html-plugin是3.2版本,所以暂时不能使用tap形式。
compilation.plugin(
'html-webpack-plugin-after-html-processing',
(data) => {
// 读取目标模版
let htmlContent = data.html
// 匹配script的reg
const jsReg = /<script[^<]*?src="?[^<]*?\?__inline"?.*?>.*?<\/script>/gmi
// 匹配script片段
htmlContent = htmlContent.replace(jsReg, (matched) => {
// 获取script绝对地址
const absolutePath = getScriptAbsolutePath(matched, /src="?(.*)\?__inline/)
// 获取script对应地址的内容
const jsContent = fs.readFileSync(absolutePath, 'utf-8')
return `<script type="text/javascript">${jsContent}</script>`
})
data.html = htmlContent
}
)
})
}
}
module.exports = ScriptInlinePlugin
Основные функции уже доступны, но после некоторого времени использования я обнаружил некоторые проблемы.
Опыт Соображения
Мы обнаружили, что функция реализована, но не очень удобна для разработки.
Шаги анализа
Теперь учащимся, занимающимся разработкой, необходимо разработать экран-скелет, что требует нескольких шагов.
- Заполнитель предварительно загруженного изображения устанавливается в vue, чтобы можно было связать предварительно загруженное изображение.
<template>
<div :class="$style.app">
<div :class="$style.button">
</div>
<div :class="$style.cat">
</div>
</div>
</template>
<style lang="less" module>
// 预渲染使用的全局class占位符
:global {
.app {}
.button {}
.cat {}
}
</style>
- Вам нужно знать, какие переменные возвращаются внутренним интерфейсом, а затем установить их в соответствующий файл .tcss.
// backgroundColor
// backgroundImage
// buttonImage
// catImage
- tcss устанавливает соответствующий стиль предварительной загрузки для записи
.app {
background-image: url("{{backgroundImage}}");
}
.button {
background-image: url("{{buttonImage}}");
}
.cat {
background-image: url("{{catImage}}");
}
Умственные затраты на развитие одноклассников все еще относительно велики, и ему нужно слишком много заботиться.
Предварительная информация
На следующем рисунке показан основной поток действия.
Забудьте о деталях и сосредоточьтесь на потоке ключевых действий.
- Пользователь заходит на активную страницу, и сервер начинает рендеринг;
- Разработка деятельности обеспечивает код деятельности, развитие инфраструктуры обеспечивает общедоступный код;
- Операция по деятельности Настройка действий через платформу управления операциями;
- Сервер запускает объединение шаблонов и получает код и конфигурацию из базы данных для объединения;
- Браузер представляет активную страницу.
Поскольку одно из наших мероприятий имеет фиксированный основной образ и стиль, там не тысячи людей.
Таким образом, мы можем подтвердить, какие изображения могут быть предварительно загружены при настройке платформы управления операциями.
Конфигурировать вместо кодирования
Вы можете настроить его на платформе управления операциями и выбрать изображение, которое необходимо предварительно загрузить, например фоновое изображение на рисунке ниже.
Возьмите все выбранные изображения в виде списка и передайте его в бэкэнд SSR.Поскольку наш бэкенд Java отображается как шаблон, используется шаблон Velocity.
// 通过Velocity循环渲染。
#foreach( $key in $preloadImageList )
<link rel="preload" as="image" href="$preloadImageList.get($key)">
#end
Таким образом, мы освобождаем передовых студентов от бремени кодирования и предварительной загрузки.
Инженерная часть сделана, теперь нам нужно посмотреть, как оптимизировать нашу производительность и, что более важно, изображения.
〇 Более быстрая загрузка изображений
Примечание ⚠️, для просмотра сети ниже сначала выполните следующие действия: Chrome Dev Tools -> Network -> Disable Cache -> Fast 3G.
Порядок загрузки ресурсов
В приведенном выше примере мы используем новый метод Image для загрузки изображения, но будут проблемы.
новая загрузка изображения
Давайте разберемся в этой проблеме на примере. На этой странице необходимо загрузить 3 изображения, 2 файла CSS и 6 файлов JS.
<!DOCTYPE html>
<html>
<head>
<script>
function preloadImage(arr) {
for (let i = 0; i < arr.length; i++) {
const images = []
images[i] = new Image()
images[i].src = arr[i]
}
}
preloadImage(
[
'http://yun.tuisnake.com/tuia/dist/image/1.jpg',
'http://yun.tuisnake.com/tuia/dist/image/2.png',
'http://yun.tuisnake.com/tuia/dist/image/3.png'
])
</script>
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue5.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
Мы обнаружили, что образ был загружен после того, как были загружены первые четыре файла JS. Но мы бы хотели, чтобы изображения можно было загрузить раньше.
Для студентов, которые не понимают приведенную выше картинку, вы можете прочитать ее.Timing breakdown phases explained
Почему это? Во-первых, в протоколе Http1.1 одновременно может быть открыто только 6 TCP-соединений под одним и тем же доменным именем, поэтому требуется организация очереди ресурсов.
Ресурсы имеют приоритет
Но этого кажется недостаточно для объяснения 🧐, продолжаем искать причину.
Мы обнаружили, что есть поле «Приоритет», а ресурс изображения — «Низкий».Похоже, что изображение имеет самый низкий приоритет в очереди.
Но для чего этот приоритет?
Подумайте об этом 🤔️Как наш браузер узнает, какие ресурсы скачивать первыми? Здесь не продается.
- В первую очередь необходимо четко классифицировать ресурсы браузера.
- Затем вычислить приоритет ресурса
- Наконец, загрузите последовательно в соответствии с приоритетом ресурса
Дальнейшее чтение можно найти здесь:Процесс загрузки ресурсов страницы браузера и оптимизация.
Таким образом, мы обнаружили, что браузер имеет приоритет загрузки ресурсов, например, CSS, HTML, JS и т. д. являются всеми основными ресурсами, поэтому приоритет является наивысшим, а изображения, видео и аудио не являются основными ресурсами, а приоритет равен относительно низко. Обычно, когда последний сталкивается с первым, ему необходимо «уступить дорогу» и перейти в состояние ожидания.
приоритет изображения
Возвращаясь к этой статье, почему у нас низкий приоритет изображения, можно ли его улучшить? Итак, давайте кое-что узнаем.
Avoid chaining critical requestsупомянулОтчет о приоритетах браузера(Предоставлено Пэтом Минаном), показывающим, как Chrome с ядром Blink, начиная с Chrome 46 и выше, отдает приоритет различным ресурсам.
Следующая картинка — это приоритет загрузки Chrome в статье выше, вы можете его наблюдать.
PS: Эта картинка 2015 года, и поведение браузеров сейчас может быть другим. Я надеюсь, что есть студенты, которые знают обновленную версию, пожалуйста, оставьте сообщение, спасибо.
Мы видим, что Image имеет два типа приоритета, один находится внутри представления — Image (в области просмотра), а другой вне представления — Image. Соответствует высокому и низкому приоритетам соответственно.
Мы также нашли подсказки по этому поводу в другой статье на сайте web.dev.Fast load timesв модулеPrioritize resourcesВведение в статью.
for example, an image that is part of the initial render is prioritized higher than an image that starts offscreen.
Загрузка в качестве фонового изображения
Так говорит теория, давайте проверим. Мы добавили стили, DOM и настройки фонового изображения ко всем трем изображениям, чтобы они существовали в представлении.
<!DOCTYPE html>
<html>
<head>
<style>
.image1, .image2, .image3 {
height: 300px;
width: 400px;
}
.image1 {
background-image: url('http://yun.tuisnake.com/tuia/dist/image/1.jpg');
}
.image2 {
background-image: url('http://yun.tuisnake.com/tuia/dist/image/2.png');
}
.image3 {
background-image: url('http://yun.tuisnake.com/tuia/dist/image/3.png');
}
</style>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<div class="image1"></div>
<div class="image2"></div>
<div class="image3"></div>
<!-- 利用vue作为测试js,有6个 -->
<script src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue5.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
Результат показан ниже.
Судя по картинке выше, это правда, что приоритет картинки поднят до High, но она все равно грузится после js файла, почему так?
Изображение background-image, существующее в css background image, не начнет загружаться до тех пор, пока не будет загружена структура (после отображения всего содержимого веб-страницы); в то время как тег img в html является частью структуры (контента) веб-страницы и будет загружаться в процессе загрузки структуры load.
А что, если мы попытаемся использовать тег img напрямую?
Загрузка как вкладка
<!DOCTYPE html>
<html>
<head>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<!-- 修改点 -->
<img src="http://yun.tuisnake.com/tuia/dist/image/1.jpg"/>
<img src="http://yun.tuisnake.com/tuia/dist/image/2.png"/>
<!-- 利用vue作为测试js,有6个 -->
<script src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
</body>
</html>
Видим, что картинка действительно загружается в первый раз.
Так ли это на самом деле, заранее ли загружается изображение, если оно в виде тега img?
Давайте попробуем еще одну DEMO, 2 файла CSS и 4 файла JS.
<!DOCTYPE html>
<html>
<head>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<!-- 修改点 -->
<img src="http://yun.tuisnake.com/tuia/dist/image/1.jpg"/>
<img src="http://yun.tuisnake.com/tuia/dist/image/2.png"/>
<!-- 利用vue作为测试js,有6个 -->
<script src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
</body>
</html>
Давайте посмотрим на результаты.
Мы обнаружили, что два графических ресурса отстают. Вот почему а? ? ?
Вспоминая предзнаменование, которое я похоронил перед этим разделом, 2 CSS + 4 JS уже заполнили лимит в 6 запросов на доменное имя, и картина отстает.
Так почему же изображения не могут захватывать CSS и JS? Посмотрите еще раз на эту картинку.
Когда изображение инициализировано, приоритетом по умолчанию является Низкий, и только когда браузер отображает изображение, он вычисляет, следует ли упоминать изображение в представлении с высоким приоритетом. Вы видели что-нибудь, потому что CSS и Script будут иметь более высокий приоритет, чем изображение.
Загрузка изображений через image тоже не очень надежна, есть ли другой способ?
Приоритетная загрузка изображения
Давайте взглянем на один API, preload.
Способ 1: предварительная загрузка изображений
Также прочитайте эту статьюPreload, Prefetch And Priorities in ChromeПоймите, вот переведенная версия,Preload, Prefetch и их приоритетная оптимизация производительности в Chrome.
Приоритет изображения увеличен, но оно не обязательно загружается с первого раза. Как я могу принудительно увеличить порядок загрузки изображений?
- Три типа ресурсов, html, css и шрифт, имеют наивысший приоритет;
- Затем ресурс предварительной загрузки (черезпредварительная загрузка тега), скрипт, запрос xhr.
<!DOCTYPE html>
<html>
<head>
<!-- 修改点 -->
<link rel="preload" as="image" href="http://yun.tuisnake.com/tuia/dist/image/1.jpg">
<link rel="preload" as="image" href="http://yun.tuisnake.com/tuia/dist/image/2.png">
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<!-- 利用vue作为测试js,有6个 -->
<script src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue5.js"></script>
<script src="http://yun.tuisnake.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
С помощью предварительной загрузки мы можем изменить порядок загрузки ресурсов браузера.
Как видно из изображения выше, наше изображение действительно загружается впервые.
Есть ли другой способ?
Способ 2: отдельное доменное имя изображения
Мы знаем, что http1.1 ограничен 6 ссылками под одним и тем же доменным именем, поэтому мы можем попробовать несколько доменных имен? Дайте изображению другое уникальное доменное имя.
<!DOCTYPE html>
<html>
<head>
<script>
function preloadImage(arr) {
for (let i = 0; i < arr.length; i++) {
const images = []
images[i] = new Image()
images[i].src = arr[i]
}
}
</script>
<!-- 测试图片3张 -->
<script>
preloadImage(
[
'https://yun.tuipink.com/tuia/dist/image/1.jpg',
'https://yun.tuipink.com/tuia/dist/image/2.png',
'https://yun.tuipink.com/tuia/dist/image/3.png'
])
</script>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="https://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="https://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<!-- 利用vue作为测试js,有6个 -->
<script src="https://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script src="https://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script src="https://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script src="https://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
<script src="https://yun.tuisnake.com/tuia/dist/js/vue5.js"></script>
<script src="https://yun.tuisnake.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
Хорошо видно, что картинка тоже загружается в первый раз.
Выглядит тоже хорошо, не так ли, есть ли другой способ?
Способ 3: понизить приоритет других ресурсов
Благодаря таблице приоритетов мы знаем, что если JS загружается позже, изображение загружается раньше.
Сначала мы можем рассмотреть теги async и defer.
async vs defer attributes - Growing with the Web
Но я обнаружил, что async и defer не меняют порядок запросов js-файлов, они по-прежнему находятся перед изображением.
"асинхронный"
<!DOCTYPE html>
<html>
<head>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<image src="http://yun.tuisnake.com/tuia/dist/image/1.jpg"></image>
<image src="http://yun.tuisnake.com/tuia/dist/image/2.png"></image>
<image src="http://yun.tuisnake.com/tuia/dist/image/3.png"></image>
<!-- 利用vue作为测试js,有6个 -->
<script async src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script async src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script async src="http://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script async src="http://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
<script async src="http://yun.tuisnake.com/tuia/dist/js/vue5.js"></script>
<script async src="http://yun.tuisnake.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
Изображение по-прежнему загружается последним.
"отложить"
<!DOCTYPE html>
<html>
<head>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="http://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<image src="http://yun.tuisnake.com/tuia/dist/image/1.jpg"></image>
<image src="http://yun.tuisnake.com/tuia/dist/image/2.png"></image>
<image src="http://yun.tuisnake.com/tuia/dist/image/3.png"></image>
<!-- 利用vue作为测试js,有6个 -->
<script defer src="http://yun.tuisnake.com/tuia/dist/js/vue1.js"></script>
<script defer src="http://yun.tuisnake.com/tuia/dist/js/vue2.js"></script>
<script defer src="http://yun.tuisnake.com/tuia/dist/js/vue3.js"></script>
<script defer src="http://yun.tuisnake.com/tuia/dist/js/vue4.js"></script>
<script defer src="http://yun.tuisnake.com/tuia/dist/js/vue5.js"></script>
<script defer src="http://yun.tuisnake.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
Это тоже не работает, изображение все равно загружается последним.
Вышеуказанные два способа не работают, есть ли другой способ?
«JS-загрузчик»
Поскольку нативный вариант не годится, давайте использовать код JS для управления временем вставки скриптов и CSS.
<!DOCTYPE html>
<html>
<head>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="https://yun.tuisnake.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="https://yun.tuisnake.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<!-- 修改点 -->
<image src="https://yun.tuisnake.com/tuia/dist/image/1.jpg"></image>
<image src="https://yun.tuisnake.com/tuia/dist/image/2.png"></image>
<image src="https://yun.tuisnake.com/tuia/dist/image/3.png"></image>
<script src="https://yun.tuisnake.com/tuia/dist/js/loader.js"></script>
<script>
Loader.async([
'https://yun.tuisnake.com/tuia/dist/js/vue1.js',
'https://yun.tuisnake.com/tuia/dist/js/vue2.js',
'https://yun.tuisnake.com/tuia/dist/js/vue3.js',
'https://yun.tuisnake.com/tuia/dist/js/vue4.js',
'https://yun.tuisnake.com/tuia/dist/js/vue5.js',
'https://yun.tuisnake.com/tuia/dist/js/vue6.js'
])
</script>
</body>
</html>
Вы можете видеть, что очевидные изображения загружаются первыми.
Так много было сказано ранее о Http1.1, давайте теперь рассмотрим случай Http2.0.
Способ 4: HTTP2.0
Нам нужно включить Http2.0,Для доменного имени yun.tuiapple.com включен Http2.0.
<!DOCTYPE html>
<html>
<head>
<script>
function preloadImage(arr) {
for (let i = 0; i < arr.length; i++) {
const images = []
images[i] = new Image()
images[i].src = arr[i]
}
}
</script>
<!-- 测试图片3张 -->
<script>
preloadImage(
[
'https://yun.tuiapple.com/tuia/dist/image/1.jpg',
'https://yun.tuiapple.com/tuia/dist/image/2.png',
'https://yun.tuiapple.com/tuia/dist/image/3.png'
])
</script>
<!-- 利用bootstrap作为测试css,有2个 -->
<link rel="stylesheet"
href="https://yun.tuiapple.com/tuia/dist/css/bootstrap1.css">
<link rel="stylesheet"
href="https://yun.tuiapple.com/tuia/dist/css/bootstrap2.css">
</head>
<body>
<!-- 利用vue作为测试js,有6个 -->
<script src="https://yun.tuiapple.com/tuia/dist/js/vue1.js"></script>
<script src="https://yun.tuiapple.com/tuia/dist/js/vue2.js"></script>
<script src="https://yun.tuiapple.com/tuia/dist/js/vue3.js"></script>
<script src="https://yun.tuiapple.com/tuia/dist/js/vue4.js"></script>
<script src="https://yun.tuiapple.com/tuia/dist/js/vue5.js"></script>
<script src="https://yun.tuiapple.com/tuia/dist/js/vue6.js"></script>
</body>
</html>
Мы обнаружили, что изображения загружаются почти одновременно с CSS и JS.
окончательный план
Поскольку наша бизнес-среда предназначена для подключения ко многим носителям, но некоторые носители не поддерживают Https, нам необходимо одновременно учитывать среду Http2.0 и Http1.0.
В случае поддержки Https мы используем схему Http2.0, в случае отсутствия поддержки Https используем предварительную загрузку ссылки в сочетании с одним доменным именем изображения. Метод 3 ограничивает метод внешней загрузки, который является более интрузивным и пока не будет рассматриваться.
Что касается предварительной загрузки, после нескольких лет разработки браузера совместимость не является большой проблемой, вы можете обратиться кcaniuse.
более быстрые фоновые изображения
Условно говоря, фоновое изображение все еще относительно велико, как его можно отобразить быстрее?
прогрессивный
Тип JPEG имеет прогрессивный JPEG, который позволяет избежать полностью белого экрана в плохих условиях сети.
Progressive jpeg (прогрессивный jpeg) картинки и связанные с ними
компрессия
Мы также можем сжимать изображения в форматах JPEG и PNG. Мы сделали специальный сервис сжатия изображений, который будет представлен в следующих главах.
Та же картинка, сжатая webp, будет меньше.
тайник
Мы также можем использовать кеш, чтобы принудительно кэшировать изображение, чтобы ускорить второй раз.
Видно, что во второй раз мы получили изображение из кэша браузера без запроса пользователя.
в линию
Взгляните на это, вставив «маленькое изображение» base64.небольшая демонстрация, конкретная конструкция здесь повторяться не будет, но дается идея.
〇 Краткое резюме
Реализация похожа на каркасный экран.
- Заранее запустите страницу через безголовый браузер, чтобы получить текущую структуру DOM.
- Предоставляя css-подобный шаблон (когда разработчики кодируют, задают картинки, цвета и т. д. в шаблоне), сгенерируйте кусок js (с возможностью загрузки картинок и генерации css-фрагментов) через компиляцию и вставьте html-заголовок.
- В сочетании с динамически сгенерированным css во время выполнения, структурой DOM страницы, полученной заранее, и загруженными изображениями представлена грубая «скелетная диаграмма».
- И, наконец, обсуждение загрузки изображений.
Или отвечу на начало, в этой статье могут быть какие-то недочеты, надеюсь вы сделаете еще замечания и предложения, спасибо 😘.
Эпилог
Просматривая предыдущие статьи автора с высокой похвалой, можно обнаружить неожиданные успехи!
-
"Тщательный самостоятельный мониторинг ошибок во внешнем интерфейсе", 650+ лайков
-
"Геймплей с динамическими компонентами, о котором вы могли не знать 🍉", 350+ лайков
😘Нравится + Комментарий + Ретвит 😘, создать оригинальную статью непросто, поощряйте написание новых статей
Расширенное чтение
Порядок загрузки ресурсов
Если вам интересно, вы можете проанализировать его с точки зрения исходного кода.
Посмотрите, как браузер загружает ресурсы из исходного кода Chrome Develop Paper
Если вам интересно узнать о приоритете JS, вы можете обратиться к
Сжатие изображения