入口文件

80 阅读1分钟

入口文件

上章了解到入口web入口文件是entry-runtime-with-compiler ,那么这个文件做了什么呢?

主要代码为导入依赖文件,实现mount函数和导出Vue。导入和导出暂不做了解,主要了解了解mount的实现

一些校验

// 挂载的节点不能直接在body或者html上面 
if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
// 处理之后的template不可为空
if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
// template必须符合既定的几种规则,否则报错        
if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this

处理template

// 如果没有render函数
if (!options.render) {
    let template = options.template
    // 有template模版
    if (template) {
      if (typeof template === 'string') { // 如果template是字符串按照字符串处理
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) { // 如果template是dom对象
        template = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) { // 没有template但是有el
      template = getOuterHTML(el)
    }
    if (template) {
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }
        /// 按照处理完之后的template生成render函数
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile end')
        measure(`vue ${this._name} compile`, 'compile', 'compile end')
      }
    }
  }

drawio

image.png

上面的template主要对vue实例的options的template进行了判断处理,有render则按照传入的render函数处理,没有render则根据传入的option(template/el)生成相应的render函数。下面是几种template的四种写法,对应上面图中的四种:

第一种,没有template但是有el

 <div id="app">
        <div>
            姓名: {{ name }}
        </div>
    </div>

    const vm = new Vue({
        el: '#app',
        data: {
            name: 'ludeng',
        }
    });

第二种,template以#开始

 <template id="tmp">
        <div>
            姓名: {{ name }}
        </div>
    </template>

    const vm = new Vue({
        el: '#app',
        template:'#tmp',
        data: {
            name: 'ludeng',
        }
    });

第三种,template是字符串且不以#开头

<div id="app"></div>

const vm = new Vue({
        el: '#app',
        template:`<div>姓名:{{ name }}<div>`,
        data: {
            name: 'ludeng',
        }
    });

第四种,template是一个dom对象

document.querySelector('#tmp')

    <div id="app"></div>
    <div  id="tmp">
        <div>
            姓名: {{ name }}
        </div>
    </div>

const vm = new Vue({
        el: '#app',
        template:document.querySelector('#tmp'),
        data: {
            name: 'ludeng',
        }
    });

compileToFunctions生成render函数

import { compileToFunctions } from './compiler/index'
// 这个函数来源于platforms/web/compiler/index.js

// platforms/web/compiler/index.js
import { createCompiler } from 'compiler/index'
const { compile, compileToFunctions } = createCompiler(baseOptions)


// compiler/index 这里有parse,optimize,generate函数核心
import { createCompilerCreator } from './create-compiler'
export const createCompiler = createCompilerCreator(function baseCompile (参数){...})


// /create-compiler 这里有compile函数核心
在这里的createCompilerCreator 函数调用了createCompileToFunctionFn函数
 return {
      compile,
      compileToFunctions: createCompileToFunctionFn(compile)
    }


// createCompileToFunctionFn 函数在to-function文件中
export function createCompileToFunctionFn (compile: Function): Function {
    return function compileToFunctions (){...}
}


drawio

image转存失败,建议直接上传图片文件

所以实际上的核心代码还是在conmpiler/index文件中

compiler/to-function文件

入口文件entry-runtime-with-compiler执行的compileToFunctions的实现,其实就是该文件下的createCompileToFunctionFn 函数,函数内部主要执行了compile函数,compile函数来源于入参,而调用入参在 compiler/create-compiler文件中

export function createCompileToFunctionFn (compile: Function): Function {
    const compiled = compile(template, options)
} 

compiler/create-compiler文件

主要执行了baseCompile函数,该函数是函数来源于compiler/index文件中

export function createCompilerCreator (baseCompile: Function): Function {
  return function createCompiler (baseOptions: CompilerOptions) {
    function compile ( template, options ) {
    ...
     const compiled = baseCompile(template.trim(), finalOptions)
    ...
    }
    return {
      compile,
      compileToFunctions: createCompileToFunctionFn(compile)
    }
  }     
}

compiler/index文件

执行了compiler/create-compiler文件中的createCompilerCreator函数,入参如下

function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  const ast = parse(template.trim(), options) // 重要
  if (options.optimize !== false) {
    optimize(ast, options) // 重要
  }
  const code = generate(ast, options) // 重要
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }

总结

  1. 入口文件首先导入了Vue构造函数,该构造函数实际上在生成的时候做了一系列处理,但是这里先不解析
  2. 入口文件实现了$mount函数,该函数主要是对生成示例的options的template做一些检测(是否符合既定的template规则)和处理(生成相应的render函数)
  3. 生成render这个步骤,最核心的代码实际上是compiler/index文件中的createCompilerCreator函数的参数函数,改函数生成了ast和render函数