最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

concent漸進(jìn)式重構(gòu)react應(yīng)用使用詳解

 更新時(shí)間:2022年11月07日 12:00:41   作者:騰訊新聞前端團(tuán)隊(duì)  
這篇文章主要為大家介紹了concent漸進(jìn)式重構(gòu)react應(yīng)用的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

傳統(tǒng)的redux項(xiàng)目里,我們寫在reducer里的狀態(tài)一定是要打通到store的,我們一開始就要規(guī)劃好state、reducer等定義,有沒有什么方法,既能夠快速享受ui與邏輯分離的福利,又不需要照本宣科的從條條框框開始呢?本文從普通的react寫法開始,當(dāng)你一個(gè)收到一個(gè)需求后,腦海里有了組件大致的接口定義,然后絲滑般的接入到concent世界里,感受漸進(jìn)式的快感以及全新api的獨(dú)有魅力吧!

需求來(lái)了

上周天氣其實(shí)不是很好,記得下了好幾場(chǎng)雨,不過北京總部大廈的隔音太好了,以致于都沒有感受到外面的風(fēng)雨飄搖,在工位上正在思索著整理下現(xiàn)有代碼時(shí),接到一個(gè)普通的需求,大致是要實(shí)現(xiàn)一個(gè)彈窗。

  • 左側(cè)有一個(gè)可選字段列表,點(diǎn)擊任意一個(gè)字段,就會(huì)進(jìn)入右側(cè)。
  • 右側(cè)有一個(gè)已選字段列表,該列表可以上下拖拽決定字段順序決定表格里的列字段顯示順序,同時(shí)也可以刪除,將其恢復(fù)到可選擇列表。
  • 點(diǎn)擊保存,將用戶的字段配置存儲(chǔ)到后端,用戶下次再次使用查看該表格時(shí),使用已配置的顯示字段來(lái)展示。

這是一個(gè)非常普通的需求,我相信不少碼神看完后,腦海里已經(jīng)把代碼雛形大致寫完了吧,嘿嘿,但是還請(qǐng)耐性看完本篇文章,來(lái)看看在concent的加持下,你的react應(yīng)用將如何變得更加靈活與美妙,正如我們的slogan:

concent, power your react

準(zhǔn)備工作

產(chǎn)品同學(xué)期望快速見到一般效果原型,而我希望原型是可以持續(xù)重構(gòu)和迭代的基礎(chǔ)代碼,當(dāng)然要認(rèn)真對(duì)待了,不能為了交差而亂寫一版,所以要快速整理需求并開始準(zhǔn)備工作了。

因?yàn)轫?xiàng)目大量基于antd來(lái)書寫UI,聽完需求后,腦海里冒出了一個(gè)穿梭框模樣的組件,但因?yàn)橛覀?cè)是一個(gè)可拖拽列表,查閱了下沒有類似的組件,那就自己實(shí)現(xiàn)一個(gè)吧,初步整理下,大概列出了以下思路。

  • 組件命名為ColumnConfModal,基于antdModal, Card實(shí)現(xiàn)布局,antdList來(lái)實(shí)現(xiàn)左側(cè)的選擇列表,基于react-beautiful-dnd的可拖拽api來(lái)實(shí)現(xiàn)右側(cè)的拖拽列表。

  • 因?yàn)檫@個(gè)彈窗組件在不同頁(yè)面被不同的table使用,傳入的列定義數(shù)據(jù)是不一樣的,所以我們使用事件的方式,來(lái)觸發(fā)打開彈窗并傳遞表格id,打開彈窗后獲取該表格的所有字段定義,以及用戶針對(duì)表哥的已選擇字段數(shù)據(jù),這樣把表格元數(shù)據(jù)的初始化工作收斂在ColumnConfModal內(nèi)部。
  • 基于表格左右兩側(cè)的交互,大致定義一下內(nèi)部接口 1 moveToSelectedList(移入到已選擇列表 ) 2 moveToSelectableList(移入到可選擇列表) 3 saveSelectedList(保存用戶的已選擇列表) 4 handleDragEnd(處理已選擇列表順序調(diào)整完成時(shí)) 5 其他略.....

UI 實(shí)現(xiàn)

