AntV 開發(fā) —— x6 圖編輯引擎

其實谓谦,只是想找個輪子

前序:

碎碎念:

  • 此文個人筆記咽安,官網(wǎng)文檔搬運
  • 因為官網(wǎng)對于vue結合Element-UI的示例就幾個,本意只是看中了人工智能建模 DAG 圖逃沿,但是這玩意demo是React,雖然核心的東西一樣幻锁,但是傻瓜式才方便凯亮,故特意按vue2寫了一次,沒用ts哄尔,用ts的其實轉(zhuǎn)換的成本不高
  • demo 待上傳中【ps:因demo已魔改假消,那應該是不可能上傳了 →_→ 】
  • v1版本過于繁瑣,想入手還是請看v2版本
  • x6 版本:1.32.8
  • x6-vue-shape 版本:1.4.0
  • composition-api 版本:1.7.0
  • vue 版本:2.6.11
  • vue-template-compiler 版本:2.6.11
  • Element-UI 版本:2.15.9

一:步驟:

1 —— 創(chuàng)建vue2項目:詳情請看 vue開發(fā) —— CLI(開發(fā)環(huán)境)搭建

2 —— 引入開發(fā)組件【Element-UI岭接、antv.x6】

npm i element-ui
npm install @antv/x6 
npm install @antv/x6-vue-shape
// 在vue2.x 若你引入x6-vue-shape富拗,目前這個版本是必須要的,因由在常見問題2
npm install @vue/composition-api --dev

2.1 —— 生成的項目目錄如下:

Demo 
├─ node_modules
├─ public
     ├─ favicon.ico
     └─ index.html
├─ src
     ├─ assets
           └─ logo.png
     ├─ components
           └─ HelloWorld.vue
     ├─ App.vue
     └─ main.js
├─ .browserslistrc
├─ .eslintrc.js
├─  babel.config.js
├─ package.json
├─ package-lock.json
└─ README.md

2.2 —— 修改生成的項目【編輯package.json鸣戴、編輯main.js啃沪、編輯App.vue、新增vue.config.js】

// package.json 
// 此處修改為解決常見問題一
 "dependencies": {
    "vue": "2.6.11"
  },
  "devDependencies": {
    "vue-template-compiler": "2.6.11"
  }
// main.js
import Vue from "vue";
import App from "./App.vue";
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.config.productionTip = false;
Vue.use(ElementUI);

new Vue({
  render: (h) => h(App),
}).$mount("#app");
// App.vue
<template>
  <div id="app"></div>
</template>

<script>
export default {
  name: 'App',
  components: {},
};
</script>

<style></style>
// vue.config.js
// 此處新增為解決常見問題二
module.exports = {
    runtimeCompiler: true
}

2.3 —— 開啟搬運工生涯【創(chuàng)建畫布窄锅、創(chuàng)建節(jié)點模板创千、新增節(jié)點、新增多個節(jié)點、定時改變多個節(jié)點狀態(tài)追驴、達成官網(wǎng)效果械哟、搭配Element-UI、收工】

官網(wǎng)效果圖

2.3.1 —— 創(chuàng)建畫布

// App.vue
<template>
  <div id="app"></div>
</template>

<script>
import { Graph } from '@antv/x6';
import '@antv/x6-vue-shape'

let graph = null;

export default {
  name: 'App',
  components: {},
  mounted() {
    graph = new Graph({
      container: document.getElementById('app'),
      grid: true,
      autoResize: true,
    });
  },
};
</script>

<style>
#app {
  width: 100%;
  min-height: 500px;
}
</style>
創(chuàng)建畫布

2.3.2 —— 創(chuàng)建節(jié)點模板

// components/NodeTemplate.vue
<template>
  <div :class="nodeClass" class="node">
    <img :src="logo" />
    <span class="label">{{ label }}</span>
    <span class="status">
      <img :src="statusImg" v-if="statusImg" />
    </span>
  </div>
</template>

