欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解vue 組件的實(shí)現(xiàn)原理

 更新時間:2020年11月12日 09:17:39   作者:快狗打車前端團(tuán)隊  
這篇文章主要介紹了詳解vue 組件的實(shí)現(xiàn)原理,幫助大家更好的理解和學(xué)習(xí)vue框架,感興趣的朋友可以了解下

組件機(jī)制的設(shè)計,可以讓開發(fā)者把一個復(fù)雜的應(yīng)用分割成一個個功能獨(dú)立組件,降低開發(fā)的難度的同時,也提供了極好的復(fù)用性和可維護(hù)性。本文我們一起從源碼的角度,了解一下組件的底層實(shí)現(xiàn)原理。

組件注冊時做了什么?

在Vue中使用組件,要做的第一步就是注冊。Vue提供了全局注冊和局部注冊兩種方式。

全局注冊方式如下:

Vue.component('my-component-name', { /* ... */ })

局部注冊方式如下:

var ComponentA = { /* ... */ }

new Vue({
 el: '#app',
 components: {
  'component-a': ComponentA
 }
})

全局注冊的組件,會在任何Vue實(shí)例中使用。局部注冊的組件,只能在該組件的注冊地,也就是注冊該組件的Vue實(shí)例中使用,甚至Vue實(shí)例的子組件中也不能使用。

有一定Vue使用經(jīng)驗的小伙伴都了解上面的差異,但是為啥會有這樣的差異呢?我們從組件注冊的代碼實(shí)現(xiàn)上進(jìn)行解釋。

// Vue.component的核心代碼
// ASSET_TYPES = ['component', 'directive', 'filter']
ASSET_TYPES.forEach(type => {
  Vue[type] = function (id, definition
  ){
   if (!definition) {
    return this.options[type + 's'][id]
   } else {
    // 組件注冊
    if (type === 'component' && isPlainObject(definition)) {
     definition.name = definition.name || id
     // 如果definition是一個對象,需要調(diào)用Vue.extend()轉(zhuǎn)換成函數(shù)。Vue.extend會創(chuàng)建一個Vue的子類(組件類),并返回子類的構(gòu)造函數(shù)。
     definition = this.options._base.extend(definition)
    }
    
    // ...省略其他代碼
    // 這里很關(guān)鍵,將組件添加到構(gòu)造函數(shù)的選項對象中Vue.options上。
    this.options[type + 's'][id] = definition
    return definition
   }
  }
 })
// Vue的構(gòu)造函數(shù)
function Vue(options){
 if (process.env.NODE_ENV !== 'production' &&
  !(this instanceof Vue)
 ) {
  warn('Vue is a constructor and should be called with the `new` keyword')
 }
 this._init(options)
  
}

// Vue的初始化中進(jìn)行選項對象的合并
Vue.prototype._init = function (options) {
  const vm = this
  vm._uid = uid++
  vm._isVue = true
  // ...省略其他代碼
  if (options && options._isComponent) {
   initInternalComponent(vm, options)
  } else {
   // 合并vue選項對象,合并構(gòu)造函數(shù)的選項對象和實(shí)例中的選項對象
   vm.$options = mergeOptions(
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
   )
  }
  // ...省略其他代碼
 }

以上摘取了組件注冊的主要代碼。可以看到Vue實(shí)例的選項對象由Vue的構(gòu)造函數(shù)選項對象和Vue實(shí)例的選項對象兩部分組成。

全局注冊的組件,實(shí)際上通過Vue.component添加到了Vue構(gòu)造函數(shù)的選項對象 Vue.options.components 上了。

Vue 在實(shí)例化時(new Vue(options))所指定的選項對象會與構(gòu)造函數(shù)的選項對象合并作為Vue實(shí)例最終的選項對象。因此,全局注冊的組件在所有的Vue實(shí)例中都可以使用,而在Vue實(shí)例中局部注冊的組件只會影響Vue實(shí)例本身。

為啥在HTML模板中可以正常使用組件標(biāo)簽?