因?yàn)樽?cè)為concent組件后天生擁有了emit&on的能力,而且不需要手動(dòng)off,concent在實(shí)例銷毀前自動(dòng)就幫你解除其事件監(jiān)聽,所以我們可以注冊(cè)完成后,很方便的監(jiān)聽openColumnConf事件了。

我們先拋棄各種store和reducer定義,快速的基于class擼出一個(gè)原型,利用register接口將普通組件注冊(cè)為concent組件,偽代碼如下

import { register } from 'concent';
class ColumnConfModal extends React.Component {
  state = {
    selectedColumnKeys: [],
    selectableColumnKeys: [],
    visible: false,
  };
  componentDidMount(){
    this.ctx.on('openColumnConf', ()=>{
      this.setState({visible:true});
    });
  }
  moveToSelectedList = ()=>{
    //code here
  }
  moveToSelectableList = ()=>{
    //code here
  }
  saveSelectedList = ()=>{
    //code here
  }
  handleDragEnd = ()=>{
    //code here
  }
  render(){
    const {selectedColumnKeys, selectableColumnKeys, visible} = this.state;
    return (
      <Modal title="設(shè)置顯示字段" visible={state._visible} onCancel={settings.closeModal}>
        <Head />
        <Card title="可選字段">
          <List dataSource={selectableColumnKeys} render={item=>{
            //...code here
          }}/>
        </Card>
        <Card title="已選字段">
          <DraggableList dataSource={selectedColumnKeys} onDragEnd={this.handleDragEnd}/>
        </Card>
      </Modal>
    );
  }
}
// es6裝飾器還處于實(shí)驗(yàn)階段,這里就直接包裹類了
// 等同于在class上@register( )來(lái)裝飾類
export default register( )(ColumnConfModal)

可以發(fā)現(xiàn),這個(gè)類的內(nèi)部和傳統(tǒng)的react類寫法并無(wú)區(qū)別,唯一的區(qū)別是concent會(huì)為每一個(gè)實(shí)例注入一個(gè)上下文對(duì)象ctx來(lái)暴露concentreact帶來(lái)的新特性api。

消滅生命周期函數(shù)

因?yàn)槭录谋O(jiān)聽只需要執(zhí)行一次,所以例子中我們?cè)?code>componentDidMount里完成了事件openColumnConf的監(jiān)聽注冊(cè)。

根據(jù)需求,顯然的我們還要在這里書寫獲取表格列定義元數(shù)據(jù)和獲取用戶的個(gè)性化列定義數(shù)據(jù)的業(yè)務(wù)邏輯

  componentDidMount() {
    this.ctx.on('openColumnConf', () => {
      this.setState({ visible: true });
    });
    const tableId = this.props.tid;
    tableService.getColumnMeta(`/getMeta/${tableId}`, (columns) => {
      userService.getUserColumns(`/getUserColumns/${tableId}`, (userColumns) => {
        //根據(jù)columns userColumns 計(jì)算selectedList selectableList
      });
    });
  }

所有的concent實(shí)例可以定義setup鉤子函數(shù),該函數(shù)只會(huì)在初次渲染前調(diào)用一次。

現(xiàn)在讓我們來(lái)用setup代替掉此生命周期

  //class 里定義的setup加$$前綴
  $$setup(ctx){
    //這里定義on監(jiān)聽,在組件掛載完畢后開始真正監(jiān)聽on事件
    ctx.on('openColumnConf', () => {
      this.setState({ visible: true });
    });
    //標(biāo)記依賴列表為空數(shù)組,在組件初次渲染只執(zhí)行一次
    //模擬componentDidMount
    ctx.effect(()=>{
      //service call balabala.....
    }, []);
  }

如果已熟悉hook的同學(xué),看到setup里的effectapi語(yǔ)法是不是和useEffect有點(diǎn)像?

effectuseEffect的執(zhí)行時(shí)機(jī)是一樣的,即每次組件渲染完畢之后,但是effect只需要在setup調(diào)用一次,相當(dāng)于是靜態(tài)的,更具有性能提升空間,假設(shè)我們加一個(gè)需求,每次vibible變?yōu)閒alse時(shí),上報(bào)后端一個(gè)操作日志,就可以寫為

    //依賴列表填入key的名稱,表示當(dāng)這個(gè)key的值發(fā)生變化時(shí),觸發(fā)副作用
    ctx.effect( ctx=>{
      if(!ctx.state.visible){
        //當(dāng)前最新的visible已是false,上報(bào)
      }
    }, ['visible']);