<script>
export default {
  inject: ['getGraph', 'getNode'],
  data() {
    return {
      status: '',
      label: '',
      statusImg: '',
      logo: '',
    };
  },

  methods: {
    mapper(source, target) {
      for (let key in target) {
        target[key] = source?.[key] ?? target[key];
      }
    },
  },

  created() {
    let node = this.getNode();
    // 初始化數(shù)據(jù)綁定
    this.mapper(node.data, this.$data);
    console.info(node);
    // 節(jié)點數(shù)據(jù)變化監(jiān)聽殿雪,從而綁定數(shù)據(jù)
    node.on('change:data', ({ current }) => this.mapper(current, this.$data));
  },

  computed: {
    nodeClass: function () {
      let clazz = {};
      if (this.status) clazz[this.status] = true;
      return clazz;
    },
  },
};
</script>

2.3.3 —— 新增節(jié)點

// App.vue
<template>
  <div id="app"></div>
</template>

<script>
import { Graph } from '@antv/x6';
import '@antv/x6-vue-shape';
import nodetemplate from '@/components/NodeTemplate';

let graph = null;

export default {
  name: 'App',

  data() {
    return {
      node: {
        id: '1',
        shape: 'vue-shape',
        component: 'nodetemplate',
        width: 180,
        height: 36,
        x: 290,
        y: 110,
        data: {
          label: '讀數(shù)據(jù)',
          status: 'success',
          statusImg: '',
          logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ',
        },
        ports: {
          groups: {
            top: {
              position: 'top',
              attrs: {
                circle: {
                  r: 4,
                  magnet: true,
                  stroke: '#C2C8D5',
                  strokeWidth: 1,
                  fill: '#fff',
                },
              },
            },
            bottom: {
              position: 'bottom',
              attrs: {
                circle: {
                  r: 4,
                  magnet: true,
                  stroke: '#C2C8D5',
                  strokeWidth: 1,
                  fill: '#fff',
                },
              },
            },
          },
          items: [
            {
              id: '1-1',
              group: 'bottom',
            },
          ],
        },
      },
    };
  },

  mounted() {
    graph = new Graph({
      container: document.getElementById('app'),
      grid: true,
      autoResize: true,
    });
    // 注冊 nodeTemplate
    Graph.registerVueComponent(
      'nodetemplate',
      {
        template: `<nodetemplate />`,
        components: {
          nodetemplate,
        },
      },
      true
    );

    graph.addNode(this.node);

    graph.centerContent();
  },
};
</script>

<style>
#app {
  width: 100%;
  min-height: 500px;
}

.node {
  display: flex;
  align-items: center;
  width: 100%;
  height: 100%;
  background-color: #fff;
  border: 1px solid #c2c8d5;
  border-left: 4px solid #5f95ff;
  border-radius: 4px;
  box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06);
}
.node img {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  margin-left: 8px;
}
.node .label {
  display: inline-block;
  flex-shrink: 0;
  width: 104px;
  margin-left: 8px;
  color: #666;
  font-size: 12px;
}
.node .status {
  flex-shrink: 0;
}
.node.success {
  border-left: 4px solid #52c41a;
}
.node.failed {
  border-left: 4px solid #ff4d4f;
}
.node.running .status img {
  animation: spin 1s linear infinite;
}
.x6-node-selected .node {
  border-color: #1890ff;
  border-radius: 2px;
  box-shadow: 0 0 0 4px #d4e8fe;
}
.x6-node-selected .node.success {
  border-color: #52c41a;
  border-radius: 2px;
  box-shadow: 0 0 0 4px #ccecc0;
}
.x6-node-selected .node.failed {
  border-color: #ff4d4f;
  border-radius: 2px;
  box-shadow: 0 0 0 4px #fedcdc;
}
.x6-edge:hover path:nth-child(2) {
  stroke: #1890ff;
  stroke-width: 1px;
}

.x6-edge-selected path:nth-child(2) {
  stroke: #1890ff;
  stroke-width: 1.5px !important;
}

@keyframes running-line {
  to {
    stroke-dashoffset: -1000;
  }
}
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>
單個節(jié)點

2.3.4 —— 新增多個節(jié)點

