作者:五洋顽石_449 | 来源:互联网 | 2023-08-25 17:01
目录今天开始,我将和大家一起探索vue源码,大家一起学习!首先去git下载一份vue,我这里是v2.6.9版本的,如果要跟着一起分析的话,推荐使用同一版本!刚下载的vue的目
目录
今天开始,我将和大家一起探索vue源码,大家一起学习!
首先去git下载一份vue,我这里是v2.6.9版本的,如果要跟着一起分析的话,推荐使用同一版本!
刚下载的vue的目录结构是这样的
- dist是打包生成的各种版本的vue,分为AMD,UMD,CMD,以及CommonJS四种打包格式
- examples是一些测试用例
- flow 是vue2用来进行静态类型检查的,这里文件是对flow语法的支持
- types 是对ts的支持
- packages是一些单独切出来的包,像weex
- scripts 是关于打包的一些配置文件
- src 就是源代码了
关于入口
很多人想看源码,但是不知道从哪一块下手,所以这里我说一下我是怎么找入口的。
首先dist文件夹中有vue.js,这是已经被打包好的js文件,src中的所有js代码都合并到了这里,我们直接去看这个vue.js肯定是会懵逼的,1w多行跳来跳去的,所以我们是不是要找到打包的入口?
那么让我们进入到scripts文件夹,因为这是打包的相关配置文件夹
我们这里先看build.js
看10行左右的这里 可以看到引入了config.js中所有的配置
let builds = require('./config').getAllBuilds()
27行左右开始递归打包
build(builds)
既然引入了配置,那我们就要进入到config.js看配置了
看38行左右
const builds = {}
我们这里可以直接ctrl+f搜索vue.js,找到dest中为vue.js的那个打包配置
这样就来到了120行左右
'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},
可以看到输出为dist下面的vue.js,那么入口函数当然就是web/entry-runtime-with-compiler.js
显然,这个web是一个别名,我们可以通过alias.js去查找这个别名对应的目录
进入alias.js
可以在第10行看到
web: resolve('src/platforms/web'),
web别名对应着src/platforms/web
那么入口函数的路径已经出来了src/platforms/web/entry-runtime-with-compiler.js
接下来我们根据路径打开这个js文件
路径:vue-2.6.9\src\platforms\web\entry-runtime-with-compiler.js
这次分享就分析这一个js文件
将其几个代码块折叠一下,可以看到这文件其实也就3个函数:
- idTotemplate
- $mount
- getOuterHTML
$mount逐行分析
这里是不是就遇到了我们平时见到的$mount,那我们就从这个$mount开始分析
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el?: string | Element,hydrating?: boolean
): Component {el = el && query(el)if (el === document.body || el === document.documentElement) {process.env.NODE_ENV !== 'production' && warn(`Do not mount Vue to or - mount to normal elements instead.`)return this}const options = this.$optionsif (!options.render) {let template = options.templateif (template) {if (typeof template === 'string') {if (template.charAt(0) === '#') {template = idToTemplate(template)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) {template = getOuterHTML(el)}if (template) {if (process.env.NODE_ENV !== 'production' && config.performance && mark) {mark('compile')}const { render, staticRenderFns } = compileToFunctions(template, {outputSourceRange: process.env.NODE_ENV !== 'production',shouldDecodeNewlines,shouldDecodeNewlinesForHref,delimiters: options.delimiters,comments: options.comments}, this)options.render = renderoptions.staticRenderFns = staticRenderFnsif (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)
}
很多解释已经在代码里敲好了,这里总结一下:
- 首先会保存老的$mount,然后进入函数,获取el和options
- 判断options.render是否存在,如果存在直接执行老$mount,中途那些东西都是在options.render不存在时执行的,因此可以看出render的优先级非常高了
- 然后当options.render不存在时,就会先判断template,再判断el,优先级由此可见:render>template>el
- template会检测#app,document.querySelector(’#app’),分别进行对应操作
- el会检测
”
,进行对应操作
- 当然template还会被compile解析,这里先留个todo,之后再去看compile是怎么将template转化为render函数的
这是我画的一个贼丑的图,描述了一些过程
idToTemplate函数
路径:当前目录
const idToTemplate = cached(id => {const el = query(id)return el && el.innerHTML
})
getOuterHTML函数
路径:当前目录
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}
}
query函数
路径:vue-2.6.9\src\platforms\web\util\index.js
import { warn } from 'core/util/index'export * from './attrs'
export * from './class'
export * from './element'
export function query (el: string | Element): Element {if (typeof el === 'string') {const selected = document.querySelector(el)if (!selected) {process.env.NODE_ENV !== 'production' && warn('Cannot find element: ' + el)return document.createElement('div')}return selected} else {return el}
}
总结
总结一下:这个入口函数,最主要的还是实现了$mount这一个函数。
下一次分享,我们将进入到vue初始化的那里去探索,当然也是通过这个入口文件去找到。怎么去找到vue初始化那里,先留个悬念在这。