中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

詳解vue掛載到dom上會發生什么

發布時間:2020-09-23 11:01:12 來源:腳本之家 閱讀:208 作者:跟上大部隊 欄目:web開發

vue 掛載到dom 元素后發生了什么

前一篇文章分析了new vue() 初始化時所執行的操作,主要包括調用vue._init 執行一系列的初始化,包括生命周期,事件系統,beforeCreate和Created hook,在在這里發生,重點分析了 initState,即對我們常用到的data props computed 等等進行的初始化,最后,執行$mount 對dom進行了掛載,本篇文章將對掛載后所發生的事情進行進一步闡述,

Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && inBrowser ? query(el) : undefined
 return mountComponent(this, el, hydrating)
}

mount 的代碼很簡單,直接執行了moutComponent方法,

export function mountComponent (
 vm: Component,
 el: ?Element,
 hydrating?: boolean
): Component {
 vm.$el = el
 if (!vm.$options.render) {
 vm.$options.render = createEmptyVNode
 if (process.env.NODE_ENV !== 'production') {
  /* istanbul ignore if */
  if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
  vm.$options.el || el) {
  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.',
   vm
  )
  } else {
  warn(
   'Failed to mount component: template or render function not defined.',
   vm
  )
  }
 }
 }
 callHook(vm, 'beforeMount')

 let updateComponent
 /* istanbul ignore if */
 if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
 updateComponent = () => {
  const name = vm._name
  const id = vm._uid
  const startTag = `vue-perf-start:${id}`
  const endTag = `vue-perf-end:${id}`

  mark(startTag)
  const vnode = vm._render()
  mark(endTag)
  measure(`vue ${name} render`, startTag, endTag)

  mark(startTag)
  vm._update(vnode, hydrating)
  mark(endTag)
  measure(`vue ${name} patch`, startTag, endTag)
 }
 } else {
 updateComponent = () => {
  vm._update(vm._render(), hydrating)
 }
 }

 // we set this to vm._watcher inside the watcher's constructor
 // since the watcher's initial patch may call $forceUpdate (e.g. inside child
 // component's mounted hook), which relies on vm._watcher being already defined
 new Watcher(vm, updateComponent, noop, {
 before () {
  if (vm._isMounted && !vm._isDestroyed) {
  callHook(vm, 'beforeUpdate')
  }
 }
 }, true /* isRenderWatcher */)
 hydrating = false

 // manually mounted instance, call mounted on self
 // mounted is called for render-created child components in its inserted hook
 if (vm.$vnode == null) {
 vm._isMounted = true
 callHook(vm, 'mounted')
 }
 return vm
}

moutComponent 這里判斷了render函數,正常開發過程中,對于dom的寫法有很多種,可以直接寫templete,也可以寫render函數,也可以直接把dom寫在掛載元素里面,但是在編譯階段(通常是通過webpack執行的),統統會把這些寫法都編譯成render函數,所以,最后執行的都是render函數,判斷完render可以看到,beforeMount hook在這里執行,最后執行了new Watcher() 我們進入new Watcher

export default class Watcher {
 vm: Component;
 expression: string;
 cb: Function;
 id: number;
 deep: boolean;
 user: boolean;
 lazy: boolean;
 sync: boolean;
 dirty: boolean;
 active: boolean;
 deps: Array<Dep>;
 newDeps: Array<Dep>;
 depIds: SimpleSet;
 newDepIds: SimpleSet;
 before: ?Function;
 getter: Function;
 value: any;

 constructor (
 vm: Component,
 expOrFn: string | Function,
 cb: Function,
 options?: ?Object,
 isRenderWatcher?: boolean
 ) {
 this.vm = vm
 if (isRenderWatcher) {
  vm._watcher = this
 }
 vm._watchers.push(this)
 // options
 if (options) {
  this.deep = !!options.deep
  this.user = !!options.user
  this.lazy = !!options.lazy
  this.sync = !!options.sync
  this.before = options.before
 } else {
  this.deep = this.user = this.lazy = this.sync = false
 }
 this.cb = cb
 this.id = ++uid // uid for batching
 this.active = true
 this.dirty = this.lazy // for lazy watchers
 this.deps = []
 this.newDeps = []
 this.depIds = new Set()
 this.newDepIds = new Set()
 this.expression = process.env.NODE_ENV !== 'production'
  ? expOrFn.toString()
  : ''
 // parse expression for getter
 if (typeof expOrFn === 'function') {
  this.getter = expOrFn
 } else {
  this.getter = parsePath(expOrFn)
  if (!this.getter) {
  this.getter = noop
  process.env.NODE_ENV !== 'production' && warn(
   `Failed watching path: "${expOrFn}" ` +
   'Watcher only accepts simple dot-delimited paths. ' +
   'For full control, use a function instead.',
   vm
  )
  }
 }
 this.value = this.lazy
  ? undefined
  : this.get()
 }

其他方法暫時不提,可以看到,但是我們也能大致猜到他在做些什么,這里只是截取了部分Watcher的構造方法,,重點是最后執行了this.get 而this.get則執行了this.getter,最后等于執行了Watcher構造方法中傳入的第二個參數,也就是上一環節moutComponent中的updateComponent方法,updateComponent方法也是在moutComponent方法中定義

updateComponent = () => {
 vm._update(vm._render(), hydrating)
 }

這里先是執行編譯而成的render方法,然后作為參數傳到_update方法中執行,render方法執行后返回一個vnode 即Virtual dom,然后將這個Virtual dom作為參數傳到update方法中,這里我們先介紹一下Virtual dom 然后在介紹最后執行掛載的update方法,

render函數

Vue.prototype._render = function (): VNode {
 const vm: Component = this
 const { render, _parentVnode } = vm.$options

 if (_parentVnode) {
  vm.$scopedSlots = normalizeScopedSlots(
  _parentVnode.data.scopedSlots,
  vm.$slots
  )
 }

 // set parent vnode. this allows render functions to have access
 // to the data on the placeholder node.
 vm.$vnode = _parentVnode
 // render self
 let vnode
 try {
  vnode = render.call(vm._renderProxy, vm.$createElement)
 } catch (e) {
  handleError(e, vm, `render`)
  // return error render result,
  // or previous vnode to prevent render error causing blank component
  /* istanbul ignore else */
  if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
  try {
   vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
  } catch (e) {
   handleError(e, vm, `renderError`)
   vnode = vm._vnode
  }
  } else {
  vnode = vm._vnode
  }
 }
 // if the returned array contains only a single node, allow it
 if (Array.isArray(vnode) && vnode.length === 1) {
  vnode = vnode[0]
 }
 // return empty vnode in case the render function errored out
 if (!(vnode instanceof VNode)) {
  if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
  warn(
   'Multiple root nodes returned from render function. Render function ' +
   'should return a single root node.',
   vm
  )
  }
  vnode = createEmptyVNode()
 }
 // set parent
 vnode.parent = _parentVnode
 return vnode
 }