// App.vue
// 因一些固定數(shù)據(jù)過于龐大暇咆,省略的部分請復制上一節(jié)的代碼數(shù)據(jù)
<template>
  <!--  -->
  <div id="app"></div>
</template>

<script>
import { Graph } from '@antv/x6';
import '@antv/x6-vue-shape';
import nodetemplate from '@/components/NodeTemplate';

let graph = null;

export default {
  name: 'App',

  data() {
    return {
      nodeData: [
        {
          id: '1',
          shape: 'vue-shape',
          x: 290,
          y: 110,
          data: {
            label: '讀數(shù)據(jù)',
            status: 'success',
          },
          ports: [
            {
              id: '1-1',
              group: 'bottom',
            },
          ],
        },
        {
          id: '2',
          shape: 'vue-shape',
          x: 290,
          y: 225,
          data: {
            label: '邏輯回歸',
            status: 'success',
          },
          ports: [
            {
              id: '2-1',
              group: 'top',
            },
            {
              id: '2-2',
              group: 'bottom',
            },
            {
              id: '2-3',
              group: 'bottom',
            },
          ],
        },
        {
          id: '3',
          shape: 'vue-shape',
          x: 170,
          y: 350,
          data: {
            label: '模型預測',
            status: 'success',
          },
          ports: [
            {
              id: '3-1',
              group: 'top',
            },
            {
              id: '3-2',
              group: 'bottom',
            },
          ],
        },
        {
          id: '4',
          shape: 'vue-shape',
          x: 450,
          y: 350,
          data: {
            label: '讀取參數(shù)',
            status: 'success',
          },
          ports: [
            {
              id: '4-1',
              group: 'top',
            },
            {
              id: '4-2',
              group: 'bottom',
            },
          ],
        },
        {
          id: '5',
          shape: 'dag-edge',
          source: {
            cell: '1',
            port: '1-1',
          },
          target: {
            cell: '2',
            port: '2-1',
          },
          zIndex: 0,
        },
        {
          id: '6',
          shape: 'dag-edge',
          source: {
            cell: '2',
            port: '2-2',
          },
          target: {
            cell: '3',
            port: '3-1',
          },
          zIndex: 0,
        },
        {
          id: '7',
          shape: 'dag-edge',
          source: {
            cell: '2',
            port: '2-3',
          },
          target: {
            cell: '4',
            port: '4-1',
          },
          zIndex: 0,
        },
      ],
      nodeImage: {
        logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ',
        success:
          'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ',
        failed:
          'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ',
        running:
          'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*t8fURKfgSOgAAAAAAAAAAAAAARQnAQ',
      },
    };
  },

  methods: {
    init(data) {
      let cells = [];
      data.forEach((item) => {
        if (item.shape === 'vue-shape') {
          item.width = 180;
          item.height = 36;
          item.component = 'nodetemplate';
          item.data.logo = this.nodeImage.logo;
          item.data.statusImg = '';
          if (item.ports)
            item.ports = {
              groups: {
                top: {
                  position: 'top',
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: '#C2C8D5',
                      strokeWidth: 1,
                      fill: '#fff',
                    },
                  },
                },
                bottom: {
                  position: 'bottom',
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: '#C2C8D5',
                      strokeWidth: 1,
                      fill: '#fff',
                    },
                  },
                },
              },
              items: item.ports,
            };
          cells.push(graph.createNode(item));
        } else {
          cells.push(graph.createEdge(item));
        }
      });
      graph.resetCells(cells);
    },
  },

  mounted() {
    graph = new Graph({
      container: document.getElementById('app'),
      grid: true,
      autoResize: true,
    });
    // 注冊 nodeTemplate
    Graph.registerVueComponent(
      'nodetemplate',
      {
        template: `<nodetemplate />`,
        components: {
          nodetemplate,
        },
      },
      true
    );

    // 注冊 nodeTemplate 的鏈接線
    Graph.registerEdge(
      'dag-edge',
      {
        inherit: 'edge',
        attrs: {
          line: {
            stroke: '#C2C8D5',
            strokeWidth: 1,
            targetMarker: null,
          },
        },
      },
      true
    );

    this.init(this.nodeData);

    graph.centerContent();
  },
};
</script>

