Skip to content

Commit

Permalink
fix(0.8.4): fix performance problem in firefox
Browse files Browse the repository at this point in the history
  • Loading branch information
bailicangdu committed Jan 25, 2022
1 parent f918f4f commit 8a4a69d
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 46 deletions.
8 changes: 8 additions & 0 deletions docs/en-us/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
- 修订版本号:每周末会进行日常 bugfix 更新。(如果有紧急的 bugfix,则任何时候都可发布)

---
### 0.8.4

`2022-01-25`

- **Bug Fix**

- 🐞 Fixed the problem that the execution speed of style isolation is too slow in Firefox browser 80 and above.


### 0.8.3

Expand Down
9 changes: 9 additions & 0 deletions docs/zh-cn/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@

---

### 0.8.4

`2022-01-25`

- **Bug Fix**

- 🐞 修复了在火狐浏览器80及以上版本中,样式隔离执行速度过慢的问题。


### 0.8.3

`2022-01-20`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@micro-zoe/micro-app",
"version": "0.8.3",
"version": "0.8.4",
"description": "A lightweight, efficient and powerful micro front-end framework",
"private": false,
"main": "lib/index.min.js",
Expand Down
47 changes: 45 additions & 2 deletions src/__tests__/source/scoped_css.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ describe('source scoped_css', () => {
setAppName('test-app3')
// 动态创建style
const dynamicStyle = document.createElement('style')
dynamicStyle.textContent = '@font-face {font-family: test-font;} @media screen and (max-width: 300px) {body {background:lightblue;}} @supports (display: grid) {div {display: grid;}}'
dynamicStyle.textContent = '@font-face {font-family: test-font;} @media screen and (max-width: 300px) {body {background:lightblue;}} @supports (display: grid) {div {display: grid;}} @unknown {}'

document.head.appendChild(dynamicStyle)

defer(() => {
expect(dynamicStyle.textContent).toBe('@font-face {font-family: test-font;} @media screen and (max-width: 300px) {micro-app[name=test-app3] micro-app-body{background:lightblue;}} @supports (display: grid) {micro-app[name=test-app3] div{display: grid;}}')
expect(dynamicStyle.textContent).toBe('@font-face {font-family: test-font;} @media screen and (max-width: 300px) {micro-app[name=test-app3] micro-app-body{background:lightblue;}} @supports (display: grid) {micro-app[name=test-app3] div{display: grid;}} micro-app[name=test-app3] @unknown{}')
resolve(true)
})
}, false)
Expand Down Expand Up @@ -381,8 +381,51 @@ describe('source scoped_css', () => {
document.head.appendChild(dynamicStyle6)
expect(dynamicStyle6.textContent).toBe('micro-app[name=test-app11] .test1{color: re/d;}')

// keep separator info
const dynamicStyle7 = document.createElement('style')
dynamicStyle7.textContent = '.test1, .test2 {color: red}'
document.head.appendChild(dynamicStyle7)
expect(dynamicStyle7.textContent).toBe('micro-app[name=test-app11] .test1, micro-app[name=test-app11] .test2{color: red}')

resolve(true)
}, false)
})
})

// 火狐浏览器中编码result
test('encode result in firefox', async () => {
const rawUserAgent = navigator.userAgent
Object.defineProperty(navigator, 'userAgent', {
value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:96.0) Gecko/20100101 Firefox/96.0',
writable: true,
configurable: true,
})

const microAppElement12 = document.createElement('micro-app')
microAppElement12.setAttribute('name', 'test-app12')
microAppElement12.setAttribute('url', `http://127.0.0.1:${ports.scoped_css}/dynamic/`)

appCon.appendChild(microAppElement12)
await new Promise((resolve) => {
microAppElement12.addEventListener('mounted', () => {
setAppName('test-app12')
// 动态创建style
const dynamicStyle = document.createElement('style')
dynamicStyle.textContent = '#root {color: red;}'

document.head.appendChild(dynamicStyle)

defer(() => {
expect(dynamicStyle.textContent).toBe('micro-app[name=test-app12] #root{color: red;}')
resolve(true)
})
}, false)
})

Object.defineProperty(navigator, 'userAgent', {
value: rawUserAgent,
writable: true,
configurable: true,
})
})
})
4 changes: 4 additions & 0 deletions src/libs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,7 @@ export function getRootContainer (target: HTMLElement | ShadowRoot): HTMLElement
export function trim (str: string): string {
return str ? str.replace(/^\s+|\s+$/g, '') : ''
}

export function isFireFox (): boolean {
return navigator.userAgent.indexOf('Firefox') > -1
}
90 changes: 47 additions & 43 deletions src/source/scoped_css.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-useless-escape, no-cond-assign */
import type { AppInterface } from '@micro-app/types'
import { CompletionPath, getLinkFileDir, logError, trim } from '../libs/utils'
import { CompletionPath, getLinkFileDir, logError, trim, isFireFox } from '../libs/utils'
import microApp from '../micro_app'