關(guān)于effect就點(diǎn)到為止,說得太多扯不完了,我們繼續(xù)回到本文的組件上。

提升狀態(tài)到store

我們希望組件的狀態(tài)變更可以被記錄下來(lái),方便觀察數(shù)據(jù)變化,so,我們先定義一個(gè)store的子模塊,名為ColumnConf,

定義其sate為

// code in ColumnConfModal/model/state.js
export function getInitialState() {
  return {
    selectedColumnKeys: [],
    selectableColumnKeys: [],
	visible: false,
  };
}
export default getInitialState();

然后利用concentconfigure接口載入此配置

// code in ColumnConfModal/model/index.js
import { configure } from 'concent';
import state from './state';
// 配置模塊ColumnConf
configure('ColumnConf', {
  state,
});

注意這里,讓model跟著組件定義走,方便我們維護(hù)model里的業(yè)務(wù)邏輯。

整個(gè)store已經(jīng)被concent掛載到了window.sss下,為了方便查看store,當(dāng)當(dāng)當(dāng)當(dāng),你可以打開console,直接查看store各個(gè)模塊當(dāng)前的最新數(shù)據(jù)。

然后我們把class注冊(cè)為'配置模ColumnConf的組件,現(xiàn)在class里的state聲明可以直接被我們干掉了。

import './model';//引用一下model文件,觸發(fā)model配置到concent
@register('ColumnConf')
class ColumnConfModal extends React.Component {
  // state = {
  //   selectedColumnKeys: [],
  //   selectableColumnKeys: [],
  //   visible: false,
  // };
  render(){
    const {selectedColumnKeys, selectableColumnKeys, visible} = this.state;
  }
}

大家可能注意到了,這樣暴力的注釋掉,render里的代碼會(huì)不會(huì)出問題?放心吧,不會(huì)的,concent組件的state和store是天生打通的,同樣的setState也是和store打通的,我們先來(lái)安裝一個(gè)插件concent-plugin-redux-devtool

import ReduxDevToolPlugin from 'concent-plugin-redux-devtool';
import { run } from 'concent';
// storeConfig配置略,詳情可參考concent官網(wǎng)
run(storeConfig, {
	plugins: [ ReduxDevToolPlugin ]
});

注意哦,concent驅(qū)動(dòng)ui渲染的原理和redux完全不一樣的,核心邏輯部分也不是在redux之上做包裝,和redux一點(diǎn)關(guān)系都沒有的^_^,這里只是橋接了redux-dev-tool插件,來(lái)輔助做狀態(tài)變更記錄的,小伙伴們千萬(wàn)不要誤會(huì),沒有redux,concent一樣能夠正常運(yùn)作,但是由于concent提供完善的插件機(jī)制,為啥不利用社區(qū)現(xiàn)有的優(yōu)秀資源呢,重復(fù)造無(wú)意義的輪子很辛苦滴(⊙﹏⊙)b......

現(xiàn)在讓我們打開chrome的redux插件看看效果吧。

