浅理解vue2中的模板编译

  1. vue组件实例在初始化完成各种状态数据后,会触发vm.$mount()方法来进行模板编译阶段,有两种触发方式
javascript 复制代码
// 方法一:主动触发 new Vue({ el: '#app' })
if (vm.$options.el) {
 vm.$mount(vm.$options.el);
}

// 方法二:手动调用 new Vue().$mount("#app")
  1. 生成render渲染函数:执行$mount(el)方法,获取dom元素,判断vm.options中是否有render函数,没有的话需要先获取template的HTML片段,再执行compileToFunctions方法生成render函数。拿到render函数后接着触发mountComponent函数的执行
javascript 复制代码
Vue.prototype.$mount = function (el) {
  // 获取dom
  el = el && query(el);
  let options = this.$options;
  // 没有render函数,则需要解析 模板/el 并生成render函数
  if (!options.render) {
    // 获取模板字符串
    let template = options.template;
    if (template) {
      if (typeof template === "string") {
        // 模板第一个字符串为# 则判断该字符串为dom的id
        if (template.charAt(0) === "#") {
          template = document.querySelector(template).innerHTML;
        }
      } else if (template.nodeType) {
        // template是dom元素
        template = template.innerHTML;
      }
    } else if (el) {
      // outerHTML:dom元素的序列化HTML片段
      template = el.outerHTML;
    }
    if (template) {
      let _a = compileToFunctions(template, { outputSourceRange: true }, this);
      options.render = _a.render;
      options.staticRenderFns = _a.staticRenderFns;
    }
  }
  return mountComponent(this, el, false);
};

// 获取dom元素
function query(el) {
  if (typeof el === "string") {
    var selected = document.querySelector(el);
    if (!selected) {
      return document.createElement("div");
    }
    return selected;
  } else {
    return el;
  }
}
  1. compileToFunctions方法中,生成render函数的步骤:①.parseHTML方法生成ast语法树,主要是利用正则与字符串方法循环处理HTML片段;②.generate(ast,options)方法生成render函数,主要是将ast语法树转换成render渲染函数,源码看不动了没贴
javascript 复制代码
let cache = Object.create(null);
function compileToFunctions(template, options, vm) {
  if (cache[template]) {
    return cache[template];
  }
  let compiled = compile(template, options);
  let res = {};
  let fnGenErrors = [];
  res.render = createFunction(compiled.render, fnGenErrors);
  res.staticRenderFns = compiled.staticRenderFns.map(function (code) {
    return createFunction(code, fnGenErrors);
  });
  return (cache[key] = res);
}

let baseOptions = {
  expectHTML: true,
  modules: modules,
  directives: directives,
  isPreTag: isPreTag,
  isUnaryTag: isUnaryTag,
  mustUseProp: mustUseProp,
  canBeLeftOpenTag: canBeLeftOpenTag,
  isReservedTag: isReservedTag,
  getTagNamespace: getTagNamespace,
  staticKeys: genStaticKeys$1(modules)
};

function compile(template, options) {
  let finalOptions = Object.create(baseOptions);
  if (options) {
    for (let key in options) {
      if (key !== 'modules' && key !== 'directives') {
          finalOptions[key] = options[key];
      }
    }
  }
  let compiled = baseCompile(template.trim(), finalOptions);
  return compiled;
}

// ......
  1. 执行mountComponent方法,首先是生成el,接着执行beforeMount生命周期中的函数,接着生成渲染watcher实例,主动触发watcher实例的getter方法,getter方法就是updateComponent。 在vm._render方法中生成了**虚拟dom** (vnode),在生成虚拟dom的过程中触发了render函数,在读取模板中的响应式数据时进而实现了响应式数据的依赖收集。 在vm._update方法中将虚拟dom转成真实dom节点,实现了**DOM的挂载**,并赋值给vm.el,核心方法是patch()方法
javascript 复制代码
function mountComponent(vm, el, hydrating) {
  vm.$el = el;
  callHook$1(vm, 'beforeMount');
  let updateComponent = function () {
    vm._update(vm._render(), hydrating);
  };
  new Watcher(vm, updateComponent, noop, watcherOptions, true);
}

function _update(vnode) {
  if (!prevVnode) {
    // initial render
    vm.$el = patch(vm.$el, vnode, hydrating, false /* removeOnly */);
  }
  else {
    // updates
    vm.$el = patch(prevVnode, vnode);
  }
}
  1. 接着执行callHook$1(vm, 'mounted')方法,触发mounted生命周期中的函数执行
javascript 复制代码
function mountComponent(vm, el, hydrating) {
  // ...
  new Watcher(vm, updateComponent, noop, watcherOptions, true);
  callHook$1(vm, 'mounted');
}

模板编译

  1. 获取template HTML片段,根据HTML片段生成ast语法树;
  2. 根据ast语法树生成rende渲染函数;
  3. 执行render函数生成虚拟dom(vnode);
  4. 将虚拟dom转成真实dom节点并挂载在页面上;
相关推荐
GDAL28 分钟前
为什么Cesium不使用vue或者react,而是 保留 Knockout
前端·vue.js·react.js
芜青43 分钟前
【Vue2手录11】Vue脚手架(@vue_cli)详解(环境搭建+项目开发示例)
前端·javascript·vue.js
麦麦大数据2 小时前
J002 Vue+SpringBoot电影推荐可视化系统|双协同过滤推荐算法评论情感分析spark数据分析|配套文档1.34万字
vue.js·spring boot·数据分析·spark·可视化·推荐算法
BillKu6 小时前
Vue3 + Element-Plus 抽屉关闭按钮居中
前端·javascript·vue.js
给月亮点灯|12 小时前
Vue基础知识-Vue集成 Element UI全量引入与按需引入
前端·javascript·vue.js
知识分享小能手12 小时前
React学习教程,从入门到精通,React 组件生命周期详解(适用于 React 16.3+,推荐函数组件 + Hooks)(17)
前端·javascript·vue.js·学习·react.js·前端框架·vue3
wow_DG13 小时前
【Vue2 ✨】Vue2 入门之旅 · 进阶篇(二):虚拟 DOM 与 Diff 算法
开发语言·javascript·vue.js·算法·前端框架
Hexene...14 小时前
【前端Vue】el-dialog关闭后黑色遮罩依然存在如何解决?
前端·javascript·vue.js·elementui·前端框架
Jay_See14 小时前
JC链客云——项目过程中获得的知识、遇到的问题及解决
前端·javascript·vue.js
叫我詹躲躲15 小时前
偷偷收藏!前端老鸟绝不外传的150个JS插件,让你效率翻3倍…
前端·vue.js