Vue2之在线运行组件

Vue2之组件在线编辑器

  • 通过props、events 实现父子组件通信
  • 通过ref属性获取组件实例
src             
├─ components     
│  ├─ edit.vue  
│  └─ show.vue  
├─ App.vue  

实现效果

run

App.vue


<template>
  <div id="app">
    <Edit @input="handleInput" @run="handleRun"></Edit>
    <Show :code="code" ref="show"></Show>
  </div>
</template>

<script>
import Edit from "./components/edit.vue";
import Show from "./components/show.vue";
export default {
  data() {
    return {
      code: "",
    };
  },
  components: {
    Edit,
    Show,
  },
  created() {},
  mounted() {},
  methods: {
    handleInput(value) {
      this.code = value;
    },
    handleRun() {
      this.$refs.show.run();
    },
  },
};
</script>

<style  lang="css">
* {
  margin: 0;
  padding: 0;
}
html,
body,
#app {
  height: 100vh;
  width: 100%;
  margin: 0;
}
#app {
  display: flex;
  justify-content: space-around;
}
</style>

edit.vue

<template>
  <div class="edit">
    <div class="edit-list">
      <button @click="$emit('run')">运行</button>
      <button>清空</button>
    </div>
    <div class="edit-box">
      <textarea :value="code" @input="handleInput"></textarea>

      <!--对于原生标签 v-model 并不是在任何时候都等于:value +@input ,内部会对输入法做处理 -->
      <!-- <textarea v-model="code"></textarea> -->
    </div>
  </div>

</template>

<script>
export default {
  data() {
    return {
      code: "",
    };
  },
  created() {},
  mounted() {},
  methods: {
    handleInput(e) {
      this.code = e.target.value;
      this.$emit("input", this.code);
    },
  },
};
</script>

<style scoped lang="css">
.edit {
  width: 45%;
}
.edit .edit-list {
  padding: 20px;
  position: relative;
}
.edit .edit-list button {
  width: 150px;
  height: 40px;
  margin-right: 10px;
  background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
  border-radius: 30px;
  border: 1px solid #ccc;
  outline: none;
  color: #fff;
  cursor: pointer;
  background-size: 400%;
}
button:hover {
  animation: animate 8s linear infinite;
}
button::before {
  content: "";
  inset: -5px;
  z-index: -1;
  background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
  border-radius: 40px;
  background-size: 400%;
  opacity: 0;
}
button:hover::before {
  filter: blur(20px);
  opacity: 1;
  animation: animate 8s linear infinite;
}

.edit-box textarea {
  border: 1px solid #ccc;
  outline: none;
  width: 100%;
  height: calc(100vh - 200px);
}

@keyframes animate {
  form {
    background-position: 0%;
  }
  to {
    background-position: 400%;
  }
}
</style>

show.vue


<template>
  <div class="show">
    <h2>代码运行结果</h2>
    <div class="result-box" ref="show">

    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  props: {
    code: {
      type: String,
      default: "",
    },
  },
  created() {},
  mounted() {},
  methods: {
    /** 获取模板内容 */
    getSource(type) {
      // 开始标签
      let reg = new RegExp(`<${type}[^>]*>`);
      let code = this.code;
      let matched = code.match(reg);
      if (matched) {
        let tag = matched[0];
        return code.slice(
          code.indexOf(tag) + tag.length,
          code.lastIndexOf(`</${type}>`)
        );
      }
      return "";
    },
    run() {
      // 获取模板中的内容 js css
      const template = this.getSource("template");
      const script = this.getSource("script").replace(
        /export default/,
        "return"
      );
      const style = this.getSource("style");
      // 动态加载组件
      let component = {};
      if (script) {
        component = new Function(script)();
      }
      if (template) {
        component.template = template;
        // $mount进行挂载 this.$options._base 就是大Vue
        // 解析出对应的内容,采用Vue.extend构造Vue组件,手动挂载到对应的元素上.当ref属性指定在DOM身上时,代表的是真实的DOM元素
        let instance = new (this.$options._base.extend(component))();
        // instance.$mount().$el在内存中进行挂载,挂载的结果放到$el上
        this.$refs.show.appendChild(instance.$mount().$el);
      }
      if (style) {
        let element = document.createElement("style");
        element.type = "text/css";
        element.innerText = style;
        document.body.appendChild(element);
      }
    },
  },
};
</script>

<style scoped lang="css">
.show {
  width: 45%;
}
.show h2 {
  padding: 20px;
  height: 80px;
  box-sizing: border-box;
}
.result-box {
  width: 100%;
  height: calc(100vh - 200px);
  border: 1px solid #ccc;
  padding: 20px;
  box-sizing: border-box;
}
</style>

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值