使用 vue-cli 构建深度嵌套 html 需要很长时间
Building deeply nested html with vue-cli takes forever
我发现 vue-cli(2.9.6,但 3.0.0 beta* 有同样的问题)一旦模板的 html 变得相对深入,它的构建过程就会永远持续下去。
例如,我只是在预先包含的 App.vue
中添加了一些 div
:
<template>
<div id="app">
<img src="./assets/logo.png">
<div><div><div><div></div></div></div></div>
<HelloWorld/>
</div>
</template>
不需要那么长时间。
但是一旦它得到这个:
<template>
<div id="app">
<img src="./assets/logo.png">
<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
<HelloWorld/>
</div>
</template>
然后建造过程需要很长时间,我相信这种深度的鸟巢并不少见。
我该如何处理这个问题?
编辑(详细信息)
看来问题可能与环境有关,所以这里是详细信息。
至少可以在这些环境中重现此问题:
- Mac mini 上的 macOS High Sierra(2014 年末)
- Ubuntu 戴尔 XPS 13 上的 18.04
node 和 npm 版本是:
node --version
# prints
v8.9.4
# and
npm version
# prints
{ npm: '6.1.0',
ares: '1.10.1-DEV',
cldr: '31.0.1',
http_parser: '2.7.0',
icu: '59.1',
modules: '57',
nghttp2: '1.25.0',
node: '8.9.4',
openssl: '1.0.2n',
tz: '2017b',
unicode: '9.0',
uv: '1.15.0',
v8: '6.1.534.50',
zlib: '1.2.11' }
有了这些,我在 Mac 上重试了以下操作:
npm uninstall -g vue-cli
npm install -g vue-cli
vue init webpack divnest
# then some Enter keys - everything is default
cd divnest
然后,打开App.vue
并放置许多div:
<template>
<div id="app">
<img src="./assets/logo.png">
<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
<router-view/>
</div>
</template>
(由于我这里使用默认设置,所以包含了<router-view/>
,不像原来的post,但应该不是问题。)
最后,
npm run dev
这需要很长时间 - 具体来说,进程在此时停止:
13% building modules 28/31 modules 3 active ...myname/Documents/divnest/src/App.vue
如
npm run build
,进程在此时停止:
> divnest@1.0.0 build /Users/myname/Documents/divnest
> node build/build.js
Hash: 483ebabc54d5aed79fd7
Version: webpack 3.12.0
Time: 13742ms
Asset Size Chunks Chunk Names
static/js/vendor.7fed9fa7b7ba482410b7.js 112 kB 0 [emitted] vendor
static/js/app.f1ebca7a6e0ec0b7ebdf.js 12 kB 1 [emitted] app
static/js/manifest.2ae2e69a05c33dfc65f8.js 857 bytes 2 [emitted] manifest
static/css/app.30790115300ab27614ce176899523b62.css 432 bytes 1 [emitted] app
static/css/app.30790115300ab27614ce176899523b62.css.map 828 bytes [emitted]
static/js/vendor.7fed9fa7b7ba482410b7.js.map 553 kB 0 [emitted] vendor
static/js/app.f1ebca7a6e0ec0b7ebdf.js.map 23.3 kB 1 [emitted] app
static/js/manifest.2ae2e69a05c33dfc65f8.js.map 4.97 kB 2 [emitted] manifest
index.html 509 bytes [emitted]
Build complete.
Tip: built files are meant to be served over an HTTP server.
Opening index.html over file:// won't work.
94% asset optimization
如果我放手,需要... 1155409ms!!!!
DONE Compiled successfully in 1155409ms 13:35:34
I Your application is running here: http://localhost:8080
更多编辑
正如@tony19 所指出的,prettier 是最有可能的嫌疑人。按照建议,我尝试了 Ubuntu 18.04 的一些模式(不是 Mac 因为 Mac 现在不在这里,抱歉),我的结果是:
- vue-cli 2.9.6 +
npm run dev
- 挂起
- vue-cli 2.9.6 +
npm run build
- 6 秒(这太令人困惑了。上面的 100 万秒是多少!?也许重新安装 vue-cli 欠了这个变化?)
- vue-cli 3.0.0-beta16 +
vue serve
- 挂起(与@tony19 的报告相反)
- vue-cli 3.0.0-beta16 +
vue build
- 5 秒
更多编辑
看来,这肯定是prettier造成的。
https://github.com/prettier/prettier/issues/1250
是解决此问题的原始问题,开发团队认为 https://github.com/prettier/prettier/pull/2259 fixed it, but the reality is that it couldn't handle my case, as @tony19 shows it on https://github.com/prettier/prettier/issues/4672 。好吧。
"SOLUTION"
我最终这样做了 - 根据 @tony19 的报告,更改 /node_modules/vue-loader/lib/template-compiler/index.js
行 78:81
if (!isProduction) {
code = prettier.format(code, { semi: false })
}
至
// if (!isProduction) {
// code = prettier.format(code, { semi: false })
// }
这样问题就解决了。谢谢前端,谢谢。
我对你的 25 个嵌套 <div>
没有任何特别的问题:(下面是 Vue 运行时编译器的例子,这样你就可以直接在你的浏览器中轻松测试它)
new Vue({
el: '#app',
template: '#app-template',
});
#app div {
border: 1px solid grey;
padding: 1px;
}
<script src="https://unpkg.com/vue@2"></script>
<div id="app">
</div>
<template id="app-template">
<div id="app">
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>25 nested <code><div></code>'s</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
这是一个在 CodeSandbox 上使用 Vue CLI 项目的演示,可能更接近您的示例:https://codesandbox.io/s/v3knpl447l
(即预编译模板,因此构建过程实际上发生在 CodeSandbox 服务器上)
我可以重现您描述的性能问题(macOS High Sierra 10.13.4、Node 8.9.4 和 9.11.1)。新创建的 vue-cli
3.x 项目也会出现此问题。
挂起实际发生在 prettier
,从 vue-loader
's template compiler 调用。嵌套的 <div>
由 vue-loader
转换为 JavaScript,并成为以下片段:
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{"id":"app"}},[_c('img',{attrs:{"src":require("./assets/logo.png")}}),_vm._v(" "),_c('router-view'),_vm._v(" "),_vm._m(0)],1)}
var staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div')])])])])])])])])])])])])])])])])])])])])])])])])}]
vue-loader
将这个长字符串传递给 prettier
,处理大约需要 159 秒。错误的原因是创建 div
s 的深层嵌套函数调用。我已经在 prettier
(Issue 4672) 中报告了这个错误。
与此同时,我建议重构您的 HTML 以避免深度嵌套。如果您需要坚持使用旧模板,可以通过在生产模式下构建来解决此问题,因为 vue-loader
跳过 prettier
用于生产构建:
NODE_ENV=production npm run dev
UPDATE vue-loader
v15.5.0 添加了 prettify
选项以允许禁用 prettier
(更新到最新版本 @vue/cli
以确保您的 vue-loader
是最新的新选项)。您可以按如下方式使用此选项:
将 vue.config.js
(如果它不存在)添加到项目的根目录。
编辑文件以包含:
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.prettify = false
return options
})
}
}
我在 vue-cli 3.1.1 (TypeScript + SCSS) 和 Bootstrap(默认情况下需要一些嵌套)中遇到了类似的问题。示例结构:
<template>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card-deck">
<div class="card">
<div class="card-body">
<div class="accordion">
<div class="card">
<div class="card-header">
...
</div>
<div class="collapse">
<div class="card-body">
<div class="row">
<div class="col-12 form-group">
<label>...</label>
<div class="dropdown">
<button class="custom-select" type="button" data-toggle="dropdown">{{someValue}}</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" :data-key="somekey1" @click="onClickMethod">value1</a>
<a class="dropdown-item" href="#" :data-key="somekey2" @click="onClickMethod">value2</a>
...
需要大约 12 秒来编译 400 多行代码(模板 + TypeScript + SCSS)。移除后:
:data-key="somekey1" @click="onClickMethod"
:data-key="somekey2" @click="onClickMethod"
代码需要 ~5-6 秒来编译。将代码移动到自定义组件后(以及一些 TypeScript 代码从 Vue 组件移动到 Helper.ts 文件):
<template>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card-deck">
<div class="card">
<div class="card-body">
<div class="accordion">
<div class="card">
<div class="card-header">
...
</div>
<div class="collapse">
<div class="card-body">
<SubComponent/>
编译需要 ~700ms(一个主要组件和两个附加子组件,每个文件少于 100 行代码 + Helper.ts 刚好有 97 行代码)。
因此,如果您的 npm run serve
性能不佳,请先尝试子组件,我在调用 npm run build
时没有注意到编译时间有太大差异,所以我假设(可能是错误的)这问题也是由为 serve
启用但为 build
禁用的代码美化器引起的(在保存操作时未调用 TSLint,因此在我的情况下它不会影响 npm run serve
)。
2019 年更新
最近 vue-loader
在他们的选项中添加了一个标志,以便在开发过程中也禁用 prettier
。
只需将 prettify: false
添加到您的 vue-loader
选项。
https://vue-loader.vuejs.org/options.html#prettify
Notice: Just make sure you have the latest vue-loader version
我发现 vue-cli(2.9.6,但 3.0.0 beta* 有同样的问题)一旦模板的 html 变得相对深入,它的构建过程就会永远持续下去。
例如,我只是在预先包含的 App.vue
中添加了一些 div
:
<template>
<div id="app">
<img src="./assets/logo.png">
<div><div><div><div></div></div></div></div>
<HelloWorld/>
</div>
</template>
不需要那么长时间。
但是一旦它得到这个:
<template>
<div id="app">
<img src="./assets/logo.png">
<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
<HelloWorld/>
</div>
</template>
然后建造过程需要很长时间,我相信这种深度的鸟巢并不少见。
我该如何处理这个问题?
编辑(详细信息)
看来问题可能与环境有关,所以这里是详细信息。
至少可以在这些环境中重现此问题:
- Mac mini 上的 macOS High Sierra(2014 年末)
- Ubuntu 戴尔 XPS 13 上的 18.04
node 和 npm 版本是:
node --version
# prints
v8.9.4
# and
npm version
# prints
{ npm: '6.1.0',
ares: '1.10.1-DEV',
cldr: '31.0.1',
http_parser: '2.7.0',
icu: '59.1',
modules: '57',
nghttp2: '1.25.0',
node: '8.9.4',
openssl: '1.0.2n',
tz: '2017b',
unicode: '9.0',
uv: '1.15.0',
v8: '6.1.534.50',
zlib: '1.2.11' }
有了这些,我在 Mac 上重试了以下操作:
npm uninstall -g vue-cli
npm install -g vue-cli
vue init webpack divnest
# then some Enter keys - everything is default
cd divnest
然后,打开App.vue
并放置许多div:
<template>
<div id="app">
<img src="./assets/logo.png">
<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
<router-view/>
</div>
</template>
(由于我这里使用默认设置,所以包含了<router-view/>
,不像原来的post,但应该不是问题。)
最后,
npm run dev
这需要很长时间 - 具体来说,进程在此时停止:
13% building modules 28/31 modules 3 active ...myname/Documents/divnest/src/App.vue
如
npm run build
,进程在此时停止:
> divnest@1.0.0 build /Users/myname/Documents/divnest
> node build/build.js
Hash: 483ebabc54d5aed79fd7
Version: webpack 3.12.0
Time: 13742ms
Asset Size Chunks Chunk Names
static/js/vendor.7fed9fa7b7ba482410b7.js 112 kB 0 [emitted] vendor
static/js/app.f1ebca7a6e0ec0b7ebdf.js 12 kB 1 [emitted] app
static/js/manifest.2ae2e69a05c33dfc65f8.js 857 bytes 2 [emitted] manifest
static/css/app.30790115300ab27614ce176899523b62.css 432 bytes 1 [emitted] app
static/css/app.30790115300ab27614ce176899523b62.css.map 828 bytes [emitted]
static/js/vendor.7fed9fa7b7ba482410b7.js.map 553 kB 0 [emitted] vendor
static/js/app.f1ebca7a6e0ec0b7ebdf.js.map 23.3 kB 1 [emitted] app
static/js/manifest.2ae2e69a05c33dfc65f8.js.map 4.97 kB 2 [emitted] manifest
index.html 509 bytes [emitted]
Build complete.
Tip: built files are meant to be served over an HTTP server.
Opening index.html over file:// won't work.
94% asset optimization
如果我放手,需要... 1155409ms!!!!
DONE Compiled successfully in 1155409ms 13:35:34
I Your application is running here: http://localhost:8080
更多编辑
正如@tony19 所指出的,prettier 是最有可能的嫌疑人。按照建议,我尝试了 Ubuntu 18.04 的一些模式(不是 Mac 因为 Mac 现在不在这里,抱歉),我的结果是:
- vue-cli 2.9.6 +
npm run dev
- 挂起 - vue-cli 2.9.6 +
npm run build
- 6 秒(这太令人困惑了。上面的 100 万秒是多少!?也许重新安装 vue-cli 欠了这个变化?) - vue-cli 3.0.0-beta16 +
vue serve
- 挂起(与@tony19 的报告相反) - vue-cli 3.0.0-beta16 +
vue build
- 5 秒
更多编辑
看来,这肯定是prettier造成的。 https://github.com/prettier/prettier/issues/1250 是解决此问题的原始问题,开发团队认为 https://github.com/prettier/prettier/pull/2259 fixed it, but the reality is that it couldn't handle my case, as @tony19 shows it on https://github.com/prettier/prettier/issues/4672 。好吧。
"SOLUTION"
我最终这样做了 - 根据 @tony19 的报告,更改 /node_modules/vue-loader/lib/template-compiler/index.js
行 78:81
if (!isProduction) {
code = prettier.format(code, { semi: false })
}
至
// if (!isProduction) {
// code = prettier.format(code, { semi: false })
// }
这样问题就解决了。谢谢前端,谢谢。
我对你的 25 个嵌套 <div>
没有任何特别的问题:(下面是 Vue 运行时编译器的例子,这样你就可以直接在你的浏览器中轻松测试它)
new Vue({
el: '#app',
template: '#app-template',
});
#app div {
border: 1px solid grey;
padding: 1px;
}
<script src="https://unpkg.com/vue@2"></script>
<div id="app">
</div>
<template id="app-template">
<div id="app">
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>25 nested <code><div></code>'s</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
这是一个在 CodeSandbox 上使用 Vue CLI 项目的演示,可能更接近您的示例:https://codesandbox.io/s/v3knpl447l
(即预编译模板,因此构建过程实际上发生在 CodeSandbox 服务器上)
我可以重现您描述的性能问题(macOS High Sierra 10.13.4、Node 8.9.4 和 9.11.1)。新创建的 vue-cli
3.x 项目也会出现此问题。
挂起实际发生在 prettier
,从 vue-loader
's template compiler 调用。嵌套的 <div>
由 vue-loader
转换为 JavaScript,并成为以下片段:
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{"id":"app"}},[_c('img',{attrs:{"src":require("./assets/logo.png")}}),_vm._v(" "),_c('router-view'),_vm._v(" "),_vm._m(0)],1)}
var staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div',[_c('div')])])])])])])])])])])])])])])])])])])])])])])])])}]
vue-loader
将这个长字符串传递给 prettier
,处理大约需要 159 秒。错误的原因是创建 div
s 的深层嵌套函数调用。我已经在 prettier
(Issue 4672) 中报告了这个错误。
与此同时,我建议重构您的 HTML 以避免深度嵌套。如果您需要坚持使用旧模板,可以通过在生产模式下构建来解决此问题,因为 vue-loader
跳过 prettier
用于生产构建:
NODE_ENV=production npm run dev
UPDATE vue-loader
v15.5.0 添加了 prettify
选项以允许禁用 prettier
(更新到最新版本 @vue/cli
以确保您的 vue-loader
是最新的新选项)。您可以按如下方式使用此选项:
将
vue.config.js
(如果它不存在)添加到项目的根目录。编辑文件以包含:
module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap(options => { options.prettify = false return options }) } }
我在 vue-cli 3.1.1 (TypeScript + SCSS) 和 Bootstrap(默认情况下需要一些嵌套)中遇到了类似的问题。示例结构:
<template>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card-deck">
<div class="card">
<div class="card-body">
<div class="accordion">
<div class="card">
<div class="card-header">
...
</div>
<div class="collapse">
<div class="card-body">
<div class="row">
<div class="col-12 form-group">
<label>...</label>
<div class="dropdown">
<button class="custom-select" type="button" data-toggle="dropdown">{{someValue}}</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" :data-key="somekey1" @click="onClickMethod">value1</a>
<a class="dropdown-item" href="#" :data-key="somekey2" @click="onClickMethod">value2</a>
...
需要大约 12 秒来编译 400 多行代码(模板 + TypeScript + SCSS)。移除后:
:data-key="somekey1" @click="onClickMethod"
:data-key="somekey2" @click="onClickMethod"
代码需要 ~5-6 秒来编译。将代码移动到自定义组件后(以及一些 TypeScript 代码从 Vue 组件移动到 Helper.ts 文件):
<template>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card-deck">
<div class="card">
<div class="card-body">
<div class="accordion">
<div class="card">
<div class="card-header">
...
</div>
<div class="collapse">
<div class="card-body">
<SubComponent/>
编译需要 ~700ms(一个主要组件和两个附加子组件,每个文件少于 100 行代码 + Helper.ts 刚好有 97 行代码)。
因此,如果您的 npm run serve
性能不佳,请先尝试子组件,我在调用 npm run build
时没有注意到编译时间有太大差异,所以我假设(可能是错误的)这问题也是由为 serve
启用但为 build
禁用的代码美化器引起的(在保存操作时未调用 TSLint,因此在我的情况下它不会影响 npm run serve
)。
2019 年更新
最近 vue-loader
在他们的选项中添加了一个标志,以便在开发过程中也禁用 prettier
。
只需将 prettify: false
添加到您的 vue-loader
选项。
https://vue-loader.vuejs.org/options.html#prettify
Notice: Just make sure you have the latest vue-loader version