<style>
/* 和上一節(jié)一樣  */
</style>
多個節(jié)點

2.3.5 —— 定時改變多個節(jié)點狀態(tài)

// App.vue
// 因一些固定數(shù)據(jù)過于龐大,省略的部分請復制上一節(jié)的代碼數(shù)據(jù)
<template>
  <div id="app"></div>
</template>

<script>
import { Graph } from '@antv/x6';
import '@antv/x6-vue-shape';
import nodetemplate from '@/components/NodeTemplate';

let graph = null;

export default {
  name: 'App',

  data() {
    return {
     // 和上節(jié)一樣
      nodeStatusList: [
        [
          {
            id: '1',
            status: 'running',
          },
          {
            id: '2',
            status: 'default',
          },
          {
            id: '3',
            status: 'default',
          },
          {
            id: '4',
            status: 'default',
          },
        ],
        [
          {
            id: '1',
            status: 'success',
          },
          {
            id: '2',
            status: 'running',
          },
          {
            id: '3',
            status: 'default',
          },
          {
            id: '4',
            status: 'default',
          },
        ],
        [
          {
            id: '1',
            status: 'success',
          },
          {
            id: '2',
            status: 'success',
          },
          {
            id: '3',
            status: 'running',
          },
          {
            id: '4',
            status: 'running',
          },
        ],
        [
          {
            id: '1',
            status: 'success',
          },
          {
            id: '2',
            status: 'success',
          },
          {
            id: '3',
            status: 'success',
          },
          {
            id: '4',
            status: 'failed',
          },
        ],
      ],
    };
  },

  methods: {
  // 和上節(jié)一樣
    showNodeStatus(statusList) {
      let status = statusList.shift();
      status?.forEach((item) => {
        let { id, status } = item;
        let node = graph.getCellById(id);
        let data = node.getData();
        node.setData({
          ...data,
          status: status,
          statusImg: this.nodeImage[status],
        });
      });
      setTimeout(() => {
        this.showNodeStatus(statusList);
      }, 3000);
    },
  },

  mounted() {
    // 和上節(jié)一樣
    this.init(this.nodeData);
    this.showNodeStatus(this.nodeStatusList);
    graph.centerContent();
  },
};
</script>

<style>
  // 和上節(jié)一樣
</style>

定時改變多個節(jié)點狀態(tài)

2.3.6 —— 添加屬性達成官網(wǎng)效果

// App.vue
// 因一些固定數(shù)據(jù)過于龐大冠摄,省略的部分請復制上一節(jié)的代碼數(shù)據(jù)
<template>
  <div id="app"></div>
</template>

<script>
import { Graph, Path } from '@antv/x6';
import '@antv/x6-vue-shape';
import nodetemplate from '@/components/NodeTemplate';

let graph = null;

