This article belongs to the series Read Vue Source Code.
In this article, we will:
- Get the code
- Open your editor
- Find the entry
You can read code using GitHub, but it's slow and hard to see the directory structure. So let's download the code first.
Open Vue's GitHub page, click "Clone or download" button and then click "Download ZIP".
You can also use git clone [email protected]:vuejs/vue.git
if you like using console.
I use the latest dev code I can get, so it may have some differences with the code you download.
Don't worry, this series is aimed at telling you how to understand the source code. You can use the same methods even if the code is different.
You can also download the version I use if you like.
I prefer to use Sublime Text, you can use your favorite editor.
Open your editor, double click the zip file you downloaded and drag the folder to your editor.
Now we meet our first question: where should we start?
It's a common question for big open source projects. Vue is a npm package, so we can open package.json
first.
{
"name": "vue",
"version": "2.3.3",
"description": "Reactive, component-oriented view layer for modern web interfaces.",
"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",
"unpkg": "dist/vue.js",
"typings": "types/index.d.ts",
"files": [
"src",
"dist/*.js",
"types/*.d.ts"
],
"scripts": {
"dev": "rollup -w -c build/config.js --environment TARGET:web-full-dev",
"dev:cjs": "rollup -w -c build/config.js --environment TARGET:web-runtime-cjs",
"dev:esm": "rollup -w -c build/config.js --environment TARGET:web-runtime-esm",
"dev:test": "karma start build/karma.dev.config.js",
"dev:ssr": "rollup -w -c build/config.js --environment TARGET:web-server-renderer",
...
First three keys are name
, version
and description
, no need to explain.
There is a dist/
in main
, module
and unpkg
, that implies they are related to generated files. Since we are looking for the entry, just ignore them.
Next is typings
. After Googling, we know it's a TypeScript definition. We can see many type definitions in types/index.d.ts
.
Go on.
files
contains three paths. First one is src
, it's the source code directory. This narrows the range, but we still don't know which file to start with.
Next key is scripts
. Oh, the dev
script! This command must know where to start.
rollup -w -c build/config.js --environment TARGET:web-full-dev
Now we have build/config.js
and TARGET:web-full-dev
. Open build/config.js
and search web-full-dev
:
// Runtime+compiler development build (Browser)
'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
},
Great!
This is the config of dev building. It's entry is web/entry-runtime-with-compiler.js
. But wait, where is the web/
directory?
Check the entry value again, there is a resolve
. Search resolve
:
const aliases = require('./alias')
const resolve = p => {
const base = p.split('/')[0]
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
Go through resolve('web/entry-runtime-with-compiler.js')
with the parameters we have:
- p is
web/entry-runtime-with-compiler.js
- base is
web
- convert the directory to
alias['web']
Let's jump to alias
to find alias['web']
.
module.exports = {
vue: path.resolve(__dirname, '../src/platforms/web/entry-runtime-with-compiler'),
compiler: path.resolve(__dirname, '../src/compiler'),
core: path.resolve(__dirname, '../src/core'),
shared: path.resolve(__dirname, '../src/shared'),
web: path.resolve(__dirname, '../src/platforms/web'),
weex: path.resolve(__dirname, '../src/platforms/weex'),
server: path.resolve(__dirname, '../src/server'),
entries: path.resolve(__dirname, '../src/entries'),
sfc: path.resolve(__dirname, '../src/sfc')
}
Okay, it's src/platforms/web
. Concat it with the input file name, we get 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'
import { query } from './util/index'
import { shouldDecodeNewlines } from './util/compat'
import { compileToFunctions } from './compiler/index'
const idToTemplate = cached(id => {
const el = query(id)
return el && el.innerHTML
})
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) {
let template = options.template
if (template) {
if (typeof template === 'string') {
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 = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
...
Cool! You have found the entry!
Have you noticed the
/* flow */
comment at the top? After Google, we know it's a type checker. It reminds me oftypings
, I search again and find out thatflow
can usetypings
definitions to validate your code.
Maybe next time I can use
flow
andtypings
in my own project. You see, even if we haven't really started reading the code, we have learned some useful and practical things.
Let's go through this file step-by-step:
- import config
- import some util functions
- import Vue(What? Another Vue?)
- define
idToTemplate
- define
getOuterHTML
- define
Vue.prototype.$mount
which useidTotemplate
andgetOuterHTML
- define
Vue.compile
The two important points are:
- This is NOT the real Vue code, we should know that from the filename, it's just an entry
- This file extracts the
$mount
function and defines a new$mount
. After reading the new definition, we know it just add some validations before calling the real mount
Now you know how to find the entry of a brand new project. In next article, we will go on to find the core code of Vue.
Read next chapter: Dig into the Core.
Remember those "util functions"? Read their codes and tell what they do. It's not difficult, but you need to be patient.
Don't miss shouldDecodeNewlines
, you can see how they fight with IE.