微信小程序有了_详解vue挂载到dom上会发生什么

摘要: 详细说明vue挂载到dom上面产生甚么 本文关键详细介绍了详细说明vue挂载到dom上面产生甚么,网编感觉挺好的,如今共享给大伙儿,也给大伙儿做下参照。一起追随网编回来看一下...

详解vue挂载到dom上会发生什么       这篇文章主要介绍了详解vue挂载到dom上会发生什么,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

前一篇文章分析了new vue() 初始化时所执行的操作,主要包括调用vue._init 执行一系列的初始化,包括生命周期,事件系统,beforeCreate和Created hook,在在这里发生,重点分析了 initState,puted 等等进行的初始化,最后,执行$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. pile the templates into ' +
 'render functions, or piler-included build.',
 } else {
 warn(
 'Failed ponent: template or render function not defined.',
 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-ponents 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;
 de凡科抠图: Array Dep 
 newDe凡科抠图: 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.de凡科抠图 = []
 this.newDe凡科抠图 = []
 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.',
 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 ponent
 /* 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.',
 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",

这时候我们使用的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 ponent :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 {
 // ponent 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 h3 p等,会返回一个保留VNode,等等,最后,回到上面,vnode 创建完毕,_render会返回这个vnode,最后走回vm._update(),update 中,便是将vnode 通过dom操作插入到真正的文档流中,下一节我们聊聊update

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持凡科。




联系我们

全国服务热线:4000-399-000 公司邮箱:343111187@qq.com

  工作日 9:00-18:00

关注我们

官网公众号

官网公众号

Copyright?2020 广州凡科互联网科技股份有限公司 版权所有 粤ICP备10235580号 客服热线 18720358503

技术支持:小程序开发