vue源码分析(四)>>:compile
今天分析一下$mount后编译文档节点的执行流程,看下指令和插值都是怎么解析的
1. 执行流程
> 在create钩子函数执行完毕后,判断如果传了el,就走进:vm.$mount(vm.$options.el);//5088行 方法;
> 进入$mount方法,需要注意的是vue中定义了两个$mount方法,断点发现是先执行11977这一个然后执行9132这一个;
> 执行第一个$mount时候又走了11757行的compileToFunctions方法,在这个方法里执行了11857行的compile方法;
> 然后执行第二个$mount,然后执行mountComponent,在这里执行beforeMount钩子函数,这里创建watcher,然后执行mounted钩子函数;
2. 各流程分析
>2.1 11977行这个$mount方法:没有什么实质性的东西
var mount = Vue.prototype.$mount; // 改变$mount指向 Vue.prototype.$mount = function ( el, hydrating ) { debugger el = el && query(el);// 获取元素 // 这里看到el不能是body或者html if (el === document.body || el === document.documentElement) { warn( "Do not mount Vue to <html> or <body> - mount to normal elements instead." ); return this } var options = this.$options; // resolve template/el and convert to render function if (!options.render) { var template = options.template; if (template) { if (typeof template === 'string') { if (template.charAt(0) === '#') { template = idToTemplate(template); /* istanbul ignore if */ if (!template) { warn( ("Template element not found or is empty: " + (options.template)), this ); } } } else if (template.nodeType) { template = template.innerHTML;// 获取#app里边的内容 } else { { warn('invalid template option:' + template, this); } return this } } else if (el) { template = getOuterHTML(el); } if (template) { /* istanbul ignore if */ if (config.performance && mark) { mark('compile'); } var ref = compileToFunctions(template, { // 11794 outputSourceRange: "development" !== 'production', shouldDecodeNewlines: shouldDecodeNewlines, shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this); var render = ref.render; var staticRenderFns = ref.staticRenderFns; options.render = render; options.staticRenderFns = staticRenderFns; /* istanbul ignore if */ if (config.performance && mark) { mark('compile end'); measure(("vue " + (this._name) + " compile"), 'compile', 'compile end'); } } } return mount.call(this, el, hydrating)// 9138 };
> 2.2 再看11794行的compileToFunctions:这个方法没看明白什么csp什么catch什么根据render转化成方法的,继续接着看11857行的compile:在这里整合了一下配置信息:然后调用baseCompile:
var createCompiler = createCompilerCreator(function baseCompile ( template, options ) { // 把html字符串转化成对象形式 var ast = parse(template.trim(), options);// 9689 if (options.optimize !== false) { optimize(ast, options); } // 把解析好的html转化成render函数 var code = generate(ast, options); return { ast: ast, render: code.render, staticRenderFns: code.staticRenderFns } });
在这里将把html字符串转化成render函数然后返回generate里边很复杂以后再详细说;
> 2.3再看9138行的$mount:
Vue.prototype.$mount = function ( el, hydrating ) { el = el && inBrowser ? query(el) : undefined; return mountComponent(this, el, hydrating) };
> 2.4 再看4049行的mountComponent方法
function mountComponent ( vm, el, hydrating ) { vm.$el = el; callHook(vm, 'beforeMount'); var updateComponent = function () { 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: function 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 }
over