Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
CoCo_Code_Op2
next.js
提交
c8cd77a8
N
next.js
项目概览
CoCo_Code_Op2
/
next.js
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
N
next.js
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
c8cd77a8
编写于
12月 01, 2020
作者:
J
Janicklas Ralph
提交者:
GitHub
12月 01, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Script loader component (#18281)
上级
6e4632ef
变更
21
隐藏空白更改
内联
并排
Showing
21 changed file
with
548 addition
and
20 deletion
+548
-20
packages/next/build/webpack-config.ts
packages/next/build/webpack-config.ts
+3
-0
packages/next/client/experimental-script.tsx
packages/next/client/experimental-script.tsx
+159
-0
packages/next/client/head-manager.ts
packages/next/client/head-manager.ts
+1
-1
packages/next/experimental-script.d.ts
packages/next/experimental-script.d.ts
+2
-0
packages/next/experimental-script.js
packages/next/experimental-script.js
+1
-0
packages/next/export/worker.ts
packages/next/export/worker.ts
+2
-2
packages/next/next-server/lib/head-manager-context.ts
packages/next/next-server/lib/head-manager-context.ts
+2
-0
packages/next/next-server/lib/utils.ts
packages/next/next-server/lib/utils.ts
+1
-0
packages/next/next-server/server/config.ts
packages/next/next-server/server/config.ts
+1
-0
packages/next/next-server/server/render.tsx
packages/next/next-server/server/render.tsx
+10
-0
packages/next/pages/_document.tsx
packages/next/pages/_document.tsx
+62
-17
test/integration/script-loader/next.config.js
test/integration/script-loader/next.config.js
+3
-0
test/integration/script-loader/pages/_app.js
test/integration/script-loader/pages/_app.js
+7
-0
test/integration/script-loader/pages/_document.js
test/integration/script-loader/pages/_document.js
+30
-0
test/integration/script-loader/pages/index.js
test/integration/script-loader/pages/index.js
+16
-0
test/integration/script-loader/pages/page1.js
test/integration/script-loader/pages/page1.js
+16
-0
test/integration/script-loader/pages/page2.js
test/integration/script-loader/pages/page2.js
+16
-0
test/integration/script-loader/pages/page3.js
test/integration/script-loader/pages/page3.js
+23
-0
test/integration/script-loader/pages/page4.js
test/integration/script-loader/pages/page4.js
+38
-0
test/integration/script-loader/styles/styles.css
test/integration/script-loader/styles/styles.css
+6
-0
test/integration/script-loader/test/index.test.js
test/integration/script-loader/test/index.test.js
+149
-0
未找到文件。
packages/next/build/webpack-config.ts
浏览文件 @
c8cd77a8
...
...
@@ -367,6 +367,9 @@ export default async function getBaseWebpackConfig(
// Which makes bundles slightly smaller, but also skips parsing a module that we know will result in this alias
'
next/head
'
:
'
next/dist/next-server/lib/head.js
'
,
'
next/router
'
:
'
next/dist/client/router.js
'
,
'
next/experimental-script
'
:
config
.
experimental
.
scriptLoader
?
'
next/dist/client/experimental-script.js
'
:
''
,
'
next/config
'
:
'
next/dist/next-server/lib/runtime-config.js
'
,
'
next/dynamic
'
:
'
next/dist/next-server/lib/dynamic.js
'
,
next
:
NEXT_PROJECT_ROOT
,
...
...
packages/next/client/experimental-script.tsx
0 → 100644
浏览文件 @
c8cd77a8
import
React
,
{
useEffect
,
useContext
}
from
'
react
'
import
{
ScriptHTMLAttributes
}
from
'
react
'
import
{
HeadManagerContext
}
from
'
../next-server/lib/head-manager-context
'
import
{
DOMAttributeNames
}
from
'
./head-manager
'
import
requestIdleCallback
from
'
./request-idle-callback
'
const
ScriptCache
=
new
Map
()
const
LoadCache
=
new
Set
()
interface
Props
extends
ScriptHTMLAttributes
<
HTMLScriptElement
>
{
strategy
?:
'
defer
'
|
'
lazy
'
|
'
dangerouslyBlockRendering
'
|
'
eager
'
id
?:
string
onLoad
?:
()
=>
void
onError
?:
()
=>
void
children
?:
React
.
ReactNode
preload
?:
boolean
}
const
loadScript
=
(
props
:
Props
)
=>
{
const
{
src
=
''
,
onLoad
=
()
=>
{},
dangerouslySetInnerHTML
,
children
=
''
,
id
,
onError
,
}
=
props
const
cacheKey
=
id
||
src
if
(
ScriptCache
.
has
(
src
))
{
if
(
!
LoadCache
.
has
(
cacheKey
))
{
LoadCache
.
add
(
cacheKey
)
// Execute onLoad since the script loading has begun
ScriptCache
.
get
(
src
).
then
(
onLoad
,
onError
)
}
return
}
const
el
=
document
.
createElement
(
'
script
'
)
const
loadPromise
=
new
Promise
((
resolve
,
reject
)
=>
{
el
.
addEventListener
(
'
load
'
,
function
()
{
resolve
()
if
(
onLoad
)
{
onLoad
.
call
(
this
)
}
})
el
.
addEventListener
(
'
error
'
,
function
()
{
reject
()
if
(
onError
)
{
onError
()
}
})
})
if
(
src
)
{
ScriptCache
.
set
(
src
,
loadPromise
)
LoadCache
.
add
(
cacheKey
)
}
if
(
dangerouslySetInnerHTML
)
{
el
.
innerHTML
=
dangerouslySetInnerHTML
.
__html
||
''
}
else
if
(
children
)
{
el
.
textContent
=
typeof
children
===
'
string
'
?
children
:
Array
.
isArray
(
children
)
?
children
.
join
(
''
)
:
''
}
else
if
(
src
)
{
el
.
src
=
src
}
for
(
const
[
k
,
value
]
of
Object
.
entries
(
props
))
{
if
(
value
===
undefined
)
{
continue
}
const
attr
=
DOMAttributeNames
[
k
]
||
k
.
toLowerCase
()
el
.
setAttribute
(
attr
,
value
)
}
document
.
body
.
appendChild
(
el
)
}
export
default
function
Script
(
props
:
Props
)
{
const
{
src
=
''
,
onLoad
=
()
=>
{},
dangerouslySetInnerHTML
,
children
=
''
,
strategy
=
'
defer
'
,
onError
,
preload
=
false
,
...
restProps
}
=
props
// Context is available only during SSR
const
{
updateScripts
,
scripts
}
=
useContext
(
HeadManagerContext
)
useEffect
(()
=>
{
if
(
strategy
===
'
defer
'
)
{
loadScript
(
props
)
}
else
if
(
strategy
===
'
lazy
'
)
{
window
.
addEventListener
(
'
load
'
,
()
=>
{
requestIdleCallback
(()
=>
loadScript
(
props
))
})
}
},
[
strategy
,
props
])
if
(
strategy
===
'
dangerouslyBlockRendering
'
)
{
const
syncProps
:
Props
=
{
...
restProps
}
for
(
const
[
k
,
value
]
of
Object
.
entries
({
src
,
onLoad
,
onError
,
dangerouslySetInnerHTML
,
children
,
}))
{
if
(
!
value
)
{
continue
}
if
(
k
===
'
children
'
)
{
syncProps
.
dangerouslySetInnerHTML
=
{
__html
:
typeof
value
===
'
string
'
?
value
:
Array
.
isArray
(
value
)
?
value
.
join
(
''
)
:
''
,
}
}
else
{
;(
syncProps
as
any
)[
k
]
=
value
}
}
return
<
script
{
...
syncProps
}
/>
}
else
if
(
strategy
===
'
defer
'
)
{
if
(
updateScripts
&&
preload
)
{
scripts
.
defer
=
(
scripts
.
defer
||
[]).
concat
([
src
])
updateScripts
(
scripts
)
}
}
else
if
(
strategy
===
'
eager
'
)
{
if
(
updateScripts
)
{
scripts
.
eager
=
(
scripts
.
eager
||
[]).
concat
([
{
src
,
onLoad
,
onError
,
...
restProps
,
},
])
updateScripts
(
scripts
)
}
}
return
null
}
packages/next/client/head-manager.ts
浏览文件 @
c8cd77a8
const
DOMAttributeNames
:
Record
<
string
,
string
>
=
{
export
const
DOMAttributeNames
:
Record
<
string
,
string
>
=
{
acceptCharset
:
'
accept-charset
'
,
className
:
'
class
'
,
htmlFor
:
'
for
'
,
...
...
packages/next/experimental-script.d.ts
0 → 100644
浏览文件 @
c8cd77a8
export
*
from
'
./dist/client/experimental-script
'
export
{
default
}
from
'
./dist/client/experimental-script
'
packages/next/experimental-script.js
0 → 100644
浏览文件 @
c8cd77a8
module
.
exports
=
require
(
'
./dist/client/experimental-script
'
)
packages/next/export/worker.ts
浏览文件 @
c8cd77a8
...
...
@@ -297,9 +297,9 @@ export default async function exportPage({
}
else
{
/**
* This sets environment variable to be used at the time of static export by head.tsx.
* Using this from process.env allows target
t
ing both serverless and SSR by calling
* Using this from process.env allows targeting both serverless and SSR by calling
* `process.env.__NEXT_OPTIMIZE_FONTS`.
* TODO(prateekbh@): Remove this when experimental.optimizeFonts are being clened up.
* TODO(prateekbh@): Remove this when experimental.optimizeFonts are being cle
a
ned up.
*/
if
(
optimizeFonts
)
{
process
.
env
.
__NEXT_OPTIMIZE_FONTS
=
JSON
.
stringify
(
true
)
...
...
packages/next/next-server/lib/head-manager-context.ts
浏览文件 @
c8cd77a8
...
...
@@ -3,6 +3,8 @@ import React from 'react'
export
const
HeadManagerContext
:
React
.
Context
<
{
updateHead
?:
(
state
:
any
)
=>
void
mountedInstances
?:
any
updateScripts
?:
(
state
:
any
)
=>
void
scripts
?:
any
}
>
=
React
.
createContext
({})
if
(
process
.
env
.
NODE_ENV
!==
'
production
'
)
{
...
...
packages/next/next-server/lib/utils.ts
浏览文件 @
c8cd77a8
...
...
@@ -186,6 +186,7 @@ export type DocumentProps = DocumentInitialProps & {
headTags
:
any
[]
unstable_runtimeJS
?:
false
devOnlyCacheBusterQueryString
:
string
scriptLoader
:
{
defer
?:
string
[];
eager
?:
any
[]
}
locale
?:
string
}
...
...
packages/next/next-server/server/config.ts
浏览文件 @
c8cd77a8
...
...
@@ -57,6 +57,7 @@ const defaultConfig: { [key: string]: any } = {
optimizeImages
:
false
,
optimizeCss
:
false
,
scrollRestoration
:
false
,
scriptLoader
:
false
,
},
future
:
{
excludeDefaultMomentLocales
:
false
,
...
...
packages/next/next-server/server/render.tsx
浏览文件 @
c8cd77a8
...
...
@@ -210,6 +210,7 @@ function renderDocument(
appGip
,
unstable_runtimeJS
,
devOnlyCacheBusterQueryString
,
scriptLoader
,
locale
,
locales
,
defaultLocale
,
...
...
@@ -234,6 +235,7 @@ function renderDocument(
gip
?:
boolean
appGip
?:
boolean
devOnlyCacheBusterQueryString
:
string
scriptLoader
:
any
}
):
string
{
return
(
...
...
@@ -276,6 +278,7 @@ function renderDocument(
headTags
,
unstable_runtimeJS
,
devOnlyCacheBusterQueryString
,
scriptLoader
,
locale
,
...
docProps
,
})
}
...
...
@@ -557,6 +560,8 @@ export async function renderToHTML(
let
head
:
JSX
.
Element
[]
=
defaultHead
(
inAmpMode
)
let
scriptLoader
:
any
=
{}
const
AppContainer
=
({
children
}:
any
)
=>
(
<
RouterContext
.
Provider
value
=
{
router
}
>
<
AmpStateContext
.
Provider
value
=
{
ampState
}
>
...
...
@@ -565,6 +570,10 @@ export async function renderToHTML(
updateHead
:
(
state
)
=>
{
head
=
state
},
updateScripts
:
(
scripts
)
=>
{
scriptLoader
=
scripts
},
scripts
:
{},
mountedInstances
:
new
Set
(),
}
}
>
...
...
@@ -1000,6 +1009,7 @@ export async function renderToHTML(
gip
:
hasPageGetInitialProps
?
true
:
undefined
,
appGip
:
!
defaultAppGetInitialProps
?
true
:
undefined
,
devOnlyCacheBusterQueryString
,
scriptLoader
,
})
if
(
process
.
env
.
NODE_ENV
!==
'
production
'
)
{
...
...
packages/next/pages/_document.tsx
浏览文件 @
c8cd77a8
...
...
@@ -256,27 +256,55 @@ export class Head extends Component<
}
getPreloadMainLinks
(
files
:
DocumentFiles
):
JSX
.
Element
[]
|
null
{
const
{
assetPrefix
,
devOnlyCacheBusterQueryString
}
=
this
.
context
const
{
assetPrefix
,
devOnlyCacheBusterQueryString
,
scriptLoader
,
}
=
this
.
context
const
preloadFiles
=
files
.
allFiles
.
filter
((
file
:
string
)
=>
{
return
file
.
endsWith
(
'
.js
'
)
})
return
!
preloadFiles
.
length
?
null
:
preloadFiles
.
map
((
file
:
string
)
=>
(
<
link
key
=
{
file
}
nonce
=
{
this
.
props
.
nonce
}
rel
=
"preload"
href
=
{
`
${
assetPrefix
}
/_next/
${
encodeURI
(
file
)}${
devOnlyCacheBusterQueryString
}
`
}
as
=
"script"
crossOrigin
=
{
this
.
props
.
crossOrigin
||
process
.
env
.
__NEXT_CROSS_ORIGIN
}
/>
))
return
[
...(
scriptLoader
.
eager
||
[]).
map
((
file
)
=>
(
<
link
key
=
{
file
.
src
}
nonce
=
{
this
.
props
.
nonce
}
rel
=
"preload"
href
=
{
file
.
src
}
as
=
"script"
crossOrigin
=
{
this
.
props
.
crossOrigin
||
process
.
env
.
__NEXT_CROSS_ORIGIN
}
/>
)),
...
preloadFiles
.
map
((
file
:
string
)
=>
(
<
link
key
=
{
file
}
nonce
=
{
this
.
props
.
nonce
}
rel
=
"preload"
href
=
{
`
${
assetPrefix
}
/_next/
${
encodeURI
(
file
)}${
devOnlyCacheBusterQueryString
}
`
}
as
=
"script"
crossOrigin
=
{
this
.
props
.
crossOrigin
||
process
.
env
.
__NEXT_CROSS_ORIGIN
}
/>
)),
...(
scriptLoader
.
defer
||
[]).
map
((
file
:
string
)
=>
(
<
link
key
=
{
file
}
nonce
=
{
this
.
props
.
nonce
}
rel
=
"preload"
href
=
{
file
}
as
=
"script"
crossOrigin
=
{
this
.
props
.
crossOrigin
||
process
.
env
.
__NEXT_CROSS_ORIGIN
}
/>
)),
]
}
makeStylesheetInert
(
node
:
ReactNode
):
ReactNode
[]
{
...
...
@@ -583,6 +611,22 @@ export class NextScript extends Component<OriginProps> {
})
}
getPreNextScripts
()
{
const
{
scriptLoader
}
=
this
.
context
return
(
scriptLoader
.
eager
||
[]).
map
((
file
:
string
)
=>
{
return
(
<
script
{
...
file
}
nonce
=
{
this
.
props
.
nonce
}
crossOrigin
=
{
this
.
props
.
crossOrigin
||
process
.
env
.
__NEXT_CROSS_ORIGIN
}
/>
)
})
}
getScripts
(
files
:
DocumentFiles
)
{
const
{
assetPrefix
,
...
...
@@ -750,6 +794,7 @@ export class NextScript extends Component<OriginProps> {
/>
)
}
{
!
disableRuntimeJS
&&
this
.
getPolyfillScripts
()
}
{
!
disableRuntimeJS
&&
this
.
getPreNextScripts
()
}
{
disableRuntimeJS
?
null
:
this
.
getDynamicChunks
(
files
)
}
{
disableRuntimeJS
?
null
:
this
.
getScripts
(
files
)
}
</>
...
...
test/integration/script-loader/next.config.js
0 → 100644
浏览文件 @
c8cd77a8
module
.
exports
=
{
experimental
:
{
scriptLoader
:
true
},
}
test/integration/script-loader/pages/_app.js
0 → 100644
浏览文件 @
c8cd77a8
import
'
../styles/styles.css
'
function
MyApp
({
Component
,
pageProps
})
{
return
<
Component
{...
pageProps
}
/
>
}
export
default
MyApp
test/integration/script-loader/pages/_document.js
0 → 100644
浏览文件 @
c8cd77a8
import
*
as
React
from
'
react
'
/// @ts-ignore
import
Document
,
{
Main
,
NextScript
,
Head
}
from
'
next/document
'
export
default
class
MyDocument
extends
Document
{
constructor
(
props
)
{
super
(
props
)
const
{
__NEXT_DATA__
,
ids
}
=
props
if
(
ids
)
{
__NEXT_DATA__
.
ids
=
ids
}
}
render
()
{
return
(
<
html
>
<
Head
>
<
link
rel
=
"
stylesheet
"
href
=
"
https://fonts.googleapis.com/css?family=Voces
"
/>
<
/Head
>
<
body
>
<
Main
/>
<
NextScript
/>
<
/body
>
<
/html
>
)
}
}
test/integration/script-loader/pages/index.js
0 → 100644
浏览文件 @
c8cd77a8
import
Script
from
'
next/experimental-script
'
const
Page
=
()
=>
{
return
(
<
div
class
=
"
container
"
>
<
Script
id
=
"
script
"
src
=
"
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=defer
"
preload
><
/Script
>
<
div
>
index
<
/div
>
<
/div
>
)
}
export
default
Page
test/integration/script-loader/pages/page1.js
0 → 100644
浏览文件 @
c8cd77a8
import
Script
from
'
next/experimental-script
'
const
Page
=
()
=>
{
return
(
<
div
class
=
"
container
"
>
<
Script
id
=
"
script
"
src
=
"
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=eager
"
strategy
=
"
eager
"
><
/Script
>
<
div
>
page1
<
/div
>
<
/div
>
)
}
export
default
Page
test/integration/script-loader/pages/page2.js
0 → 100644
浏览文件 @
c8cd77a8
import
Script
from
'
next/experimental-script
'
const
Page
=
()
=>
{
return
(
<
div
class
=
"
container
"
>
<
Script
id
=
"
script
"
src
=
"
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=dangerouslyBlockRendering
"
strategy
=
"
dangerouslyBlockRendering
"
><
/Script
>
<
div
>
page2
<
/div
>
<
/div
>
)
}
export
default
Page
test/integration/script-loader/pages/page3.js
0 → 100644
浏览文件 @
c8cd77a8
import
Script
from
'
next/experimental-script
'
const
Page
=
()
=>
{
return
(
<
div
class
=
"
container
"
>
<
Script
>
{
`(window.onload = function () {
const newDiv = document.createElement('div')
newDiv.id = 'onload-div'
document.querySelector('.container').appendChild(newDiv)
})`
}
<
/Script
>
<
Script
id
=
"
script
"
src
=
"
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=lazy
"
strategy
=
"
lazy
"
><
/Script
>
<
div
>
page3
<
/div
>
<
/div
>
)
}
export
default
Page
test/integration/script-loader/pages/page4.js
0 → 100644
浏览文件 @
c8cd77a8
import
Script
from
'
next/experimental-script
'
const
url
=
'
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js
'
const
Page
=
()
=>
{
return
(
<
div
class
=
"
container
"
>
<
Script
src
=
{
url
}
id
=
"
script1
"
onLoad
=
{()
=>
{
// eslint-disable-next-line no-undef
document
.
getElementById
(
'
text
'
).
textContent
+=
_
.
repeat
(
'
a
'
,
3
)
}}
><
/Script
>
<
Script
src
=
{
url
}
id
=
"
script2
"
onLoad
=
{()
=>
{
// eslint-disable-next-line no-undef
document
.
getElementById
(
'
text
'
).
textContent
+=
_
.
repeat
(
'
b
'
,
3
)
}}
><
/Script
>
<
Script
src
=
{
url
}
id
=
"
script3
"
onLoad
=
{()
=>
{
// eslint-disable-next-line no-undef
document
.
getElementById
(
'
text
'
).
textContent
+=
_
.
repeat
(
'
c
'
,
3
)
}}
><
/Script
>
<
div
id
=
"
text
"
><
/div
>
<
/div
>
)
}
export
default
Page
test/integration/script-loader/styles/styles.css
0 → 100644
浏览文件 @
c8cd77a8
body
{
font-family
:
'Arial'
,
sans-serif
;
padding
:
20px
20px
60px
;
max-width
:
680px
;
margin
:
0
auto
;
}
test/integration/script-loader/test/index.test.js
0 → 100644
浏览文件 @
c8cd77a8
/* eslint-env jest */
import
{
join
}
from
'
path
'
import
{
renderViaHTTP
,
nextServer
,
startApp
,
stopApp
,
nextBuild
,
waitFor
,
}
from
'
next-test-utils
'
import
webdriver
from
'
next-webdriver
'
import
cheerio
from
'
cheerio
'
jest
.
setTimeout
(
1000
*
60
*
5
)
let
appDir
=
join
(
__dirname
,
'
..
'
)
let
server
let
appPort
describe
(
'
Script Loader
'
,
()
=>
{
beforeAll
(
async
()
=>
{
await
nextBuild
(
appDir
)
const
app
=
nextServer
({
dir
:
appDir
,
dev
:
false
,
quiet
:
true
,
})
server
=
await
startApp
(
app
)
appPort
=
server
.
address
().
port
})
afterAll
(()
=>
{
stopApp
(
server
)
})
it
(
'
priority defer
'
,
async
()
=>
{
let
browser
try
{
browser
=
await
webdriver
(
appPort
,
'
/
'
)
await
waitFor
(
1000
)
const
script
=
await
browser
.
elementById
(
'
script
'
)
const
src
=
await
script
.
getAttribute
(
'
src
'
)
const
scriptPreload
=
await
browser
.
elementsByCss
(
`link[rel=preload][href="
${
src
}
"]`
)
const
endScripts
=
await
browser
.
elementsByCss
(
'
#script ~ script[src^="/_next/static/"]
'
)
const
endPreloads
=
await
browser
.
elementsByCss
(
`link[rel=preload][href="
${
src
}
"] ~ link[rel=preload][href^="/_next/static/"]`
)
// Renders script tag
expect
(
script
).
toBeDefined
()
// Renders preload
expect
(
scriptPreload
.
length
).
toBeGreaterThan
(
0
)
// Script is inserted at the end
expect
(
endScripts
.
length
).
toBe
(
0
)
//Preload is defined at the end
expect
(
endPreloads
.
length
).
toBe
(
0
)
}
finally
{
if
(
browser
)
await
browser
.
close
()
}
})
it
(
'
priority lazy
'
,
async
()
=>
{
let
browser
try
{
browser
=
await
webdriver
(
appPort
,
'
/page3
'
)
await
browser
.
waitForElementByCss
(
'
#onload-div
'
)
await
waitFor
(
1000
)
const
script
=
await
browser
.
elementById
(
'
script
'
)
const
endScripts
=
await
browser
.
elementsByCss
(
'
#script ~ script[src^="/_next/static/"]
'
)
// Renders script tag
expect
(
script
).
toBeDefined
()
// Script is inserted at the end
expect
(
endScripts
.
length
).
toBe
(
0
)
}
finally
{
if
(
browser
)
await
browser
.
close
()
}
})
it
(
'
priority eager
'
,
async
()
=>
{
const
html
=
await
renderViaHTTP
(
appPort
,
'
/page1
'
)
const
$
=
cheerio
.
load
(
html
)
const
script
=
$
(
'
#script
'
)
const
src
=
script
.
attr
(
'
src
'
)
// Renders script tag
expect
(
script
).
toBeDefined
()
// Preload is inserted at the beginning
expect
(
$
(
`link[rel=preload][href="
${
src
}
"] ~ link[rel=preload][href^="/_next/static/"]`
).
length
&&
!
$
(
`link[rel=preload][href^="/_next/static/chunks/main"] ~ link[rel=preload][href="
${
src
}
"]`
).
length
).
toBeTruthy
()
// Preload is inserted after fonts and CSS
expect
(
$
(
`link[rel=stylesheet][href^="/_next/static/css"] ~ link[rel=preload][href="
${
src
}
"]`
).
length
).
toBeGreaterThan
(
0
)
expect
(
$
(
`link[rel=stylesheet][href="https://fonts.googleapis.com/css?family=Voces"] ~ link[rel=preload][href="
${
src
}
"]`
).
length
).
toBeGreaterThan
(
0
)
// Script is inserted before NextScripts
expect
(
$
(
'
#__NEXT_DATA__ ~ #script ~ script[src^="/_next/static/chunks/main"]
'
)
.
length
).
toBeGreaterThan
(
0
)
})
it
(
'
priority dangerouslyBlockRendering
'
,
async
()
=>
{
const
html
=
await
renderViaHTTP
(
appPort
,
'
/page2
'
)
const
$
=
cheerio
.
load
(
html
)
// Script is inserted in place
expect
(
$
(
'
.container #script
'
).
length
).
toBeGreaterThan
(
0
)
})
it
(
'
onloads fire correctly
'
,
async
()
=>
{
let
browser
try
{
browser
=
await
webdriver
(
appPort
,
'
/page4
'
)
await
waitFor
(
3000
)
const
text
=
await
browser
.
elementById
(
'
text
'
).
text
()
expect
(
text
).
toBe
(
'
aaabbbccc
'
)
}
finally
{
if
(
browser
)
await
browser
.
close
()
}
})
})
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录