上圖里是含有大量的ccApi/setState,是因?yàn)檫€有不少邏輯沒有抽離到reducer,dispatch/***模樣的type就是dispatch調(diào)用了,后面我們會(huì)提到。

這樣看狀態(tài)變遷是不是要比window.sss好多了,因?yàn)?code>sss只能看當(dāng)前最新的狀態(tài)。

這里既然提到了redux-dev-tool,我們就順道簡(jiǎn)單了解下,concent提交的數(shù)據(jù)長(zhǎng)什么樣子吧

上圖里可以看到5個(gè)字段,renderKey是用于提高性能用的,可以先不作了解,這里我們就說說其他四個(gè),module表示修改的數(shù)據(jù)所屬的模塊名,committedState表示提交的狀態(tài),sharedState表示共享到store的狀態(tài),ccUniqueKey表示觸發(fā)數(shù)據(jù)修改的實(shí)例id。

為什么要區(qū)分committedStatesharedState呢?因?yàn)?code>setState調(diào)用時(shí)允許提交自己的私有key的(即沒有在模塊里聲明的key),所以committedState是整個(gè)狀態(tài)都要再次派發(fā)給調(diào)用者,而sharedState是同步到store后,派發(fā)給同屬于module值的其他cc組件實(shí)例的。

這里就借用官網(wǎng)一張圖示意下:

所以我們可以在組件里聲明其他非模塊的key,然后在this.state里獲取到了

@register('ColumnConf')
class ColumnConfModal extends React.Component {
   state = {
		_myPrivKey:'i am a private field value, not for store',
   };
  render(){
  	//這里同時(shí)取到了模塊的數(shù)據(jù)和私有的數(shù)據(jù)
    const {selectedColumnKeys, selectableColumnKeys, visible, _myPrivKey} = this.state;
  }
}

解耦業(yè)務(wù)邏輯與UI

雖然代碼能夠正常工作,狀態(tài)也接入了store,但是我們發(fā)現(xiàn)class已經(jīng)變得臃腫不堪了,利用setState懟固然快和方便,但是后期維護(hù)和迭代的代價(jià)就會(huì)慢慢越來(lái)越大,讓我們把業(yè)務(wù)抽到reduder

export function setLoading(loading) {
  return { loading };
};
/** 移入到已選擇列表 */
export function moveToSelectedList() {
}
/** 移入到可選擇列表 */
export function moveToSelectableList() {
}
/** 初始化列表 */
export async function initSelectedList(tableId, moduleState, ctx) {
  //這里可以不用基于字符串 ctx.dispatch('setLoading', true) 去調(diào)用了,雖然這樣寫也是有效的
  await ctx.dispatch(setLoading, true);
  const columnMeta = await tableService..getColumnMeta(`/getMeta/${tableId}`);
  const userColumsn = await userService.getUserColumns(`/getUserColumns/${tableId}`);
  //計(jì)算 selectedColumnKeys selectableColumnKeys 略
  //僅返回需要設(shè)置到模塊的片斷state就可以了
  return { loading: false, selectedColumnKeys, selectableColumnKeys };
}
/** 保存已選擇列表 */
export async function saveSelectedList(tableId, moduleState, ctx) {
}
export function handleDragEnd() {
}

利用concentconfigure接口把reducer也配置進(jìn)去

// code in ColumnConfModal/model/index.js
import { configure } from 'concent';
import * as reducer from 'reducer';
import state from './state';
// 配置模塊ColumnConf
configure('ColumnConf', {
  state,
  reducer,
});

還記得上面的setup嗎,setup可以返回一個(gè)對(duì)象,返回結(jié)果將收集在settiings里,現(xiàn)在我們稍作修改,然后來(lái)看看class吧,世界是不是清靜多了呢?

import { register } from 'concent';
class ColumnConfModal extends React.Component {
  $$setup(ctx) {
    //這里定義on監(jiān)聽,在組件掛載完畢后開始真正監(jiān)聽on事件
    ctx.on('openColumnConf', () => {
      this.setState({ visible: true });
    });
    //標(biāo)記依賴列表為空數(shù)組,在組件初次渲染只執(zhí)行一次
    //模擬componentDidMount
    ctx.effect(() => {
      ctx.dispatch('initSelectedList', this.props.tid);
    }, []);
    return {
      moveToSelectedList: (payload) => {
        ctx.dispatch('moveToSelectedList', payload);
      },
      moveToSelectableList: (payload) => {
        ctx.dispatch('moveToSelectableList', payload);
      },
      saveSelectedList: (payload) => {
        ctx.dispatch('saveSelectedList', payload);
      },
      handleDragEnd: (payload) => {
        ctx.dispatch('handleDragEnd', payload);
      }
    }
  }
  render() {
    //從settings里取出這些方法
    const { moveToSelectedList, moveToSelectableList, saveSelectedList, handleDragEnd } = this.ctx.settings;
  }
}

愛class,愛hook,讓兩者和諧共處

