02-找到Vue的入口

我决定从scripts/config.js中的web-full-dev入手开始读

'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  }

其实我以前尝试过很多次,最后我发现,不应该直接读源码,而是应该读源码编译好的vue.js文件。为什么这样呢?因为模块化给出了维护的便利的同时,因为文件太多,有点分散。

先读吧,读不下去就换别的方式,条条大路通罗马嘛!!

找到入口文件web/entry-runtime-with-compiler.js,查看scripts/alias.js中知道web其实是src/platforms/web,最终入口就是src/platforms/web/entry-runtime-with-compiler.js

/* @flow */

import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'

import Vue from './runtime/index' // 原来万物始祖的Vue来自runtime/index
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'

// 给定一个id,在页面找到对应的dom节点,取其中的内容(innerHTML)作为模板字符串
const idToTemplate = cached(id => {
  const el = query(id)
  return el && el.innerHTML
})

// 为什么非得重写$mount?
// 当然是有原因的呀,对比entry-runtime.js就能知道
// 原始的$mount只负责挂载,根本不负责编译字符串模板为render函数
// 因为这个是entry-runtime-with-compiler
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el) // 找到挂载点

  /* istanbul ignore if */
  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
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) { // 没有明确指定render函数,就根据字符串模板生成render函数
    let template = options.template
    if (template) { // 设置中指定了template
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') { // 认为是dom的id
          template = idToTemplate(template) // 根据id去查找dom元素,取出里面的内容
          /* 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 = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) { // 去el中的内容作为字符串模板
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }

      // 从字符串模板编译成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')
      }
    }
  }
  return mount.call(this, el, hydrating)
}

/**
 * Get outerHTML of elements, taking care
 * of SVG elements in IE as well.
 * 如果拿不到el的outerHTML就把el包裹在一个div内,取div的innerHTML
 * 怎么那么聪明呢
 */
function getOuterHTML (el: Element): string {
  if (el.outerHTML) {
    return el.outerHTML
  } else {
    const container = document.createElement('div')
    container.appendChild(el.cloneNode(true))
    return container.innerHTML
  }
}

Vue.compile = compileToFunctions

export default Vue

感觉自己走偏了,从compileToFunctions开始,用了一个多小时,就再也没出来过,放弃吧,先创建一个空文章占位

看不下去编译器相关的,就从Vue开始吧,runtime/index开始咯

以上2020-03-23下午


突然觉得自己不应该从entry-runtime-with-compiler开始,我看不懂compiler,我应该直接从entry-runtime.js开始,只看运行时的就够了。能这块看懂了再看entry-runtime-with-compiler.js。做事情就要循序渐进,突然感觉自己长大了:)

import Vue from './runtime/index'

export default Vue

这个文件都是简单,言简意赅,直接把Vue放出了。跟entry-runtime-with-compiler.js对比一下就知道多的那些就是跟compiler相关的呗。

entry-runtime.js -> runtime/index

src/platform/web/runtime/index.js

/* @flow */

/**
 * 看到platform/web/runtine/index.js,让我想到了Vue的大致封装思路
 * core是平台无关的,封装虚拟DOM相关的内容。真正平台相关的就放到了platform中
 * 想在哪个平台运行,就开发哪个平台的移植就行了,只要能满足预先定义好的接口就行
 * 
 * 这个思路让我想到了webkit的架构,它本身也是不涉及到具体的绘图逻辑,而是使用移植
 * 提供给它的接口,真是妙!!!
 * 
 * 思路的开阔让我对Vue源码更感兴趣了:)
 */

import Vue from 'core/index' // 这就是平台无关的Vue
import config from 'core/config'
// 导入extend和noop函数,前者是简单的把对象b的属性拷贝到a上;后者是一个空函数
import { extend, noop } from 'shared/util'
// 
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'

// 导入几个函数
import {
  query, // 查询dom,接收string或Element,底层使用document.querySelector
  mustUseProp,
  isReservedTag, // 判断是否是保留的标签
  isReservedAttr, // 判断是否是保留的属性
  getTagNamespace, // 获取标签的命名空间,只有svg和math两个
  isUnknownElement // 判断标签是否是未知元素
} from 'web/util/index'

import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'

// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// install platform runtime directives & components
// 把平台相关的指令和组件合并到Vue.options中
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop

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

// devtools global hook
/* istanbul ignore next */
if (inBrowser) {
  setTimeout(() => {
    if (config.devtools) {
      if (devtools) {
        devtools.emit('init', Vue)
      } else if (
        process.env.NODE_ENV !== 'production' &&
        process.env.NODE_ENV !== 'test'
      ) {
        console[console.info ? 'info' : 'log'](
          'Download the Vue Devtools extension for a better development experience:\n' +
          'https://github.com/vuejs/vue-devtools'
        )
      }
    }
    if (process.env.NODE_ENV !== 'production' &&
      process.env.NODE_ENV !== 'test' &&
      config.productionTip !== false &&
      typeof console !== 'undefined'
    ) {
      console[console.info ? 'info' : 'log'](
        `You are running Vue in development mode.\n` +
        `Make sure to turn on production mode when deploying for production.\n` +
        `See more tips at https://vuejs.org/guide/deployment.html`
      )
    }
  }, 0)
}

export default Vue

把platforms/web中的结构大致过了一遍,跟platforms/weex做了对比,对vue的设计还是深感佩服的。也有的深入的了解。vue对于跨平台的支持还是下了功夫的。

现在来看看src目录下的结构吧

  • compiler 编译器相关的内容,看着头疼
  • core vue的核心功能,平台无关的部分
  • platforms 各个平台的移植
  • server 服务器端的内容,应该是服务器端渲染的
  • sfc 单文件组件的内容single file component
  • shared 全局的帮助方法,平台无关的,都是js的相关内容

接下来主攻core目录,后面再看compiler和server吧。

觉得好的给我点赞哦:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值