我們知道組件可以跟普通的HTML一樣在模板中直接使用。例如:

<div id="app">
 <!--使用組件button-counter-->
 <button-counter></button-counter>
</div>
// 全局注冊一個名為 button-counter 的組件
Vue.component('button-counter', {
 data: function () {
  return {
   count: 0
  }
 },
 template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

// 創(chuàng)建Vue實(shí)例
new Vue({
  el: '#app'
})

那么,當(dāng)Vue解析到自定義的組件標(biāo)簽時是如何處理的呢?

Vue 對組件標(biāo)簽的解析與普通HTML標(biāo)簽的解析一樣,不會因為是非 HTML標(biāo)準(zhǔn)的標(biāo)簽而特殊處理。處理過程中第一個不同的地方出現(xiàn)在vnode節(jié)點(diǎn)創(chuàng)建時。vue 內(nèi)部通過_createElement函數(shù)實(shí)現(xiàn)vnode的創(chuàng)建。

export function _createElement (
 context: Component,
 tag?: string | Class<Component> | Function | Object,
 data?: VNodeData,
 children?: any,
 normalizationType?: number
): VNode | Array<VNode> {

 //...省略其他代碼
 
 let vnode, ns
 if (typeof tag === 'string') {
  let Ctor
  ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
  // 如果是普通的HTML標(biāo)簽
  if (config.isReservedTag(tag)) {
   vnode = new VNode(
    config.parsePlatformTagName(tag), data, children,
    undefined, undefined, context
   )
  } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
   // 如果是組件標(biāo)簽,e.g. my-custom-tag
   vnode = createComponent(Ctor, data, context, children, tag)
  } else {
   vnode = new VNode(
    tag, data, children,
    undefined, undefined, context
   )
  }
 } else {
  // direct component options / constructor
  vnode = createComponent(tag, data, context, children)
 }
 
 if (Array.isArray(vnode)) {
  return vnode
 } else if (isDef(vnode)) {
  if (isDef(ns)) applyNS(vnode, ns)
  if (isDef(data)) registerDeepBindings(data)
  return vnode
 } else {
  return createEmptyVNode()
 }
}

以文中的button-counter組件為例,由于button-counter標(biāo)簽不是合法的HTML標(biāo)簽,不能直接new VNode()創(chuàng)建vnode。Vue 會通過resolveAsset函數(shù)檢查該標(biāo)簽是否為自定義組件的標(biāo)簽。

export function resolveAsset (
 options: Object,
 type: string,
 id: string,
 warnMissing?: boolean
): any {
 /* istanbul ignore if */
 if (typeof id !== 'string') {
  return
 }
 const assets = options[type]

 // 首先檢查vue實(shí)例本身有無該組件
 if (hasOwn(assets, id)) return assets[id]
 const camelizedId = camelize(id)
 if (hasOwn(assets, camelizedId)) return assets[camelizedId]
 const PascalCaseId = capitalize(camelizedId)
 if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]

 // 如果實(shí)例上沒有找到,去查找原型鏈
 const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
 if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
  warn(
   'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
   options
  )
 }
 return res
}

button-counter是我們?nèi)肿缘慕M件,顯然可以在this.$options.components找到其定義。因此,Vue會執(zhí)行createComponent函數(shù)來生成組件的vnode。

