├── vue-src ├── platforms │ ├── weex │ │ ├── runtime │ │ │ ├── directives │ │ │ │ └── index.js │ │ │ ├── components │ │ │ │ ├── index.js │ │ │ │ └── transition.js │ │ │ ├── text-node.js │ │ │ ├── modules │ │ │ │ ├── index.js │ │ │ │ ├── attrs.js │ │ │ │ ├── events.js │ │ │ │ ├── style.js │ │ │ │ └── class.js │ │ │ ├── patch.js │ │ │ ├── index.js │ │ │ └── node-ops.js │ │ ├── compiler.js │ │ ├── compiler │ │ │ ├── directives │ │ │ │ ├── index.js │ │ │ │ └── model.js │ │ │ ├── modules │ │ │ │ ├── index.js │ │ │ │ ├── append.js │ │ │ │ ├── props.js │ │ │ │ ├── class.js │ │ │ │ └── style.js │ │ │ └── index.js │ │ ├── runtime-factory.js │ │ └── util │ │ │ └── index.js │ └── web │ │ ├── server │ │ ├── directives │ │ │ ├── index.js │ │ │ └── show.js │ │ ├── modules │ │ │ ├── index.js │ │ │ ├── class.js │ │ │ ├── style.js │ │ │ ├── attrs.js │ │ │ └── dom-props.js │ │ └── util.js │ │ ├── runtime.js │ │ ├── compiler │ │ ├── modules │ │ │ ├── index.js │ │ │ ├── class.js │ │ │ └── style.js │ │ ├── directives │ │ │ ├── index.js │ │ │ ├── html.js │ │ │ └── text.js │ │ ├── index.js │ │ └── util.js │ │ ├── runtime │ │ ├── directives │ │ │ ├── index.js │ │ │ └── show.js │ │ ├── components │ │ │ └── index.js │ │ ├── modules │ │ │ ├── index.js │ │ │ ├── class.js │ │ │ ├── events.js │ │ │ ├── attrs.js │ │ │ ├── dom-props.js │ │ │ └── style.js │ │ ├── patch.js │ │ ├── class-util.js │ │ ├── node-ops.js │ │ └── index.js │ │ ├── compiler.js │ │ ├── util │ │ ├── compat.js │ │ ├── index.js │ │ ├── attrs.js │ │ ├── style.js │ │ ├── class.js │ │ └── element.js │ │ ├── server-renderer.js │ │ └── runtime-with-compiler.js ├── core │ ├── components │ │ ├── index.js │ │ └── keep-alive.js │ ├── vdom │ │ ├── modules │ │ │ ├── index.js │ │ │ ├── ref.js │ │ │ └── directives.js │ │ ├── helpers │ │ │ ├── index.js │ │ │ ├── get-first-component-child.js │ │ │ ├── merge-hook.js │ │ │ ├── extract-props.js │ │ │ ├── update-listeners.js │ │ │ ├── normalize-children.js │ │ │ └── resolve-async-component.js │ │ ├── create-functional-component.js │ │ └── vnode.js │ ├── util │ │ ├── index.js │ │ ├── error.js │ │ ├── perf.js │ │ ├── lang.js │ │ └── debug.js │ ├── index.js │ ├── instance │ │ ├── render-helpers │ │ │ ├── resolve-filter.js │ │ │ ├── check-keycodes.js │ │ │ ├── render-list.js │ │ │ ├── render-slot.js │ │ │ ├── bind-object-props.js │ │ │ ├── resolve-slots.js │ │ │ └── render-static.js │ │ ├── index.js │ │ ├── inject.js │ │ └── proxy.js │ ├── global-api │ │ ├── mixin.js │ │ ├── use.js │ │ ├── assets.js │ │ └── index.js │ ├── observer │ │ ├── dep.js │ │ └── array.js │ └── config.js ├── server │ ├── util.js │ ├── webpack-plugin │ │ ├── util.js │ │ ├── server.js │ │ └── client.js │ ├── write.js │ ├── template-renderer │ │ ├── parse-template.js │ │ ├── create-async-file-mapper.js │ │ └── template-stream.js │ ├── bundle-renderer │ │ └── source-map-support.js │ ├── render-stream.js │ └── create-renderer.js ├── compiler │ ├── directives │ │ ├── index.js │ │ ├── bind.js │ │ └── model.js │ ├── parser │ │ ├── entity-decoder.js │ │ ├── text-parser.js │ │ └── filter-parser.js │ └── error-detector.js ├── shared │ └── constants.js └── sfc │ └── parser.js ├── images ├── AST1.png ├── AST2.png ├── VNode.png ├── diff1.png ├── diff2.png ├── diff3.png ├── diff4.png ├── diff5.png ├── diff6.png ├── diff7.png ├── diff8.png ├── diff9.png ├── VNode2.png ├── diff10.png ├── youxuan.png ├── VNode.sketch ├── VNode2.sketch ├── diff1.sketch ├── diff10.sketch ├── diff2.sketch ├── diff3.sketch ├── diff4.sketch ├── diff5.sketch ├── diff6.sketch ├── diff7.sketch ├── diff8.sketch ├── diff9.sketch └── youxuan2.jpg ├── vue-router-src ├── util │ ├── dom.js │ ├── async.js │ ├── warn.js │ ├── params.js │ ├── push-state.js │ ├── path.js │ ├── location.js │ ├── query.js │ └── resolve-components.js ├── history │ ├── abstract.js │ └── html5.js └── install.js ├── vuex-src ├── index.js ├── index.esm.js ├── plugins │ ├── devtool.js │ └── logger.js ├── mixin.js ├── util.js └── module │ ├── module.js │ └── module-collection.js ├── README.md └── docs ├── 说说element组件库broadcast与dispatch.MarkDown ├── 响应式原理.MarkDown └── 依赖收集.MarkDown /vue-src/platforms/weex/runtime/directives/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | } 3 | -------------------------------------------------------------------------------- /images/AST1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/AST1.png -------------------------------------------------------------------------------- /images/AST2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/AST2.png -------------------------------------------------------------------------------- /images/VNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/VNode.png -------------------------------------------------------------------------------- /images/diff1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff1.png -------------------------------------------------------------------------------- /images/diff2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff2.png -------------------------------------------------------------------------------- /images/diff3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff3.png -------------------------------------------------------------------------------- /images/diff4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff4.png -------------------------------------------------------------------------------- /images/diff5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff5.png -------------------------------------------------------------------------------- /images/diff6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff6.png -------------------------------------------------------------------------------- /images/diff7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff7.png -------------------------------------------------------------------------------- /images/diff8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff8.png -------------------------------------------------------------------------------- /images/diff9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff9.png -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler.js: -------------------------------------------------------------------------------- 1 | export { compile } from 'weex/compiler/index' 2 | -------------------------------------------------------------------------------- /images/VNode2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/VNode2.png -------------------------------------------------------------------------------- /images/diff10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff10.png -------------------------------------------------------------------------------- /images/youxuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/youxuan.png -------------------------------------------------------------------------------- /images/VNode.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/VNode.sketch -------------------------------------------------------------------------------- /images/VNode2.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/VNode2.sketch -------------------------------------------------------------------------------- /images/diff1.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff1.sketch -------------------------------------------------------------------------------- /images/diff10.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff10.sketch -------------------------------------------------------------------------------- /images/diff2.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff2.sketch -------------------------------------------------------------------------------- /images/diff3.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff3.sketch -------------------------------------------------------------------------------- /images/diff4.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff4.sketch -------------------------------------------------------------------------------- /images/diff5.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff5.sketch -------------------------------------------------------------------------------- /images/diff6.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff6.sketch -------------------------------------------------------------------------------- /images/diff7.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff7.sketch -------------------------------------------------------------------------------- /images/diff8.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff8.sketch -------------------------------------------------------------------------------- /images/diff9.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/diff9.sketch -------------------------------------------------------------------------------- /images/youxuan2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/learnVue/HEAD/images/youxuan2.jpg -------------------------------------------------------------------------------- /vue-router-src/util/dom.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export const inBrowser = typeof window !== 'undefined' 4 | -------------------------------------------------------------------------------- /vue-src/core/components/index.js: -------------------------------------------------------------------------------- 1 | import KeepAlive from './keep-alive' 2 | 3 | export default { 4 | KeepAlive 5 | } 6 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/directives/index.js: -------------------------------------------------------------------------------- 1 | import show from './show' 2 | 3 | export default { 4 | show 5 | } 6 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | 3 | export default { 4 | model 5 | } 6 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from './runtime/index' 4 | /*Github:https://github.com/answershuto*/ 5 | export default Vue 6 | -------------------------------------------------------------------------------- /vue-src/core/vdom/modules/index.js: -------------------------------------------------------------------------------- 1 | import directives from './directives' 2 | import ref from './ref' 3 | 4 | export default [ 5 | ref, 6 | directives 7 | ] 8 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/modules/index.js: -------------------------------------------------------------------------------- 1 | import klass from './class' 2 | import style from './style' 3 | 4 | export default [ 5 | klass, 6 | style 7 | ] 8 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import show from './show' 3 | 4 | export default { 5 | model, 6 | show 7 | } 8 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export { parseComponent } from 'sfc/parser' 4 | export { compile, compileToFunctions } from './compiler/index' 5 | -------------------------------------------------------------------------------- /vue-src/server/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export const isJS = (file: string): boolean => /\.js(\?[^.]+)?$/.test(file) 4 | 5 | export const isCSS = (file: string): boolean => /\.css(\?[^.]+)?$/.test(file) 6 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/components/index.js: -------------------------------------------------------------------------------- 1 | import Transition from './transition' 2 | import TransitionGroup from './transition-group' 3 | 4 | export default { 5 | Transition, 6 | TransitionGroup 7 | } 8 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/components/index.js: -------------------------------------------------------------------------------- 1 | import Transition from './transition' 2 | import TransitionGroup from './transition-group' 3 | 4 | export default { 5 | Transition, 6 | TransitionGroup 7 | } 8 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import text from './text' 3 | import html from './html' 4 | 5 | export default { 6 | model, 7 | text, 8 | html 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import bind from './bind' 4 | import { noop } from 'shared/util' 5 | /*Github:https://github.com/answershuto*/ 6 | export default { 7 | bind, 8 | cloak: noop 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime-factory.js: -------------------------------------------------------------------------------- 1 | // this entry is built and wrapped with a factory function 2 | // used to generate a fresh copy of Vue for every Weex instance. 3 | 4 | import Vue from './runtime/index' 5 | 6 | exports.Vue = Vue 7 | -------------------------------------------------------------------------------- /vue-src/compiler/parser/entity-decoder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | let decoder 4 | 5 | export function decode (html: string): string { 6 | decoder = decoder || document.createElement('div') 7 | decoder.innerHTML = html 8 | return decoder.textContent 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/text-node.js: -------------------------------------------------------------------------------- 1 | let latestNodeId = 1 2 | 3 | export default function TextNode (text) { 4 | this.instanceId = '' 5 | this.nodeId = latestNodeId++ 6 | this.parentNode = null 7 | this.nodeType = 3 8 | this.text = text 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/modules/index.js: -------------------------------------------------------------------------------- 1 | import attrs from './attrs' 2 | import domProps from './dom-props' 3 | import klass from './class' 4 | import style from './style' 5 | 6 | export default [ 7 | attrs, 8 | domProps, 9 | klass, 10 | style 11 | ] 12 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler/modules/index.js: -------------------------------------------------------------------------------- 1 | import klass from './class' 2 | import style from './style' 3 | import props from './props' 4 | import append from './append' 5 | 6 | export default [ 7 | klass, 8 | style, 9 | props, 10 | append 11 | ] 12 | -------------------------------------------------------------------------------- /vue-src/core/vdom/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from './merge-hook' 4 | export * from './extract-props' 5 | export * from './update-listeners' 6 | export * from './normalize-children' 7 | export * from './resolve-async-component' 8 | export * from './get-first-component-child' 9 | -------------------------------------------------------------------------------- /vue-src/core/util/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from 'shared/util' 4 | export * from './lang' 5 | export * from './env' 6 | export * from './options' 7 | export * from './debug' 8 | export * from './props' 9 | export * from './error' 10 | export { defineReactive } from '../observer/index' 11 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/directives/show.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | /*Github:https://github.com/answershuto*/ 3 | export default function show (node: VNodeWithData, dir: VNodeDirective) { 4 | if (!dir.value) { 5 | const style: any = node.data.style || (node.data.style = {}) 6 | style.display = 'none' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/components/transition.js: -------------------------------------------------------------------------------- 1 | // reuse same transition component logic from web 2 | export { 3 | transitionProps, 4 | extractTransitionData 5 | } from 'web/runtime/components/transition' 6 | 7 | import Transition from 'web/runtime/components/transition' 8 | 9 | export default Transition 10 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/directives/html.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | /*Github:https://github.com/answershuto*/ 5 | export default function html (el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'innerHTML', `_s(${dir.value})`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/directives/text.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | /*Github:https://github.com/answershuto*/ 5 | export default function text (el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'textContent', `_s(${dir.value})`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/modules/index.js: -------------------------------------------------------------------------------- 1 | import attrs from './attrs' 2 | import klass from './class' 3 | import events from './events' 4 | import style from './style' 5 | import transition from './transition' 6 | 7 | export default [ 8 | attrs, 9 | klass, 10 | events, 11 | style, 12 | transition 13 | ] 14 | -------------------------------------------------------------------------------- /vue-src/compiler/directives/bind.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | /*Github:https://github.com/answershuto*/ 3 | export default function bind (el: ASTElement, dir: ASTDirective) { 4 | el.wrapData = (code: string) => { 5 | return `_b(${code},'${el.tag}',${dir.value}${ 6 | dir.modifiers && dir.modifiers.prop ? ',true' : '' 7 | })` 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue-src/core/index.js: -------------------------------------------------------------------------------- 1 | import Vue from './instance/index' 2 | import { initGlobalAPI } from './global-api/index' 3 | import { isServerRendering } from 'core/util/env' 4 | 5 | initGlobalAPI(Vue) 6 | 7 | Object.defineProperty(Vue.prototype, '$isServer', { 8 | get: isServerRendering 9 | }) 10 | 11 | Vue.version = '__VERSION__' 12 | 13 | export default Vue 14 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/resolve-filter.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { identity, resolveAsset } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for resolving filters 7 | */ 8 | /*处理filters*/ 9 | export function resolveFilter (id: string): Function { 10 | return resolveAsset(this.$options, 'filters', id, true) || identity 11 | } 12 | -------------------------------------------------------------------------------- /vuex-src/index.js: -------------------------------------------------------------------------------- 1 | import { Store, install } from './store' 2 | import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' 3 | 4 | export default { 5 | Store, 6 | install, 7 | version: '__VERSION__', 8 | mapState, 9 | mapMutations, 10 | mapGetters, 11 | mapActions, 12 | createNamespacedHelpers 13 | } 14 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { escape } from 'he' 4 | import { genClassForVnode } from 'web/util/index' 5 | 6 | export default function renderClass (node: VNodeWithData): ?string { 7 | const classList = genClassForVnode(node) 8 | if (classList !== '') { 9 | return ` class="${escape(classList)}"` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /vue-src/core/global-api/mixin.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { mergeOptions } from '../util/index' 4 | 5 | /*初始化mixin*/ 6 | export function initMixin (Vue: GlobalAPI) { 7 | /*https://cn.vuejs.org/v2/api/#Vue-mixin*/ 8 | Vue.mixin = function (mixin: Object) { 9 | /*mergeOptions合并optiuons*/ 10 | this.options = mergeOptions(this.options, mixin) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/modules/index.js: -------------------------------------------------------------------------------- 1 | import attrs from './attrs' 2 | import klass from './class' 3 | import events from './events' 4 | import domProps from './dom-props' 5 | import style from './style' 6 | import transition from './transition' 7 | 8 | export default [ 9 | attrs, 10 | klass, 11 | events, 12 | domProps, 13 | style, 14 | transition 15 | ] 16 | -------------------------------------------------------------------------------- /vue-router-src/util/async.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function runQueue (queue: Array, fn: Function, cb: Function) { 4 | const step = index => { 5 | if (index >= queue.length) { 6 | cb() 7 | } else { 8 | if (queue[index]) { 9 | fn(queue[index], () => { 10 | step(index + 1) 11 | }) 12 | } else { 13 | step(index + 1) 14 | } 15 | } 16 | } 17 | step(0) 18 | } 19 | -------------------------------------------------------------------------------- /vue-src/core/vdom/helpers/get-first-component-child.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef } from 'shared/util' 4 | 5 | /* 获取第一个子组件 */ 6 | export function getFirstComponentChild (children: ?Array): ?VNode { 7 | if (Array.isArray(children)) { 8 | for (let i = 0; i < children.length; i++) { 9 | const c = children[i] 10 | if (isDef(c) && isDef(c.componentOptions)) { 11 | return c 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vue-src/shared/constants.js: -------------------------------------------------------------------------------- 1 | /*用来标记是否是服务端渲染*/ 2 | export const SSR_ATTR = 'data-server-rendered' 3 | 4 | /*选项/资源集合*/ 5 | export const ASSET_TYPES = [ 6 | 'component', 7 | 'directive', 8 | 'filter' 9 | ] 10 | 11 | /*钩子函数集合*/ 12 | export const LIFECYCLE_HOOKS = [ 13 | 'beforeCreate', 14 | 'created', 15 | 'beforeMount', 16 | 'mounted', 17 | 'beforeUpdate', 18 | 'updated', 19 | 'beforeDestroy', 20 | 'destroyed', 21 | 'activated', 22 | 'deactivated' 23 | ] 24 | -------------------------------------------------------------------------------- /vuex-src/index.esm.js: -------------------------------------------------------------------------------- 1 | import { Store, install } from './store' 2 | import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' 3 | 4 | export default { 5 | Store, 6 | install, 7 | version: '__VERSION__', 8 | mapState, 9 | mapMutations, 10 | mapGetters, 11 | mapActions, 12 | createNamespacedHelpers 13 | } 14 | 15 | export { 16 | Store, 17 | install, 18 | mapState, 19 | mapMutations, 20 | mapGetters, 21 | mapActions, 22 | createNamespacedHelpers 23 | } 24 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/patch.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import * as nodeOps from 'web/runtime/node-ops' 4 | import { createPatchFunction } from 'core/vdom/patch' 5 | import baseModules from 'core/vdom/modules/index' 6 | import platformModules from 'web/runtime/modules/index' 7 | 8 | // the directive module should be applied last, after all 9 | // built-in modules have been applied. 10 | const modules = platformModules.concat(baseModules) 11 | 12 | export const patch: Function = createPatchFunction({ nodeOps, modules }) 13 | -------------------------------------------------------------------------------- /vue-router-src/util/warn.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function assert (condition: any, message: string) { 4 | if (!condition) { 5 | throw new Error(`[vue-router] ${message}`) 6 | } 7 | } 8 | 9 | export function warn (condition: any, message: string) { 10 | if (process.env.NODE_ENV !== 'production' && !condition) { 11 | typeof console !== 'undefined' && console.warn(`[vue-router] ${message}`) 12 | } 13 | } 14 | 15 | export function isError (err: any): boolean { 16 | return Object.prototype.toString.call(err).indexOf('Error') > -1 17 | } 18 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/patch.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import * as nodeOps from 'weex/runtime/node-ops' 4 | import { createPatchFunction } from 'core/vdom/patch' 5 | import baseModules from 'core/vdom/modules/index' 6 | import platformModules from 'weex/runtime/modules/index' 7 | 8 | // the directive module should be applied last, after all 9 | // built-in modules have been applied. 10 | const modules = platformModules.concat(baseModules) 11 | 12 | export const patch: Function = createPatchFunction({ 13 | nodeOps, 14 | modules, 15 | LONG_LIST_THRESHOLD: 10 16 | }) 17 | -------------------------------------------------------------------------------- /vue-src/platforms/web/util/compat.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { inBrowser } from 'core/util/index' 4 | 5 | // check whether current browser encodes a char inside attribute values 6 | function shouldDecode (content: string, encoded: string): boolean { 7 | const div = document.createElement('div') 8 | div.innerHTML = `
` 9 | return div.innerHTML.indexOf(encoded) > 0 10 | } 11 | 12 | // #3663 13 | // IE encodes newlines inside attribute values while other browsers don't 14 | export const shouldDecodeNewlines = inBrowser ? shouldDecode('\n', ' ') : false 15 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/check-keycodes.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | 5 | /** 6 | * Runtime helper for checking keyCodes from config. 7 | */ 8 | /*从config配置中检查eventKeyCode是否存在*/ 9 | export function checkKeyCodes ( 10 | eventKeyCode: number, 11 | key: string, 12 | builtInAlias: number | Array | void 13 | ): boolean { 14 | const keyCodes = config.keyCodes[key] || builtInAlias 15 | if (Array.isArray(keyCodes)) { 16 | return keyCodes.indexOf(eventKeyCode) === -1 17 | } else { 18 | return keyCodes !== eventKeyCode 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /vue-src/core/util/error.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from '../config' 4 | import { warn } from './debug' 5 | import { inBrowser } from './env' 6 | 7 | export function handleError (err: Error, vm: any, info: string) { 8 | if (config.errorHandler) { 9 | config.errorHandler.call(null, err, vm, info) 10 | } else { 11 | if (process.env.NODE_ENV !== 'production') { 12 | warn(`Error in ${info}: "${err.toString()}"`, vm) 13 | } 14 | /* istanbul ignore else */ 15 | if (inBrowser && typeof console !== 'undefined') { 16 | console.error(err) 17 | } else { 18 | throw err 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler/modules/append.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | function preTransformNode (el: ASTElement, options: CompilerOptions) { 4 | if (el.tag === 'cell' && !el.attrsList.some(item => item.name === 'append')) { 5 | el.attrsMap.append = 'tree' 6 | el.attrsList.push({ name: 'append', value: 'tree' }) 7 | } 8 | if (el.attrsMap.append === 'tree') { 9 | el.appendAsTree = true 10 | } 11 | } 12 | 13 | function genData (el: ASTElement): string { 14 | return el.appendAsTree ? `appendAsTree:true,` : '' 15 | } 16 | 17 | export default { 18 | staticKeys: ['appendAsTree'], 19 | preTransformNode, 20 | genData 21 | } 22 | -------------------------------------------------------------------------------- /vue-src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | 3 | export let mark 4 | export let measure 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | const perf = inBrowser && window.performance 8 | /* istanbul ignore if */ 9 | if ( 10 | perf && 11 | perf.mark && 12 | perf.measure && 13 | perf.clearMarks && 14 | perf.clearMeasures 15 | ) { 16 | mark = tag => perf.mark(tag) 17 | measure = (name, startTag, endTag) => { 18 | perf.measure(name, startTag, endTag) 19 | perf.clearMarks(startTag) 20 | perf.clearMarks(endTag) 21 | perf.clearMeasures(name) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /vue-src/platforms/web/util/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from 'core/util/index' 4 | 5 | export * from './attrs' 6 | export * from './class' 7 | export * from './element' 8 | 9 | /** 10 | * Query an element selector if it's not an element already. 11 | */ 12 | /*返回一个元素的DOM实例对象*/ 13 | export function query (el: string | Element): Element { 14 | if (typeof el === 'string') { 15 | const selected = document.querySelector(el) 16 | if (!selected) { 17 | process.env.NODE_ENV !== 'production' && warn( 18 | 'Cannot find element: ' + el 19 | ) 20 | return document.createElement('div') 21 | } 22 | return selected 23 | } else { 24 | return el 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vue-src/core/instance/index.js: -------------------------------------------------------------------------------- 1 | import { initMixin } from './init' 2 | import { stateMixin } from './state' 3 | import { renderMixin } from './render' 4 | import { eventsMixin } from './events' 5 | import { lifecycleMixin } from './lifecycle' 6 | import { warn } from '../util/index' 7 | /*Github:https://github.com/answershuto*/ 8 | function Vue (options) { 9 | if (process.env.NODE_ENV !== 'production' && 10 | !(this instanceof Vue)) { 11 | warn('Vue is a constructor and should be called with the `new` keyword') 12 | } 13 | /*初始化*/ 14 | this._init(options) 15 | } 16 | 17 | initMixin(Vue) 18 | stateMixin(Vue) 19 | eventsMixin(Vue) 20 | lifecycleMixin(Vue) 21 | renderMixin(Vue) 22 | 23 | export default Vue 24 | -------------------------------------------------------------------------------- /vue-router-src/util/params.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from './warn' 4 | import Regexp from 'path-to-regexp' 5 | 6 | // $flow-disable-line 7 | const regexpCompileCache: { 8 | [key: string]: Function 9 | } = Object.create(null) 10 | 11 | export function fillParams ( 12 | path: string, 13 | params: ?Object, 14 | routeMsg: string 15 | ): string { 16 | try { 17 | const filler = 18 | regexpCompileCache[path] || 19 | (regexpCompileCache[path] = Regexp.compile(path)) 20 | return filler(params || {}, { pretty: true }) 21 | } catch (e) { 22 | if (process.env.NODE_ENV !== 'production') { 23 | warn(false, `missing param for ${routeMsg}: ${e.message}`) 24 | } 25 | return '' 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vue-src/core/global-api/use.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toArray } from '../util/index' 4 | 5 | /*初始化use*/ 6 | export function initUse (Vue: GlobalAPI) { 7 | /*https://cn.vuejs.org/v2/api/#Vue-use*/ 8 | Vue.use = function (plugin: Function | Object) { 9 | /* istanbul ignore if */ 10 | /*标识位检测该插件是否已经被安装*/ 11 | if (plugin.installed) { 12 | return 13 | } 14 | // additional parameters 15 | const args = toArray(arguments, 1) 16 | /*a*/ 17 | args.unshift(this) 18 | if (typeof plugin.install === 'function') { 19 | /*install执行插件安装*/ 20 | plugin.install.apply(plugin, args) 21 | } else if (typeof plugin === 'function') { 22 | plugin.apply(null, args) 23 | } 24 | plugin.installed = true 25 | return this 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vuex-src/plugins/devtool.js: -------------------------------------------------------------------------------- 1 | /* 从window对象的__VUE_DEVTOOLS_GLOBAL_HOOK__中获取devtool插件 */ 2 | const devtoolHook = 3 | typeof window !== 'undefined' && 4 | window.__VUE_DEVTOOLS_GLOBAL_HOOK__ 5 | 6 | export default function devtoolPlugin (store) { 7 | if (!devtoolHook) return 8 | 9 | /* devtoll插件实例存储在store的_devtoolHook上 */ 10 | store._devtoolHook = devtoolHook 11 | 12 | /* 出发vuex的初始化事件,并将store的引用地址传给deltool插件,使插件获取store的实例 */ 13 | devtoolHook.emit('vuex:init', store) 14 | 15 | /* 监听travel-to-state事件 */ 16 | devtoolHook.on('vuex:travel-to-state', targetState => { 17 | /* 重制state */ 18 | store.replaceState(targetState) 19 | }) 20 | 21 | /* 订阅store的变化 */ 22 | store.subscribe((mutation, state) => { 23 | devtoolHook.emit('vuex:mutation', mutation, state) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { genStaticKeys } from 'shared/util' 4 | import { createCompiler } from 'compiler/index' 5 | 6 | import modules from './modules/index' 7 | import directives from './directives/index' 8 | 9 | import { 10 | isUnaryTag, 11 | mustUseProp, 12 | isReservedTag, 13 | canBeLeftOpenTag, 14 | getTagNamespace 15 | } from '../util/index' 16 | 17 | export const baseOptions: CompilerOptions = { 18 | modules, 19 | directives, 20 | isUnaryTag, 21 | mustUseProp, 22 | canBeLeftOpenTag, 23 | isReservedTag, 24 | getTagNamespace, 25 | preserveWhitespace: false, 26 | staticKeys: genStaticKeys(modules) 27 | } 28 | 29 | const { compile, compileToFunctions } = createCompiler(baseOptions) 30 | export { compile, compileToFunctions } 31 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isUnaryTag, canBeLeftOpenTag } from './util' 4 | import { genStaticKeys } from 'shared/util' 5 | import { createCompiler } from 'compiler/index' 6 | 7 | import modules from './modules/index' 8 | import directives from './directives/index' 9 | 10 | import { 11 | isPreTag, 12 | mustUseProp, 13 | isReservedTag, 14 | getTagNamespace 15 | } from '../util/index' 16 | 17 | export const baseOptions: CompilerOptions = { 18 | expectHTML: true, 19 | modules, 20 | directives, 21 | isPreTag, 22 | isUnaryTag, 23 | mustUseProp, 24 | canBeLeftOpenTag, 25 | isReservedTag, 26 | getTagNamespace, 27 | staticKeys: genStaticKeys(modules) 28 | } 29 | 30 | /*这里会根据不同平台传递不同的baseOptions创建编译器*/ 31 | const { compile, compileToFunctions } = createCompiler(baseOptions) 32 | export { compile, compileToFunctions } 33 | -------------------------------------------------------------------------------- /vue-src/server/webpack-plugin/util.js: -------------------------------------------------------------------------------- 1 | const { red, yellow } = require('chalk') 2 | 3 | const prefix = `[vue-server-renderer-webpack-plugin]` 4 | const warn = exports.warn = msg => console.error(red(`${prefix} ${msg}\n`)) 5 | const tip = exports.tip = msg => console.log(yellow(`${prefix} ${msg}\n`)) 6 | 7 | export const validate = compiler => { 8 | if (compiler.options.target !== 'node') { 9 | warn('webpack config `target` should be "node".') 10 | } 11 | 12 | if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') { 13 | warn('webpack config `output.libraryTarget` should be "commonjs2".') 14 | } 15 | 16 | if (!compiler.options.externals) { 17 | tip( 18 | 'It is recommended to externalize dependencies in the server build for ' + 19 | 'better build performance.' 20 | ) 21 | } 22 | } 23 | 24 | export { isJS, isCSS } from '../util' 25 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { escape } from 'he' 4 | import { hyphenate } from 'shared/util' 5 | import { getStyle } from 'web/util/style' 6 | 7 | function genStyleText (vnode: VNode): string { 8 | let styleText = '' 9 | const style = getStyle(vnode, false) 10 | for (const key in style) { 11 | const value = style[key] 12 | const hyphenatedKey = hyphenate(key) 13 | if (Array.isArray(value)) { 14 | for (let i = 0, len = value.length; i < len; i++) { 15 | styleText += `${hyphenatedKey}:${value[i]};` 16 | } 17 | } else { 18 | styleText += `${hyphenatedKey}:${value};` 19 | } 20 | } 21 | return styleText 22 | } 23 | 24 | export default function renderStyle (vnode: VNodeWithData): ?string { 25 | const styleText = genStyleText(vnode) 26 | if (styleText !== '') { 27 | return ` style=${JSON.stringify(escape(styleText))}` 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/modules/attrs.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend } from 'shared/util' 4 | 5 | function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) { 6 | if (!oldVnode.data.attrs && !vnode.data.attrs) { 7 | return 8 | } 9 | let key, cur, old 10 | const elm = vnode.elm 11 | const oldAttrs = oldVnode.data.attrs || {} 12 | let attrs = vnode.data.attrs || {} 13 | // clone observed objects, as the user probably wants to mutate it 14 | if (attrs.__ob__) { 15 | attrs = vnode.data.attrs = extend({}, attrs) 16 | } 17 | 18 | for (key in attrs) { 19 | cur = attrs[key] 20 | old = oldAttrs[key] 21 | if (old !== cur) { 22 | elm.setAttr(key, cur) 23 | } 24 | } 25 | for (key in oldAttrs) { 26 | if (attrs[key] == null) { 27 | elm.setAttr(key) 28 | } 29 | } 30 | } 31 | 32 | export default { 33 | create: updateAttrs, 34 | update: updateAttrs 35 | } 36 | -------------------------------------------------------------------------------- /vue-src/server/write.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | const MAX_STACK_DEPTH = 1000 4 | 5 | export function createWriteFunction ( 6 | write: (text: string, next: Function) => boolean, 7 | onError: Function 8 | ): Function { 9 | let stackDepth = 0 10 | const cachedWrite = (text, next) => { 11 | if (text && cachedWrite.caching) { 12 | cachedWrite.cacheBuffer[cachedWrite.cacheBuffer.length - 1] += text 13 | } 14 | const waitForNext = write(text, next) 15 | if (waitForNext !== true) { 16 | if (stackDepth >= MAX_STACK_DEPTH) { 17 | process.nextTick(() => { 18 | try { next() } catch (e) { 19 | onError(e) 20 | } 21 | }) 22 | } else { 23 | stackDepth++ 24 | next() 25 | stackDepth-- 26 | } 27 | } 28 | } 29 | cachedWrite.caching = false 30 | cachedWrite.cacheBuffer = [] 31 | cachedWrite.componentBuffer = [] 32 | return cachedWrite 33 | } 34 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler/modules/props.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { cached, camelize } from 'shared/util' 4 | 5 | const normalize = cached(camelize) 6 | 7 | function normalizeKeyName (str: string) : string { 8 | if (str.match(/^v\-/)) { 9 | return str.replace(/(v-[a-z\-]+\:)([a-z\-]+)$/i, ($, directive, prop) => { 10 | return directive + normalize(prop) 11 | }) 12 | } 13 | return normalize(str) 14 | } 15 | 16 | function transformNode (el: ASTElement, options: CompilerOptions) { 17 | if (Array.isArray(el.attrsList)) { 18 | el.attrsList.forEach(attr => { 19 | if (attr.name && attr.name.match(/\-/)) { 20 | const realName = normalizeKeyName(attr.name) 21 | if (el.attrsMap) { 22 | el.attrsMap[realName] = el.attrsMap[attr.name] 23 | delete el.attrsMap[attr.name] 24 | } 25 | attr.name = realName 26 | } 27 | }) 28 | } 29 | } 30 | export default { 31 | transformNode 32 | } 33 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isObject } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | /*处理v-for列表渲染*/ 9 | export function renderList ( 10 | val: any, 11 | render: () => VNode 12 | ): ?Array { 13 | /*根据类型循环render*/ 14 | let ret: ?Array, i, l, keys, key 15 | if (Array.isArray(val) || typeof val === 'string') { 16 | ret = new Array(val.length) 17 | for (i = 0, l = val.length; i < l; i++) { 18 | ret[i] = render(val[i], i) 19 | } 20 | } else if (typeof val === 'number') { 21 | ret = new Array(val) 22 | for (i = 0; i < val; i++) { 23 | ret[i] = render(i + 1, i) 24 | } 25 | } else if (isObject(val)) { 26 | keys = Object.keys(val) 27 | ret = new Array(keys.length) 28 | for (i = 0, l = keys.length; i < l; i++) { 29 | key = keys[i] 30 | ret[i] = render(val[key], key, i) 31 | } 32 | } 33 | return ret 34 | } 35 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server-renderer.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | process.env.VUE_ENV = 'server' 4 | 5 | import modules from './server/modules/index' 6 | import baseDirectives from './server/directives/index' 7 | import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' 8 | 9 | import { createRenderer as _createRenderer } from 'server/create-renderer' 10 | import { createBundleRendererCreator } from 'server/bundle-renderer/create-bundle-renderer' 11 | 12 | export function createRenderer (options?: Object = {}): { 13 | renderToString: Function, 14 | renderToStream: Function 15 | } { 16 | return _createRenderer(Object.assign({}, options, { 17 | isUnaryTag, 18 | canBeLeftOpenTag, 19 | modules, 20 | // user can provide server-side implementations for custom directives 21 | // when creating the renderer. 22 | directives: Object.assign(baseDirectives, options.directives) 23 | })) 24 | } 25 | 26 | export const createBundleRenderer = createBundleRendererCreator(createRenderer) 27 | -------------------------------------------------------------------------------- /vue-src/core/util/lang.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export const emptyObject = Object.freeze({}) 4 | 5 | /** 6 | * Check if a string starts with $ or _ 7 | */ 8 | export function isReserved (str: string): boolean { 9 | const c = (str + '').charCodeAt(0) 10 | return c === 0x24 || c === 0x5F 11 | } 12 | 13 | /** 14 | * Define a property. 15 | */ 16 | export function def (obj: Object, key: string, val: any, enumerable?: boolean) { 17 | Object.defineProperty(obj, key, { 18 | value: val, 19 | enumerable: !!enumerable, 20 | writable: true, 21 | configurable: true 22 | }) 23 | } 24 | 25 | /** 26 | * Parse simple path. 27 | */ 28 | const bailRE = /[^\w.$]/ 29 | export function parsePath (path: string): any { 30 | if (bailRE.test(path)) { 31 | return 32 | } 33 | const segments = path.split('.') 34 | return function (obj) { 35 | for (let i = 0; i < segments.length; i++) { 36 | if (!obj) return 37 | obj = obj[segments[i]] 38 | } 39 | return obj 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/render-slot.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend, warn } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering 7 | */ 8 | /*处理slot的渲染*/ 9 | export function renderSlot ( 10 | name: string, 11 | fallback: ?Array, 12 | props: ?Object, 13 | bindObject: ?Object 14 | ): ?Array { 15 | const scopedSlotFn = this.$scopedSlots[name] 16 | if (scopedSlotFn) { // scoped slot 17 | props = props || {} 18 | if (bindObject) { 19 | extend(props, bindObject) 20 | } 21 | return scopedSlotFn(props) || fallback 22 | } else { 23 | const slotNodes = this.$slots[name] 24 | // warn duplicate slot usage 25 | if (slotNodes && process.env.NODE_ENV !== 'production') { 26 | slotNodes._rendered && warn( 27 | `Duplicate presence of slot "${name}" found in the same render tree ` + 28 | `- this will likely cause render errors.`, 29 | this 30 | ) 31 | slotNodes._rendered = true 32 | } 33 | return slotNodes || fallback 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vue-src/core/vdom/helpers/merge-hook.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { createFnInvoker } from './update-listeners' 4 | import { remove, isDef, isUndef, isTrue } from 'shared/util' 5 | 6 | export function mergeVNodeHook (def: Object, hookKey: string, hook: Function) { 7 | let invoker 8 | const oldHook = def[hookKey] 9 | 10 | function wrappedHook () { 11 | hook.apply(this, arguments) 12 | // important: remove merged hook to ensure it's called only once 13 | // and prevent memory leak 14 | remove(invoker.fns, wrappedHook) 15 | } 16 | 17 | if (isUndef(oldHook)) { 18 | // no existing hook 19 | invoker = createFnInvoker([wrappedHook]) 20 | } else { 21 | /* istanbul ignore if */ 22 | if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { 23 | // already a merged invoker 24 | invoker = oldHook 25 | invoker.fns.push(wrappedHook) 26 | } else { 27 | // existing plain hook 28 | invoker = createFnInvoker([oldHook, wrappedHook]) 29 | } 30 | } 31 | 32 | invoker.merged = true 33 | def[hookKey] = invoker 34 | } 35 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/compiler/directives/model.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addHandler, addAttr } from 'compiler/helpers' 4 | import { genComponentModel, genAssignmentCode } from 'compiler/directives/model' 5 | 6 | export default function model ( 7 | el: ASTElement, 8 | dir: ASTDirective, 9 | _warn: Function 10 | ): ?boolean { 11 | if (el.tag === 'input' || el.tag === 'textarea') { 12 | genDefaultModel(el, dir.value, dir.modifiers) 13 | } else { 14 | genComponentModel(el, dir.value, dir.modifiers) 15 | } 16 | } 17 | 18 | function genDefaultModel ( 19 | el: ASTElement, 20 | value: string, 21 | modifiers: ?ASTModifiers 22 | ): ?boolean { 23 | const { lazy, trim, number } = modifiers || {} 24 | const event = lazy ? 'change' : 'input' 25 | 26 | let valueExpression = `$event.target.attr.value${trim ? '.trim()' : ''}` 27 | if (number) { 28 | valueExpression = `_n(${valueExpression})` 29 | } 30 | 31 | const code = genAssignmentCode(value, valueExpression) 32 | addAttr(el, 'value', `(${value})`) 33 | addHandler(el, event, code, null, true) 34 | } 35 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { makeMap } from 'shared/util' 4 | /*Github:https://github.com/answershuto*/ 5 | export const isUnaryTag = makeMap( 6 | 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' + 7 | 'link,meta,param,source,track,wbr' 8 | ) 9 | 10 | // Elements that you can, intentionally, leave open 11 | // (and which close themselves) 12 | export const canBeLeftOpenTag = makeMap( 13 | 'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source' 14 | ) 15 | /*Github:https://github.com/answershuto*/ 16 | // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3 17 | // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content 18 | export const isNonPhrasingTag = makeMap( 19 | 'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' + 20 | 'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' + 21 | 'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' + 22 | 'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' + 23 | 'title,tr,track' 24 | ) 25 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from 'core/index' 4 | import { patch } from 'weex/runtime/patch' 5 | import { mountComponent } from 'core/instance/lifecycle' 6 | import platformDirectives from 'weex/runtime/directives/index' 7 | import platformComponents from 'weex/runtime/components/index' 8 | 9 | import { 10 | query, 11 | mustUseProp, 12 | isReservedTag, 13 | isUnknownElement 14 | } from 'weex/util/index' 15 | 16 | // install platform specific utils 17 | Vue.config.mustUseProp = mustUseProp 18 | Vue.config.isReservedTag = isReservedTag 19 | Vue.config.isUnknownElement = isUnknownElement 20 | 21 | // install platform runtime directives and components 22 | Vue.options.directives = platformDirectives 23 | Vue.options.components = platformComponents 24 | 25 | // install platform patch function 26 | Vue.prototype.__patch__ = patch 27 | 28 | // wrap mount 29 | Vue.prototype.$mount = function ( 30 | el?: any, 31 | hydrating?: boolean 32 | ): Component { 33 | return mountComponent( 34 | this, 35 | el && query(el, this.$document), 36 | hydrating 37 | ) 38 | } 39 | 40 | export default Vue 41 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isDef, 5 | isUndef 6 | } from 'shared/util' 7 | 8 | import { 9 | concat, 10 | stringifyClass, 11 | genClassForVnode 12 | } from 'web/util/index' 13 | 14 | /*更新VNode的class*/ 15 | function updateClass (oldVnode: any, vnode: any) { 16 | const el = vnode.elm 17 | const data: VNodeData = vnode.data 18 | const oldData: VNodeData = oldVnode.data 19 | if ( 20 | isUndef(data.staticClass) && 21 | isUndef(data.class) && ( 22 | isUndef(oldData) || ( 23 | isUndef(oldData.staticClass) && 24 | isUndef(oldData.class) 25 | ) 26 | ) 27 | ) { 28 | return 29 | } 30 | 31 | let cls = genClassForVnode(vnode) 32 | 33 | // handle transition classes 34 | const transitionClass = el._transitionClasses 35 | if (isDef(transitionClass)) { 36 | cls = concat(cls, stringifyClass(transitionClass)) 37 | } 38 | 39 | // set the class 40 | if (cls !== el._prevClass) { 41 | el.setAttribute('class', cls) 42 | el._prevClass = cls 43 | } 44 | } 45 | 46 | export default { 47 | create: updateClass, 48 | update: updateClass 49 | } 50 | -------------------------------------------------------------------------------- /vue-src/server/template-renderer/parse-template.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | const compile = require('lodash.template') 4 | const compileOptions = { 5 | escape: /{{[^{]([\s\S]+?)[^}]}}/g, 6 | interpolate: /{{{([\s\S]+?)}}}/g 7 | } 8 | 9 | export type ParsedTemplate = { 10 | head: (data: any) => string; 11 | neck: (data: any) => string; 12 | tail: (data: any) => string; 13 | }; 14 | 15 | export function parseTemplate ( 16 | template: string, 17 | contentPlaceholder?: string = '' 18 | ): ParsedTemplate { 19 | if (typeof template === 'object') { 20 | return template 21 | } 22 | 23 | let i = template.indexOf('') 24 | const j = template.indexOf(contentPlaceholder) 25 | 26 | if (j < 0) { 27 | throw new Error(`Content placeholder not found in template.`) 28 | } 29 | 30 | if (i < 0) { 31 | i = template.indexOf('') 32 | if (i < 0) { 33 | i = j 34 | } 35 | } 36 | 37 | return { 38 | head: compile(template.slice(0, i), compileOptions), 39 | neck: compile(template.slice(i, j), compileOptions), 40 | tail: compile(template.slice(j + contentPlaceholder.length), compileOptions) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vuex-src/mixin.js: -------------------------------------------------------------------------------- 1 | export default function (Vue) { 2 | /*获取Vue版本,鉴别Vue1.0还是Vue2.0*/ 3 | const version = Number(Vue.version.split('.')[0]) 4 | 5 | if (version >= 2) { 6 | /*通过mixin将vuexInit混淆到Vue实例的beforeCreate钩子中*/ 7 | Vue.mixin({ beforeCreate: vuexInit }) 8 | } else { 9 | // override init and inject vuex init procedure 10 | // for 1.x backwards compatibility. 11 | /*将vuexInit放入_init中调用*/ 12 | const _init = Vue.prototype._init 13 | Vue.prototype._init = function (options = {}) { 14 | options.init = options.init 15 | ? [vuexInit].concat(options.init) 16 | : vuexInit 17 | _init.call(this, options) 18 | } 19 | } 20 | 21 | /** 22 | * Vuex init hook, injected into each instances init hooks list. 23 | */ 24 | /*Vuex的init钩子,会存入每一个Vue实例等钩子列表*/ 25 | function vuexInit () { 26 | const options = this.$options 27 | // store injection 28 | if (options.store) { 29 | /*存在store其实代表的就是Root节点,直接执行store(function时)或者使用store(非function)*/ 30 | this.$store = typeof options.store === 'function' 31 | ? options.store() 32 | : options.store 33 | } else if (options.parent && options.parent.$store) { 34 | /*子组件直接从父组件中获取$store,这样就保证了所有组件都公用了全局的同一份store*/ 35 | this.$store = options.parent.$store 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/bind-object-props.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | import { isObject, warn, toObject } from 'core/util/index' 5 | 6 | /** 7 | * Runtime helper for merging v-bind="object" into a VNode's data. 8 | */ 9 | /*合并v-bind指令到VNode中*/ 10 | export function bindObjectProps ( 11 | data: any, 12 | tag: string, 13 | value: any, 14 | asProp?: boolean 15 | ): VNodeData { 16 | if (value) { 17 | if (!isObject(value)) { 18 | /*v-bind必须提供一个Object或者Array作为参数*/ 19 | process.env.NODE_ENV !== 'production' && warn( 20 | 'v-bind without argument expects an Object or Array value', 21 | this 22 | ) 23 | } else { 24 | if (Array.isArray(value)) { 25 | /*合并Array数组中的每一个对象到一个新的Object中*/ 26 | value = toObject(value) 27 | } 28 | let hash 29 | for (const key in value) { 30 | if (key === 'class' || key === 'style') { 31 | hash = data 32 | } else { 33 | const type = data.attrs && data.attrs.type 34 | hash = asProp || config.mustUseProp(tag, type, key) 35 | ? data.domProps || (data.domProps = {}) 36 | : data.attrs || (data.attrs = {}) 37 | } 38 | if (!(key in hash)) { 39 | hash[key] = value[key] 40 | } 41 | } 42 | } 43 | } 44 | return data 45 | } 46 | -------------------------------------------------------------------------------- /vue-src/compiler/parser/text-parser.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { cached } from 'shared/util' 4 | import { parseFilters } from './filter-parser' 5 | 6 | const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g 7 | const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g 8 | 9 | const buildRegex = cached(delimiters => { 10 | const open = delimiters[0].replace(regexEscapeRE, '\\$&') 11 | const close = delimiters[1].replace(regexEscapeRE, '\\$&') 12 | return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') 13 | }) 14 | /*Github:https://github.com/answershuto*/ 15 | export function parseText ( 16 | text: string, 17 | delimiters?: [string, string] 18 | ): string | void { 19 | const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE 20 | if (!tagRE.test(text)) { 21 | return 22 | } 23 | const tokens = [] 24 | let lastIndex = tagRE.lastIndex = 0 25 | let match, index 26 | while ((match = tagRE.exec(text))) { 27 | index = match.index 28 | // push text token 29 | if (index > lastIndex) { 30 | tokens.push(JSON.stringify(text.slice(lastIndex, index))) 31 | } 32 | // tag token 33 | const exp = parseFilters(match[1].trim()) 34 | tokens.push(`_s(${exp})`) 35 | lastIndex = index + match[0].length 36 | } 37 | if (lastIndex < text.length) { 38 | tokens.push(JSON.stringify(text.slice(lastIndex))) 39 | } 40 | return tokens.join('+') 41 | } 42 | -------------------------------------------------------------------------------- /vue-src/core/global-api/assets.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from '../config' 4 | import { ASSET_TYPES } from 'shared/constants' 5 | import { warn, isPlainObject } from '../util/index' 6 | 7 | export function initAssetRegisters (Vue: GlobalAPI) { 8 | /** 9 | * Create asset registration methods. 10 | */ 11 | ASSET_TYPES.forEach(type => { 12 | Vue[type] = function ( 13 | id: string, 14 | definition: Function | Object 15 | ): Function | Object | void { 16 | if (!definition) { 17 | return this.options[type + 's'][id] 18 | } else { 19 | /* istanbul ignore if */ 20 | if (process.env.NODE_ENV !== 'production') { 21 | if (type === 'component' && config.isReservedTag(id)) { 22 | warn( 23 | 'Do not use built-in or reserved HTML elements as component ' + 24 | 'id: ' + id 25 | ) 26 | } 27 | } 28 | if (type === 'component' && isPlainObject(definition)) { 29 | definition.name = definition.name || id 30 | definition = this.options._base.extend(definition) 31 | } 32 | if (type === 'directive' && typeof definition === 'function') { 33 | definition = { bind: definition, update: definition } 34 | } 35 | this.options[type + 's'][id] = definition 36 | return definition 37 | } 38 | } 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/modules/attrs.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { escape } from 'he' 4 | 5 | import { 6 | isDef, 7 | isUndef 8 | } from 'shared/util' 9 | 10 | import { 11 | isBooleanAttr, 12 | isEnumeratedAttr, 13 | isFalsyAttrValue 14 | } from 'web/util/attrs' 15 | 16 | export default function renderAttrs (node: VNodeWithData): string { 17 | let attrs = node.data.attrs 18 | let res = '' 19 | 20 | let parent = node.parent 21 | while (isDef(parent)) { 22 | if (isDef(parent.data) && isDef(parent.data.attrs)) { 23 | attrs = Object.assign({}, attrs, parent.data.attrs) 24 | } 25 | parent = parent.parent 26 | } 27 | 28 | if (isUndef(attrs)) { 29 | return res 30 | } 31 | 32 | for (const key in attrs) { 33 | if (key === 'style') { 34 | // leave it to the style module 35 | continue 36 | } 37 | res += renderAttr(key, attrs[key]) 38 | } 39 | return res 40 | } 41 | 42 | export function renderAttr (key: string, value: string): string { 43 | if (isBooleanAttr(key)) { 44 | if (!isFalsyAttrValue(value)) { 45 | return ` ${key}="${key}"` 46 | } 47 | } else if (isEnumeratedAttr(key)) { 48 | return ` ${key}="${isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'}"` 49 | } else if (!isFalsyAttrValue(value)) { 50 | return ` ${key}="${typeof value === 'string' ? escape(value) : value}"` 51 | } 52 | return '' 53 | } 54 | -------------------------------------------------------------------------------- /vue-src/core/vdom/modules/ref.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { remove } from 'shared/util' 4 | 5 | export default { 6 | create (_: any, vnode: VNodeWithData) { 7 | registerRef(vnode) 8 | }, 9 | update (oldVnode: VNodeWithData, vnode: VNodeWithData) { 10 | if (oldVnode.data.ref !== vnode.data.ref) { 11 | registerRef(oldVnode, true) 12 | registerRef(vnode) 13 | } 14 | }, 15 | destroy (vnode: VNodeWithData) { 16 | registerRef(vnode, true) 17 | } 18 | } 19 | 20 | /*注册一个ref(即在$refs中添加或者删除对应的Dom实例),isRemoval代表是增加还是移除,*/ 21 | export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { 22 | const key = vnode.data.ref 23 | if (!key) return 24 | 25 | const vm = vnode.context 26 | const ref = vnode.componentInstance || vnode.elm 27 | const refs = vm.$refs 28 | if (isRemoval) { 29 | /*移除一个ref*/ 30 | if (Array.isArray(refs[key])) { 31 | remove(refs[key], ref) 32 | } else if (refs[key] === ref) { 33 | refs[key] = undefined 34 | } 35 | } else { 36 | /*增加一个ref*/ 37 | if (vnode.data.refInFor) { 38 | /*如果是在一个for循环中,则refs中key对应的是一个数组,里面存放了所有ref指向的Dom实例*/ 39 | if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) { 40 | refs[key].push(ref) 41 | } else { 42 | refs[key] = [ref] 43 | } 44 | } else { 45 | /*不在一个for循环中则直接放入refs即可,ref指向Dom实例*/ 46 | refs[key] = ref 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/modules/events.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { updateListeners } from 'core/vdom/helpers/update-listeners' 4 | 5 | let target: any 6 | 7 | function add ( 8 | event: string, 9 | handler: Function, 10 | once: boolean, 11 | capture: boolean 12 | ) { 13 | if (capture) { 14 | console.log('Weex do not support event in bubble phase.') 15 | return 16 | } 17 | if (once) { 18 | const oldHandler = handler 19 | const _target = target // save current target element in closure 20 | handler = function (ev) { 21 | const res = arguments.length === 1 22 | ? oldHandler(ev) 23 | : oldHandler.apply(null, arguments) 24 | if (res !== null) { 25 | remove(event, null, null, _target) 26 | } 27 | } 28 | } 29 | target.addEvent(event, handler) 30 | } 31 | 32 | function remove ( 33 | event: string, 34 | handler: any, 35 | capture: any, 36 | _target?: any 37 | ) { 38 | (_target || target).removeEvent(event) 39 | } 40 | 41 | function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { 42 | if (!oldVnode.data.on && !vnode.data.on) { 43 | return 44 | } 45 | const on = vnode.data.on || {} 46 | const oldOn = oldVnode.data.on || {} 47 | target = vnode.elm 48 | updateListeners(on, oldOn, add, remove, vnode.context) 49 | } 50 | 51 | export default { 52 | create: updateDOMListeners, 53 | update: updateDOMListeners 54 | } 55 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/modules/dom-props.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import VNode from 'core/vdom/vnode' 4 | import { renderAttr } from './attrs' 5 | import { isDef, isUndef } from 'shared/util' 6 | import { propsToAttrMap, isRenderableAttr } from '../util' 7 | 8 | export default function renderDOMProps (node: VNodeWithData): string { 9 | let props = node.data.domProps 10 | let res = '' 11 | 12 | let parent = node.parent 13 | while (isDef(parent)) { 14 | if (parent.data && parent.data.domProps) { 15 | props = Object.assign({}, props, parent.data.domProps) 16 | } 17 | parent = parent.parent 18 | } 19 | 20 | if (isUndef(props)) { 21 | return res 22 | } 23 | 24 | const attrs = node.data.attrs 25 | for (const key in props) { 26 | if (key === 'innerHTML') { 27 | setText(node, props[key], true) 28 | } else if (key === 'textContent') { 29 | setText(node, props[key], false) 30 | } else { 31 | const attr = propsToAttrMap[key] || key.toLowerCase() 32 | if (isRenderableAttr(attr) && 33 | // avoid rendering double-bound props/attrs twice 34 | !(isDef(attrs) && isDef(attrs[attr]))) { 35 | res += renderAttr(attr, props[key]) 36 | } 37 | } 38 | } 39 | return res 40 | } 41 | 42 | function setText (node, text, raw) { 43 | const child = new VNode(undefined, undefined, undefined, text) 44 | child.raw = raw 45 | node.children = [child] 46 | } 47 | -------------------------------------------------------------------------------- /vue-src/server/bundle-renderer/source-map-support.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | const SourceMapConsumer = require('source-map').SourceMapConsumer 4 | 5 | const filenameRE = /\(([^)]+\.js):(\d+):(\d+)\)$/ 6 | 7 | export function createSourceMapConsumers (rawMaps: Object) { 8 | const maps = {} 9 | Object.keys(rawMaps).forEach(file => { 10 | maps[file] = new SourceMapConsumer(rawMaps[file]) 11 | }) 12 | return maps 13 | } 14 | 15 | export function rewriteErrorTrace (e: any, mapConsumers: { 16 | [key: string]: SourceMapConsumer 17 | }) { 18 | if (e && typeof e.stack === 'string') { 19 | e.stack = e.stack.split('\n').map(line => { 20 | return rewriteTraceLine(line, mapConsumers) 21 | }).join('\n') 22 | } 23 | } 24 | 25 | function rewriteTraceLine (trace: string, mapConsumers: { 26 | [key: string]: SourceMapConsumer 27 | }) { 28 | const m = trace.match(filenameRE) 29 | const map = m && mapConsumers[m[1]] 30 | if (m != null && map) { 31 | const originalPosition = map.originalPositionFor({ 32 | line: Number(m[2]), 33 | column: Number(m[3]) 34 | }) 35 | if (originalPosition.source != null) { 36 | const { source, line, column } = originalPosition 37 | const mappedPosition = `(${source.replace(/^webpack:\/\/\//, '')}:${String(line)}:${String(column)})` 38 | return trace.replace(filenameRE, mappedPosition) 39 | } else { 40 | return trace 41 | } 42 | } else { 43 | return trace 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { 5 | getAndRemoveAttr, 6 | getBindingAttr, 7 | baseWarn 8 | } from 'compiler/helpers' 9 | 10 | function transformNode (el: ASTElement, options: CompilerOptions) { 11 | const warn = options.warn || baseWarn 12 | const staticClass = getAndRemoveAttr(el, 'class') 13 | if (process.env.NODE_ENV !== 'production' && staticClass) { 14 | const expression = parseText(staticClass, options.delimiters) 15 | if (expression) { 16 | warn( 17 | `class="${staticClass}": ` + 18 | 'Interpolation inside attributes has been removed. ' + 19 | 'Use v-bind or the colon shorthand instead. For example, ' + 20 | 'instead of
, use
.' 21 | ) 22 | } 23 | } 24 | if (staticClass) { 25 | el.staticClass = JSON.stringify(staticClass) 26 | } 27 | const classBinding = getBindingAttr(el, 'class', false /* getStatic */) 28 | if (classBinding) { 29 | el.classBinding = classBinding 30 | } 31 | } 32 | 33 | function genData (el: ASTElement): string { 34 | let data = '' 35 | if (el.staticClass) { 36 | data += `staticClass:${el.staticClass},` 37 | } 38 | if (el.classBinding) { 39 | data += `class:${el.classBinding},` 40 | } 41 | return data 42 | } 43 | 44 | export default { 45 | staticKeys: ['staticClass'], 46 | transformNode, 47 | genData 48 | } 49 | -------------------------------------------------------------------------------- /vue-router-src/history/abstract.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type Router from '../index' 4 | import { History } from './base' 5 | 6 | export class AbstractHistory extends History { 7 | index: number; 8 | stack: Array; 9 | 10 | constructor (router: Router, base: ?string) { 11 | super(router, base) 12 | this.stack = [] 13 | this.index = -1 14 | } 15 | 16 | push (location: RawLocation, onComplete?: Function, onAbort?: Function) { 17 | this.transitionTo(location, route => { 18 | this.stack = this.stack.slice(0, this.index + 1).concat(route) 19 | this.index++ 20 | onComplete && onComplete(route) 21 | }, onAbort) 22 | } 23 | 24 | replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { 25 | this.transitionTo(location, route => { 26 | this.stack = this.stack.slice(0, this.index).concat(route) 27 | onComplete && onComplete(route) 28 | }, onAbort) 29 | } 30 | 31 | go (n: number) { 32 | const targetIndex = this.index + n 33 | if (targetIndex < 0 || targetIndex >= this.stack.length) { 34 | return 35 | } 36 | const route = this.stack[targetIndex] 37 | this.confirmTransition(route, () => { 38 | this.index = targetIndex 39 | this.updateRoute(route) 40 | }) 41 | } 42 | 43 | getCurrentLocation () { 44 | const current = this.stack[this.stack.length - 1] 45 | return current ? current.fullPath : '/' 46 | } 47 | 48 | ensureURL () { 49 | // noop 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/util/index.js: -------------------------------------------------------------------------------- 1 | /* globals renderer */ 2 | 3 | import { makeMap } from 'shared/util' 4 | 5 | /*判断是否是保留的标签*/ 6 | export const isReservedTag = makeMap( 7 | 'template,script,style,element,content,slot,link,meta,svg,view,' + 8 | 'a,div,img,image,text,span,richtext,input,switch,textarea,spinner,select,' + 9 | 'slider,slider-neighbor,indicator,trisition,trisition-group,canvas,' + 10 | 'list,cell,header,loading,loading-indicator,refresh,scrollable,scroller,' + 11 | 'video,web,embed,tabbar,tabheader,datepicker,timepicker,marquee,countdown', 12 | true 13 | ) 14 | 15 | // Elements that you can, intentionally, leave open (and which close themselves) 16 | // more flexable than web 17 | export const canBeLeftOpenTag = makeMap( 18 | 'web,spinner,switch,video,textarea,canvas,' + 19 | 'indicator,marquee,countdown', 20 | true 21 | ) 22 | 23 | export const isUnaryTag = makeMap( 24 | 'embed,img,image,input,link,meta', 25 | true 26 | ) 27 | 28 | export function mustUseProp () { /* console.log('mustUseProp') */ } 29 | export function getTagNamespace () { /* console.log('getTagNamespace') */ } 30 | export function isUnknownElement () { /* console.log('isUnknownElement') */ } 31 | 32 | export function query (el, document) { 33 | // renderer is injected by weex factory wrapper 34 | const placeholder = new renderer.Comment('root') 35 | placeholder.hasAttribute = placeholder.removeAttribute = function () {} // hack for patch 36 | document.documentElement.appendChild(placeholder) 37 | return placeholder 38 | } 39 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/class-util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * Add class with compatibility for SVG since classList is not supported on 5 | * SVG elements in IE 6 | */ 7 | export function addClass (el: HTMLElement, cls: ?string) { 8 | /* istanbul ignore if */ 9 | if (!cls || !(cls = cls.trim())) { 10 | return 11 | } 12 | 13 | /* istanbul ignore else */ 14 | if (el.classList) { 15 | if (cls.indexOf(' ') > -1) { 16 | cls.split(/\s+/).forEach(c => el.classList.add(c)) 17 | } else { 18 | el.classList.add(cls) 19 | } 20 | } else { 21 | const cur = ` ${el.getAttribute('class') || ''} ` 22 | if (cur.indexOf(' ' + cls + ' ') < 0) { 23 | el.setAttribute('class', (cur + cls).trim()) 24 | } 25 | } 26 | } 27 | 28 | /** 29 | * Remove class with compatibility for SVG since classList is not supported on 30 | * SVG elements in IE 31 | */ 32 | export function removeClass (el: HTMLElement, cls: ?string) { 33 | /* istanbul ignore if */ 34 | if (!cls || !(cls = cls.trim())) { 35 | return 36 | } 37 | 38 | /* istanbul ignore else */ 39 | if (el.classList) { 40 | if (cls.indexOf(' ') > -1) { 41 | cls.split(/\s+/).forEach(c => el.classList.remove(c)) 42 | } else { 43 | el.classList.remove(cls) 44 | } 45 | } else { 46 | let cur = ` ${el.getAttribute('class') || ''} ` 47 | const tar = ' ' + cls + ' ' 48 | while (cur.indexOf(tar) >= 0) { 49 | cur = cur.replace(tar, ' ') 50 | } 51 | el.setAttribute('class', cur.trim()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /vue-src/platforms/web/server/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { makeMap } from 'shared/util' 4 | 5 | const isAttr = makeMap( 6 | 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + 7 | 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + 8 | 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + 9 | 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + 10 | 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + 11 | 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + 12 | 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + 13 | 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + 14 | 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + 15 | 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + 16 | 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + 17 | 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + 18 | 'target,title,type,usemap,value,width,wrap' 19 | ) 20 | 21 | /* istanbul ignore next */ 22 | const isRenderableAttr = (name: string): boolean => { 23 | return ( 24 | isAttr(name) || 25 | name.indexOf('data-') === 0 || 26 | name.indexOf('aria-') === 0 27 | ) 28 | } 29 | export { isRenderableAttr } 30 | 31 | export const propsToAttrMap = { 32 | acceptCharset: 'accept-charset', 33 | className: 'class', 34 | htmlFor: 'for', 35 | httpEquiv: 'http-equiv' 36 | } 37 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/resolve-slots.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * Runtime helper for resolving raw children VNodes into a slot object. 5 | */ 6 | export function resolveSlots ( 7 | children: ?Array, 8 | context: ?Component 9 | ): { [key: string]: Array } { 10 | const slots = {} 11 | if (!children) { 12 | return slots 13 | } 14 | const defaultSlot = [] 15 | for (let i = 0, l = children.length; i < l; i++) { 16 | const child = children[i] 17 | // named slots should only be respected if the vnode was rendered in the 18 | // same context. 19 | if ((child.context === context || child.functionalContext === context) && 20 | child.data && child.data.slot != null) { 21 | const name = child.data.slot 22 | const slot = (slots[name] || (slots[name] = [])) 23 | if (child.tag === 'template') { 24 | slot.push.apply(slot, child.children) 25 | } else { 26 | slot.push(child) 27 | } 28 | } else { 29 | defaultSlot.push(child) 30 | } 31 | } 32 | // ignore whitespace 33 | if (!defaultSlot.every(isWhitespace)) { 34 | slots.default = defaultSlot 35 | } 36 | return slots 37 | } 38 | 39 | function isWhitespace (node: VNode): boolean { 40 | return node.isComment || node.text === ' ' 41 | } 42 | 43 | /*处理ScopedSlots*/ 44 | export function resolveScopedSlots ( 45 | fns: Array<[string, Function]> 46 | ): { [key: string]: Function } { 47 | const res = {} 48 | for (let i = 0; i < fns.length; i++) { 49 | res[fns[i][0]] = fns[i][1] 50 | } 51 | return res 52 | } 53 | -------------------------------------------------------------------------------- /vue-src/platforms/web/compiler/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { parseStyleText } from 'web/util/style' 5 | import { 6 | getAndRemoveAttr, 7 | getBindingAttr, 8 | baseWarn 9 | } from 'compiler/helpers' 10 | 11 | function transformNode (el: ASTElement, options: CompilerOptions) { 12 | const warn = options.warn || baseWarn 13 | const staticStyle = getAndRemoveAttr(el, 'style') 14 | if (staticStyle) { 15 | /* istanbul ignore if */ 16 | if (process.env.NODE_ENV !== 'production') { 17 | const expression = parseText(staticStyle, options.delimiters) 18 | if (expression) { 19 | warn( 20 | `style="${staticStyle}": ` + 21 | 'Interpolation inside attributes has been removed. ' + 22 | 'Use v-bind or the colon shorthand instead. For example, ' + 23 | 'instead of
, use
.' 24 | ) 25 | } 26 | } 27 | el.staticStyle = JSON.stringify(parseStyleText(staticStyle)) 28 | } 29 | 30 | const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) 31 | if (styleBinding) { 32 | el.styleBinding = styleBinding 33 | } 34 | } 35 | 36 | function genData (el: ASTElement): string { 37 | let data = '' 38 | if (el.staticStyle) { 39 | data += `staticStyle:${el.staticStyle},` 40 | } 41 | if (el.styleBinding) { 42 | data += `style:(${el.styleBinding}),` 43 | } 44 | return data 45 | } 46 | 47 | export default { 48 | staticKeys: ['staticStyle'], 49 | transformNode, 50 | genData 51 | } 52 | -------------------------------------------------------------------------------- /vue-src/core/observer/dep.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type Watcher from './watcher' 4 | import { remove } from '../util/index' 5 | 6 | let uid = 0 7 | /*Github:https://github.com/answershuto*/ 8 | /** 9 | * A dep is an observable that can have multiple 10 | * directives subscribing to it. 11 | */ 12 | export default class Dep { 13 | static target: ?Watcher; 14 | id: number; 15 | subs: Array; 16 | 17 | constructor () { 18 | this.id = uid++ 19 | this.subs = [] 20 | } 21 | 22 | /*添加一个观察者对象*/ 23 | addSub (sub: Watcher) { 24 | this.subs.push(sub) 25 | } 26 | 27 | /*移除一个观察者对象*/ 28 | removeSub (sub: Watcher) { 29 | remove(this.subs, sub) 30 | } 31 | 32 | /*依赖收集,当存在Dep.target的时候添加观察者对象*/ 33 | depend () { 34 | if (Dep.target) { 35 | Dep.target.addDep(this) 36 | } 37 | } 38 | 39 | /*通知所有订阅者*/ 40 | notify () { 41 | // stabilize the subscriber list first 42 | const subs = this.subs.slice() 43 | for (let i = 0, l = subs.length; i < l; i++) { 44 | subs[i].update() 45 | } 46 | } 47 | } 48 | 49 | // the current target watcher being evaluated. 50 | // this is globally unique because there could be only one 51 | // watcher being evaluated at any time. 52 | /*依赖收集完需要将Dep.target设为null,防止后面重复添加依赖。*/ 53 | Dep.target = null 54 | const targetStack = [] 55 | 56 | /*将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中*/ 57 | export function pushTarget (_target: Watcher) { 58 | if (Dep.target) targetStack.push(Dep.target) 59 | Dep.target = _target 60 | } 61 | 62 | /*将观察者实例从target栈中取出并设置给Dep.target*/ 63 | export function popTarget () { 64 | Dep.target = targetStack.pop() 65 | } 66 | -------------------------------------------------------------------------------- /vue-router-src/util/push-state.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { inBrowser } from './dom' 4 | import { saveScrollPosition } from './scroll' 5 | 6 | export const supportsPushState = inBrowser && (function () { 7 | const ua = window.navigator.userAgent 8 | 9 | if ( 10 | (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) && 11 | ua.indexOf('Mobile Safari') !== -1 && 12 | ua.indexOf('Chrome') === -1 && 13 | ua.indexOf('Windows Phone') === -1 14 | ) { 15 | return false 16 | } 17 | 18 | return window.history && 'pushState' in window.history 19 | })() 20 | 21 | // use User Timing api (if present) for more accurate key precision 22 | const Time = inBrowser && window.performance && window.performance.now 23 | ? window.performance 24 | : Date 25 | 26 | let _key: string = genKey() 27 | 28 | function genKey (): string { 29 | return Time.now().toFixed(3) 30 | } 31 | 32 | export function getStateKey () { 33 | return _key 34 | } 35 | 36 | export function setStateKey (key: string) { 37 | _key = key 38 | } 39 | 40 | export function pushState (url?: string, replace?: boolean) { 41 | saveScrollPosition() 42 | // try...catch the pushState call to get around Safari 43 | // DOM Exception 18 where it limits to 100 pushState calls 44 | const history = window.history 45 | try { 46 | if (replace) { 47 | history.replaceState({ key: _key }, '', url) 48 | } else { 49 | _key = genKey() 50 | history.pushState({ key: _key }, '', url) 51 | } 52 | } catch (e) { 53 | window.location[replace ? 'replace' : 'assign'](url) 54 | } 55 | } 56 | 57 | export function replaceState (url?: string) { 58 | pushState(url, true) 59 | } 60 | -------------------------------------------------------------------------------- /vue-src/core/observer/array.js: -------------------------------------------------------------------------------- 1 | /* 2 | * not type checking this file because flow doesn't play well with 3 | * dynamically accessing methods on Array prototype 4 | */ 5 | /*Github:https://github.com/answershuto*/ 6 | import { def } from '../util/index' 7 | 8 | /*取得原生数组的原型*/ 9 | const arrayProto = Array.prototype 10 | /*创建一个新的数组对象,修改该对象上的数组的七个方法,防止污染原生数组方法*/ 11 | export const arrayMethods = Object.create(arrayProto) 12 | 13 | /** 14 | * Intercept mutating methods and emit events 15 | */ 16 | /*这里重写了数组的这些方法,在保证不污染原生数组原型的情况下重写数组的这些方法,截获数组的成员发生的变化,执行原生数组操作的同时dep通知关联的所有观察者进行响应式处理*/ 17 | ;[ 18 | 'push', 19 | 'pop', 20 | 'shift', 21 | 'unshift', 22 | 'splice', 23 | 'sort', 24 | 'reverse' 25 | ] 26 | .forEach(function (method) { 27 | // cache original method 28 | /*将数组的原生方法缓存起来,后面要调用*/ 29 | const original = arrayProto[method] 30 | def(arrayMethods, method, function mutator () { 31 | // avoid leaking arguments: 32 | // http://jsperf.com/closure-with-arguments 33 | let i = arguments.length 34 | const args = new Array(i) 35 | while (i--) { 36 | args[i] = arguments[i] 37 | } 38 | /*调用原生的数组方法*/ 39 | const result = original.apply(this, args) 40 | 41 | /*数组新插入的元素需要重新进行observe才能响应式*/ 42 | const ob = this.__ob__ 43 | let inserted 44 | switch (method) { 45 | case 'push': 46 | inserted = args 47 | break 48 | case 'unshift': 49 | inserted = args 50 | break 51 | case 'splice': 52 | inserted = args.slice(2) 53 | break 54 | } 55 | if (inserted) ob.observeArray(inserted) 56 | 57 | // notify change 58 | /*dep通知所有注册的观察者进行响应式处理*/ 59 | ob.dep.notify() 60 | return result 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /vue-src/server/template-renderer/create-async-file-mapper.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * Creates a mapper that maps components used during a server-side render 5 | * to async chunk files in the client-side build, so that we can inline them 6 | * directly in the rendered HTML to avoid waterfall requests. 7 | */ 8 | 9 | import type { ClientManifest } from './index' 10 | 11 | export type AsyncFileMapper = (files: Array) => Array; 12 | 13 | export function createMapper ( 14 | clientManifest: ClientManifest 15 | ): AsyncFileMapper { 16 | const map = createMap(clientManifest) 17 | // map server-side moduleIds to client-side files 18 | return function mapper (moduleIds: Array): Array { 19 | const res = new Set() 20 | for (let i = 0; i < moduleIds.length; i++) { 21 | const mapped = map.get(moduleIds[i]) 22 | if (mapped) { 23 | for (let j = 0; j < mapped.length; j++) { 24 | res.add(mapped[j]) 25 | } 26 | } 27 | } 28 | return Array.from(res) 29 | } 30 | } 31 | 32 | function createMap (clientManifest) { 33 | const map = new Map() 34 | Object.keys(clientManifest.modules).forEach(id => { 35 | map.set(id, mapIdToFile(id, clientManifest)) 36 | }) 37 | return map 38 | } 39 | 40 | function mapIdToFile (id, clientManifest) { 41 | const files = [] 42 | const fileIndices = clientManifest.modules[id] 43 | if (fileIndices) { 44 | fileIndices.forEach(index => { 45 | const file = clientManifest.all[index] 46 | // only include async files or non-js assets 47 | if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) { 48 | files.push(file) 49 | } 50 | }) 51 | } 52 | return files 53 | } 54 | -------------------------------------------------------------------------------- /vue-src/platforms/web/util/attrs.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { makeMap } from 'shared/util' 4 | 5 | // these are reserved for web because they are directly compiled away 6 | // during template compilation 7 | export const isReservedAttr = makeMap('style,class') 8 | 9 | // attributes that should be using props for binding 10 | const acceptValue = makeMap('input,textarea,option,select') 11 | export const mustUseProp = (tag: string, type: ?string, attr: string): boolean => { 12 | return ( 13 | (attr === 'value' && acceptValue(tag)) && type !== 'button' || 14 | (attr === 'selected' && tag === 'option') || 15 | (attr === 'checked' && tag === 'input') || 16 | (attr === 'muted' && tag === 'video') 17 | ) 18 | } 19 | 20 | export const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck') 21 | 22 | export const isBooleanAttr = makeMap( 23 | 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' + 24 | 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' + 25 | 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' + 26 | 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' + 27 | 'required,reversed,scoped,seamless,selected,sortable,translate,' + 28 | 'truespeed,typemustmatch,visible' 29 | ) 30 | 31 | export const xlinkNS = 'http://www.w3.org/1999/xlink' 32 | 33 | export const isXlink = (name: string): boolean => { 34 | return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' 35 | } 36 | 37 | export const getXlinkProp = (name: string): string => { 38 | return isXlink(name) ? name.slice(6, name.length) : '' 39 | } 40 | 41 | export const isFalsyAttrValue = (val: any): boolean => { 42 | return val == null || val === false 43 | } 44 | -------------------------------------------------------------------------------- /vuex-src/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the first item that pass the test 3 | * by second argument function 4 | * 5 | * @param {Array} list 6 | * @param {Function} f 7 | * @return {*} 8 | */ 9 | function find (list, f) { 10 | return list.filter(f)[0] 11 | } 12 | 13 | /** 14 | * Deep copy the given object considering circular structure. 15 | * This function caches all nested objects and its copies. 16 | * If it detects circular structure, use cached copy to avoid infinite loop. 17 | * 18 | * @param {*} obj 19 | * @param {Array} cache 20 | * @return {*} 21 | */ 22 | export function deepCopy (obj, cache = []) { 23 | // just return if obj is immutable value 24 | if (obj === null || typeof obj !== 'object') { 25 | return obj 26 | } 27 | 28 | // if obj is hit, it is in circular structure 29 | const hit = find(cache, c => c.original === obj) 30 | if (hit) { 31 | return hit.copy 32 | } 33 | 34 | const copy = Array.isArray(obj) ? [] : {} 35 | // put the copy into cache at first 36 | // because we want to refer it in recursive deepCopy 37 | cache.push({ 38 | original: obj, 39 | copy 40 | }) 41 | 42 | Object.keys(obj).forEach(key => { 43 | copy[key] = deepCopy(obj[key], cache) 44 | }) 45 | 46 | return copy 47 | } 48 | 49 | /** 50 | * forEach for object 51 | */ 52 | export function forEachValue (obj, fn) { 53 | Object.keys(obj).forEach(key => fn(obj[key], key)) 54 | } 55 | 56 | export function isObject (obj) { 57 | return obj !== null && typeof obj === 'object' 58 | } 59 | 60 | /* 判断是否是Promise */ 61 | export function isPromise (val) { 62 | return val && typeof val.then === 'function' 63 | } 64 | 65 | export function assert (condition, msg) { 66 | if (!condition) throw new Error(`[vuex] ${msg}`) 67 | } 68 | -------------------------------------------------------------------------------- /vue-router-src/util/path.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function resolvePath ( 4 | relative: string, 5 | base: string, 6 | append?: boolean 7 | ): string { 8 | const firstChar = relative.charAt(0) 9 | if (firstChar === '/') { 10 | return relative 11 | } 12 | 13 | if (firstChar === '?' || firstChar === '#') { 14 | return base + relative 15 | } 16 | 17 | const stack = base.split('/') 18 | 19 | // remove trailing segment if: 20 | // - not appending 21 | // - appending to trailing slash (last segment is empty) 22 | if (!append || !stack[stack.length - 1]) { 23 | stack.pop() 24 | } 25 | 26 | // resolve relative path 27 | const segments = relative.replace(/^\//, '').split('/') 28 | for (let i = 0; i < segments.length; i++) { 29 | const segment = segments[i] 30 | if (segment === '..') { 31 | stack.pop() 32 | } else if (segment !== '.') { 33 | stack.push(segment) 34 | } 35 | } 36 | 37 | // ensure leading slash 38 | if (stack[0] !== '') { 39 | stack.unshift('') 40 | } 41 | 42 | return stack.join('/') 43 | } 44 | 45 | export function parsePath (path: string): { 46 | path: string; 47 | query: string; 48 | hash: string; 49 | } { 50 | let hash = '' 51 | let query = '' 52 | 53 | const hashIndex = path.indexOf('#') 54 | if (hashIndex >= 0) { 55 | hash = path.slice(hashIndex) 56 | path = path.slice(0, hashIndex) 57 | } 58 | 59 | const queryIndex = path.indexOf('?') 60 | if (queryIndex >= 0) { 61 | query = path.slice(queryIndex + 1) 62 | path = path.slice(0, queryIndex) 63 | } 64 | 65 | return { 66 | path, 67 | query, 68 | hash 69 | } 70 | } 71 | 72 | export function cleanPath (path: string): string { 73 | return path.replace(/\/\//g, '/') 74 | } 75 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend, cached, camelize } from 'shared/util' 4 | 5 | const normalize = cached(camelize) 6 | 7 | function createStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) { 8 | if (!vnode.data.staticStyle) { 9 | updateStyle(oldVnode, vnode) 10 | return 11 | } 12 | const elm = vnode.elm 13 | const staticStyle = vnode.data.staticStyle 14 | for (const name in staticStyle) { 15 | if (staticStyle[name]) { 16 | elm.setStyle(normalize(name), staticStyle[name]) 17 | } 18 | } 19 | updateStyle(oldVnode, vnode) 20 | } 21 | 22 | function updateStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) { 23 | if (!oldVnode.data.style && !vnode.data.style) { 24 | return 25 | } 26 | let cur, name 27 | const elm = vnode.elm 28 | const oldStyle: any = oldVnode.data.style || {} 29 | let style: any = vnode.data.style || {} 30 | 31 | const needClone = style.__ob__ 32 | 33 | // handle array syntax 34 | if (Array.isArray(style)) { 35 | style = vnode.data.style = toObject(style) 36 | } 37 | 38 | // clone the style for future updates, 39 | // in case the user mutates the style object in-place. 40 | if (needClone) { 41 | style = vnode.data.style = extend({}, style) 42 | } 43 | 44 | for (name in oldStyle) { 45 | if (!style[name]) { 46 | elm.setStyle(normalize(name), '') 47 | } 48 | } 49 | for (name in style) { 50 | cur = style[name] 51 | elm.setStyle(normalize(name), cur) 52 | } 53 | } 54 | 55 | function toObject (arr) { 56 | const res = {} 57 | for (var i = 0; i < arr.length; i++) { 58 | if (arr[i]) { 59 | extend(res, arr[i]) 60 | } 61 | } 62 | return res 63 | } 64 | 65 | export default { 66 | create: createStyle, 67 | update: updateStyle 68 | } 69 | -------------------------------------------------------------------------------- /vue-src/core/global-api/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from '../config' 4 | import { initUse } from './use' 5 | import { initMixin } from './mixin' 6 | import { initExtend } from './extend' 7 | import { initAssetRegisters } from './assets' 8 | import { set, del } from '../observer/index' 9 | import { ASSET_TYPES } from 'shared/constants' 10 | import builtInComponents from '../components/index' 11 | 12 | import { 13 | warn, 14 | extend, 15 | nextTick, 16 | mergeOptions, 17 | defineReactive 18 | } from '../util/index' 19 | 20 | export function initGlobalAPI (Vue: GlobalAPI) { 21 | // config 22 | const configDef = {} 23 | configDef.get = () => config 24 | if (process.env.NODE_ENV !== 'production') { 25 | configDef.set = () => { 26 | warn( 27 | 'Do not replace the Vue.config object, set individual fields instead.' 28 | ) 29 | } 30 | } 31 | Object.defineProperty(Vue, 'config', configDef) 32 | 33 | // exposed util methods. 34 | // NOTE: these are not considered part of the public API - avoid relying on 35 | // them unless you are aware of the risk. 36 | Vue.util = { 37 | warn, 38 | extend, 39 | mergeOptions, 40 | defineReactive 41 | } 42 | 43 | Vue.set = set 44 | Vue.delete = del 45 | Vue.nextTick = nextTick 46 | 47 | Vue.options = Object.create(null) 48 | ASSET_TYPES.forEach(type => { 49 | Vue.options[type + 's'] = Object.create(null) 50 | }) 51 | 52 | // this is used to identify the "base" constructor to extend all plain-object 53 | // components with in Weex's multi-instance scenarios. 54 | /*_base被用来标识基本构造函数(也就是Vue),以便在多场景下添加组件扩展*/ 55 | Vue.options._base = Vue 56 | 57 | extend(Vue.options.components, builtInComponents) 58 | 59 | initUse(Vue) 60 | initMixin(Vue) 61 | initExtend(Vue) 62 | initAssetRegisters(Vue) 63 | } 64 | -------------------------------------------------------------------------------- /vue-src/core/vdom/create-functional-component.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import VNode from './vnode' 4 | import { createElement } from './create-element' 5 | import { resolveInject } from '../instance/inject' 6 | import { resolveSlots } from '../instance/render-helpers/resolve-slots' 7 | 8 | import { 9 | isDef, 10 | camelize, 11 | validateProp 12 | } from '../util/index' 13 | 14 | export function createFunctionalComponent ( 15 | Ctor: Class, 16 | propsData: ?Object, 17 | data: VNodeData, 18 | context: Component, 19 | children: ?Array 20 | ): VNode | void { 21 | const props = {} 22 | const propOptions = Ctor.options.props 23 | if (isDef(propOptions)) { 24 | for (const key in propOptions) { 25 | props[key] = validateProp(key, propOptions, propsData || {}) 26 | } 27 | } else { 28 | if (isDef(data.attrs)) mergeProps(props, data.attrs) 29 | if (isDef(data.props)) mergeProps(props, data.props) 30 | } 31 | // ensure the createElement function in functional components 32 | // gets a unique context - this is necessary for correct named slot check 33 | const _context = Object.create(context) 34 | const h = (a, b, c, d) => createElement(_context, a, b, c, d, true) 35 | const vnode = Ctor.options.render.call(null, h, { 36 | data, 37 | props, 38 | children, 39 | parent: context, 40 | listeners: data.on || {}, 41 | injections: resolveInject(Ctor.options.inject, context), 42 | slots: () => resolveSlots(children, context) 43 | }) 44 | if (vnode instanceof VNode) { 45 | vnode.functionalContext = context 46 | if (data.slot) { 47 | (vnode.data || (vnode.data = {})).slot = data.slot 48 | } 49 | } 50 | return vnode 51 | } 52 | 53 | function mergeProps (to, from) { 54 | for (const key in from) { 55 | to[camelize(key)] = from[key] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/node-ops.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /*这个文件是根据平台(web或者weex)操作真实节点(如web上的Dom)的操作函数进行了一层适配,对外可以统一提供操作真实节点的接口,内部实现根据平台的变化而变化*/ 4 | 5 | import { namespaceMap } from 'web/util/index' 6 | 7 | export function createElement (tagName: string, vnode: VNode): Element { 8 | const elm = document.createElement(tagName) 9 | if (tagName !== 'select') { 10 | return elm 11 | } 12 | // false or null will remove the attribute but undefined will not 13 | if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) { 14 | elm.setAttribute('multiple', 'multiple') 15 | } 16 | return elm 17 | } 18 | 19 | export function createElementNS (namespace: string, tagName: string): Element { 20 | return document.createElementNS(namespaceMap[namespace], tagName) 21 | } 22 | 23 | export function createTextNode (text: string): Text { 24 | return document.createTextNode(text) 25 | } 26 | 27 | export function createComment (text: string): Comment { 28 | return document.createComment(text) 29 | } 30 | 31 | export function insertBefore (parentNode: Node, newNode: Node, referenceNode: Node) { 32 | parentNode.insertBefore(newNode, referenceNode) 33 | } 34 | 35 | export function removeChild (node: Node, child: Node) { 36 | node.removeChild(child) 37 | } 38 | 39 | export function appendChild (node: Node, child: Node) { 40 | node.appendChild(child) 41 | } 42 | 43 | export function parentNode (node: Node): ?Node { 44 | return node.parentNode 45 | } 46 | 47 | export function nextSibling (node: Node): ?Node { 48 | return node.nextSibling 49 | } 50 | 51 | export function tagName (node: Element): string { 52 | return node.tagName 53 | } 54 | 55 | export function setTextContent (node: Node, text: string) { 56 | node.textContent = text 57 | } 58 | 59 | export function setAttribute (node: Element, key: string, val: string) { 60 | node.setAttribute(key, val) 61 | } 62 | -------------------------------------------------------------------------------- /vuex-src/module/module.js: -------------------------------------------------------------------------------- 1 | import { forEachValue } from '../util' 2 | 3 | /*Module构造类*/ 4 | export default class Module { 5 | constructor (rawModule, runtime) { 6 | this.runtime = runtime 7 | this._children = Object.create(null) 8 | /*保存module*/ 9 | this._rawModule = rawModule 10 | /*保存modele的state*/ 11 | const rawState = rawModule.state 12 | this.state = (typeof rawState === 'function' ? rawState() : rawState) || {} 13 | } 14 | 15 | /* 获取namespace */ 16 | get namespaced () { 17 | return !!this._rawModule.namespaced 18 | } 19 | 20 | /*插入一个子module,存入_children中*/ 21 | addChild (key, module) { 22 | this._children[key] = module 23 | } 24 | 25 | /*移除一个子module*/ 26 | removeChild (key) { 27 | delete this._children[key] 28 | } 29 | 30 | /*根据key获取子module*/ 31 | getChild (key) { 32 | return this._children[key] 33 | } 34 | 35 | /* 更新module */ 36 | update (rawModule) { 37 | this._rawModule.namespaced = rawModule.namespaced 38 | if (rawModule.actions) { 39 | this._rawModule.actions = rawModule.actions 40 | } 41 | if (rawModule.mutations) { 42 | this._rawModule.mutations = rawModule.mutations 43 | } 44 | if (rawModule.getters) { 45 | this._rawModule.getters = rawModule.getters 46 | } 47 | } 48 | 49 | /* 遍历child */ 50 | forEachChild (fn) { 51 | forEachValue(this._children, fn) 52 | } 53 | 54 | /* 遍历getter */ 55 | forEachGetter (fn) { 56 | if (this._rawModule.getters) { 57 | forEachValue(this._rawModule.getters, fn) 58 | } 59 | } 60 | 61 | /* 遍历action */ 62 | forEachAction (fn) { 63 | if (this._rawModule.actions) { 64 | forEachValue(this._rawModule.actions, fn) 65 | } 66 | } 67 | 68 | /* 遍历matation */ 69 | forEachMutation (fn) { 70 | if (this._rawModule.mutations) { 71 | forEachValue(this._rawModule.mutations, fn) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vue-src/core/instance/render-helpers/render-static.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { cloneVNode, cloneVNodes } from 'core/vdom/vnode' 4 | 5 | /** 6 | * Runtime helper for rendering static trees. 7 | */ 8 | /*处理static树的渲染*/ 9 | export function renderStatic ( 10 | index: number, 11 | isInFor?: boolean 12 | ): VNode | Array { 13 | /*从_staticTrees中取出tree,如果已经被渲染则会存在*/ 14 | let tree = this._staticTrees[index] 15 | // if has already-rendered static tree and not inside v-for, 16 | // we can reuse the same tree by doing a shallow clone. 17 | /*如果已经被渲染的static tree并且它不是在v-for中,我们能够通过浅拷贝来重用同一棵树*/ 18 | if (tree && !isInFor) { 19 | return Array.isArray(tree) 20 | ? cloneVNodes(tree) 21 | : cloneVNode(tree) 22 | } 23 | // otherwise, render a fresh tree. 24 | /*否则渲染一刻新的树,同时存储在_staticTrees中,供上面检测是否已经渲染过*/ 25 | tree = this._staticTrees[index] = 26 | this.$options.staticRenderFns[index].call(this._renderProxy) 27 | markStatic(tree, `__static__${index}`, false) 28 | return tree 29 | } 30 | 31 | /** 32 | * Runtime helper for v-once. 33 | * Effectively it means marking the node as static with a unique key. 34 | */ 35 | /*处理v-once的渲染函数*/ 36 | export function markOnce ( 37 | tree: VNode | Array, 38 | index: number, 39 | key: string 40 | ) { 41 | markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true) 42 | return tree 43 | } 44 | 45 | function markStatic ( 46 | tree: VNode | Array, 47 | key: string, 48 | isOnce: boolean 49 | ) { 50 | /*处理static节点*/ 51 | if (Array.isArray(tree)) { 52 | for (let i = 0; i < tree.length; i++) { 53 | if (tree[i] && typeof tree[i] !== 'string') { 54 | markStaticNode(tree[i], `${key}_${i}`, isOnce) 55 | } 56 | } 57 | } else { 58 | markStaticNode(tree, key, isOnce) 59 | } 60 | } 61 | 62 | /*处理static节点*/ 63 | function markStaticNode (node, key, isOnce) { 64 | node.isStatic = true 65 | node.key = key 66 | node.isOnce = isOnce 67 | } 68 | -------------------------------------------------------------------------------- /vuex-src/plugins/logger.js: -------------------------------------------------------------------------------- 1 | // Credits: borrowed code from fcomb/redux-logger 2 | 3 | import { deepCopy } from '../util' 4 | 5 | export default function createLogger ({ 6 | collapsed = true, 7 | filter = (mutation, stateBefore, stateAfter) => true, 8 | transformer = state => state, 9 | mutationTransformer = mut => mut 10 | } = {}) { 11 | return store => { 12 | let prevState = deepCopy(store.state) 13 | 14 | store.subscribe((mutation, state) => { 15 | if (typeof console === 'undefined') { 16 | return 17 | } 18 | const nextState = deepCopy(state) 19 | 20 | if (filter(mutation, prevState, nextState)) { 21 | const time = new Date() 22 | const formattedTime = ` @ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}` 23 | const formattedMutation = mutationTransformer(mutation) 24 | const message = `mutation ${mutation.type}${formattedTime}` 25 | const startMessage = collapsed 26 | ? console.groupCollapsed 27 | : console.group 28 | 29 | // render 30 | try { 31 | startMessage.call(console, message) 32 | } catch (e) { 33 | console.log(message) 34 | } 35 | 36 | console.log('%c prev state', 'color: #9E9E9E; font-weight: bold', transformer(prevState)) 37 | console.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation) 38 | console.log('%c next state', 'color: #4CAF50; font-weight: bold', transformer(nextState)) 39 | 40 | try { 41 | console.groupEnd() 42 | } catch (e) { 43 | console.log('—— log end ——') 44 | } 45 | } 46 | 47 | prevState = nextState 48 | }) 49 | } 50 | } 51 | 52 | function repeat (str, times) { 53 | return (new Array(times + 1)).join(str) 54 | } 55 | 56 | function pad (num, maxLength) { 57 | return repeat('0', maxLength - num.toString().length) + num 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # learnVue 2 | 3 | ## 介绍 4 | 5 | Vue.js源码分析,记录了个人学习Vue.js源码的过程中的一些心得以及收获。以及对于Vue框架,周边库的一些个人见解。 6 | 7 | 在学习的过程中我为Vue.js(2.3.0)、Vuex(2.4.0)、Vue-router(3.0.1)加上了注释,分别在文件夹[vue-src](./vue-src)、[vuex-src](./vuex-src)以及[vue-router-src](./vue-router-src)中,希望可以帮助有需要的同学更好地学习理解Vue.js及周边库的源码。 8 | 9 | 感谢[尤大](https://github.com/yyx990803)提高生产力。 10 | 11 | 本项目希望对Vue.js做更进一步的探索与学习,Vue.js基础内容请参考Vue.js官网,[https://cn.vuejs.org/v2/guide/](https://cn.vuejs.org/v2/guide/)。 12 | 可能会有理解存在偏差的地方,欢迎提issue指出,共同学习,共同进步。 13 | 14 | --- 15 | 16 | ## 目录 17 | 18 | ### 源码相关 19 | 20 | [Vue.js响应式原理](./docs/响应式原理.MarkDown) 21 | 22 | [Vue.js依赖收集](./docs/依赖收集.MarkDown) 23 | 24 | [从Vue.js源码角度再看数据绑定](./docs/从源码角度再看数据绑定.MarkDown) 25 | 26 | [Vue.js事件机制](./docs/Vue事件机制.MarkDown) 27 | 28 | [VNode节点(Vue.js实现)](./docs/VNode节点.MarkDown) 29 | 30 | [Virtual DOM与diff(Vue.js实现)](./docs/VirtualDOM与diff(Vue实现).MarkDown) 31 | 32 | [聊聊Vue.js的template编译](./docs/聊聊Vue的template编译.MarkDown) 33 | 34 | [Vue.js异步更新DOM策略及nextTick](./docs/Vue.js异步更新DOM策略及nextTick.MarkDown) 35 | 36 | [从template到DOM(Vue.js源码角度看内部运行机制)](./docs/从template到DOM(Vue.js源码角度看内部运行机制).MarkDown) 37 | 38 | [Vuex源码解析](./docs/Vuex源码解析.MarkDown) 39 | 40 | [聊聊keep-alive组件的使用及其实现原理](./docs/聊聊keep-alive组件的使用及其实现原理.MarkDown) 41 | 42 | ### 随笔杂谈 43 | 44 | [Vue组件间通信](./docs/Vue组件间通信.MarkDown) 45 | 46 | [说说element组件库broadcast与dispatch](./docs/说说element组件库broadcast与dispatch.MarkDown) 47 | 48 | --- 49 | 50 | ## 对于新手同学 51 | 52 | 由于以上内容都是针对 Vue.js 源码进行讲解了,可能有一些不太熟悉源码的同学读起来感觉晦涩难懂。 53 | 54 | 笔者撰写的[《剖析 Vue.js 内部运行机制》](https://juejin.im/book/5a36661851882538e2259c0f)或许可以帮到你。 55 | 56 | 57 | ## 关于作者 58 | 59 | 作者: 染陌 60 | 61 | Email:answershuto@gmail.com 62 | 63 | Github: [https://github.com/answershuto](https://github.com/answershuto) 64 | 65 | 知乎:[https://www.zhihu.com/people/cao-yang-49/activities](https://www.zhihu.com/people/cao-yang-49/activities) 66 | 67 | 掘金:[https://juejin.im/user/58f87ae844d9040069ca7507](https://juejin.im/user/58f87ae844d9040069ca7507) 68 | 69 | 对内容有任何疑问,欢迎联系我。 -------------------------------------------------------------------------------- /vue-src/platforms/web/runtime/directives/show.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isIE9 } from 'core/util/env' 4 | import { enter, leave } from '../modules/transition' 5 | 6 | // recursively search for possible transition defined inside the component root 7 | function locateNode (vnode: VNode): VNodeWithData { 8 | return vnode.componentInstance && (!vnode.data || !vnode.data.transition) 9 | ? locateNode(vnode.componentInstance._vnode) 10 | : vnode 11 | } 12 | 13 | export default { 14 | bind (el: any, { value }: VNodeDirective, vnode: VNodeWithData) { 15 | vnode = locateNode(vnode) 16 | const transition = vnode.data && vnode.data.transition 17 | const originalDisplay = el.__vOriginalDisplay = 18 | el.style.display === 'none' ? '' : el.style.display 19 | if (value && transition && !isIE9) { 20 | vnode.data.show = true 21 | enter(vnode, () => { 22 | el.style.display = originalDisplay 23 | }) 24 | } else { 25 | el.style.display = value ? originalDisplay : 'none' 26 | } 27 | }, 28 | 29 | update (el: any, { value, oldValue }: VNodeDirective, vnode: VNodeWithData) { 30 | /* istanbul ignore if */ 31 | if (value === oldValue) return 32 | vnode = locateNode(vnode) 33 | const transition = vnode.data && vnode.data.transition 34 | if (transition && !isIE9) { 35 | vnode.data.show = true 36 | if (value) { 37 | enter(vnode, () => { 38 | el.style.display = el.__vOriginalDisplay 39 | }) 40 | } else { 41 | leave(vnode, () => { 42 | el.style.display = 'none' 43 | }) 44 | } 45 | } else { 46 | el.style.display = value ? el.__vOriginalDisplay : 'none' 47 | } 48 | }, 49 | 50 | unbind ( 51 | el: any, 52 | binding: VNodeDirective, 53 | vnode: VNodeWithData, 54 | oldVnode: VNodeWithData, 55 | isDestroy: boolean 56 | ) { 57 | if (!isDestroy) { 58 | el.style.display = el.__vOriginalDisplay 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /vue-src/platforms/weex/runtime/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend } from 'shared/util' 4 | 5 | function updateClass (oldVnode: VNodeWithData, vnode: VNodeWithData) { 6 | const el = vnode.elm 7 | const ctx = vnode.context 8 | 9 | const data: VNodeData = vnode.data 10 | const oldData: VNodeData = oldVnode.data 11 | if (!data.staticClass && !data.class && 12 | (!oldData || (!oldData.staticClass && !oldData.class))) { 13 | return 14 | } 15 | 16 | const oldClassList = [] 17 | // unlike web, weex vnode staticClass is an Array 18 | const oldStaticClass: any = oldData.staticClass 19 | if (oldStaticClass) { 20 | oldClassList.push.apply(oldClassList, oldStaticClass) 21 | } 22 | if (oldData.class) { 23 | oldClassList.push.apply(oldClassList, oldData.class) 24 | } 25 | 26 | const classList = [] 27 | // unlike web, weex vnode staticClass is an Array 28 | const staticClass: any = data.staticClass 29 | if (staticClass) { 30 | classList.push.apply(classList, staticClass) 31 | } 32 | if (data.class) { 33 | classList.push.apply(classList, data.class) 34 | } 35 | 36 | const style = getStyle(oldClassList, classList, ctx) 37 | for (const key in style) { 38 | el.setStyle(key, style[key]) 39 | } 40 | } 41 | 42 | function getStyle (oldClassList: Array, classList: Array, ctx: Component): Object { 43 | // style is a weex-only injected object 44 | // compiled from