读vue源码笔记
gobal-api/index.js
initMixin(Vue) 对Vue.mixin方法进行定义,参数为要混入到Vue.options对象的对象。
Vue.mixin = function (mixin: Object) { this.options = mergeOptions(this.options, mixin) return this }
gobal-api/use.js
这段源码简单,global-api/use.js中,this._installedPlugins储存插件的数组。
export function initUse (Vue: GlobalAPI) { Vue.use = function (plugin: Function | Object) { const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) // 已经执行过了插件暴露的方法就不需要执行了 if (installedPlugins.indexOf(plugin) > -1) { return this } // additional parameters,数组中的参数后移一位。 const args = toArray(arguments, 1) args.unshift(this) // 第一个参数是vue本身了 if (typeof plugin.install === 'function') { // 插件要实现install函数,或者本身就是函数, plugin.install.apply(plugin, args) } else if (typeof plugin === 'function') { plugin.apply(null, args) } installedPlugins.push(plugin) return this }
使用的过程中:vue.use(vue-router); vue-router 是个函数,在window下执行plugin.apply(null, args)。第一个参数就是Vue。然后把函数推入installedPlugins数组中。
mergeOptions是utils/options.js
export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { if (process.env.NODE_ENV !== 'production') { //核实子对象中components的名字合法 checkComponents(child) } if (typeof child === 'function') { child = child.options } // 对options中的props、inject、 directive格式化对象 normalizeProps(child, vm) normalizeInject(child, vm) normalizeDirectives(child) // extends、mixins 格式化变成对象 const extendsFrom = child.extends if (extendsFrom) { parent = mergeOptions(parent, extendsFrom, vm) } if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm) } } const options = {} // 对 vm.constructor的成员和目前options成员的key进行自定义化处理,一般不会自定义化处理的。 let key for (key in parent) { mergeField(key) } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } return options }
initState
// 对vm实例的props,method、data、computed、watch初始化。 在beforeCreated 和created 钩子函数之间执行 export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
export function pluckModuleFunction<F: Function> ( modules: ?Array<Object>, key: string ): Array<F> { return modules ? modules.map(m => m[key]).filter(_ => _) : [] } //看看下面的例子就明白了,map返回经过每个数组成员函数处理的的值的数组,filter返回每个成员经过函数处理返回为true的数组。 function A (modules, key) { return modules ? modules.map(m => m[key]).filter(_ => _) : [] }
// map 处理返回[undefind, ['33']], filter返回['33'] let a = [{ '1': '11', '2': '22' },{ '1': '11', '3': '33' }] console.log(A(a, '3')) //['33']
/compiler/helpers
// 改变AST树的数据,如果removeFromMap为true, 则把AST树的attrsMap元素删除。最终返回name属性值。 export function getAndRemoveAttr ( el: ASTElement, name: string, removeFromMap?: boolean ): ?string { let val if ((val = el.attrsMap[name]) != null) { const list = el.attrsList for (let i = 0, l = list.length; i < l; i++) { if (list[i].name === name) { list.splice(i, 1) break } } } if (removeFromMap) { delete el.attrsMap[name] } return val }
/parser/html-parser.js
const start = html.match(startTagOpen) // match匹配,/^<((?:[a-zA-Z_][\w\-\.]*\:)?[a-zA-Z_][\w\-\.]*)/ // ?:忽略分组 if (start) { const match = { tagName: start[1], attrs: [], start: index } advance(start[0].length) let end, attr // /^\s*(\/?)>/ 匹配 空格> 或者 空格/> while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { advance(attr[0].length) match.attrs.push(attr) } // 可能是"/" if (end) { match.unarySlash = end[1] advance(end[0].length) match.end = index return match } }
options.start
start (tag, attrs, unary, start, end) { // check namespace. // inherit parent ns if there is one const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag) // handle IE svg bug /* istanbul ignore if */ if (isIE && ns === 'svg') { attrs = guardIESVGBug(attrs) } let element: ASTElement = createASTElement(tag, attrs, currentParent) if (ns) { element.ns = ns } if (process.env.NODE_ENV !== 'production') { if (options.outputSourceRange) { element.start = start element.end = end element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => { cumulated[attr.name] = attr return cumulated }, {}) } attrs.forEach(attr => { if (invalidAttributeRE.test(attr.name)) { warn( `Invalid dynamic argument expression: attribute names cannot contain ` + `spaces, quotes, <, >, / or =.`, { start: attr.start + attr.name.indexOf(`[`), end: attr.start + attr.name.length } ) } }) } if (isForbiddenTag(element) && !isServerRendering()) { element.forbidden = true process.env.NODE_ENV !== 'production' && warn( 'Templates should only be responsible for mapping the state to the ' + 'UI. Avoid placing tags with side-effects in your templates, such as ' + `<${tag}>` + ', as they will not be parsed.', { start: element.start } ) } // apply pre-transforms for (let i = 0; i < preTransforms.length; i++) { element = preTransforms[i](element, options) || element } if (!inVPre) { processPre(element) //处理v-pre,不参与编译。 if (element.pre) { inVPre = true } } // 特定平台设置的不需要编译标签,默认没有。 if (platformIsPreTag(element.tag)) { inPre = true } // 如果是不需要参与编译的element if (inVPre) { // ele得到一个数组,ele.attrs = [] processRawAttrs(element) } else if (!element.processed) { // structural directives processFor(element) processIf(element) processOnce(element) } if (!root) { root = element if (process.env.NODE_ENV !== 'production') { //根节点不能有v-for,template,slot checkRootConstraints(root) } } //不是自闭标签 if (!unary) { // 类似指针,形成链表,start 的核心代码 currentParent = element stack.push(element) } else { closeElement(element) } },