export default {
  name: 'App',

  data() {
    return {
       // 和上節(jié)一樣
    };
  },

  methods: {
    // 和上節(jié)一樣
  },

  mounted() {
    graph = new Graph({
      container: document.getElementById('app'),
      grid: true,
      autoResize: true,
      panning: {
        enabled: true,
        eventTypes: ['leftMouseDown', 'mouseWheel'],
      },
      mousewheel: {
        enabled: true,
        modifiers: 'ctrl',
        factor: 1.1,
        maxScale: 1.5,
        minScale: 0.5,
      },
      highlighting: {
        magnetAdsorbed: {
          name: 'stroke',
          args: {
            attrs: {
              fill: '#fff',
              stroke: '#31d0c6',
              strokeWidth: 4,
            },
          },
        },
      },
      connecting: {
        snap: true,
        allowBlank: false,
        allowLoop: false,
        highlight: true,
        connector: 'algo-connector',
        connectionPoint: 'anchor',
        anchor: 'center',
        validateMagnet({ magnet }) {
          return magnet.getAttribute('port-group') !== 'top';
        },
        createEdge() {
          return graph.createEdge({
            shape: 'dag-edge',
            attrs: {
              line: {
                strokeDasharray: '5 5',
              },
            },
            zIndex: -1,
          });
        },
      },
      selecting: {
        enabled: true,
        multiple: true,
        rubberEdge: true,
        rubberNode: true,
        modifiers: 'shift',
        rubberband: true,
      },
    });
    // 注冊 nodeTemplate
    Graph.registerVueComponent(
      'nodetemplate',
      {
        template: `<nodetemplate />`,
        components: {
          nodetemplate,
        },
      },
      true
    );

    // 注冊 nodeTemplate 的鏈接線
    Graph.registerEdge(
      'dag-edge',
      {
        inherit: 'edge',
        attrs: {
          line: {
            stroke: '#C2C8D5',
            strokeWidth: 1,
            targetMarker: null,
          },
        },
      },
      true
    );

    // 注冊 nodeTemplate 的鏈接之間的線樣式
    Graph.registerConnector(
      'algo-connector',
      (s, e) => {
        const offset = 4;
        const deltaY = Math.abs(e.y - s.y);
        const control = Math.floor((deltaY / 3) * 2);

        const v1 = { x: s.x, y: s.y + offset + control };
        const v2 = { x: e.x, y: e.y - offset - control };

        return Path.normalize(
          `M ${s.x} ${s.y}
       L ${s.x} ${s.y + offset}
       C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
       L ${e.x} ${e.y}
      `
        );
      },
      true
    );

    // 控制線鏈接時的樣式
    graph.on('edge:connected', ({ edge }) => {
      edge.attr({
        line: {
          strokeDasharray: '',
        },
      });
    });

    // 控制節(jié)點數(shù)據(jù)變更時線的樣式
    graph.on('node:change:data', ({ node }) => {
      let edges = graph.getIncomingEdges(node);
      let { status } = node.getData();
      edges?.forEach((edge) => {
        if (status === 'running') {
          edge.attr('line/strokeDasharray', 5);
          edge.attr('line/style/animation', 'running-line 30s infinite linear');
        } else {
          edge.attr('line/strokeDasharray', '');
          edge.attr('line/style/animation', '');
        }
      });
    });

    this.init(this.nodeData);
    this.showNodeStatus(this.nodeStatusList);
    graph.centerContent();
  },
};
</script>

<style>
    // 和上節(jié)一樣
</style>

達成效果

2.3.6 —— 廢話了這么多糯崎,就是和一開始引入的Element-UI無關,客官莫急河泳,菜來也

// components/EleTemplate.vue
<template>
  <el-alert title="瀟風劍易水" type="warning" close-text="賽雷"> </el-alert>
</template>
// App.vue
import eletemplate from '@/components/EleTemplate';
   // 注冊 eletemplate
    Graph.registerVueComponent(
      'eletemplate',
      {
        template: `<eletemplate />`,
        components: {
          eletemplate,
        },
      },
      true
    );

    graph.addNode({
      id: '1',
      shape: 'vue-shape',
      component: 'eletemplate',
      width: 180,
      height: 36,
      x: 290,
      y: 110,
    });
    
    graph.centerContent();
搭配Element-UI效果圖

2.3.7 —— 收工沃呢,翻歸賣豉油

二:常見問題:

1 —— 運行時編譯報錯如下:

Vue packages version mismatch:

- vue@2.6.14 (D:\x6-dag\node_modules\vue\dist\vue.runtime.common.js)
- vue-template-compiler@2.7.5 (D:\x6-dag\node_modules\vue-template-compiler\package.json)

This may cause things to work incorrectly. Make sure to use the same version for both.
If you are using vue-loader@>=10.0, simply update vue-template-compiler.
If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump vue-template-compiler to the latest.

Error: 

Vue packages version mismatch:

- vue@2.6.14 (D:\x6-dag\node_modules\vue\dist\vue.runtime.common.js)
- vue-template-compiler@2.7.5 (D:\x6-dag\node_modules\vue-template-compiler\package.json)

This may cause things to work incorrectly. Make sure to use the same version for both.
If you are using vue-loader@>=10.0, simply update vue-template-compiler.
If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump vue-template-compiler to the latest.