react社區(qū)轟轟烈烈推動(dòng)了Hook,讓大家逐步用Hook組件代替class組件,但是本質(zhì)上Hook逃離了this,精簡(jiǎn)了dom渲染層級(jí),但是也帶來(lái)了組件存在期間大量的臨時(shí)匿名閉包重復(fù)創(chuàng)建。

來(lái)看看concent怎么解決這個(gè)問題的吧,上面已提到setup支持返回結(jié)果,將被收集在settiings里,現(xiàn)在讓稍微的調(diào)整下代碼,將class組件吧變身為Hook組件吧。

import { useConcent } from 'concent';
const setup = (ctx) => {
  //這里定義on監(jiān)聽,在組件掛載完畢后開始真正監(jiān)聽on事件
  ctx.on('openColumnConf', (tid) => {
    ctx.setState({ visible: true, tid });
  });
  //標(biāo)記依賴列表為空數(shù)組,在組件初次渲染只執(zhí)行一次
  //模擬componentDidMount
  ctx.effect(() => {
    ctx.dispatch('initSelectedList', ctx.state.tid);
  }, []);
  return {
    moveToSelectedList: (payload) => {
      ctx.dispatch('moveToSelectedList', payload);
    },
    moveToSelectableList: (payload) => {
      ctx.dispatch('moveToSelectableList', payload);
    },
    saveSelectedList: (payload) => {
      ctx.dispatch('saveSelectedList', payload);
    },
    handleDragEnd: (payload) => {
      ctx.dispatch('handleDragEnd', payload);
    }
  }
}
const iState = { _myPrivKey: 'myPrivate state', tid:null };
export function ColumnConfModal() {
  const ctx = useConcent({ module: 'ColumnConf', setup, state: iState });
  const { moveToSelectedList, moveToSelectableList, saveSelectedList, handleDragEnd } = ctx.settings;
  const { selectedColumnKeys, selectableColumnKeys, visible, _myPrivKey } = ctx.state;
  // return your ui
}

在這里要感謝尤雨溪老師的這篇Vue Function-based API RFC,給了我很大的靈感,現(xiàn)在你可以看到所以的方法的都在setup里定義完成,當(dāng)你的組件很多的時(shí)候,給gc減小的壓力是顯而易見的。

由于兩者的寫法高度一致,從classHook是不是非常的自然呢?我們其實(shí)不需要爭(zhēng)論該用誰(shuí)更好了,按照你的個(gè)人喜好就可以,就算某天你看class不順眼了,在concent的代碼風(fēng)格下,重構(gòu)的代價(jià)幾乎為0。

使用組件

上面我們定義了一個(gè)on事件openColumnConf,那么我們?cè)谄渌?yè)面里引用組件ColumnConfModal時(shí),當(dāng)然需要觸發(fā)這個(gè)事件打開其彈窗了。

import { emit } from 'concent';
class Foo extends React.Component {
  openColumnConfModal = () => {
    //如果這個(gè)類是一個(gè)concent組件
    this.ctx.emit('openColumnConfModal', 3);
    //如果不是則可以調(diào)用頂層api emit
    emit('openColumnConfModal', 3);
  }
  render() {
    return (
      <div>
        <button onClick={this.openColumnConfModal}>配置可見字段</button>
        <Table />
          <ColumnConfModal />
      </div>
    );
  }
}

上述寫法里,如果有其他很多頁(yè)面都需要引入ColumnConfModal,都需要寫一個(gè)openColumnConfModal,我們可以把這個(gè)打開邏輯抽象到modalService里,專門用來(lái)打開各種彈窗,而避免在業(yè)務(wù)見到openColumnConfModal這個(gè)常量字符串

//code in service/modal.js
import { emit } from 'concent';
export function openColumnConfModal(tid) {
  emit('openColumnConfModal', tid);
}

現(xiàn)在可以這樣使用組件來(lái)觸發(fā)事件調(diào)用了

import * as modalService from 'service/modal';
class Foo extends React.Component {
  openColumnConfModal = () => {
    modalService.openColumnConfModal(6);
  }
  render() {
    return (
      <div>
        <button onClick={this.openColumnConfModal}>配置可見字段</button>
        <Table />
        <ColumnConfModal />
      </div>
    );
  }
}

結(jié)語(yǔ)

