02-找到Vue的入口

本文深入探讨Vue.js的源码,从web-full-dev配置出发,解析编译与运行时的区别,对比entry-runtime与entry-runtime-with-compiler,揭示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吧。

觉得好的给我点赞哦:)

### 安装与使用 Vue Devtools (兼容 Vue 2) 为了在 Vue 2 项目中成功安装并启用 Vue Devtools,可以按照以下方式操作: #### 1. **安装 Vue Devtools** 可以通过浏览器扩展程序的方式安装 Vue Devtools。以下是具体步骤: - 对于 Chrome 浏览器,访问 [Chrome Web Store](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd),点击“添加至 Chrome”按钮完成安装[^3]。 - 如果使用 Firefox,则可以从官方 GitHub 页面找到对应的构建命令来强制启动开发工具支持 `$ yarn run build` 和 `$ yarn run run:firefox`。 #### 2. **配置 Vue 应用以启用 Devtools** 在项目入口文件 `App.vue` 中,通过设置全局变量 `Vue.config.devtools` 来控制是否允许调试模式运行。例如,在开发环境中开启此功能可按如下代码实现: ```javascript // main.js 或其他初始化文件 import Vue from 'vue'; import App from './App.vue'; Vue.config.productionTip = false; if (process.env.NODE_ENV === 'development') { Vue.config.devtools = true; // 开发环境下启用 devtools 支持 } else { Vue.config.devtools = false; // 生产环境关闭 devtools 防止性能损耗 } new Vue({ render: h => h(App), }).$mount('#app'); ``` 上述逻辑确保仅当应用处于开发阶段时才激活开发者工具连接能力。 #### 3. **验证安装效果** 一旦完成了以上两步之后,重新加载您的 Vue CLI 构建的应用页面,并打开浏览器的开发者选项卡查看是否有新的面板显示出来用于监控数据流以及组件树结构等内容。如果一切正常的话,应该能够看到类似于下图所示界面(假设已正确集成)。 请注意版本匹配问题——当前最新版可能默认针对 Vue 3 设计;因此建议下载指定分支如 legacy 版本来保障完全适配旧框架特性需求。 ```bash npm install -g @vue/cli vue add vuetify # 只是一个例子说明如何利用插件系统定制化工作流程 yarn global add vue-devtools@next || npm i -D vue-devtools@legacy ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值