// common reg
Expand Down Expand Up @@ -46,7 +46,7 @@ class CSSParser {
this.baseURI = baseURI
this.linkPath = linkPath || ''
this.matchRules()
return this.result
return isFireFox() ? decodeURIComponent(this.result) : this.result
}

public reset (): void {
Expand All @@ -70,14 +70,14 @@ class CSSParser {

// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule
private matchStyleRule (): boolean | void {
const selectorList = this.formatSelector()
const selectors = this.formatSelector(true)

// reset scopecssDisableNextLine
this.scopecssDisableNextLine = false

if (!selectorList) return parseError('selector missing', this.linkPath)
if (!selectors) return parseError('selector missing', this.linkPath)

this.result += (selectorList as Array<string>).join(', ')
this.recordResult(selectors)

this.matchComments()

Expand All @@ -88,6 +88,33 @@ class CSSParser {
return true
}

private formatSelector (skip: boolean): false | string {
const m = this.commonMatch(/^([^{]+)/, skip)
if (!m) return false

return m[0].replace(/(^|,[\n\s]*)([^,]+)/g, (_, separator, selector) => {
selector = trim(selector)
if (!(
this.scopecssDisableNextLine ||
(
this.scopecssDisable && (
!this.scopecssDisableSelectors.length ||
this.scopecssDisableSelectors.includes(selector)
)
) ||
rootSelectorREG.test(selector)
)) {
if (bodySelectorREG.test(selector)) {
selector = selector.replace(bodySelectorREG, this.prefix + ' micro-app-body')
} else {
selector = this.prefix + ' ' + selector
}
}

return separator + selector
})
}

// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
private styleDeclarations (): boolean | void {
if (!this.matchOpenBrace()) return parseError("Declaration missing '{'", this.linkPath)
Expand Down Expand Up @@ -121,7 +148,7 @@ class CSSParser {
})
}

this.result += cssValue
this.recordResult(cssValue)
}

// reset scopecssDisableNextLine
Expand All @@ -139,39 +166,6 @@ class CSSParser {
return this.matchAllDeclarations()
}

private formatSelector (): boolean | Array<string> {
const m = this.commonMatch(/^([^{]+)/, true)
if (!m) return false
return trim(m[0])
.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
.replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (r) => {
return r.replace(/,/g, '\u200C')
})
.split(/\s*(?![^(]*\)),\s*/)
.map((s: string) => {
const selectorText = s.replace(/\u200C/g, ',')
if (this.scopecssDisableNextLine) {
return selectorText
} else if (this.scopecssDisable) {
if (
!this.scopecssDisableSelectors.length ||
this.scopecssDisableSelectors.includes(selectorText)
) {
return selectorText
}
}

if (selectorText === '*') {
return this.prefix + ' *'
} else if (bodySelectorREG.test(selectorText)) {
return selectorText.replace(bodySelectorREG, this.prefix + ' micro-app-body')
} else if (rootSelectorREG.test(selectorText)) { // ignore root selector
return selectorText
}
return this.prefix + ' ' + selectorText
})
}

private matchAtRule (): boolean | void {
if (this.cssText[0] !== '@') return false
// reset scopecssDisableNextLine
Expand Down Expand Up @@ -242,7 +236,7 @@ class CSSParser {
private pageRule (): boolean | void {
if (!this.commonMatch(/^@page */)) return false

this.formatSelector()
this.formatSelector(false)

// reset scopecssDisableNextLine
this.scopecssDisableNextLine = false
Expand Down Expand Up @@ -295,7 +289,7 @@ class CSSParser {
return () => {
if (!this.commonMatch(reg)) return false
this.matchLeadingSpaces()
return false
return true
}
}

Expand Down Expand Up @@ -334,7 +328,7 @@ class CSSParser {
// get comment content
let commentText = this.cssText.slice(2, i - 2)

this.result += `/*${commentText}*/`
this.recordResult(`/*${commentText}*/`)

commentText = trim(commentText.replace(/^\s*!/, ''))

Expand Down Expand Up @@ -368,7 +362,7 @@ class CSSParser {
if (!matchArray) return
const matchStr = matchArray[0]
this.cssText = this.cssText.slice(matchStr.length)
if (!skip) this.result += matchStr
if (!skip) this.recordResult(matchStr)
return matchArray
}

Expand All @@ -384,6 +378,16 @@ class CSSParser {
private matchLeadingSpaces (): void {
this.commonMatch(/^\s*/)
}

// splice string
private recordResult (strFragment: string): void {
// Firefox is slow when string contain special characters, see https://github.com/micro-zoe/micro-app/issues/256
if (isFireFox()) {
this.result += encodeURIComponent(strFragment)
} else {
this.result += strFragment
}
}
}

/**
Expand Down

0 comments on commit 8a4a69d

Please sign in to comment.