以上代碼在任何一個(gè)階段都是有效的,想要了解漸進(jìn)式重構(gòu)的在線demo可以點(diǎn)這里

由于本篇主題主要是介紹漸進(jìn)式重構(gòu)組件,所以其他特性諸如sync、computed$watch、高性能殺手锏renderKey等等內(nèi)容就不在這里展開講解了,更多關(guān)于concent重構(gòu)react的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解一個(gè)基于react+webpack的多頁(yè)面應(yīng)用配置

    詳解一個(gè)基于react+webpack的多頁(yè)面應(yīng)用配置

    這篇文章主要介紹了詳解一個(gè)基于react+webpack的多頁(yè)面應(yīng)用配置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2019-01-01
  • 使用webpack搭建react開發(fā)環(huán)境的方法

    使用webpack搭建react開發(fā)環(huán)境的方法

    本篇文章主要介紹了使用webpack搭建react開發(fā)環(huán)境的方法,在這篇文章中我們開始利用我們之前所學(xué)搭建一個(gè)簡(jiǎn)易的React開發(fā)環(huán)境,用以鞏固我們之前學(xué)習(xí)的Webpack知識(shí)。一起跟隨小編過來(lái)看看吧
    2018-05-05
  • React18從0實(shí)現(xiàn)dispatch?update流程

    React18從0實(shí)現(xiàn)dispatch?update流程

    這篇文章主要為大家介紹了React18從0實(shí)現(xiàn)dispatch?update流程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • React Hooks中模擬Vue生命周期函數(shù)的指南

    React Hooks中模擬Vue生命周期函數(shù)的指南

    React Hooks 提供了一種在函數(shù)組件中使用狀態(tài)和其他 React 特性的方式,而不需要編寫類組件,Vue 的生命周期函數(shù)和 React Hooks 之間有一定的對(duì)應(yīng)關(guān)系,本文給大家介紹了React Hooks中模擬Vue生命周期函數(shù)的指南,需要的朋友可以參考下
    2024-10-10
  • react中context傳值和生命周期詳解

    react中context傳值和生命周期詳解

    這篇文章主要介紹了react中context傳值和生命周期,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • React函數(shù)組件useContext useReducer自定義hooks

    React函數(shù)組件useContext useReducer自定義hooks

    這篇文章主要為大家介紹了React函數(shù)組件useContext useReducer自定義hooks示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • React Native中WebView與html雙向通信遇到的坑

    React Native中WebView與html雙向通信遇到的坑

    這篇文章主要介紹了React Native中WebView與html雙向通信的一些問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-01-01
  • react項(xiàng)目實(shí)踐之webpack-dev-serve

    react項(xiàng)目實(shí)踐之webpack-dev-serve

    這篇文章主要介紹了react項(xiàng)目實(shí)踐之webpack-dev-serve,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2018-09-09
  • 一百多行代碼實(shí)現(xiàn)react拖拽hooks

    一百多行代碼實(shí)現(xiàn)react拖拽hooks

    這篇文章主要介紹了一百多行代碼實(shí)現(xiàn)react拖拽hooks,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • react?hooks?d3實(shí)現(xiàn)企查查股權(quán)穿透圖結(jié)構(gòu)圖效果詳解

    react?hooks?d3實(shí)現(xiàn)企查查股權(quán)穿透圖結(jié)構(gòu)圖效果詳解

    這篇文章主要為大家介紹了react?hooks?d3實(shí)現(xiàn)企查查股權(quán)穿透圖結(jié)構(gòu)圖效果詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01

最新評(píng)論

汉源县| 林口县| 虎林市| 湘潭市| 阿瓦提县| 桐梓县| 叙永县| 珠海市| 增城市| 栖霞市| 丽水市| 江城| 抚远县| 襄垣县| 日喀则市| 岳池县| 栖霞市| 呼伦贝尔市| 周至县| 乐昌市| 建平县| 淳化县| 磐石市| 永嘉县| 林芝县| 兰西县| 南川市| 平泉县| 芒康县| 大宁县| 鹤壁市| 麦盖提县| 孝昌县| 岳西县| 隆昌县| 阳原县| 镇远县| 唐山市| 北川| 乡城县| 霸州市|