// createComponent
export function createComponent (
 Ctor: Class<Component> | Function | Object | void,
 data: ?VNodeData,
 context: Component,
 children: ?Array<VNode>,
 tag?: string
): VNode | Array<VNode> | void {
 if (isUndef(Ctor)) {
  return
 }
 
 // 獲取Vue的構(gòu)造函數(shù)
 const baseCtor = context.$options._base

 // 如果Ctor是一個選項對象,需要使用Vue.extend使用選項對象,創(chuàng)建將組件選項對象轉(zhuǎn)換成一個Vue的子類
 if (isObject(Ctor)) {
  Ctor = baseCtor.extend(Ctor)
 }

 // 如果Ctor還不是一個構(gòu)造函數(shù)或者異步組件工廠函數(shù),不再往下執(zhí)行。
 if (typeof Ctor !== 'function') {
  if (process.env.NODE_ENV !== 'production') {
   warn(`Invalid Component definition: ${String(Ctor)}`, context)
  }
  return
 }

 // 異步組件
 let asyncFactory
 if (isUndef(Ctor.cid)) {
  asyncFactory = Ctor
  Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
  if (Ctor === undefined) {
   // return a placeholder node for async component, which is rendered
   // as a comment node but preserves all the raw information for the node.
   // the information will be used for async server-rendering and hydration.
   return createAsyncPlaceholder(
    asyncFactory,
    data,
    context,
    children,
    tag
   )
  }
 }

 data = data || {}

 // 重新解析構(gòu)造函數(shù)的選項對象,在組件構(gòu)造函數(shù)創(chuàng)建后,Vue可能會使用全局混入造成構(gòu)造函數(shù)選項對象改變。
 resolveConstructorOptions(Ctor)

 // 處理組件的v-model
 if (isDef(data.model)) {
  transformModel(Ctor.options, data)
 }

 // 提取props
 const propsData = extractPropsFromVNodeData(data, Ctor, tag)

 // 函數(shù)式組件
 if (isTrue(Ctor.options.functional)) {
  return createFunctionalComponent(Ctor, propsData, data, context, children)
 }

 const listeners = data.on
 data.on = data.nativeOn

 if (isTrue(Ctor.options.abstract)) {
  const slot = data.slot
  data = {}
  if (slot) {
   data.slot = slot
  }
 }

 // 安裝組件hooks
 installComponentHooks(data)

 // 創(chuàng)建 vnode
 const name = Ctor.options.name || tag
 const vnode = new VNode(
  `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
  data, undefined, undefined, undefined, context,
  { Ctor, propsData, listeners, tag, children },
  asyncFactory
 )

 return vnode
}
 

由于Vue允許通過一個選項對象定義組件,Vue需要使用Vue.extend將組件的選項對象轉(zhuǎn)換成一個構(gòu)造函數(shù)。

/**
  * Vue類繼承,以Vue的原型為原型創(chuàng)建Vue組件子類。繼承實(shí)現(xiàn)方式是采用Object.create(),在內(nèi)部實(shí)現(xiàn)中,加入了緩存的機(jī)制,避免重復(fù)創(chuàng)建子類。
  */
 Vue.extend = function (extendOptions: Object): Function {
  // extendOptions 是組件的選項對象,與vue所接收的一樣
  extendOptions = extendOptions || {}
  // Super變量保存對父類Vue的引用
  const Super = this
  // SuperId 保存父類的cid
  const SuperId = Super.cid
  // 緩存構(gòu)造函數(shù)
  const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  if (cachedCtors[SuperId]) {
   return cachedCtors[SuperId]
  }

  // 獲取組件的名字
  const name = extendOptions.name || Super.options.name
  if (process.env.NODE_ENV !== 'production' && name) {
   validateComponentName(name)
  }

  // 定義組件的構(gòu)造函數(shù)
  const Sub = function VueComponent (options) {
   this._init(options)
  }

  // 組件的原型對象指向Vue的選項對象
  Sub.prototype = Object.create(Super.prototype)
  Sub.prototype.constructor = Sub

  // 為組件分配一個cid
  Sub.cid = cid++

  // 將組件的選項對象與Vue的選項合并
  Sub.options = mergeOptions(
   Super.options,
   extendOptions
  )
  // 通過super屬性指向父類
  Sub['super'] = Super
  
  // 將組件實(shí)例的props和computed屬代理到組件原型對象上,避免每個實(shí)例創(chuàng)建的時候重復(fù)調(diào)用Object.defineProperty。
  if (Sub.options.props) {
   initProps(Sub)
  }

  if (Sub.options.computed) {
   initComputed(Sub)
  }

  // 復(fù)制父類Vue上的extend/mixin/use等全局方法
  Sub.extend = Super.extend
  Sub.mixin = Super.mixin
  Sub.use = Super.use

  // 復(fù)制父類Vue上的component、directive、filter等資源注冊方法
  ASSET_TYPES.forEach(function (type) {
   Sub[type] = Super[type]
  })

  // enable recursive self-lookup
  if (name) {
   Sub.options.components[name] = Sub
  }

  // 保存父類Vue的選項對象
  Sub.superOptions = Super.options
  // 保存組件的選項對象
  Sub.extendOptions = extendOptions
  // 保存最終的選項對象
  Sub.sealedOptions = extend({}, Sub.options)

  // 緩存組件的構(gòu)造函數(shù)
  cachedCtors[SuperId] = Sub
  return Sub
 }
}

還有一處重要的代碼是installComponentHooks(data)。該方法會給組件vnode的data添加組件鉤子,這些鉤子在組件的不同階段被調(diào)用,例如init鉤子在組件patch時會調(diào)用。

function installComponentHooks (data: VNodeData) {
 const hooks = data.hook || (data.hook = {})
 for (let i = 0; i < hooksToMerge.length; i++) {
  const key = hooksToMerge[i]
  // 外部定義的鉤子
  const existing = hooks[key]
  // 內(nèi)置的組件vnode鉤子
  const toMerge = componentVNodeHooks[key]
  // 合并鉤子
  if (existing !== toMerge && !(existing && existing._merged)) {
   hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
  }
 }
}

// 組件vnode的鉤子。
const componentVNodeHooks = {
 // 實(shí)例化組件
 init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
  if (
   vnode.componentInstance &&
   !vnode.componentInstance._isDestroyed &&
   vnode.data.keepAlive
  ) {
   // kept-alive components, treat as a patch
   const mountedNode: any = vnode // work around flow
   componentVNodeHooks.prepatch(mountedNode, mountedNode)
  } else {
   // 生成組件實(shí)例
   const child = vnode.componentInstance = createComponentInstanceForVnode(
    vnode,
    activeInstance
   )
   // 掛載組件,與vue的$mount一樣
   child.$mount(hydrating ? vnode.elm : undefined, hydrating)
  }
 },

 prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
  const options = vnode.componentOptions
  const child = vnode.componentInstance = oldVnode.componentInstance
  updateChildComponent(
   child,
   options.propsData, // updated props
   options.listeners, // updated listeners
   vnode, // new parent vnode
   options.children // new children
  )
 },

 insert (vnode: MountedComponentVNode) {
  const { context, componentInstance } = vnode
  if (!componentInstance._isMounted) {
   componentInstance._isMounted = true
   // 觸發(fā)組件的mounted鉤子
   callHook(componentInstance, 'mounted')
  }
  if (vnode.data.keepAlive) {
   if (context._isMounted) {
    queueActivatedComponent(componentInstance)
   } else {
    activateChildComponent(componentInstance, true /* direct */)
   }
  }
 },

 destroy (vnode: MountedComponentVNode) {
  const { componentInstance } = vnode
  if (!componentInstance._isDestroyed) {
   if (!vnode.data.keepAlive) {
    componentInstance.$destroy()
   } else {
    deactivateChildComponent(componentInstance, true /* direct */)
   }
  }
 }
}

const hooksToMerge = Object.keys(componentVNodeHooks)

最后,與普通HTML標(biāo)簽一樣,為組件生成vnode節(jié)點(diǎn):

// 創(chuàng)建 vnode
 const vnode = new VNode(
  `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
  data, undefined, undefined, undefined, context,
  { Ctor, propsData, listeners, tag, children },
  asyncFactory
 )

組件在patch時對vnode的處理與普通標(biāo)簽有所不同。

Vue 如果發(fā)現(xiàn)正在patch的vnode是組件,那么調(diào)用createComponent方法。

 function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
  let i = vnode.data
  if (isDef(i)) {
   const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
   // 執(zhí)行組件鉤子中的init鉤子,創(chuàng)建組件實(shí)例
   if (isDef(i = i.hook) && isDef(i = i.init)) {
    i(vnode, false /* hydrating */)
   }
   
   // init鉤子執(zhí)行后,如果vnode是個子組件,該組件應(yīng)該創(chuàng)建一個vue子實(shí)例,并掛載到DOM元素上。子組件的vnode.elm也設(shè)置完成。然后我們只需要返回該DOM元素。
   if (isDef(vnode.componentInstance)) {
    // 設(shè)置vnode.elm
    initComponent(vnode, insertedVnodeQueue)
    // 將組件的elm插入到父組件的dom節(jié)點(diǎn)上
    insert(parentElm, vnode.elm, refElm)
    if (isTrue(isReactivated)) {
     reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
    }
    return true
   }
  }
 }

