nf-Press —— 線上文件也可以載入元件和編寫程式碼

金色海洋(jyk)發表於2022-03-21

如果幫助文件可以載入元件,那麼在介紹的同時就可以執行演示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

相關文章