feat: support scoped css

This commit is contained in:
dntzhang 2019-10-05 08:40:32 +08:00
parent 6e718c0ae6
commit e2f3b7534f
4 changed files with 108 additions and 8 deletions

View File

@ -26,6 +26,7 @@
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="root2"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

View File

@ -1,16 +1,27 @@
import * as Preact from 'preact'
import './preact-css'
function Counter() {
function Comp() {
return (
<>
<div>Hello Preact X</div>
<h1>Hello Preact X</h1>
<div>Hello Preact CSS</div>
</>
)
}
Preact.render(<Counter />, document.querySelector('#root'))
Comp.css = `
h1{
color: red;
}
`
Preact.render(<Comp />, document.querySelector('#root'))
//Test Multiple rendering only append style once
Preact.render(<Comp />, document.querySelector('#root2'))

View File

@ -1,5 +1,5 @@
import { options } from 'preact'
import { getStyleId, appendStyle } from './style'
let componentNode;
@ -24,9 +24,10 @@ options.vnode = vnode => {
if (component) {
// component is the component instance
//component.css;
component._styleId = 'abcef';
const styleId = getStyleId(component.constructor);
appendStyle(component.constructor.css, styleId);
// example: assign component's unique CSS ID:
(vnode.props || (vnode.props = {}))[component._styleId] = '';
(vnode.props || (vnode.props = {}))[styleId] = ''
}
if (old) old(vnode);
};

View File

@ -0,0 +1,87 @@
let styleId = 0
const styleList = []
const cache = {}
export function getStyleId(ctor) {
for (let i = 0, len = styleList.length; i < len; i++) {
let item = styleList[i]
if (item.ctor === ctor) {
return item.attrName
}
}
let attrName = '_ss' + styleId
styleList.push({ ctor, attrName })
styleId++
return attrName
}
// many thanks to https://github.com/thomaspark/scoper/
export function scoper(css, prefix) {
console.log(css, prefix)
prefix = '[' + prefix.toLowerCase() + ']'
// https://www.w3.org/TR/css-syntax-3/#lexical
css = css.replace(/\/\*[^*]*\*+([^/][^*]*\*+)*\//g, '')
// eslint-disable-next-line
let re = new RegExp('([^\r\n,{}:]+)(:[^\r\n,{}]+)?(,(?=[^{}]*{)|\s*{)', 'g')
/**
* Example:
*
* .classname::pesudo { color:red }
*
* g1 is normal selector `.classname`
* g2 is pesudo class or pesudo element
* g3 is the suffix
*/
css = css.replace(re, (g0, g1, g2, g3) => {
if (typeof g2 === 'undefined') {
g2 = ''
}
/* eslint-ignore-next-line */
if (
g1.match(
/^\s*(@media|\d+%?|@-webkit-keyframes|@keyframes|to|from|@font-face)/
)
) {
return g1 + g2 + g3
}
let appendClass = g1.replace(/(\s*)$/, '') + prefix + g2
return appendClass + g3
})
return css
}
export function addStyle(cssText, id) {
id = id.toLowerCase()
let ele = document.getElementById(id)
let head = document.getElementsByTagName('head')[0]
if (ele && ele.parentNode === head) {
head.removeChild(ele)
}
let someThingStyles = document.createElement('style')
head.appendChild(someThingStyles)
someThingStyles.setAttribute('type', 'text/css')
someThingStyles.setAttribute('id', id)
if (window.ActiveXObject) {
someThingStyles.styleSheet.cssText = cssText
} else {
someThingStyles.textContent = cssText
}
}
export function appendStyle(style, attr) {
if (!cache[attr]) {
addStyle(scoper(style, attr), attr)
cache[attr] = true
}
}