CSS in JS
CSS in JS
представляет собой набор идей для решения проблем css, а не конкретную библиотеку. отCSS in JS
Из буквального значения видно, что он записывает стиль css в файл JavaScript без необходимости отделять.css
,.less
файлы, такие как . Добавление css в js упрощает использование js.变量
,模块化
,tree-shaking
. Он также решает некоторые проблемы в css, такие как:更方便解决基于状态的样式
,更容易追溯依赖关系
,生成唯一的选择器来锁定作用域
. несмотря на то чтоCSS in JS
Это не очень новая технология, но отечественная популярность не высока. Поскольку и Vue, и Angular имеют собственный набор стилей для определения стилей, React не заботится о том, как его определяют пользователи.стиль компонента,такCSS in JS
Он очень популярен в сообществе React.
достигнуто до сих порCSS in JS
Есть много сторонних библиотек:кликните сюда. рисунокJSS,styled-componentsЖдать. Мы не будем здесь вдаваться в подробности (ссылки по теме размещены ниже), в этой статье основное внимание уделяетсяJS in CSS
😀.
Что такое JS в CSS?
выше мы упоминалиCSS in JS
заключается в написании CSS на JavaScript, тогдаJS in CSS
Мы можем сделать вывод, что можно использовать скрипты JavaScript в CSS, как показано ниже. Функциональность Paint API может быть написана на CSS. Также посетите: ctx, geom. Даже мы можем написать свои собственные пользовательские свойства css и т. д. Реализация этих функций основана наCSS Houdini.
.el {
--color: cyan;
--multiplier: 0.24;
--pad: 30;
--slant: 20;
--background-canvas: (ctx, geom) => {
let multiplier = var(--multiplier);
let c = `var(--color)`;
let pad = var(--pad);
let slant = var(--slant);
ctx.moveTo(0, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier + slant, geom.height);
ctx.lineTo(0, geom.height);
ctx.fillStyle = c;
ctx.fill();
};
background: paint(background-canvas);
transition: --multiplier .4s;
}
.el:hover {
--multiplier: 1;
}
Нажмите здесь, чтобы просмотреть приведенную выше онлайн-демонстрацию
Какую проблему решает Гудини
Сравнение стандартного процесса разработки CSS и JS
В современной веб-разработке JavaScript занимает почти большую часть кода проекта. Мы можем использовать ES 2020, ES2021 и даже новые функции в предложениях (например:Decorator), даже если браузер еще не поддерживает его, вы можете написатьPolyfill
или использоватьBabel
такие инструменты, как транспиляция, позволяющие нам применять новейшие функции в производственной среде (как показано на рисунке ниже).
CSS отличается. Помимо времени, необходимого для формулирования стандартов и спецификаций CSS, различия в версиях и фактическом прогрессе различных браузеров еще более продолжительны (как показано на рисунке ниже). В лучшем случае такие инструменты, как PostCSS и Sass, используется, чтобы помочь нам переводить и просматривать CSS, который может принять браузер. Что могут сделать разработчики, так это управлять через JSDOM
иCSSOM
чтобы повлиять на изменения страницы, но для следующихLayout
,Paint
иComposite
Почти нет контроля. Чтобы решить вышеуказанные проблемы, чтобы магия CSS не ограничивалась браузером,Houdini
Так родился.
CSS Polyfill
Выше мы упоминали, что мы можем написать полифилл для предлагаемых функций на JavaScript, и внедрение новых функций в производственную среду занимает совсем немного времени. В этот момент первая мысль, которая пришла мне в голову, былаCSS Polyfill
, пока CSS Polyfill достаточно силен, CSS может развиваться с той же скоростью, что и JavaScript, к сожалению, написаниеCSS Polyfill
Исключительно сложно, и в основном невозможно сделать, не нарушая производительность. Это связано с тем, что JavaScript являетсяязык динамических сценариев. Это обеспечивает отличную расширяемость, и благодаря этому мы можем легко использовать JavaScript для создания полифиллов JavaScript. Но CSS не является динамическим, в некоторых сценариях мы можем преобразовать одну форму CSS в другую во время компиляции (например,PostCSS). Если ваш Polyfill зависит от структуры DOM или макета, позиционирования и т. д. элемента, тогда наш Polyfill не может быть выполнен во время компиляции, а должен запускаться в браузере. К сожалению, реализовать эту схему в браузере непросто.
Как показано на рисунке выше, это весь процесс от браузера до HTML и рендеринга на экране Мы видим, что только часть с цветом (розовый, синий) является той частью, которой может управлять JavaScript. Во-первых, мы не можем контролировать, как браузер анализирует HTML и CSS и преобразует их вDOM
иCSSOM
процесс, иCascade
,Layout
,Paint
,Composite
Мы ничего не можем сделать. Единственное, что у нас есть полный контроль над всем процессом, этоDOM
,Кроме тогоCSSOM
Частично управляемый.
Как упоминалось в черновике CSS Houdini, этот уровень раскрытия не определен, совместимость нестабильна, а поддержка ключевых функций отсутствует. Например, в браузереCSSOM
Он не говорит нам, как он обрабатывает междоменные таблицы стилей, и то, как он обрабатывает операторы CSS, которые браузер не может разобрать, не анализируется, то есть — если мы используемCSS polyfill
Пусть браузер поддерживает свойства, которые он еще не поддерживает, тогда его нельзя будет использовать вCSSOM
Для этого мы можем пройти через DOM только один раз, чтобы найти<style>
или<link rel="stylesheet">
теги, получить в них стили CSS, разобрать, переписать и, наконец, добавить их обратно в дерево DOM. К сожалению, это обновление дерева DOM приведет к повторному рендерингу страницы (как показано ниже).
Тем не менее, некоторые люди могут сказать: «У нас нет выбора, кроме этого метода, и он не окажет большого влияния на производительность сайта». Так что для некоторых сайтов это так. но если нашPolyfill
Нужно ли это для интерактивных страниц? Напримерscroll
,resize
,mousemove
,keyup
Подождите, эти события будут срабатывать в любое время, а это значит, что страница будет перерисована в любой момент, взаимодействие будет не таким плавным, как в оригинале, или даже вызовет сбой страницы, что крайне плохо для Пользовательский опыт.
Подводя итог, если мы хотим, чтобы браузер анализировал неизвестные ему стили (браузеры с низкими версиями используют макет сетки), но мы не можем вмешиваться в процесс рендеринга, мы можем только обновить DOM вручную, что принесет много проблем,Houdini
Появление посвящено их решению.
Houdini API
Houdini – это набор низкоуровневых API. Он раскрывает различные части движка CSS. Как показано на рисунке ниже, отображается новый API, соответствующий каждой ссылке (серая часть еще не реализована в основных браузерах), поэтому что разработчики могут добавлять браузеры, добавляя процесс стилизации и компоновки механизма рендеринга для расширения CSS. Houdini был разработан рабочей группой инженеров из Mozilla, Apple, Opera, Microsoft, HP, Intel и Google. Они предоставляют разработчикам прямой доступ к объектной модели CSS (CSSOM), позволяя разработчикам писать код, который браузер может анализировать в CSS для создания новых функций CSS, не дожидаясь их собственной реализации в браузере.
Properties & Values API
Хотя уже есть переменные CSS, которые позволяют разработчикам контролировать значения свойств, они не могут ограничивать тип или определять более строго.С новым API CSS Houdini мы можем расширять переменные CSS и определять переменные CSS.类型
,初始值
,继承
. Он более мощный и гибкий для переменных css.
Статус переменных CSS:
.dom {
--my-color: green;
--my-color: url('not-a-color'); // 它并不知道当前的变量类型
color: var(--my-color);
}
Houdini предоставляет два способа регистрации пользовательских свойств: в js и css.
CSS.registerProperty({
name: '--my-prop', // String 自定义属性名
syntax: '<color>', // String 如何去解析当前的属性,即属性类型,默认 *
inherits: false, // Boolean 如果是true,子节点将会继承
initialValue: '#c0ffee', // String 属性点初始值
});
Мы также можем зарегистрироваться в css для достижения вышеуказанного эффекта.
@property --my-prop {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
Самая захватывающая функция этого API — добавление анимации к пользовательским свойствам, например:transition: --multiplier 0.4s;
, эта функция, которую мы представили ранее, что такое js в css, чтоdemoиспользовал. мы также можем использовать+
сделатьsyntax
Свойства поддерживают один или несколько типов, а также могут использовать|
делить. Болееsyntax
Стоимость имущества:
значение атрибута | описывать |
---|---|
<length> |
значение длины |
<number> |
номер |
<percentage> |
процент |
<length-percentage> |
длина или процент, calc — это выражение, которое объединяет длину и процент |
<color> |
цвет |
<image> |
изображение |
<url> |
URL-адрес |
<integer> |
целое число |
<angle> |
угол |
<time> |
время |
<resolution> |
разрешение |
<transform-list> |
функция преобразования |
<custom-ident> |
ident |
Worklets
Worklets
является расширением механизма рендеринга, которое концептуально похоже наWeb Workers, но есть несколько важных отличий:
- спроектированы так, чтобы быть параллельными, каждый
Worklets
Всегда должно быть два или более экземпляра, любой из которых может запускаться при вызове. - Область действия мала, что ограничивает API-интерфейсы, которые не могут получить доступ к глобальной области (за исключением функций Worklet).
- Механизм рендеринга будет вызывать их при необходимости, вместо того, чтобы мы вызывали их вручную.
Worklet — это модуль JavaScript, который добавляется путем вызова метода addModule рабочеголета (который является обещанием). НапримерregisterLayout
, registerPaint
, registerAnimator
Нам всем нужно поместить Worklet
//加载单个
await demoWorklet.addModule('path/to/script.js');
// 一次性加载多个worklet
Promise.all([
demoWorklet1.addModule('script1.js'),
demoWorklet2.addModule('script2.js'),
]).then(results => {});
registerDemoWorklet('name', class {
// 每个Worklet可以定义要使用的不同函数
// 他们将由渲染引擎在需要时调用
process(arg) {
return !arg;
}
});
Жизненный цикл ворклетов
- Жизненный цикл ворклета начинается внутри механизма рендеринга.
- Для JavaScript механизм рендеринга запускает основной поток JavaScript.
- Затем он запустит несколько рабочих процессов, и он запустится. Эти процессы в идеале являются отдельными потоками от основного потока, поэтому они не блокируют основной поток (но им и не нужно блокировать).
- Затем загрузите JavaScript нашего браузера в основной поток
- Вызов JavaScript
worklet.addModule
и загрузить ворклет асинхронно - После загрузки загрузите рабочий файл в два или более доступных рабочих процесса.
- При необходимости механизм рендеринга выполнит Worklet, вызвав соответствующую функцию-обработчик из загруженного Worklet. Вызов может быть направлен против любого параллельного экземпляра Worklet.
Typed OM
Typed OM
к существующемуCSSOM
расширение и реализацияParsing API
иProperties & Values API
связанные особенности. Он преобразует значения css в объекты JavaScript осмысленного типа, а не в строки, как сейчас. Если мы попытаемся преобразовать значение строкового типа в осмысленный тип и вернуть его, это может привести к большим потерям производительности, поэтому этот API позволяет нам более эффективно использовать значения CSS.
Чтение значений CSS теперь добавляет новый базовый классCSSStyleValue
, который имеет множество подклассов, которые могут более точно описать тип значения css:
Подкласс | описывать |
---|---|
CSSKeywordValue | Ключевые слова CSS и другие идентификаторы (например, наследование или сетка) |
CSSPositionValue | Информация о местоположении (x, y) |
CSSImageValue | объект, представляющий свойство value изображения |
CSSUnitValue | Выражается как одно значение с одной единицей измерения (например, 50 пикселей), также может быть выражено как одно значение или процент без единицы измерения. |
CSSMathValue | Более сложные значения, такие как calc, min и max. Сюда входят подклассыCSSMathSum , CSSMathProduct , CSSMathMin , CSSMathMax , CSSMathNegate иCSSMathInvert
|
CSSTransformValue | Зависит отCSS transforms состоит изCSSTransformComponent список, в том числеCSSTranslate , CSSRotate , CSSScale , CSSSkew , CSSSkewX , CSSSkewY , CSSPerspective иCSSMatrixComponent
|
использоватьTyped OM
Существует два основных метода:
- пройти через
attributeStyleMap
Установка и получение типизированных встроенных стилей - пройти через
computedStyleMap
получить полный элементTyped OM
стиль
Используйте attributeStyleMap для установки и получения
myElement.attributeStyleMap.set('font-size', CSS.em(2));
myElement.attributeStyleMap.get('font-size'); // CSSUnitValue { value: 2, unit: 'em' }
myElement.attributeStyleMap.set('opacity', CSS.number(.5));
myElement.attributeStyleMap.get('opacity'); // CSSUnitValue { value: 0.5, unit: 'number' };
Использование вычисляемого стиля
.foo {
transform: translateX(1em) rotate(50deg) skewX(10deg);
vertical-align: baseline;
width: calc(100% - 3em);
}
const cs = document.querySelector('.foo').computedStyleMap();
cs.get('vertical-align');
// CSSKeywordValue {
// value: 'baseline',
// }
cs.get('width');
// CSSMathSum {
// operator: 'sum',
// length: 2,
// values: CSSNumericArray {
// 0: CSSUnitValue { value: -90, unit: 'px' },
// 1: CSSUnitValue { value: 100, unit: 'percent' },
// },
// }
cs.get('transform');
// CSSTransformValue {
// is2d: true,
// length: 3,
// 0: CSSTranslate {
// is2d: true,
// x: CSSUnitValue { value: 20, unit: 'px' },
// y: CSSUnitValue { value: 0, unit: 'px' },
// z: CSSUnitValue { value: 0, unit: 'px' },
// },
// 1: CSSRotate {...},
// 2: CSSSkewX {...},
// }
Layout API
Разработчики могут реализовывать свои собственные алгоритмы компоновки с помощью этого API, а мы можем использовать свои собственные макеты, такие как собственный CSS (например,display:flex
, display:table
). существуетMasonry layout libraryВыше мы видим, как сильно разработчики хотят реализовать всевозможные сложные макеты, некоторые из которых невозможно сделать с помощью одного лишь CSS. Хотя эти макеты впечатляют, они, как правило, имеют низкую производительность страницы, а проблемы с производительностью очевидны на некоторых недорогих устройствах.
CSS Layout API предоставляетregisterLayout
Метод передается разработчику, который получает имя макета в качестве значения свойства, которое будет использоваться позже в CSS, и класс JavaScript, содержащий логику макета.
my-div {
display: layout(my-layout);
}
// layout-worklet.js
registerLayout('my-layout', class {
static get inputProperties() { return ['--foo']; }
static get childrenInputProperties() { return ['--bar']; }
async intrinsicSizes(children, edges, styleMap) {}
async layout(children, edges, constraints, styleMap) {}
});
await CSS.layoutWorklet.addModule('layout-worklet.js');
онлайн демоБольшинство браузеров в настоящее время не поддерживают его
Painting API
Мы можем сделать это в CSSbackground-image
С его помощью мы можем использовать контекст Canvas 2d, управлять изображением в зависимости от размера элемента, а также использовать пользовательские свойства.
await CSS.paintWorklet.addModule('paint-worklet.js');
registerPaint('sample-paint', class {
static get inputProperties() { return ['--foo']; }
static get inputArguments() { return ['<color>']; }
static get contextOptions() { return {alpha: true}; }
paint(ctx, size, props, args) { }
});
Animation API
Этот API позволяет нам управлять анимацией ключевых кадров на основе пользовательского ввода неблокирующим образом. Также возможно изменить свойства элемента DOM, но не заставляя механизм рендеринга пересчитывать макет или свойства стиля, такие как преобразование, непрозрачность или смещение прокрутки.Animation API
как использовать иPaint API
иLayout API
Немного иначе нам тоже нужно пройтиnew
ОдинWorkletAnimation
для регистрации ворклета.
// animation-worklet.js
registerAnimator('sample-animator', class {
constructor(options) {
}
animate(currentTime, effect) {
effect.localTime = currentTime;
}
});
await CSS.animationWorklet.addModule('animation-worklet.js');
// 需要添加动画的元素
const elem = document.querySelector('#my-elem');
const scrollSource = document.scrollingElement;
const timeRange = 1000;
const scrollTimeline = new ScrollTimeline({
scrollSource,
timeRange,
});
const effectKeyframes = new KeyframeEffect(
elem,
// 动画需要绑定的关键帧
[
{transform: 'scale(1)'},
{transform: 'scale(.25)'},
{transform: 'scale(1)'}
],
{
duration: timeRange,
},
);
new WorkletAnimation(
'sample-animator',
effectKeyframes,
scrollTimeline,
{},
).play();
Чтобы узнать больше об этом API, нажмите здесь
Parser API
Позволяет разработчикам свободно расширять лексический модуль CSS.
Правила разбора:
const background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"
const styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"
Разобрать CSS:
const style = fetch("style.css")
.then(response => CSS.parseStylesheet(response.body));
style.then(console.log);
Font Metrics API
Он предоставит методы для измерения размеров текстовых элементов, отображаемых на экране, и позволит разработчикам контролировать способ отображения текстовых элементов на экране. Эти значения трудно или невозможно измерить с помощью текущей функциональности, поэтому этот API упростит разработчикам создание функций CSS, связанных с текстом и шрифтом. Например:
- гибкий макет:
align-items
базовая функция. Необходимо знать базовую позицию первого элемента в каждом гибком блоке. - Начальные буквы: необходимо знать базовую высоту каждой буквы и максимальную высоту буквы, а также базовую длину содержимого обертки.
- Вперед и назад для одного глифа.
- Перенос строк: требуется доступ к данным шрифта, всем входным стилям для текста и информации о макете (доступная длина абзаца и т. д.).
- каждый из элементов
line boxes
Оба нуждаются в базовой линии. (line boxes
Среди представителей многоinline boxes
эта строка)
Текущий прогресс Гудини
Нажмите здесь, чтобы просмотреть сайт
План Гудини
Зная это, некоторые разработчики могут сказать: «Мне не нужны эти навороченные технологии, да и пользы они принести не могут. Я просто хочу написать несколько страниц и сделать нормальное веб-приложение, и не хочу пытаться мешать браузеру Процесс рендеринга для достижения некоторых экспериментальных или интересных функций.» Если мы так думаем, мы могли бы сделать шаг назад и подумать об этом. Вспомните недавний проект, методы, используемые для достижения эффекта страницы,grid
От макета также пришлось отказаться при рассмотрении совместимости со старыми браузерами. Мы хотим контролировать процесс рендеринга страниц в браузере не только для того, чтобы продемонстрировать свои навыки, но и для того, чтобы помочь разработчикам решить следующие две проблемы:
- Унифицировать поведение основных браузеров
- рисунок
JavaScript
Точно так же при введении новых функций мы можем передатьPolyfill
Форма быстро внедряется в производственную среду.
Оглядываясь назад через несколько лет, когда основные браузеры полностью поддерживалиHoudini
когда. Мы можем использовать в браузере любое свойство CSS, которое захотим, и все они прекрасно его поддерживают. как сегодняgrid
Вёрстка не дружит в старой версии поддержки браузера, тогда надо только установить соответствующийPolyfill
может решить подобные задачи.
Если вы найдете какие-либо ошибки в тексте, обратная связь и исправления приветствуются.
напиши в конце
Наконец, порекомендуйте набор полных серий учебников TS. В последнее время я улучшаю TS, и у меня собран набор очень хороших туториалов, которыми я бесплатно поделюсь с xdm.Woohoo. Первоклассный уровень сахара в крови.com/universal-afraid/in…
Ссылка на ссылку
Подробное объяснение CSS-in-JS
Houdini: возможно, самая захватывающая разработка в CSS, о которой вы никогда не слышали