Skip to content

Commit

Permalink
feat(utils): 新增 loadCss 加载 CSS 样式
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Dec 8, 2020
1 parent ff856e7 commit 70e4727
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/utils/__snapshots__/loadCss.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`loadCss 加载内容 1`] = `
<style type="text/css">
body {font-size: 20px}
</style>
`;

exports[`loadCss 加载内容 2`] = `
<html>
<head>
<style type="text/css">
body {font-size: 20px}
</style>
</head>
<body>
</body>
</html>
`;

exports[`loadCss 加载链接 1`] = `
<link rel="stylesheet"
href="http://foo.bar/x.css"
>
`;
exports[`loadCss 加载链接 2`] = `
<html>
<head>
<link rel="stylesheet"
href="http://foo.bar/x.css"
>
</head>
<body>
</body>
</html>
`;
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export * from './isPossibleChineseMobilePhoneNumber'
export * from './isPromiseLike'
export * from './isUrl'
export * from './keysStrict'
export * from './loadCss'
export * from './loadResource'
export * from './md5'
export * from './move'
Expand Down
43 changes: 43 additions & 0 deletions src/utils/loadCss.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { loadCss } from './loadCss'

describe('loadCss', () => {
const createElement = document.createElement.bind(document)
beforeAll(() => {
jest.spyOn(document, 'createElement').mockImplementation((tag: string) => {
const el = createElement(tag)
setTimeout(() => {
if (
/returnError/.test(
(el as HTMLScriptElement).src || (el as HTMLLinkElement).href,
)
) {
el.onerror?.(new Event('error'))
} else {
el.onload?.(new Event('load'))
}
}, 0)
return el
})
})
afterAll(() => {
jest.clearAllMocks()
})

test('加载链接', async () => {
const css = await loadCss(`http://foo.bar/x.css`)
const css2 = await loadCss(`http://foo.bar/x.css`)
expect(css.el).toBe(css2.el)
expect(css.el.outerHTML).toMatchSnapshot()
expect(document.documentElement.outerHTML).toMatchSnapshot()
css.destroy()
})

test('加载内容', async () => {
const css = await loadCss(`body {font-size: 20px}`)
const css2 = await loadCss(`body {font-size: 20px}`)
expect(css.el).toBe(css2.el)
expect(css.el.outerHTML).toMatchSnapshot()
expect(document.documentElement.outerHTML).toMatchSnapshot()
css.destroy()
})
})
59 changes: 59 additions & 0 deletions src/utils/loadCss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { isDataUrl } from './isDataUrl'
import { isUrl } from './isUrl'
import { loadResource, LoadResourceUrlType } from './loadResource'

const cache: Record<string, HTMLStyleElement> = Object.create(null)

export interface LoadCssResult {
/**
* 样式元素。
*/
el: HTMLStyleElement

/**
* 销毁函数。
*/
destroy: () => void
}

/**
* 加载 CSS 样式,支持链接和内容。
*
* @param urlOrContent 链接或内容
* @example
* ```typescript
* loadCss('https://foo.bar/global.css')
* loadCss(`body { font-size: 20px; }`)
* ```
*/
export function loadCss(urlOrContent: string): Promise<LoadCssResult> {
return (urlOrContent in cache
? Promise.resolve(cache[urlOrContent])
: isUrl(urlOrContent) || isDataUrl(urlOrContent)
? loadResource({
type: LoadResourceUrlType.css,
path: urlOrContent,
}).then<HTMLStyleElement>(res => res[0] as any)
: new Promise<HTMLStyleElement>(resolve => {
const el = document.createElement('style')
el.setAttribute('type', 'text/css')
if ('textContent' in el) {
el.textContent = urlOrContent
} else {
// @ts-ignore
el.styleSheet.cssText = urlOrContent
}
document.getElementsByTagName('head')[0].appendChild(el)
resolve(el)
})
).then<LoadCssResult>(el => {
cache[urlOrContent] = el
return {
el: el,
destroy: () => {
delete cache[urlOrContent]
el.parentNode!.removeChild(el)
},
}
})
}

0 comments on commit 70e4727

Please sign in to comment.