createComponent會調(diào)用組件vnode的data對象上定義的init鉤子方法,創(chuàng)建組件實(shí)例?,F(xiàn)在我們回過頭來看下init鉤子的代碼:

// ... 省略其他代碼
 init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
  if (
   vnode.componentInstance &&
   !vnode.componentInstance._isDestroyed &&
   vnode.data.keepAlive
  ) {
   // kept-alive components, treat as a patch
   const mountedNode: any = vnode // work around flow
   componentVNodeHooks.prepatch(mountedNode, mountedNode)
  } else {
   // 生成組件實(shí)例
   const child = vnode.componentInstance = createComponentInstanceForVnode(
    vnode,
    activeInstance
   )
   // 掛載組件,與vue的$mount一樣
   child.$mount(hydrating ? vnode.elm : undefined, hydrating)
  }
 }
 // ...省略其他代碼

由于組件是初次創(chuàng)建,因此init鉤子會調(diào)用createComponentInstanceForVnode創(chuàng)建一個組件實(shí)例,并賦值給vnode.componentInstance。

export function createComponentInstanceForVnode (
 vnode: any, 
 parent: any,
): Component {
 // 內(nèi)部組件選項
 const options: InternalComponentOptions = {
  // 標(biāo)記是否是組件
  _isComponent: true,
  // 父Vnode
  _parentVnode: vnode,
  // 父Vue實(shí)例
  parent
 }
 // check inline-template render functions
 const inlineTemplate = vnode.data.inlineTemplate
 if (isDef(inlineTemplate)) {
  options.render = inlineTemplate.render
  options.staticRenderFns = inlineTemplate.staticRenderFns
 }
 // new 一個組件實(shí)例。組件實(shí)例化 與 new Vue() 執(zhí)行的過程相同。
 return new vnode.componentOptions.Ctor(options)
}

