使用附加的 svelte 组件并在相同的 Javascript 上下文中创建窗口

Posted

技术标签:

【中文标题】使用附加的 svelte 组件并在相同的 Javascript 上下文中创建窗口【英文标题】:Create window with attached svelte component and in the same Javascript context 【发布时间】:2020-01-30 21:43:52 【问题描述】:

我使用 Sapper 框架,我想打开我自己的开发工具窗口,该窗口应该可以完全访问主窗口的 javascript 对象。

我正在尝试创建一个带有苗条组件和相同 Javascript 上下文的新窗口:

var win = window.open('abour:blank')
var container = win.document.createElement('div')
var win.document.body.appendChild(container)
var component = new ComponentClass(
    target: container,
)

它可以工作,但没有 CSS 样式。

您可以使用this REPL 进行测试。

如何为新窗口应用组件 CSS 样式?

或者创建此类窗口的最佳做法是什么?

【问题讨论】:

【参考方案1】:

我通过简单地从父窗口传输所有样式解决了这个问题(参见appendCss 函数)

如果您使用 sapper,则只能传输以下样式: link[rel="stylesheet"][href^="client/"]

完整解决方案如下,你也可以看到this REPL

ComponentWindow.ts

export class ComponentWindow 
    constructor(
        windowName = '',
        windowFeatures = 'width=600,height=400,resizable,scrollbars=yes,status=1',
        replace = false,
    : 
        windowName?: string,
        windowFeatures?: string,
        replace?: boolean,
     = ) 
        this._windowOptions = [ 'about:blank', windowName, windowFeatures, replace ]
    

    // region create window

    private readonly _windowOptions: any[]
    private _window
    public get window() 
        if (!this.isOpened) 
            this._window = window.open(...this._windowOptions)
            this.appendCss()
            this.appendContainer()
        
        return this._window
    

    private appendCss() 
        const window: _window = this

        const parentStyleElements = Array.from(window.document.querySelectorAll(
            'link[rel="stylesheet"][href^="client/"], style',
        ))

        for (let i = 0; i < parentStyleElements.length; i++) 
            const parentStyleElement = parentStyleElements[i]
            let styleElement
            switch (parentStyleElement.tagName) 
                case 'LINK':
                    styleElement = _window.document.createElement('link')
                    styleElement.rel = 'stylesheet'
                    styleElement.href = (parentStyleElement as any).href
                    break
                case 'STYLE':
                    styleElement = _window.document.createElement('style')
                    styleElement.id = parentStyleElement.id
                    styleElement.innerhtml = parentStyleElement.innerHTML
                    break
                default:
                    throw new Error('Unexpected style element: ' + styleElement.tagName)
            
            _window.document.head.appendChild(styleElement)
        
    

    private appendContainer() 
        const window = this
        window.container = window.document.createElement('div')
        window.document.body.appendChild(window.container)
    

    // endregion

    // region attachComponent

    private _component
    public attachComponent(componentClass?, options?) 
        let _component = this
        if (_component) 
            _component.$destroy()
            this._component = _component = null
        

        if (!componentClass) 
            return
        

        const window = this
        _component = new componentClass(
            ...options,
            target: window.container,
        )
        this._component = _component

        window.addEventListener('beforeunload', () => 
            this.attachComponent()
        )

        return _component
    

    // endregion

    public get isOpened() 
        return this._window && !this._window.closed
    

    public focus() 
        if (this.isOpened) 
            this._window.focus()
        
    

    public destroy() 
        this.attachComponent()
        if (this.isOpened) 
            this._window.close()
            this._window = null
        
    

用法:

<script>
    import ComponentWindow from './ComponentWindow.js'
    import ComponentClass from './ComponentClass.svelte'
    import onMount, onDestroy from 'svelte'

    let componentWindow = new ComponentWindow()
    let component
    let value = 10

    onDestroy(() => componentWindow.destroy())

    $: if (component) component.$set( value )

    async function openComponentWindow() 
        if (componentWindow.isOpened) 
            componentWindow.focus()
            return
        

        component = await componentWindow.attachComponent(ComponentClass, 
            props: 
                value               
            
        )

        componentWindow.focus()
    
</script>

<button on:click="openComponentWindow">Open component Window</button><br>
<button on:click="() => value++">Change value</button>

【讨论】:

以上是关于使用附加的 svelte 组件并在相同的 Javascript 上下文中创建窗口的主要内容,如果未能解决你的问题,请参考以下文章

导入 svelte 组件,省略 .svelte 扩展名

Svelte.js - 如何使用新道具重新渲染子组件?

如何将 Svelte 应用程序用作另一个 Svelte 应用程序中的组件?

Svelte - 从组件脚本转发事件

从 HTML 字符串渲染 Svelte 组件

尝试让 nedb 导入 Svelte 组件