• nf-Press —— 在线文档也可以加载组件和编写代码


    如果帮助文档可以加载组件,那么在介绍的同时就可以运行演示demo,是不是很酷?
    如果可以在线修改运行代码,那么是不是更容易理解?

    上一篇 https://www.cnblogs.com/jyk/p/15994934.html 介绍了一下基本功能,这里介绍一下关于代码方面的功能。

    源码和演示

    https://gitee.com/nfpress/nf-press-edit

    https://nfpress.gitee.io/nf-press-edit/

    加载组件、运行组件

    Vue提供了一个可以动态加载组件的组件,component 和 defineAsyncComponent,我们可以用其实现我们想要的效果。

    注册组件

    我们可以参考动态路由的设置方式来注册组件:

    import { createRouter } from '/nf-press-edit'
    
    // 设置 axios 的 baseUrl
    const baseUrl = (document.location.host.includes('.gitee.io')) ?
      '/nf-press-edit/' :  '/'
    
    export default createRouter({
      baseUrl,
      components: {
        testComponent: () => import('../components/testCode.vue'),
        testComponent2: () => import('../components/testCode2.vue')
      }
    })
    
    
    • baseUrl: 基础路由,比如要发布到 gitee.com 上,就需要根据情况设计第一级路径。
    • components: 需要加载的组件集合,key-value形式,可以注册多个组件。

    这里的“路由”,只需要定义需要加载的组件即可,文档的导航路由不需要设置。

    存入全局状态

    nf-press 会把注册的组件存入state,便于使用:

    // 注册组件
    if (info.components) {
      if (Object.keys(info.components).length > 0) {
        const { comp } = state
        for(let key in components) {
          comp[key] = defineAsyncComponent(components[key])
        }
      }
    }
    

    加载组件

    然后做一个组件来加载指定的组件

    • template
    <Teleport :to="'#' + item.id" :disabled="moveDisabled">
        <el-card class="box-card">
            <template #header>
              <div class="card-header">
                <span>
                  {{item.title}} &nbsp; &nbsp; 
                </span>
              </div>
            </template>
            <component
              :is="$state.comp[item.key]"
              v-bind="item.props"
            >
            </component>
        </el-card>
    </Teleport>
    

    好吧,其实只需要使用 component 来加载,el-card 是为了外观不是太难看,Teleport 是为了可以“穿越”的文档的指定位置。

    组件定位

    如果组件只能在文档末尾加载,那么不是太好看,所以还需要一个“定位”功能,在文档里面指定加载位置。

    我们可以直接在 md 格式的文档里面加一个div,设置属性即可:

    <div
      id="test2"
      data-key="testComponent"
      data-props='{"msg":"div设置的属性"}'
      data-title="加载组件的测试"
    >
      加载中
    </div>
    
    • id:注册组件时对应的key,指定要加载的组件。
    • data-key: 组件的key,要加载哪个组件。
    • data-props: 组件需要的props属性,标准json格式。
    • data-title: 组件上面显示的标题。
    • 为什么用div?
      因为还不会做 markdown-it 的插件。
    • 为什么用 data-*?
      因为只有 id 和 data-* 被保留,其他属性都被“吃掉”了。

    这样在查看文档的时候,组件就会被加载到这个div里面。

    看看效果

    加载组件.png

    在线编写代码、修改代码、运行代码

    我知道有很多第三方网站提供了完整的在线写代码的功能,一些官方文档也在用,但是总感觉有点“距离感”。因为需要点个连接打开新窗口,不知道大家有没有体验过。

    对于一些简单的演示代码,还是觉得应该在一个页面内实现,所以自己做了一个简单的功能。

    defineAsyncComponent

    一开始用 script setup + defineAsyncComponent实现,在本地运行(开发模式)一切正常,但是发布后(生成环境)就出问题了,模板部分死活加载不上来。

    改为 setup方式,不行,尝试其他方法也没有搞定。但是又不想放弃这个功能,最后只好用 CDN的方式来实现。

    iframe + CDN

    搞不定问题怎么办?绕过去吧。于是开启了古老的 iframe。

        <iframe :src="src" style="width:100%;height:100%"></iframe>
    
      import {
        defineComponent,
        watch,
        ref
      } from 'vue'
      
      import config from '../config/index.js'
    
      export default defineComponent({
        name: 'el-doc-runcode',
        inheritAttrs: false,
        props: {
          code: {
            type: Object,
            default: () => {
              return {
                id: 1,
                js: '',
                template: '',
                style: ''
              }
            }
          },
          reload: Boolean
        },
        setup (props) {
    
          const src = ref('')
     
          // 用 Window 传递代码
          if (!window.__code) {
            window.__code = {}
          }
    
          // 重新加载代码
          watch(() => props.reload, () => {
            const id = props.code.id
            window.__code[id] = props.code
            src.value = `${config.baseUrl}runcode/index.html?id=${id}&rnd=${new Date().valueOf()}`
          }, {immediate: true})
          
          return {
            src
          }
        }
      })
    

    运行代码

    首先用CDN加载vue.js等需要的文件,然后设置 template 和代码即可。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <link rel="icon" href="/nfwt.ico" />
        <link href="https://unpkg.com/element-plus@1.2.0-beta.3/dist/index.css" rel="stylesheet"/>
        <script src="https://unpkg.com/vue@3.2.31/dist/vue.global.js"></script>
        <script src="https://unpkg.com/element-plus@2.1.4/dist/index.full.js"></script>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>运行代码</title>
      </head>
      <body>
        <div id="app"></div>
        <script >
          // 使用 eval编译js代码的模板
          const mysetup = `
            (function setup (props, ctx) {
              {{code}}
            })
          `
    
          // 接收参数
          const search = decodeURI(window.location.search)
          const id = search.split('&')[0].replace('?id=','')
          const code = top.window.__code[id]
          const temp = code.template
    
          const {
            defineComponent,
            defineAsyncComponent,
            ref,
            reactive,
            // 其他需要演示的功能
            nextTick
          } = Vue
          
          const App = {
            template: temp, // 设置模板
            setup (_props, _ctx) {
              const tmpJs = code.js // 获取js代码
              let fun = null // 转换后的函数
              try {
                if (tmpJs)
                  fun = eval(mysetup.replace('{{code}}', tmpJs)) // 用 eval 把 字符串 变成js代码
              } catch (error) {
                console.error('转换出现异常:', error)
              }
              const re = typeof fun === 'function' ? fun : () => {}
    
              return {
                ...re(_props, _ctx) // 运行函数,解构返回对象
              }
            }
          }
          const app = Vue.createApp(App)
          // 挂载需要的第三方插件。
          app.use(ElementPlus).mount("#app")
        </script>
      </body>
    </html>
    
    

    这样我们就可以愉快的在线写代码了。

    查看效果

    https://nfpress.gitee.io/nf-press-edit/1010/18_runcode

    在线代码.png

    设置代码的方式

    可以点右上角,切换为编辑模式,体验一下在线编写文档。好吧,有点简陋。

    在线代码设置方式.png

  • 相关阅读:
    Codeforces Round #803 (Div. 2) A-D && 组合数学 day14
    代码审计基础php_bugs
    d3力导向图
    WhiteHoleV0.7实战开发(一)>>你的下一个项目为何不可基于SpringCloud
    这一次,称重软件查询功能完整了
    【Linux内核源码剖析】进程原理及系统调用
    计算机图形学浅谈与学习心得
    MindSpore:model.train中的dataset_sink_mode该如何理解?
    如何设计一个机器学习驱动的策略?
    ceph 笔记
  • 原文地址:https://www.cnblogs.com/jyk/p/16034137.html