createComponentInstanceForVnode 中會執(zhí)行 new vnode.componentOptions.Ctor(options)。由前面我們在創(chuàng)建組件vnode時可知,vnode.componentOptions的值是一個對象:{ Ctor, propsData, listeners, tag, children },其中包含了組件的構(gòu)造函數(shù)Ctor。因此 new vnode.componentOptions.Ctor(options)等價于new VueComponent(options)。

// 生成組件實(shí)例
const child = vnode.componentInstance = createComponentInstanceForVnode(vnode, activeInstance)
// 掛載組件,與vue的$mount一樣
child.$mount(hydrating ? vnode.elm : undefined, hydrating)

等價于:

new VueComponent(options).$mount(hydrating ? vnode.elm : undefined, hydrating)

這段代碼想必大家都很熟悉了,是組件初始化和掛載的過程。組件的初始化和掛載與在前文中所介紹Vue初始化和掛載過程相同,因此不再展開說明。大致的過程就是創(chuàng)建了一個組件實(shí)例并掛載后。使用initComponent將組件實(shí)例的$el設(shè)置為vnode.elm的值。最后,調(diào)用insert將組件實(shí)例的DOM根節(jié)點(diǎn)插入其父節(jié)點(diǎn)。然后就完成了組件的處理。

總結(jié)