解決方法:

// package.json
// 僅修改 package.json的dependencies節(jié)點的vue版本和devDependencies節(jié)點的vue-template-compiler版本,
// 均需要去掉^拆挥,保持2者的版本一致薄霜,重新npm i 再啟動
 "dependencies": {
    "vue": "2.6.11"
  },
  "devDependencies": {
    "vue-template-compiler": "2.6.11"
  }

2 —— 運行時瀏覽器報錯如下:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

解決方法:

// 項目根目錄創(chuàng)建vue.config.js并添加如下內(nèi)容
// vue.config.js
module.exports = {
    runtimeCompiler: true
}

2 —— 編譯時報錯如下:

This dependency was not found:

* @vue/composition-api in ./node_modules/vue-demi/lib/index.esm.js

To install it, you can run: npm install --save @vue/composition-api

解決方法:

npm install --save @vue/composition-api

3 —— 當卸載composition-api在運行時不會報錯正常運行,問題2不會在提示纸兔,同時缺失這個惰瓜,導致vue-demi的判斷會一直是isVue2為false,isVue3為true汉矿,從而當渲染vue模板時會一直走vue3的方法崎坊,個人認為vue-demi應該去檢查運行中的vue對象,由vue對象的某些vue3特性來判斷是否走vue3內(nèi)容洲拇,vue-demi是x6-vue-shape的依賴非本項目的依賴:

4 —— 拖曳創(chuàng)建的節(jié)點奈揍,使用node.setData不觸發(fā)綁定的change:data事件,但是實際上打印出來的node的data的確改動了赋续,在數(shù)據(jù)為null的情況下男翰,這個的確生效,但是并不是通過change:data事件觸發(fā)的纽乱,暫時從源碼也沒法看出錯誤點蛾绎,還沒排除新版本是否已修復,源碼看的新版本的鸦列,后續(xù)會更新此版本寫的demo:setData為updateData

問題因由:并不是updateData還是setData的沒生效的問題或者拖曳生成或者版本的問題(源碼版本搜索里面也可以找到和版本有一點點的關系租冠,畢竟之前是沒加這個相同就不更新的原則),而是通過node.getData()直接操作了數(shù)據(jù)薯嗤,導致setData比較了一致就不更新肺稀,其實這也是可以后期修改下,直接通知vue組件更新的同時更新node數(shù)據(jù)应民,脫離這種setData的內(nèi)置方法,但是僅僅是一個權宜之計,也是自己寫的時候太過于不顧前后導致的低級bug
解決方法:
不要直接操縱node的data數(shù)據(jù)诲锹,需要通過其自身提供的方法比如setData或者updateData繁仁,但是在考慮到x6它自身這種結合vue這種框架的事件綁定脫離了這種框架定義的東西時,其實可以適當改造归园,讓x6更符合vue的寫法黄虱,而不是遵循它自身這種數(shù)據(jù)變化的寫法

5 —— 關于html元素如何擁有鏈接樁的功能請看另一篇博文,待更庸诱,因由為現(xiàn)成的鏈接樁自定義過于麻煩捻浦,且那些算法排定位,其實對于html元素固有的元素布局來看并不是很適用桥爽,除非是大規(guī)模改進用自定義朱灿,那無疑回到了原始的畫圖狀態(tài),增加了復雜性钠四,故簡單而為是用現(xiàn)有的元素進行劃分為鏈接樁盗扒,但是這無疑脫離了x6所管理鏈接樁的機制,后續(xù)會看看要不要對接上這種機制缀去,還是它那個自定義鏈接樁就是這種玩法侣灶,我只不過在繞個圈,但是文檔關于這部分很模糊缕碎,畢竟脫離了它ponts定義的機制褥影,而是添加了成為鏈接樁的特征去實現(xiàn)鏈接狀功能的做法

6 —— 關于右鍵菜單自定義問題,因為菜單這一塊咏雌,很不好的是這玩意是要引入React組件凡怎,其實為何會有問題5和6,明顯就是依賴過重处嫌,抽象層不應該牽扯太多具體的框架栅贴,即使明明知道這玩意靠監(jiān)聽事件就能處理右鍵菜單的問題,這一塊的自定義也是基于html元素來完成熏迹,會有新的一篇博文來說明這個檐薯,待更

