|
8 | 8 | - [Installation](#installation)
|
9 | 9 | - [Description](#description)
|
10 | 10 | - [Example](#example)
|
| 11 | +- [ShadowDOM Example](#shadowdom-example) |
11 | 12 | - [Browsers support](#browser-support)
|
12 | 13 | - [Options](#options)
|
13 | 14 | - [How does it work?](#how-does-it-work)
|
@@ -124,6 +125,100 @@ document.querySelector('widget-vue').prop2 = 'another string' // set prop value
|
124 | 125 | You can also change `<widget-vue>` HTML attributes and changes will be instantly reflected.
|
125 | 126 |
|
126 | 127 |
|
| 128 | +## ShadowDOM Example |
| 129 | +By default, `vue-custom-element` does not mount underneath a `ShadowDOM`. While this is easier to develop and debug, CSS stylings for the parent can contaminate the component, and stylings for the component can contaminate the parent. This is most prevalent when using prebuilt UI libraries which assume the component is the only content on the page (i.e. Vuetify). A `ShadowDOM` prevents this contamination by isolating the components and stylings within an HTML document fragment. |
| 130 | + |
| 131 | +In these situations, components should be mounted underneath a shadowDOM using the option |
| 132 | +``` js |
| 133 | +Vue.customElement('widget-vue', CustomWidget, { |
| 134 | + shadow: true, |
| 135 | + beforeCreateVueInstance(root) { |
| 136 | + const rootNode = root.el.getRootNode(); |
| 137 | + |
| 138 | + if (rootNode instanceof ShadowRoot) { |
| 139 | + root.shadowRoot = rootNode; |
| 140 | + } else { |
| 141 | + root.shadowRoot = document.head; |
| 142 | + } |
| 143 | + return root; |
| 144 | + }, |
| 145 | +); |
| 146 | +``` |
| 147 | +
|
| 148 | +The additional `beforeCreateVueInstance` is only required if your Vue component has bundled stylings and you are using `css-modules` with Webpack to bundle (which is most use cases). In addition, if you are using `vue-loader` and `vue-style-loader` plugins with Webpack, you will need to pass the `shadowMode: true` option to the plugins also. This is required so the plugins know they that CSS styles should be attached under the `shadowDOM` and not in the `document.head` (which is the default behavior). |
| 149 | +
|
| 150 | +**webpack.config.js example for scss stylings** |
| 151 | +``` js |
| 152 | +{ |
| 153 | + test: /\.vue$/, |
| 154 | + use: [ |
| 155 | + { |
| 156 | + loader: 'vue-loader', |
| 157 | + options: { |
| 158 | + shadowMode: true |
| 159 | + } |
| 160 | + } |
| 161 | + ] |
| 162 | +}, |
| 163 | +{ |
| 164 | + test: /\.scss$/, //as example I used scss |
| 165 | + use: [ |
| 166 | + { |
| 167 | + loader: 'vue-style-loader', |
| 168 | + options: { |
| 169 | + shadowMode: true |
| 170 | + } |
| 171 | + } |
| 172 | + ] |
| 173 | +} |
| 174 | +``` |
| 175 | +
|
| 176 | +**vue.config.js for Vue CLI 3** |
| 177 | +``` js |
| 178 | +function enableShadowCss(config) { |
| 179 | + const configs = [ |
| 180 | + config.module.rule('vue').use('vue-loader'), |
| 181 | + ]; |
| 182 | + // based on common rules returned by `vue inspect` |
| 183 | + const ruleSets = ['css', 'postcss', 'scss', 'sass', 'less', 'stylus']; |
| 184 | + const ruleNames = ['vue-modules', 'vue', 'normal-modules', 'normal']; |
| 185 | + |
| 186 | + ruleSets.forEach((ruleSet) => { |
| 187 | + if (config.module.rules.store.has(ruleSet)) { |
| 188 | + ruleNames.forEach((rName) => { |
| 189 | + if (config.module.rule(ruleSet).oneOfs.store.has(rName)) { |
| 190 | + if (config.module.rule(ruleSet).oneOf(rName).uses.store.has('vue-style-loader')) { |
| 191 | + configs.push(config.module.rule(ruleSet).oneOf(rName).use('vue-style-loader')); |
| 192 | + } |
| 193 | + } |
| 194 | + }); |
| 195 | + } |
| 196 | + }); |
| 197 | + if (!process.env.BUILD_MODE) { |
| 198 | + process.env.BUILD_MODE = config.store.get('mode'); |
| 199 | + } |
| 200 | + configs.forEach((c) => c.tap((options) => { |
| 201 | + options.shadowMode = true; |
| 202 | + return options; |
| 203 | + })); |
| 204 | +} |
| 205 | + |
| 206 | +module.exports = { |
| 207 | + chainWebpack: |
| 208 | + (config) => { |
| 209 | + enableShadowCss(config); |
| 210 | + }, |
| 211 | + css: { |
| 212 | + sourceMap: true, |
| 213 | + extract: false, |
| 214 | + }, |
| 215 | +}; |
| 216 | +``` |
| 217 | +
|
| 218 | +_Note: for stylings to work, CSS must be bundled in JS and injected at runtime. Otherwise you will need to manually link the CSS under the shadowDOM inside your component (which is obviously an anti-pattern)._ |
| 219 | +
|
| 220 | +For additional ShadowDom examples see: https://github.com/bryanvaz/vue-custom-element-shadow-examples |
| 221 | +
|
127 | 222 | ## Browser support
|
128 | 223 |
|
129 | 224 | | [<img src="https://raw.githubusercontent.com/godban/browsers-support-badges/master/src/images/firefox.png" alt="Firefox" width="16px" height="16px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/godban/browsers-support-badges/master/src/images/chrome.png" alt="Chrome" width="16px" height="16px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/godban/browsers-support-badges/master/src/images/safari.png" alt="Safari" width="16px" height="16px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/godban/browsers-support-badges/master/src/images/opera.png" alt="Opera" width="16px" height="16px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/godban/browsers-support-badges/master/src/images/safari-ios.png" alt="iOS Safari" width="16px" height="16px" />](http://godban.github.io/browsers-support-badges/)</br>iOS | [<img src="https://raw.githubusercontent.com/godban/browsers-support-badges/master/src/images/chrome-android.png" alt="Chrome for Android" width="16px" height="16px" />](http://godban.github.io/browsers-support-badges/)</br>Android |
|
|
0 commit comments