通過對組件底層實(shí)現(xiàn)的分析,我們可以知道,每個組件都是一個VueComponent實(shí)例,而VueComponent又是繼承自Vue。每個組件實(shí)例獨(dú)立維護(hù)自己的狀態(tài)、模板的解析、DOM的創(chuàng)建和更新。篇幅有限,文中只分析了基本的組件的注冊解析過程,未對異步組件、keep-alive等做分析。等后面再慢慢補(bǔ)上。

以上就是詳解vue 組件的實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于vue組件的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue中v-show添加表達(dá)式的問題(判斷是否顯示)

    Vue中v-show添加表達(dá)式的問題(判斷是否顯示)

    這篇文章主要介紹了關(guān)于Vue中v-show中添加表達(dá)式用于判斷是否顯示的問題,很多朋友經(jīng)常會遇到這樣的需求,有數(shù)據(jù)來源和標(biāo)簽類型兩行選項,需要實(shí)現(xiàn)點(diǎn)擊上面的某個數(shù)據(jù)來源時,標(biāo)簽類型自動切換功能,感興趣的朋友一起看看吧
    2018-03-03
  • vue 使用 echarts 繪制中國地圖的實(shí)現(xiàn)代碼

    vue 使用 echarts 繪制中國地圖的實(shí)現(xiàn)代碼

    這篇文章主要介紹了vue 使用 echarts 繪制中國地圖,內(nèi)容包括插入echarts所需模塊及完整的代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-01-01
  • 使用vue-cli初始化項目時運(yùn)行‘npm run dev’報錯及解決

    使用vue-cli初始化項目時運(yùn)行‘npm run dev’報錯及解決

    這篇文章主要介紹了使用vue-cli初始化項目時運(yùn)行‘npm run dev’報錯及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue路由傳參 router-link和編程式傳參方式

    vue路由傳參 router-link和編程式傳參方式

    這篇文章主要介紹了vue路由傳參 router-link和編程式傳參方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • vue3實(shí)現(xiàn)動態(tài)添加路由

    vue3實(shí)現(xiàn)動態(tài)添加路由

    這篇文章主要介紹了vue3實(shí)現(xiàn)動態(tài)添加路由方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • 詳解如何使用router-link對象方式傳遞參數(shù)?

    詳解如何使用router-link對象方式傳遞參數(shù)?

    這篇文章主要介紹了如何使用router-link對象方式傳遞參數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • vue使用better-scroll實(shí)現(xiàn)橫向滾動的方法實(shí)例

    vue使用better-scroll實(shí)現(xiàn)橫向滾動的方法實(shí)例

    這幾天研究項目時,看到了 better-scroll 插件,看著感覺功能挺強(qiáng),這篇文章主要給大家介紹了關(guān)于vue使用better-scroll實(shí)現(xiàn)橫向滾動的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • Vue中mixins混入的介紹與使用詳解

    Vue中mixins混入的介紹與使用詳解

    如果我們在每個組件中去重復(fù)定義這些屬性和方法會使得項目出現(xiàn)代碼冗余并提高了維護(hù)難度,針對這種情況官方提供了Mixins特性,這時使用Vue mixins混入有很大好處,下面就介紹下Vue mixins混入使用方法,需要的朋友參考下吧
    2022-12-12
  • vue項目記錄鎖定和解鎖功能實(shí)現(xiàn)

    vue項目記錄鎖定和解鎖功能實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了vue項目記錄鎖定和解鎖功能實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 詳解Vue中數(shù)據(jù)可視化詞云展示與詞云的生成

    詳解Vue中數(shù)據(jù)可視化詞云展示與詞云的生成

    數(shù)據(jù)可視化是現(xiàn)代Web應(yīng)用程序中的一個重要組成部分,詞云是一種非常流行的數(shù)據(jù)可視化形式,可以用來展示文本數(shù)據(jù)中的主題和關(guān)鍵字,本文我們將介紹如何在Vue中使用詞云庫進(jìn)行數(shù)據(jù)可視化詞云展示和詞云生成,需要的可以參考一下
    2023-06-06

最新評論