在使用vue的过程中,常常出现这种情况:一个组件在多处使用,各处使用的逻辑却有一些差异,例如A处使用了组件a,B处也使用的组件a;A处使用时a初始化调用方法和B处使用初始化调用方法不同,之前我的做法是通过props传入数据告诉a 是A使用了还是B使用了。随着软件复杂度的增加会不断在组件a中添加这样的代码。到这也许就发现久而久之a已经变得难以维护了。

用过vue的人应该对mixin都很熟悉,vue就是通过mixin模式来实现代码复用的,使用vue的mixin的时候通常的做法是把通用的方法抽取,然后在需要用来某个方法的时候进行混入,我见很多人用vue的mixin基本上都是混入方法,或者极少的数据(常常会混入不需要的方法或数据之类的,这算是一个弊端)。

有时候在面对上文提到的AB引入组件a的问题我们改变一下思路其实没什么不好。

下面用实际项目中用到的代码稍加修改作为展示

先定义一个组件 baseFrame

<template>
    <iframe :src="_getSrc()" :srcdoc="_getDocSrc()" frameborder="0" referrerpolicy="strict-origin"></iframe>
</template>

<script>
import { getUUID } from '@/libs/utils'
export default {
    name:'baseFrame',
    data() {
        return {
            id: `frameInstant${getUUID()}`,
            doc: null

        }
    },

    created() {
        if (!window.MxHtmlFrames) {
            window.MxHtmlFrames = {}
        }
        window.MxHtmlFrames[this.id] = this
    },
    beforeDestroy() {
        this.doc = null
        delete window.MxHtmlFrames[this.id]
    },
    methods: {
        _frameLoad(doc) {
            this.doc = doc
            this.$emit('load', doc)
        },
        _getDocSrc() {
            return this.getTempHtml()
        },
        _getSrc() {
            return '' +
                `javascript:void(function(){` +
                `document.open();` +
                (document.domain != location.hostname ? `document.domain="${document.domain}";` : '') +
                `document.write('${this.getTempHtml()}');` +
                `document.close();` +
                `}())`
        },
        _getCommonScript() {
            return `
                <script id="_initialScript">
                    setTimeout(function(){
                        var instant=window.parent.MxHtmlFrames["${this.id}"];
                        instant._frameLoad(document);
                    },0);
                    var _tmpScript = document.getElementById("_initialScript");
                    _tmpScript.parentNode.removeChild(_tmpScript);
                <\/script>
            `
        },
        getTempHtml() {
            throw new Error('请重写getTempHtml方法')
        }
    }
}
</script>


如果直接使用这个组件会报错,告诉你要重写getTempHtml方法,那么怎么用尼?

下面是mailFrame的示例

// mailFrame.vue
<script>
import baseFrame from './baseFrame'
export default {
    name: 'mailFrame',
    mixins: [baseFrame], // 也可以用 extends:baseFrame
    data() { return {} },
    methods: {
        showContent(content = '') {
            
            content = content.replace(/<a /igm, "<a target='_blank' ")
            
            this.doc.body.innerHTML = content
            this.scrollTop()
        },
        scrollTop(top = 0) {
            this.doc.documentElement.scrollTop = 0
        },
        getTempHtml() {
            return '' +
                `<!DOCTYPE html>` +
                `<html>` +
                `<head>` +
                `<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">` +
                `<style>` +
                `body {` +
                `font-family: -apple-system, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", Arial, sans-serif;` +
                `font-size: 14px;` +
                `-webkit-font-smoothing: antialiased;` +
                `}` +
                `::-webkit-scrollbar {` +
                `width: 5px;` +
                `height: 5px;` +
                `}` +
                `::-webkit-scrollbar-thumb {` +
                `background-color: #999;-webkit-border-radius: 5px;border-radius: 5px;` +
                `}` +
                `::-webkit-scrollbar-thumb:vertical:hover {` +
                `background-color: #666;` +
                `}` +
                `::-webkit-scrollbar-thumb:vertical:active {` +
                `background-color: #333;` +
                `}` +
                `::-webkit-scrollbar-button {` +
                `display: none;` +
                `}` +
                `::-webkit-scrollbar-track {` +
                `background-color: #f1f1f1;` +
                `}` +
                `</style>` +
                `<script>` +
                `function openlink(e, _this) {` +
                `if (e && e.preventDefault) {` +
                ` e.preventDefault()` +
                `} else {` +
                `window.event.returnValue = false` +
                `}` +
                `parent.window.openlink(_this.getAttribute("href"))` +
                `}` +
                `<\/script>` +
                `</head >` +
                `<body id="mailContent">${this._getCommonScript()}</body>` +
                `</html >`
        }
    },
    components: {}
}
</script>

我们甚至可以覆盖template内容 只需要在mailFrame上面加如下代码即可

<template>
    <iframe  :srcdoc="_getDocSrc()" frameborder="0" referrerpolicy="unfafe-url"></iframe>
</template>  

我们重新了模板的代码,移除和修改了一些属性。

这时候有一个用作其他作用的iframe需要被用到时还可以在不改变原有代码的基础上继续扩展,在一定程度上遵循开闭原则。

// previewFrame.js
import baseFrame from './baseFrame'

export default {
    name: 'previewFrame',
    data() {
        return {}
    },
    mixins: [baseFrame],
    methods: {
        addContent(content = '') {
            this.doc.body.innerHTML = content.replace(/<a /igm, "<a target='_blank' ")
        },
        getTempHtml() {
            return '' +
                `<!DOCTYPE html>` +
                `<html>` +
                `<head>` +
                `<meta charset="utf-8">` +
                `<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">` +
                `<style>` +
                `body{` +
                `font-family: PingFang SC, Lantinghei SC, Helvetica Neue, Helvetica, Arial, STHeitiSC-Light, simsun, WenQuanYi Zen Hei, WenQuanYi Micro Hei, "sans-serif";` +
                `max-width: 800px;` +
                `padding: 0;` +
                `margin: 0;` +
                `}` +
                `body * {` +
                `color: #626066;` +
                `max-width: 800px;` +
                `font-size: 15px;` +
                `line-height: 30px;` +
                `}` +
                `</style>` +
                `</head>` +
                `<body>${this._getCommonScript()}</body>` +
                `</html>`
        }
    }
}