Reading resources
Vue.js source code hosting address
Flow Static check tool address
Rollup source code construction
Flow Static check tool
Flow is a javascript static type checker developed by Facebook. Vue. Js source is the use of flow to do a static type check. Because javascript is a dynamically typed language. While language is flexible, it is also easy to cause some hidden hidden code. It looks error-free when compiled, but when it runs, it gets weird bugs. Type checking, on the other hand, is about finding bugs caused by type errors as early as possible at compile time without affecting code execution (no need for dynamic type checking at runtime). Some complex projects use tools to improve code readability and project maintainability.
Flow type check is divided into two methods:
- Type inference: Infer the type of a variable from the context in which it is used, and then check the type based on that inference.
- Type comment: Comment out the type of a variable to check the type.
// Type inference
// @flow
function split(str) {
return str.split(' ')
}
split(1) // Error!
split('abc') // Works!
Copy the code
When 1 is passed in, the flow code checks for an error, because the split() method is a method on a string prototype object that expects a string type and is passed in a number, so an error is reported. When the ‘ABC’ string is passed, the code can be executed normally.
// Type annotation
// @flow
function concat(a: string, b: string) {
return a + b;
}
concat("A"."B"); // Works!
concat(1.2); // Error!
Copy the code
Because the addition operator, that is, can perform the addition of numbers, but also can perform string concatenation. So the concat() method comments out the type of the argument in advance. The received arguments A and b are both strings, so call concat(1, 2); An error will be reported. Calls to concat(“A”, “B”) will work.
Note that: If the @flow annotation appears in a file, it indicates that the file needs to use flow for type check. Otherwise, the flow check is not performed. For more information, see the official documentation of the Flow static check tool.
Flow application in vue.js source code
In the home directory of vue.js is the.flowconfig file, which is the configuration file for Flow. As follows:
[ignore] // File ignored. */node_modules/. *. */test/. *. */scripts/. *. */examples/. *. */benchmarks/.*
[include]
[libs] // where [libs] is set to flow, the specified library definitions are in the flow folder
flow // The corresponding flow directory
[options]
unsafe.enable_getters_and_setters=true
module.name_mapper='^compiler/\(.*\)$' -> '<PROJECT_ROOT>/src/compiler/\1'
module.name_mapper='^core/\(.*\)$' -> '<PROJECT_ROOT>/src/core/\1'
module.name_mapper='^shared/\(.*\)$' -> '<PROJECT_ROOT>/src/shared/\1'
module.name_mapper='^web/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/web/\1'
module.name_mapper='^weex/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/weex/\1'
module.name_mapper='^server/\(.*\)$' -> '<PROJECT_ROOT>/src/server/\1'
module.name_mapper='^entries/\(.*\)$' -> '<PROJECT_ROOT>/src/entries/\1'
module.name_mapper='^sfc/\(.*\)$' -> '<PROJECT_ROOT>/src/sfc/\1'
suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line
Copy the code
Flow directory description in vue.js
Flow ├ ─ ─ compiler. Js# compile related├ ─ ─ component. JsComponent data structures├ ─ ─ global - API. Js# Global API structure├ ─ ─ modules. Js# Third party library definition├ ─ ─ options. Js# Option correlation├ ─ ─ SSR. JsServer side rendering is relevant├ ─ ─ vnode. Js# Virtual Node related
Copy the code
Vue source directory structure
Vue. Js source code in the SRC directory, directory structure is as follows:
The SRC ├ ─ ─ the compilerContains all compile-related code for vue.js.├ ─ ─ the core# contains the core code of vue.js, including built-in components, global API encapsulation, Vue instantiation, observer, virtual DOM, utility functions, and more.├ ─ ─ platforms# vue.js is a cross-platform MVVM framework that runs on the Web and native clients with WEEX.├ ─ ─ serverAll server-side rendering logic is in this directory.├ ─ ─ the SFC# vue.js writes components from a.vue single file. The code logic in this directory parses the.vue file contents into a JavaScript object.├ ─ ─ SharedThe utility methods defined here are shared between vue. js on the browser side and vue. js on the server side.
Copy the code
From the perspective of the whole directory structure, the author disassembled the functional modules very clearly, and the relevant logic is maintained in the same directory. Reusable code also becomes a separate folder.
Vue.js source code construction
The vue.js source code is built based on Rollup, and its buildrelated configurations are in the scripts directory. Rollup is a javascript module packaging tool. Much lighter than WebPack. To learn more, visit Rollup Github
The build script
Every project managed by NPM has a package.json file that describes the project. The script field defines the execution script of the NPM. The script for vue.js to execute the build is as follows:
"scripts": {
"build": "node scripts/build.js"."build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer"."build:weex": "npm run build -- weex",},Copy the code
In other words, when we execute NPM run build, we actually execute node scripts/build.js. So scripts/build.js is the js file of the build entry.
The build process
1. Start with the build entry file:scripts/build.js
let builds = require('./config').getAllBuilds() // Get all the configuration
// filter builds via command line arg
if (process.argv[2]) {
const filters = process.argv[2].split(', ')
builds = builds.filter(b= > {
return filters.some(f= > b.output.file.indexOf(f) > - 1 || b._name.indexOf(f) > - 1)})}else {
// filter out weex builds by default
builds = builds.filter(b= > {
return b.output.file.indexOf('weex') = = =- 1
})
}
build(builds)
Copy the code
The above part of the code first reads configuration data from the configuration file scripts/config.js and filters the configuration accordingly to build vue.js for different purposes.
2. View the built configuration file:scripts/config.js
const builds = {
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.dev.js'),
format: 'cjs'.env: 'development',
banner
},
'web-runtime-cjs-prod': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.prod.js'),
format: 'cjs'.env: 'production',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.dev.js'),
format: 'cjs'.env: 'development'.alias: { he: './entity-decoder' },
banner
},
'web-full-cjs-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.prod.js'),
format: 'cjs'.env: 'production'.alias: { he: './entity-decoder' },
banner
},
// Runtime only ES modules build (for bundlers)
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
},
// Runtime+compiler ES modules build (for bundlers)
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.js'),
format: 'es'.alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler ES modules build (for direct import in browser)
'web-full-esm-browser-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.browser.js'),
format: 'es'.transpile: false.env: 'development'.alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler ES modules build (for direct import in browser)
'web-full-esm-browser-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.browser.min.js'),
format: 'es'.transpile: false.env: 'production'.alias: { he: './entity-decoder' },
banner
},
// runtime-only build (Browser)
'web-runtime-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.js'),
format: 'umd'.env: 'development',
banner
},
// runtime-only production build (Browser)
'web-runtime-prod': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.min.js'),
format: 'umd'.env: 'production',
banner
},
// 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
},
// Runtime+compiler production build (Browser)
'web-full-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.min.js'),
format: 'umd'.env: 'production'.alias: { he: './entity-decoder' },
banner
},
// Web compiler (CommonJS).
'web-compiler': {
entry: resolve('web/entry-compiler.js'),
dest: resolve('packages/vue-template-compiler/build.js'),
format: 'cjs'.external: Object.keys(require('.. /packages/vue-template-compiler/package.json').dependencies)
},
// Web compiler (UMD for in-browser use).
'web-compiler-browser': {
entry: resolve('web/entry-compiler.js'),
dest: resolve('packages/vue-template-compiler/browser.js'),
format: 'umd'.env: 'development'.moduleName: 'VueTemplateCompiler'.plugins: [node(), cjs()]
},
// Web server renderer (CommonJS).
'web-server-renderer-dev': {
entry: resolve('web/entry-server-renderer.js'),
dest: resolve('packages/vue-server-renderer/build.dev.js'),
format: 'cjs'.env: 'development'.external: Object.keys(require('.. /packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-prod': {
entry: resolve('web/entry-server-renderer.js'),
dest: resolve('packages/vue-server-renderer/build.prod.js'),
format: 'cjs'.env: 'production'.external: Object.keys(require('.. /packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-basic': {
entry: resolve('web/entry-server-basic-renderer.js'),
dest: resolve('packages/vue-server-renderer/basic.js'),
format: 'umd'.env: 'development'.moduleName: 'renderVueComponentToString'.plugins: [node(), cjs()]
},
'web-server-renderer-webpack-server-plugin': {
entry: resolve('server/webpack-plugin/server.js'),
dest: resolve('packages/vue-server-renderer/server-plugin.js'),
format: 'cjs'.external: Object.keys(require('.. /packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-webpack-client-plugin': {
entry: resolve('server/webpack-plugin/client.js'),
dest: resolve('packages/vue-server-renderer/client-plugin.js'),
format: 'cjs'.external: Object.keys(require('.. /packages/vue-server-renderer/package.json').dependencies)
},
// Weex runtime factory
'weex-factory': {
weex: true.entry: resolve('weex/entry-runtime-factory.js'),
dest: resolve('packages/weex-vue-framework/factory.js'),
format: 'cjs'.plugins: [weexFactoryPlugin]
},
// Weex runtime framework (CommonJS).
'weex-framework': {
weex: true.entry: resolve('weex/entry-framework.js'),
dest: resolve('packages/weex-vue-framework/index.js'),
format: 'cjs'
},
// Weex compiler (CommonJS). Used by Weex's Webpack loader.
'weex-compiler': {
weex: true.entry: resolve('weex/entry-compiler.js'),
dest: resolve('packages/weex-template-compiler/build.js'),
format: 'cjs'.external: Object.keys(require('.. /packages/weex-template-compiler/package.json').dependencies)
}
}
Copy the code
The above part of the code is vue.js build configuration, server render Webpack plug-in, weeX packaging configuration. For individual configurations, the Rollup build rules are followed. Configuration description:
- Entry property: The address of the build entry JS file.
- Dest property: the address of the JS file after the build is complete
- Format property: The format of the build file. ‘CJS’ indicates that the built file complies with the CommonJS specification; ‘ES’ indicates that the built file conforms to the ES Module specification; ‘umD’ indicates that the built file complies with the UMD specification.
- Banner property: A simple description of vue.js. Contains author information, version number, etc.
3. Explore the build process using a configuration as an example:web-runtime-cjs
'web-runtime-cjs-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.dev.js'),
format: 'cjs',
env: 'development',
banner
},
Copy the code
As you can see from the configuration, the resolve() method is called for both the entry js file address and the completed JS address.
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)
}
}
Copy the code
The resolve() method calls split() on the passed parameter p, splits it into an array by ‘/’, and sets the first element to base, which in this case is web. But base is not a real path, but is configured with an alias. The alias configuration code is as follows: scripts/alias
const path = require('path')
const resolve = p= > path.resolve(__dirname, '.. / ', p)
// A mapping to the real file
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
compiler: resolve('src/compiler'),
core: resolve('src/core'),
shared: resolve('src/shared'),
web: resolve('src/platforms/web'),
weex: resolve('src/platforms/weex'),
server: resolve('src/server'),
sfc: resolve('src/sfc')}Copy the code
Resolve (__dirname, ‘.. / ‘, ‘SRC/platforms/web’). Thus finds its entry document is SRC/platforms/web/entry – runtime. Js it after the construction of a Rollup packaged, would go on to dist/vue.runtime.com mon. Js.
Runtime Only VS Runtime + Compiler
We usually use vue-CLI to initialize our vue. js project and ask whether we should use Runtime Only version or Runtime + Compiler version. Their differences are as follows:
-
Runtime Only usually requires a vue-loader tool such as Webpack to compile.vue files into JavaScript and a template into the render function. Because it is done at compile time, it contains only the run-time vue.js code, which is also lighter in size.
-
Runtime + Compiler If we do not precompile the code, but use Vue’s template property and pass in a string, we need to compile the template on the client side as follows:
// The version of the compiler required
new Vue({
template: '<div>{{ hi }}</div>'
})
// This is not necessary
new Vue({
render (h) {
return h('div'.this.hi)
}
})
Copy the code
In Vue. Js 2.0, the final rendering is done by the render function. If you write the template property, you will need to compile to the render function, which will happen at runtime, so you need a version of the compiler. Obviously, this compilation takes a toll on performance, so Runtime Only is recommended.
The entry of the vue
What exactly does import Vue from ‘Vue’ do when we develop it? Follow the Runtime Only constructed the vue. Js its entrance is in the SRC/platforms/web/entry – the Runtime. Js code is as follows:
/* @flow */
import Vue from './runtime/index'
export default Vue
Copy the code
The above code exports a Vue that is imported from./ Runtime /index.
Vue static global configuration and methods on prototype objects
/runtime/index file. The code is as follows:
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'
import {
query,
mustUseProp,
isReservedTag,
isReservedAttr,
getTagNamespace,
isUnknownElement
} from 'web/util/index'
import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'
// install platform specific utils
// Static global configurationVue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
/ / prototype __patch__
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
// Defines the $mount method on the prototype
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((a)= > {
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
Copy the code
Again, the code imports a Vue from the core/index file and then exports it. This file defines some static global configuration of Vue, and methods on prototype objects.
Add static methods to the vue through initGlobalAPI
Continue to see how core/index defines Vue as follows:
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
initGlobalAPI(Vue) // Defines the static methods of vue itself
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = '__VERSION__'
export default Vue
Copy the code
Again, it imports Vue from the./instance/index file and finally exports it. This file adds static methods to the vue through the initGlobalAPI method.
Add methods to the Vue prototype through mixins
Continue to the./instance/index file with the following code:
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'
Vue is a class implemented with Function, so we instantiate it with new Vue.
function Vue (options) {
if(process.env.NODE_ENV ! = ='production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
// The method hangs on the vue prototype
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
Copy the code
At this point, the traceability is finally done. Vue is a class implemented with Function, so instantiate it with New Vue. This file adds methods to the Vue prototype through mixins.
The end of the
Recently a period of time will seriously look at the vue.js source code. [Read vue source] will follow a series to update. While sharing my own learning, I also hope to communicate with more peers, that’s all.
Import vue from ‘vue’ exactly do what? 【 Currently reading 】
Explore how templates and data are rendered into DOM.