根據flow 的類型定義,我們可以看到,_render函數最后返回一個vnode,_render主要代碼 在第一個try catch中,vnode = render.call(vm._renderProxy,vm.$CREATRElement) ,第一個參數為當前上下文this 其實就是vm本身,第二個參數是實際執行的方法,當我們在手寫render函數時,比如這樣

 render:h=>{
  return h(
    "div",
    123
   )
  }

這時候我們使用的h 就是傳入的$createElement方法,然后我們來看一下createElement方法,在看creatElement之前,我們先簡單介紹一下vdom,因為createElement返回的就是一個vdom,vdom其實就是真實dom對象的一個映射,主要包含標簽名字tag 和在它下面的標簽 children 還有一些屬性的定義等等,當我們在進行dom改變時首先是數據的改變,數據的改變映射到 vdom中,然后改變vdom,改變vdom是對js數據層面的改變所以說代價很小,在這一過程中我們還可以進行針對性的優化,復用等,最后把優化后的改變部分通過dom操作操作到真實的dom上去,另外,通過vdom這層的定義我們不僅僅可以把vdom映射到web文檔流上,甚至可以映射到app端的文檔流,桌面應用的文檔流多種,這里引用一下vue js作者對vdom的評價:Virtual DOM真正價值從來不是性能,而是它 1: 為函數式的ui編程方式打開了大門,2 :可以渲染到dom以外的backend 比如 ReactNative 。

下面我們來繼續介紹creatElement

export function _createElement (
 context: Component,
 tag?: string | Class<Component> | Function | Object,
 data?: VNodeData,
 children?: any,
 normalizationType?: number
): VNode | Array<VNode> {
 if (isDef(data) && isDef((data: any).__ob__)) {
 process.env.NODE_ENV !== 'production' && warn(
  `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
  'Always create fresh vnode data objects in each render!',
  context
 )
 return createEmptyVNode()
 }
 // object syntax in v-bind
 if (isDef(data) && isDef(data.is)) {
 tag = data.is
 }
 if (!tag) {
 // in case of component :is set to falsy value
 return createEmptyVNode()
 }
 // warn against non-primitive key
 if (process.env.NODE_ENV !== 'production' &&
 isDef(data) && isDef(data.key) && !isPrimitive(data.key)
 ) {
 if (!__WEEX__ || !('@binding' in data.key)) {
  warn(
  'Avoid using non-primitive value as key, ' +
  'use string/number value instead.',
  context
  )
 }
 }
 // support single function children as default scoped slot
 if (Array.isArray(children) &&
 typeof children[0] === 'function'
 ) {
 data = data || {}
 data.scopedSlots = { default: children[0] }
 children.length = 0
 }
 if (normalizationType === ALWAYS_NORMALIZE) {
 children = normalizeChildren(children)
 } else if (normalizationType === SIMPLE_NORMALIZE) {
 children = simpleNormalizeChildren(children)
 }
 let vnode, ns
 if (typeof tag === 'string') {
 let Ctor
 ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
 if (config.isReservedTag(tag)) {
  // platform built-in elements
  vnode = new VNode(
  config.parsePlatformTagName(tag), data, children,
  undefined, undefined, context
  )
 } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
  // component
  vnode = createComponent(Ctor, data, context, children, tag)
 } else {
  // unknown or unlisted namespaced elements
  // check at runtime because it may get assigned a namespace when its
  // parent normalizes children
  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()
 }
}

creatElement 最后結果時返回一個new VNode,并將craete時傳入的參數,經過處理,傳到VNode的初始化中,這里有幾種情況,createEmptyVNode,沒有傳參數,或參數錯誤,會返回一個空的vnode,如果tag 時瀏覽器的標簽如div h4 p等,會返回一個保留VNode,等等,最后,回到上面,vnode 創建完畢,_render會返回這個vnode,最后走回vm._update(),update 中,便是將vnode 通過dom操作插入到真正的文檔流中,下一節我們聊聊update

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平湖市| 长乐市| 柘荣县| 太康县| 项城市| 台安县| 美姑县| 庆云县| 板桥市| 禹州市| 财经| 昌图县| 广汉市| 阿巴嘎旗| 肇庆市| 井陉县| 隆回县| 图木舒克市| 庆元县| 古蔺县| 略阳县| 宜君县| 永济市| 蛟河市| 桐庐县| 山西省| 阿勒泰市| 孟连| 安仁县| 旌德县| 皮山县| 兴文县| 鞍山市| 北流市| 察隅县| 涟源市| 两当县| 凤城市| 山东省| 咸阳市| 和政县|