三:吹水:

1 首先這次的排版不是很好,畢竟本意是要循序漸進注暗,和狀態(tài)機一樣坛缕,一步步到達終點,總感覺這里的makadown定制化太少了捆昏,不能盡情表達一些動態(tài)效果

2 然后是x6-vue-shape這個感覺只是意思下而已赚楚,沒有x6-react-shape和React的配合好,畢竟主打的方向就不是vue的骗卜,這從數(shù)據(jù)的傳遞到組件可以看出宠页,單單用注入方法是不太好的左胞,畢竟你的data變化還需要監(jiān)聽一層,何不在寫template時傳參prop举户,由prop去更新里面的狀態(tài)或者附加到data烤宙,通過父組件通知子組件更新的方式,減少使用者對這一層的處理俭嘁,然后就是注冊vue模板時躺枕,那些屬性不能像普通那些傳參進去,必須要addNode帶進去的才符合供填,這明顯與結構和數(shù)據(jù)分離不相符合拐云,我覺得在addNode時,應該是各自變化的數(shù)據(jù)近她,而不是一些基礎數(shù)據(jù)叉瘩,然后節(jié)點命名那一塊需要注意,大小駝峰寫法都不行泄私,僅首字母大寫和全小寫房揭,因為時間有限我暫時都用全小寫去規(guī)避組件名稱找不到問題,還有那個ports節(jié)點晌端,官網(wǎng)的數(shù)據(jù)為數(shù)組形式捅暴,但是因為注冊vue模板這個玩意加了ports是沒效的,為了省事咧纠,只能是addNode時進行改變ports結構進行處理了蓬痒,當然也可以改節(jié)點數(shù)據(jù),其實這短短的幾個效果就需要這么多的東西支撐漆羔,還是不太好玩梧奢,后續(xù)會自行改寫,以便適應生產(chǎn)

3 要是沒瀏覽器的兼容考慮演痒,還是用vue3去搭建亲轨,而且用上ts,讓繼承來處理這種很繞的復用關系鸟顺,畢竟寫出來是給人看的

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惦蚊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子讯嫂,更是在濱河造成了極大的恐慌蹦锋,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欧芽,死亡現(xiàn)場離奇詭異莉掂,居然都是意外死亡,警方通過查閱死者的電腦和手機千扔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門憎妙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來库正,“玉大人,你說我怎么就攤上這事尚氛【髡铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵阅嘶,是天一觀的道長。 經(jīng)常有香客問我载迄,道長讯柔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任护昧,我火速辦了婚禮魂迄,結果婚禮上,老公的妹妹穿的比我還像新娘惋耙。我一直安慰自己捣炬,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布绽榛。 她就那樣靜靜地躺著湿酸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灭美。 梳的紋絲不亂的頭發(fā)上推溃,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音届腐,去河邊找鬼铁坎。 笑死,一個胖子當著我的面吹牛犁苏,可吹牛的內(nèi)容都是我干的硬萍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼围详,長吁一口氣:“原來是場噩夢啊……” “哼朴乖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起短曾,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤寒砖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嫉拐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哩都,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年婉徘,在試婚紗的時候發(fā)現(xiàn)自己被綠了漠嵌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咐汞。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖儒鹿,靈堂內(nèi)的尸體忽然破棺而出化撕,到底是詐尸還是另有隱情,我是刑警寧澤约炎,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布植阴,位于F島的核電站,受9級特大地震影響圾浅,放射性物質(zhì)發(fā)生泄漏掠手。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一狸捕、第九天 我趴在偏房一處隱蔽的房頂上張望喷鸽。 院中可真熱鬧,春花似錦灸拍、人聲如沸做祝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽混槐。三九已至,卻和暖如春纤房,著一層夾襖步出監(jiān)牢的瞬間纵隔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工炮姨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捌刮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓舒岸,卻偏偏與公主長得像绅作,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蛾派,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容