我已经为多个项目所需的通用输入框创建了一个 Web 组件。 设计功能保持不变,只是我必须在每个项目上使用切换主题。所以我决定继续使用 WebComponents。其中一个项目基于 Vue Js。在 Vue js 中,每次更新时 DOM 内容都会重新渲染启用 react 性。 vue 模板的重新渲染正在重新初始化我的自定义 Web 组件,这将导致丢失我使用 setter 分配给该组件的所有配置。 我知道以下解决方案。但我想使用 setter 方法。
- 将数据作为属性传递
- 基于事件的配置传递。
- 使用 Vue 指令。
使用 v-show 代替 v-if
-- 以上三个解决方案与我想要创建的内容并不真正匹配。
我在 jsfiddle 中创建了一个示例项目来显示我的问题。 每次我取消选中并选中该复选框时,都会创建组件的新实例。这会导致丢失我选择的主题。 (请检查事件盒子数) 对于这个特定的示例,我希望显示蓝色主题。但它不断变成红色
class InputBox extends HTMLElement {
constructor() {
super();
window.activeBoxes ? window.activeBoxes++ : window.activeBoxes = 1;
var shadow = this.attachShadow({
mode: 'open'
});
var template = `
<style>
.blue#iElem {
background: #00f !important;
color: #fff !important;
}
.green#iElem {
background: #0f0 !important;
color: #f00 !important;
}
#iElem {
background: #f00;
padding: 13px;
border-radius: 10px;
color: yellow;
border: 0;
outline: 0;
box-shadow: 0px 0px 14px -3px #000;
}
</style>
<input id="iElem" autocomplete="off" autocorrect="off" spellcheck="false" type="text" />
`;
shadow.innerHTML = template;
this._theme = 'red';
this.changeTheme = function(){
this.shadowRoot.querySelector('#iElem').className = '';
this.shadowRoot.querySelector('#iElem').classList.add(this._theme);
}
}
connectedCallback() {
this.changeTheme();
}
set theme(val){
this._theme = val;
this.changeTheme();
}
}
window.customElements.define('search-bar', InputBox);
<!DOCTYPE html>
<html>
<head>
<title>Wrapper Component</title>
<script src="https://unpkg.com/vue"></script>
<style>
html,
body {
font: 13px/18px sans-serif;
}
select {
min-width: 300px;
}
search-bar {
top: 100px;
position: absolute;
left: 300px;
}
input {
min-width: 20px;
padding: 25px;
top: 100px;
position: absolute;
}
</style>
</head>
<body>
<div id="el"></div>
<!-- using string template here to work around HTML <option> placement restriction -->
<script type="text/x-template" id="demo-template">
<div>
<div class='parent' contentEditable='true' v-if='visible'>
<search-bar ref='iBox'></search-bar>
</div>
<input type='checkbox' v-model='visible'>
</div>
</script>
<script type="text/x-template" id="select2-template">
<select>
<slot></slot>
</select>
</script>
<script>
var vm = new Vue({
el: "#el",
template: "#demo-template",
data: {
visible: true,
},
mounted(){
let self = this
setTimeout(()=>{
self.$refs.iBox.theme = 'blue';
} , 0)
}
});
</script>
</body>
</html>
最佳答案
<div class='parent' contentEditable='true' v-if='visible'>
<search-bar ref='iBox'></search-bar>
</div>
<input type='checkbox' v-model='visible'>
Vue 的 v-if
将从 DOM 添加/删除整个 DIV
所以<search-bar>
每次单击复选框时也会添加/删除
如果您想要 <search-bar>
的状态您必须将其保存在<search-bar>
之外的某个地方组件:
- JavaScript 变量
- 本地存储
- .getRootnode().host
- CSS Properties我会选择这个,因为它们会慢慢进入shadowDOM
- ...
- ...
或者更改您的复选框代码以不使用v-if
但隐藏<div>
与任何CSS:
- 显示:无
- 可见性:隐藏
- 不透明度:0
- 移至屏幕外位置
- 高度:0
- ...
和/或...
使用样式表管理多个屏幕元素
您可以使用 <style>
轻松切换样式元素:
<style id="SearchBox" onload="this.disabled=true">
... lots of CSS
... even more CSS
... and more CSS
</style>
onload
事件确保<style>
不应用于页面加载。
激活所有 CSS 样式:
(this.shadowRoot || document).getElementById("SearchBox").disabled = false
删除所有 CSS 样式:
(this.shadowRoot || document).getElementById("SearchBox").disabled = true
您确实需要CSS Properties以便与 ShadowDOM 元素结合使用。
相比框架,我更喜欢原生。 <style v-if='visible'/>
将起作用..通过残酷地删除/添加样式表。
关于javascript - 每次使用 Vue js 时,WebComponents 都会重新初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61413054/