之前文章有写到vue构造函数的实例化过程,只是对vue实例做了个粗略的描述,并没有说明vue组件实例化的过程。本文主要对vue组件的实例化过程做一些简要的描述。

  组件的实例化与vue构造函数的实例化,大部分是类似的,vue的实例可以当做一个根组件,普通组件的实例化可以当做子组件。真实的DOM是一个树形结构,虚拟DOM本质只是真实DOM的抽象,也是一个树形结构。简单来说,整个vue工程的实例化过程如下:

  

  如上图所示,在调用render函数时,会依次调用createElement方法,createElement方法的代码如下,主要作用就是生成vnode。

  1. export function _createElement (
  2. context: Component,
  3. tag?: string | Class<Component> | Function | Object,
  4. data?: VNodeData,
  5. children?: any,
  6. normalizationType?: number
  7. ): VNode | Array<VNode> {
  8. if (isDef(data) && isDef((data: any).__ob__)) {
  9. process.env.NODE_ENV !== 'production' && warn(
  10. `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
  11. 'Always create fresh vnode data objects in each render!',
  12. context
  13. )
  14. return createEmptyVNode()
  15. }
  16. // object syntax in v-bind
  17. if (isDef(data) && isDef(data.is)) {
  18. tag = data.is
  19. }
  20. if (!tag) {
  21. // in case of component :is set to falsy value
  22. return createEmptyVNode()
  23. }
  24. // warn against non-primitive key
  25. if (process.env.NODE_ENV !== 'production' &&
  26. isDef(data) && isDef(data.key) && !isPrimitive(data.key)
  27. ) {
  28. if (!__WEEX__ || !('@binding' in data.key)) {
  29. warn(
  30. 'Avoid using non-primitive value as key, ' +
  31. 'use string/number value instead.',
  32. context
  33. )
  34. }
  35. }
  36. // support single function children as default scoped slot
  37. if (Array.isArray(children) &&
  38. typeof children[0] === 'function'
  39. ) {
  40. data = data || {}
  41. data.scopedSlots = { default: children[0] }
  42. children.length = 0
  43. }
  44. // 组件格式化
  45. if (normalizationType === ALWAYS_NORMALIZE) {
  46. children = normalizeChildren(children)
  47. } else if (normalizationType === SIMPLE_NORMALIZE) {
  48. children = simpleNormalizeChildren(children)
  49. }
  50. let vnode, ns
  51. if (typeof tag === 'string') {
  52. let Ctor
  53. ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
  54. // 普通的HTML标签
  55. if (config.isReservedTag(tag)) {
  56. // platform built-in elements
  57. if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) {
  58. warn(
  59. `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
  60. context
  61. )
  62. }
  63. // 创建一个普通的DOM节点
  64. vnode = new VNode(
  65. config.parsePlatformTagName(tag), data, children,
  66. undefined, undefined, context
  67. )
  68. } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
  69. // component
  70. // 创建组件
  71. vnode = createComponent(Ctor, data, context, children, tag)
  72. } else {
  73. // unknown or unlisted namespaced elements
  74. // check at runtime because it may get assigned a namespace when its
  75. // parent normalizes children
  76. vnode = new VNode(
  77. tag, data, children,
  78. undefined, undefined, context
  79. )
  80. }
  81. } else {
  82. // direct component options / constructor
  83. vnode = createComponent(tag, data, context, children)
  84. }
  85. if (Array.isArray(vnode)) {
  86. return vnode
  87. } else if (isDef(vnode)) {
  88. if (isDef(ns)) applyNS(vnode, ns)
  89. if (isDef(data)) registerDeepBindings(data)
  90. return vnode
  91. } else {
  92. return createEmptyVNode()
  93. }
  94. }
  从上述代码中可以看出,如果存在tag且tag的类型为string,会走一些判断逻辑,主要就是判断两类,一类是HTML标签,通过config.isReservedTag判断是否是HTML标签,另外一类就是在当前实例作用域options中的component中查找,是否存在对该类标签的声明,存在,即使组件,详细流程图如下图所示:

  如上图所示,主流程与实例化Vue类似,只是在实例化Vue的过程中,额外走了一个创建组件的分支,其中createComponent方法实现如下:

  1. export function createComponent (
  2. Ctor: Class<Component> | Function | Object | void,
  3. data: ?VNodeData,
  4. context: Component,
  5. children: ?Array<VNode>,
  6. tag?: string
  7. ): VNode | Array<VNode> | void {
  8. if (isUndef(Ctor)) {
  9. return
  10. }
  11. // 获取Vue基础构造函数,在initGlobal中,将vue基础构造方法赋值给_base属性
  12. const baseCtor = context.$options._base
  13.  
  14. // plain options object: turn it into a constructor
  15. if (isObject(Ctor)) {
  16. // 将组件的配置,合并到构造方法中,extend是定义在Vue构造方法中的
  17. Ctor = baseCtor.extend(Ctor)
  18. }
  19.  
  20. // if at this stage it's not a constructor or an async component factory,
  21. // reject.
  22. if (typeof Ctor !== 'function') {
  23. if (process.env.NODE_ENV !== 'production') {
  24. warn(`Invalid Component definition: ${String(Ctor)}`, context)
  25. }
  26. return
  27. }
  28.  
  29. // async component
  30. let asyncFactory
  31. if (isUndef(Ctor.cid)) {
  32. asyncFactory = Ctor
  33. Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
  34. if (Ctor === undefined) {
  35. // return a placeholder node for async component, which is rendered
  36. // as a comment node but preserves all the raw information for the node.
  37. // the information will be used for async server-rendering and hydration.
  38. return createAsyncPlaceholder(
  39. asyncFactory,
  40. data,
  41. context,
  42. children,
  43. tag
  44. )
  45. }
  46. }
  47.  
  48. data = data || {}
  49.  
  50. // resolve constructor options in case global mixins are applied after
  51. // component constructor creation
  52. resolveConstructorOptions(Ctor)
  53.  
  54. // transform component v-model data into props & events
  55. if (isDef(data.model)) {
  56. transformModel(Ctor.options, data)
  57. }
  58.  
  59. // extract props
  60. const propsData = extractPropsFromVNodeData(data, Ctor, tag)
  61.  
  62. // functional component
  63. if (isTrue(Ctor.options.functional)) {
  64. return createFunctionalComponent(Ctor, propsData, data, context, children)
  65. }
  66.  
  67. // extract listeners, since these needs to be treated as
  68. // child component listeners instead of DOM listeners
  69. const listeners = data.on
  70. // replace with listeners with .native modifier
  71. // so it gets processed during parent component patch.
  72. data.on = data.nativeOn
  73.  
  74. if (isTrue(Ctor.options.abstract)) {
  75. // abstract components do not keep anything
  76. // other than props & listeners & slot
  77.  
  78. // work around flow
  79. const slot = data.slot
  80. data = {}
  81. if (slot) {
  82. data.slot = slot
  83. }
  84. }
  85.  
  86. // install component management hooks onto the placeholder node
  87. // 初始化组件的钩子函数
  88. installComponentHooks(data)
  89.  
  90. // return a placeholder vnode
  91. // 体现了组件名称在这里面的作用
  92. const name = Ctor.options.name || tag
  93. // 创建vnode
  94. const vnode = new VNode(
  95. `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
  96. data, undefined, undefined, undefined, context,
  97. { Ctor, propsData, listeners, tag, children },
  98. asyncFactory
  99. )
  100.  
  101. // Weex specific: invoke recycle-list optimized @render function for
  102. // extracting cell-slot template.
  103. // https://github.com/Hanks10100/weex-native-directive/tree/master/component
  104. /* istanbul ignore if */
  105. if (__WEEX__ && isRecyclableComponent(vnode)) {
  106. return renderRecyclableComponentTemplate(vnode)
  107. }
  108.  
  109. return vnode
  110. }

  从上述代码中可以看出,createComponent主要作用就是返回一个vnode,中间的流程主要作用有两点,一是组装组件的构造方法,用于实例化组件,另外一点就是调用installComponentHooks,初始化组件的生命周期入口。组件的声明周期钩子虽然与vue根实例一致,但是调用的位置还是有一定的差别,具体有以下几点:

  1. Vue构造方法是在src\core\instance\index.js中,而组件的构造方法是基于Vue根构造方法,在上述createComponet中调用Vue.extend方法进行组装而成,本质上都是调用Vue实例上的_init方法,但是组件的构造方法VueComponent声明了一些属于自己的自定义属性,具体实现代码如下:

  1. Vue.extend = function (extendOptions: Object): Function {
  2. extendOptions = extendOptions || {}
  3. const Super = this
  4. // 父级实例cid
  5. const SuperId = Super.cid
  6. const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  7. if (cachedCtors[SuperId]) {
  8. return cachedCtors[SuperId]
  9. }
  10.  
  11. const name = extendOptions.name || Super.options.name
  12. if (process.env.NODE_ENV !== 'production' && name) {
  13. validateComponentName(name)
  14. }
  15. // 定义vue初始化方法,和实例化Vue走同一个路线
  16. const Sub = function VueComponent (options) {
  17. this._init(options)
  18. }
  19. // super -> this -> Vue 继承Vue构造方法中的属性
  20. Sub.prototype = Object.create(Super.prototype)
  21. // 指定子组件的构造方法为Sub -> VueComponent
  22. Sub.prototype.constructor = Sub
  23. Sub.cid = cid++
  24. // 合并组件属性
  25. Sub.options = mergeOptions(
  26. Super.options,
  27. extendOptions
  28. )
  29. // 定义父级作用域
  30. Sub['super'] = Super
  31.  
  32. // For props and computed properties, we define the proxy getters on
  33. // the Vue instances at extension time, on the extended prototype. This
  34. // avoids Object.defineProperty calls for each instance created.
  35. if (Sub.options.props) {
  36. initProps(Sub)
  37. }
  38. if (Sub.options.computed) {
  39. initComputed(Sub)
  40. }
  41.  
  42. // allow further extension/mixin/plugin usage
  43. // 子组件的实例,保持对vue构造方法的引用
  44. Sub.extend = Super.extend
  45. Sub.mixin = Super.mixin
  46. Sub.use = Super.use
  47.  
  48. // create asset registers, so extended classes
  49. // can have their private assets too.
  50. ASSET_TYPES.forEach(function (type) {
  51. Sub[type] = Super[type]
  52. })
  53. // enable recursive self-lookup
  54. if (name) {
  55. Sub.options.components[name] = Sub
  56. }
  57.  
  58. // keep a reference to the super options at extension time.
  59. // later at instantiation we can check if Super's options have
  60. // been updated.
  61. Sub.superOptions = Super.options
  62. Sub.extendOptions = extendOptions
  63. Sub.sealedOptions = extend({}, Sub.options)
  64.  
  65. // cache constructor
  66. cachedCtors[SuperId] = Sub
  67. return Sub
  68. }
  69. }

  2. Vue根实例的模板解析与DOM挂载入口不一致,在_init方法中,提供了对根实例的模板解析与DOM挂载,而组件没有。在创建组件时,调用了installComponentHooks,componet hooks主要包含init、prepatch、insert、destory,init在实例化组件时调用,insert是插入DOM时调用,destory是在销毁组件时调用,而prepatch是在更新组件时调用,具体如下:

  1. const componentVNodeHooks = {
  2. // 组件初始化方法
  3. init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
  4. if (
  5. vnode.componentInstance &&
  6. !vnode.componentInstance._isDestroyed &&
  7. vnode.data.keepAlive
  8. ) {
  9. // kept-alive components, treat as a patch
  10. const mountedNode: any = vnode // work around flow
  11. componentVNodeHooks.prepatch(mountedNode, mountedNode)
  12. } else {
  13. // 实例化组件
  14. const child = vnode.componentInstance = createComponentInstanceForVnode(
  15. vnode,
  16. activeInstance
  17. )
  18. //挂载组件
  19. child.$mount(hydrating ? vnode.elm : undefined, hydrating)
  20. }
  21. },
  22.  
  23. prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
  24. const options = vnode.componentOptions
  25. const child = vnode.componentInstance = oldVnode.componentInstance
  26. updateChildComponent(
  27. child,
  28. options.propsData, // updated props
  29. options.listeners, // updated listeners
  30. vnode, // new parent vnode
  31. options.children // new children
  32. )
  33. },
  34.  
  35. insert (vnode: MountedComponentVNode) {
  36. const { context, componentInstance } = vnode
  37. if (!componentInstance._isMounted) {
  38. componentInstance._isMounted = true
  39. callHook(componentInstance, 'mounted')
  40. }
  41. if (vnode.data.keepAlive) {
  42. if (context._isMounted) {
  43. // vue-router#1212
  44. // During updates, a kept-alive component's child components may
  45. // change, so directly walking the tree here may call activated hooks
  46. // on incorrect children. Instead we push them into a queue which will
  47. // be processed after the whole patch process ended.
  48. queueActivatedComponent(componentInstance)
  49. } else {
  50. activateChildComponent(componentInstance, true /* direct */)
  51. }
  52. }
  53. },
  54.  
  55. destroy (vnode: MountedComponentVNode) {
  56. const { componentInstance } = vnode
  57. if (!componentInstance._isDestroyed) {
  58. if (!vnode.data.keepAlive) {
  59. componentInstance.$destroy()
  60. } else {
  61. deactivateChildComponent(componentInstance, true /* direct */)
  62. }
  63. }
  64. }
  65. }

  如上述代码所示,实例化组件调用的是createComponentInstanceForVnode,createComponentInstanceForVnode代码如下,调用在Vue.extend中组装的组件构造方法VueComponent,初始化调用的还是Vue原型上的_init方法,大致流程与Vue初始化类似,只是解析模板有所区别,组件解析模板调用的是child.$mount。

  1. // 创建组件的作用域,执行组件的_init方法,同vue实例化过程
  2. export function createComponentInstanceForVnode (
  3. vnode: any, // we know it's MountedComponentVNode but flow doesn't
  4. parent: any, // activeInstance in lifecycle state
  5. ): Component {
  6. const options: InternalComponentOptions = {
  7. _isComponent: true,
  8. _parentVnode: vnode,
  9. parent
  10. }
  11. // check inline-template render functions
  12. const inlineTemplate = vnode.data.inlineTemplate
  13. if (isDef(inlineTemplate)) {
  14. options.render = inlineTemplate.render
  15. options.staticRenderFns = inlineTemplate.staticRenderFns
  16. }
  17. // 实例化组件的构造方法
  18. return new vnode.componentOptions.Ctor(options)
  19. }

  在installComponentHooks中,在vnode的data属性中初始化了hooks,后面在_patch__中,会调用patch.js中声明的createComponent -> init -> 实例化组件。组件实例化完成后,会将真实DOM元素,插入到上一级元素。patch.js中的createComponent方法如下:

  1. // 创建组件,如果节点类型是组件,则直接走创建组件的方法
  2. function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
  3. let i = vnode.data
  4. // 判断是否存在组件的生命周期,存在,即需要走创建组件的流程
  5. if (isDef(i)) {
  6. const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
  7. if (isDef(i = i.hook) && isDef(i = i.init)) {
  8. // 执行component的init方法,获取组件的实例
  9. i(vnode, false /* hydrating */)
  10. }
  11. // after calling the init hook, if the vnode is a child component
  12. // it should've created a child instance and mounted it. the child
  13. // component also has set the placeholder vnode's elm.
  14. // in that case we can just return the element and be done.
  15. // 组件的vnode对象中存在当前组件的作用域
  16. if (isDef(vnode.componentInstance)) {
  17. initComponent(vnode, insertedVnodeQueue)
  18. // 将子组件插入到父节点中
  19. insert(parentElm, vnode.elm, refElm)
  20. if (isTrue(isReactivated)) {
  21. reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
  22. }
  23. return true
  24. }
  25. }
  26. }

  在实例化完成后,会将生成的真实DOM元素插入到上级元素中,vue在获取真实DOM时,是从低往上,一级级添加,最终将渲染的元素添加到DOM body中,__patch__主流程如下:

  1. function patch (oldVnode, vnode, hydrating, removeOnly) {
  2. if (isUndef(vnode)) {
  3. if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
  4. return
  5. }
  6.  
  7. let isInitialPatch = false
  8. const insertedVnodeQueue = []
  9.  
  10. if (isUndef(oldVnode)) {
  11. // empty mount (likely as component), create new root element
  12. isInitialPatch = true
  13. createElm(vnode, insertedVnodeQueue)
  14. } else {
  15. const isRealElement = isDef(oldVnode.nodeType)
  16. if (!isRealElement && sameVnode(oldVnode, vnode)) {
  17. // patch existing root node
  18. patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
  19. } else {
  20. if (isRealElement) {
  21. // mounting to a real element
  22. // check if this is server-rendered content and if we can perform
  23. // a successful hydration.
  24. // nodeType 1 元素 3 文字
  25. if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
  26. oldVnode.removeAttribute(SSR_ATTR)
  27. hydrating = true
  28. }
  29. if (isTrue(hydrating)) {
  30. if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
  31. invokeInsertHook(vnode, insertedVnodeQueue, true)
  32. return oldVnode
  33. } else if (process.env.NODE_ENV !== 'production') {
  34. warn(
  35. 'The client-side rendered virtual DOM tree is not matching ' +
  36. 'server-rendered content. This is likely caused by incorrect ' +
  37. 'HTML markup, for example nesting block-level elements inside ' +
  38. '<p>, or missing <tbody>. Bailing hydration and performing ' +
  39. 'full client-side render.'
  40. )
  41. }
  42. }
  43. // either not server-rendered, or hydration failed.
  44. // create an empty node and replace it
  45. oldVnode = emptyNodeAt(oldVnode)
  46. }
  47.  
  48. // replacing existing element
  49. // 获取老旧节点
  50. const oldElm = oldVnode.elm
  51. // 获取老旧节点的父节点
  52. const parentElm = nodeOps.parentNode(oldElm)
  53.  
  54. // create new node
  55. // 将虚拟DOM转换成真实DOM
  56. // 传入父级节点,一级级添加
  57. createElm(
  58. vnode,
  59. insertedVnodeQueue,
  60. // extremely rare edge case: do not insert if old element is in a
  61. // leaving transition. Only happens when combining transition +
  62. // keep-alive + HOCs. (#4590)
  63. oldElm._leaveCb ? null : parentElm,
  64. nodeOps.nextSibling(oldElm)
  65. )
  66.  
  67. // update parent placeholder node element, recursively
  68. if (isDef(vnode.parent)) {
  69. let ancestor = vnode.parent
  70. const patchable = isPatchable(vnode)
  71. while (ancestor) {
  72. for (let i = 0; i < cbs.destroy.length; ++i) {
  73. cbs.destroy[i](ancestor)
  74. }
  75. ancestor.elm = vnode.elm
  76. if (patchable) {
  77. for (let i = 0; i < cbs.create.length; ++i) {
  78. cbs.create[i](emptyNode, ancestor)
  79. }
  80. // #6513
  81. // invoke insert hooks that may have been merged by create hooks.
  82. // e.g. for directives that uses the "inserted" hook.
  83. const insert = ancestor.data.hook.insert
  84. if (insert.merged) {
  85. // start at index 1 to avoid re-invoking component mounted hook
  86. for (let i = 1; i < insert.fns.length; i++) {
  87. insert.fns[i]()
  88. }
  89. }
  90. } else {
  91. registerRef(ancestor)
  92. }
  93. ancestor = ancestor.parent
  94. }
  95. }
  96.  
  97. // destroy old node
  98. // 移除老旧节点
  99. if (isDef(parentElm)) {
  100. removeVnodes([oldVnode], 0, 0)
  101. } else if (isDef(oldVnode.tag)) {
  102. invokeDestroyHook(oldVnode)
  103. }
  104. }
  105. }
  106.  
  107. invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
  108. return vnode.elm
  109. }

  模板的解析,是先把模板解析成HTML,然后再讲老旧节点移除。

 

vue组件初始化过程的更多相关文章

  1. 谁先执行?props还是data或是其他? vue组件初始化的执行顺序详解

    初入vue的朋友可能会疑惑,组件初始化的时候,created,props,data到底谁先执行? 今天,我就带大家从源码的角度看看到底谁先执行? 我们知道,vue是个实例 那我们就从new Vue() ...

  2. JS vue 组件创建过程

    https://www.jianshu.com/p/3504a1edba42 vue.js原生组件化开发(一)——组件开发基础 0.3472017.05.09 12:00:54字数 1120阅读 33 ...

  3. 封装 vue 组件的过程

    首先,组件可以提升整个项目的开发效率.能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发的缺点:效率低,难维护,复用性等问题: 然后,使用Vue.extend方法创建一个组件,然后使用 Vue ...

  4. Vue初始化过程

    用vue也有一两年了,始终对vue一知半解,不怎么了解内部的执行过程,最近在看vue源码,还是不少收获的,其中不乏浏览器事件轮询机制.闭包.设计模式等,还是非常值得一读.本篇简要记录下vue的初始化过 ...

  5. Vue 源码解读(2)—— Vue 初始化过程

    当学习成为了习惯,知识也就变成了常识. 感谢各位的 点赞.收藏和评论. 新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn 文章已收录到 github 仓库 liyongning/blog ...

  6. Vue源码翻译之组件初始化。

    废话不多说. 我们先来看看Vue的入口文件. import { initMixin } from './init' import { stateMixin } from './state' impor ...

  7. vue 快速入门 系列 —— Vue 实例的初始化过程

    其他章节请看: vue 快速入门 系列 Vue 实例的初始化过程 书接上文,每次调用 new Vue() 都会执行 Vue.prototype._init() 方法.倘若你看过 jQuery 的源码, ...

  8. Spring MVC源码(一) ----- 启动过程与组件初始化

    SpringMVC作为MVC框架近年来被广泛地使用,其与Mybatis和Spring的组合,也成为许多公司开发web的套装.SpringMVC继承了Spring的优点,对业务代码的非侵入性,配置的便捷 ...

  9. JS组件系列——又一款MVVM组件:Vue(二:构建自己的Vue组件)

    前言:转眼距离上篇 JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查) 已有好几个月了,今天打算将它捡起来,发现好久不用,Vue相关技术点都生疏不少.经过这几个月的时间,Vue ...

随机推荐

  1. no matches for kind "Deployment" in version "extensions/v1beta1"

    0x00 Problem [root@k8sm90 demo]# kubectl create -f tomcat-deployment.yaml error: unable to recognize ...

  2. 侠梦说pinpoint--左侧服务地图调用量和WasOn过滤

    前言 这篇文章主要是从pinpoint-web界面入手,我们的目标是弄清楚两个问题: 1. pinpoint左侧服务地图上的调用量数据是怎么查询的? 2.界面查询条件WasOnly是什么意思? 左侧服 ...

  3. linux 精确延时

    void HeartBeat_Check_TASK(void *pdata){ struct timeval tv; struct timespec ts; int err; U32 dwcount= ...

  4. Django杂录

    Django杂录 因为是概括性的讲解,每一个方面没有具体到点,所以这篇是杂录 HHTP协议 超文本传输协议 四大特性 基于TCP/IP之上作用于应用层 基于socket请求响应 无状态 无连接 数据格 ...

  5. 通过哪吒动漫豆瓣影评,带你分析python爬虫与BeautifulSoup快速入门【华为云技术分享】

    久旱逢甘霖 西安连着几天温度排行全国三甲,也许是<哪吒之魔童降世>的剧组买通了老天,从踩着风火轮的小朋友首映开始,就全国性的持续高温,还好今天凌晨的一场暴雨,算是将大家从中暑边缘拯救回来了 ...

  6. 华为云EI人脸识别接口初探

    0. 准备工作 开户及申请开通人脸识别服务,可以参考https://education.huaweicloud.com:8443/courses/course-v1:HuaweiX+CBUCNXE01 ...

  7. Windows下创建Python虚拟环境的两种方法:

    在实际的项目开发中,我们会根据自己的需求去下载各种相应的框架库,但是每个项目可能使用的库不一样,或者版本不一样等等等.为了避免这些因素对我们的项目造成一些不必要的影响,我们可能需要来回的切换或者装卸等 ...

  8. SpringBoot-运行原理(四)

    1.自动配置 (1).pom.xml 在pom文件中 <parent> <groupId>org.springframework.boot</groupId> &l ...

  9. luogu P4302 [SCOI2003]字符串折叠

    题目描述 折叠的定义如下: 一个字符串可以看成它自身的折叠.记作S = S X(S)是X(X>1)个S连接在一起的串的折叠.记作X(S) = SSSS-S(X个S). 如果A = A', B = ...

  10. iOS 网络基本剖析

    一.网络通信的本质 数据传输,数据交换 Client     <======>  服务器 二.HTTP.TCP,IP.UDP.Socket关系刨析 Socket:套接字,是一个用于网络传输 ...