Vue 将任何标签中的 "open" 属性替换为值 "open"
Vue replaces "open" attribute to value "open" in any tag
我在 laravel blade 模板中使用 vue.js (v2.6.12) 组件。
对于该项目,我还使用了 MathML,其中我需要使用 <mfenced>
标记的 open
属性来设置一些自定义值。下面是用 mathml 表达数学的例子。
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="[">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
但是一旦页面呈现,open
属性就会转换成这个 open="open"
。我 100% 确定没有加载其他库或脚本来像这样更新,只是普通的 vue。这实际上打破了数学表达式。所以它看起来像这样:
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="open">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
后来我意识到,不仅在数学表达式中,任何标签,无论是 <div open="anything">...</div>
、<span open="anything">...</span>
、<custom-element open="something">...</custom-element>
具有 open
属性,其行为都是相同的。即使我使用 v-pre
属性将其从 vue js 模板编译器中排除。
这并没有发生,因为我禁用了 vue 应用程序初始化。
这里的问题是:
- 为什么 vue 会这样改变 open 属性?
- 我怎样才能停止这种行为,在 vue 应用程序区域内的整个页面或至少在我选择的地方(类似于使用
v-pre
),是否有 ary 配置或任何其他方式?
为什么
在 HTML 规范中有一些属性称为 boolean attributes。规范规定了此类属性的值:
If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.
The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
open
是布尔属性之一 - 它是为 <details>
element
定义的
Vue 2 的问题在于,它将大部分 boolean attributes 视为全局 - 没有考虑放置它的元素。结果是 open
属性总是以值“open”呈现,或者如果该值是假的(当 v-bind
ing 时)则被删除。这在 Vue 3 中已修复,如第二个示例所示...
如何
使用 v-pre
是可行的方法,但不幸的是你有一个错误。
参见this issue. The bug was already fixed with this commit(2020 年 9 月 21 日)但尚未发布...
示例 - “使用 v-pre”应该在 Vue 版本> 2.6.12 中工作
const vm = new Vue({
el: '#app',
data() {
return {
message: 'Hi!',
html: `<div open="[" close="]">Hi from html</div>`
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<div v-html="html"></div>
<div v-pre>
<p open="[" close="]">With v-pre</p>
</div>
</div>
示例 - 它适用于 Vue 3 - open
仅当放置在 <details>
上时才被视为布尔属性
const app = Vue.createApp({
data() {
return {
message: 'This works in Vue 3!',
}
},
})
app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.11/vue.global.js" integrity="sha512-1gHWIGJfX0pBsPJHfyoAV4NiZ0wjjE1regXVSwglTejjna0/x/XG8tg+i3ZAsDtuci24LLxW8azhp1+VYE5daw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<details open="[">
<summary>Details</summary>
open attribute on details element is treated as boolean (renders empty value)
</details>
</div>
一种解决方法是创建一个 directive(名为 "attr"
)来设置属性:
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
然后像 v-bind
一样在您的模板中使用它,但使用 v-attr
:
<mfenced v-attr:open="'['">
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
new Vue({ el: '#app' })
<script src="https://unpkg.com/vue@2.6.12"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<div id="app">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" v-attr:open="'['">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
</div>
我找到了解决此问题的简单方法。
为什么要破解?
因为正如@Michal 所指出的那样,它最终将在即将发布的版本中得到修复,所以现在只需快速而肮脏的 hack 就足够了。
我所做的是将数学内容放在 content 中,并将其添加到 data 属性中,并在 vue 完成其不良工作后替换原始内容(抱歉,这里只使用 blade 语法,但它会有意义).我把它放在两个地方只是为了 SEO 目的。
我需要显示数学表达式的模板。
...
<div class="proxy-content" data-proxy-content="{{ $article->content }}">
{!! $article->content !!}
</div>
...
我将它与 jQuery 一起使用,但您可以轻松地用 vue.js' $el
代替。这就是它在我的 app.js
文件中的样子。
...
const app = new Vue({
el: '#app',
methods: {
proxyContent() {
// Set Proxy Content.
jQuery('.proxy-content').each((i, el) => {
const $el = jQuery(el);
$el.html( jQuery('<textarea />').html( $el.data('proxy-content')).text() );
});
}
loadMathJax() {
// Load & Initialize MathJax Library.
const script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";
document.getElementsByTagName("head")[0].appendChild(script);
}
}
mounted(){
// Enable proxy content after mount, so we are sure no more rendering issue for templates.
this.proxyContent();
// Load MathJax library with a little delay to make sure everything is ready before loading the library.
setTimeout(() => this.loadMathJax(), 10);
}
});
...
有人可能会争辩说,我混淆了 vue 应用程序范围之外的东西。对我来说这不是问题,因为整个页面都在使用 vue.js
并且即使有另一个范围正在使用 mathml
单个东西也不会造成任何伤害(尽管它取决于实际实施)。
在那种情况下,如果您想很好地确定范围,只需使用 vue 的 $el
。
我在 laravel blade 模板中使用 vue.js (v2.6.12) 组件。
对于该项目,我还使用了 MathML,其中我需要使用 <mfenced>
标记的 open
属性来设置一些自定义值。下面是用 mathml 表达数学的例子。
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="[">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
但是一旦页面呈现,open
属性就会转换成这个 open="open"
。我 100% 确定没有加载其他库或脚本来像这样更新,只是普通的 vue。这实际上打破了数学表达式。所以它看起来像这样:
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="open">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
后来我意识到,不仅在数学表达式中,任何标签,无论是 <div open="anything">...</div>
、<span open="anything">...</span>
、<custom-element open="something">...</custom-element>
具有 open
属性,其行为都是相同的。即使我使用 v-pre
属性将其从 vue js 模板编译器中排除。
这并没有发生,因为我禁用了 vue 应用程序初始化。
这里的问题是:
- 为什么 vue 会这样改变 open 属性?
- 我怎样才能停止这种行为,在 vue 应用程序区域内的整个页面或至少在我选择的地方(类似于使用
v-pre
),是否有 ary 配置或任何其他方式?
为什么
在 HTML 规范中有一些属性称为 boolean attributes。规范规定了此类属性的值:
If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.
The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
open
是布尔属性之一 - 它是为 <details>
element
Vue 2 的问题在于,它将大部分 boolean attributes 视为全局 - 没有考虑放置它的元素。结果是 open
属性总是以值“open”呈现,或者如果该值是假的(当 v-bind
ing 时)则被删除。这在 Vue 3 中已修复,如第二个示例所示...
如何
使用 v-pre
是可行的方法,但不幸的是你有一个错误。
参见this issue. The bug was already fixed with this commit(2020 年 9 月 21 日)但尚未发布...
示例 - “使用 v-pre”应该在 Vue 版本> 2.6.12 中工作
const vm = new Vue({ el: '#app', data() { return { message: 'Hi!', html: `<div open="[" close="]">Hi from html</div>` } }, })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script> <div id="app"> <div open="[" close="]">{{ message }}</div> <div v-html="html"></div> <div v-pre> <p open="[" close="]">With v-pre</p> </div> </div>
示例 - 它适用于 Vue 3 -
open
仅当放置在<details>
上时才被视为布尔属性const app = Vue.createApp({ data() { return { message: 'This works in Vue 3!', } }, }) app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.11/vue.global.js" integrity="sha512-1gHWIGJfX0pBsPJHfyoAV4NiZ0wjjE1regXVSwglTejjna0/x/XG8tg+i3ZAsDtuci24LLxW8azhp1+VYE5daw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <div id="app"> <div open="[" close="]">{{ message }}</div> <details open="["> <summary>Details</summary> open attribute on details element is treated as boolean (renders empty value) </details> </div>
一种解决方法是创建一个 directive(名为 "attr"
)来设置属性:
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
然后像 v-bind
一样在您的模板中使用它,但使用 v-attr
:
<mfenced v-attr:open="'['">
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
new Vue({ el: '#app' })
<script src="https://unpkg.com/vue@2.6.12"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<div id="app">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" v-attr:open="'['">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
</div>
我找到了解决此问题的简单方法。
为什么要破解?
因为正如@Michal 所指出的那样,它最终将在即将发布的版本中得到修复,所以现在只需快速而肮脏的 hack 就足够了。
我所做的是将数学内容放在 content 中,并将其添加到 data 属性中,并在 vue 完成其不良工作后替换原始内容(抱歉,这里只使用 blade 语法,但它会有意义).我把它放在两个地方只是为了 SEO 目的。
我需要显示数学表达式的模板。
...
<div class="proxy-content" data-proxy-content="{{ $article->content }}">
{!! $article->content !!}
</div>
...
我将它与 jQuery 一起使用,但您可以轻松地用 vue.js' $el
代替。这就是它在我的 app.js
文件中的样子。
...
const app = new Vue({
el: '#app',
methods: {
proxyContent() {
// Set Proxy Content.
jQuery('.proxy-content').each((i, el) => {
const $el = jQuery(el);
$el.html( jQuery('<textarea />').html( $el.data('proxy-content')).text() );
});
}
loadMathJax() {
// Load & Initialize MathJax Library.
const script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";
document.getElementsByTagName("head")[0].appendChild(script);
}
}
mounted(){
// Enable proxy content after mount, so we are sure no more rendering issue for templates.
this.proxyContent();
// Load MathJax library with a little delay to make sure everything is ready before loading the library.
setTimeout(() => this.loadMathJax(), 10);
}
});
...
有人可能会争辩说,我混淆了 vue 应用程序范围之外的东西。对我来说这不是问题,因为整个页面都在使用 vue.js
并且即使有另一个范围正在使用 mathml
单个东西也不会造成任何伤害(尽管它取决于实际实施)。
在那种情况下,如果您想很好地确定范围,只需使用 vue 的 $el
。