From 2082eb9290b30a4ae2799d97e39df5dd7f95ac1e Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 19 Apr 2016 03:52:58 -0400 Subject: [PATCH] refactor wip --- src/compiler/codegen/directives/html.js | 8 +- src/compiler/codegen/directives/model.js | 46 +++------- src/compiler/helpers.js | 41 ++++++++- src/compiler/parser/index.js | 104 ++++------------------- src/runtime/instance/proxy.js | 9 +- 5 files changed, 77 insertions(+), 131 deletions(-) diff --git a/src/compiler/codegen/directives/html.js b/src/compiler/codegen/directives/html.js index 6430ece57..c74f70dfc 100644 --- a/src/compiler/codegen/directives/html.js +++ b/src/compiler/codegen/directives/html.js @@ -1,8 +1,6 @@ +import { addProp } from '../../helpers' + export function html (el, dir) { if (!dir.value) return - if (!el.props) el.props = [] - el.props.push({ - name: 'innerHTML', - value: `(${dir.value})` - }) + addProp(el, 'innerHTML', `(${dir.value})`) } diff --git a/src/compiler/codegen/directives/model.js b/src/compiler/codegen/directives/model.js index 88f64a598..a50eb75eb 100644 --- a/src/compiler/codegen/directives/model.js +++ b/src/compiler/codegen/directives/model.js @@ -1,8 +1,6 @@ -import { addHandler } from '../../helpers' +import { addHandler, addProp, getBindingAttr } from '../../helpers' export function model (el, dir) { - if (!el.events) el.events = {} - if (!el.props) el.props = [] const value = dir.value const modifiers = dir.modifiers if (el.tag === 'select') { @@ -26,19 +24,13 @@ export function model (el, dir) { } function genCheckboxModel (el, value) { - addHandler(el.events, 'change', `${value}=$event.target.checked`) - el.props.push({ - name: 'checked', - value: `!!(${value})` - }) + addProp(el, 'checked', `!!(${value})`) + addHandler(el, 'change', `${value}=$event.target.checked`) } function genRadioModel (el, value) { - addHandler(el.events, 'change', `${value}=$event.target.value`) - el.props.push({ - name: 'checked', - value: `(${value}==${getInputValue(el)})` - }) + addProp(el, 'checked', `(${value}==${getBindingAttr(el, 'value')})`) + addHandler(el, 'change', `${value}=$event.target.value`) } function genDefaultModel (el, value, modifiers) { @@ -51,15 +43,11 @@ function genDefaultModel (el, value, modifiers) { let code = number || type === 'number' ? `${value}=Number($event.target.value)` : `${value}=$event.target.value` - if (needCompositionGuard) { code = `if($event.target.composing)return;${code}` } - addHandler(el.events, event, code) - el.props.push({ - name: 'value', - value: `(${value})` - }) + addProp(el, 'value', `(${value})`) + addHandler(el, event, code) if (needCompositionGuard) { // return runtime directive code to help with composition events return '{def:__resolveDirective__("model")},' @@ -67,15 +55,12 @@ function genDefaultModel (el, value, modifiers) { } function genSelect (el, value) { - addHandler(el.events, 'change', `${value}=$event.target.value`) - el.props.push({ - name: 'value', - value: `(${value})` - }) + addProp(el, 'value', `(${value})`) + addHandler(el, 'change', `${value}=$event.target.value`) } function genMultiSelect (el, value) { - addHandler(el.events, 'change', + addHandler(el, 'change', `${value}=Array.prototype.filter .call($event.target.options,function(o){return o.selected}) .map(function(o){return o.value})`) @@ -83,16 +68,7 @@ function genMultiSelect (el, value) { for (let i = 0; i < el.children.length; i++) { let c = el.children[i] if (c.tag === 'option') { - (c.props || (c.props = [])).push({ - name: 'selected', - value: `(${value}).indexOf(${getInputValue(c)})>-1` - }) + addProp(c, 'selected', `(${value}).indexOf(${getBindingAttr(c, 'value')})>-1`) } } } - -function getInputValue (el) { - return el.attrsMap.value - ? JSON.stringify(el.attrsMap.value) - : el.attrsMap['v-bind:value'] || el.attrsMap[':value'] -} diff --git a/src/compiler/helpers.js b/src/compiler/helpers.js index dc256a90c..83f9e162a 100644 --- a/src/compiler/helpers.js +++ b/src/compiler/helpers.js @@ -1,6 +1,29 @@ import { isArray } from '../shared/util' -export function addHandler (events, name, value, modifiers) { +export function getBindingAttr (el, name, getStatic) { + const staticValue = getStatic !== false && getAndRemoveAttr(el, name) + return staticValue + ? JSON.stringify(staticValue) + : (getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name)) +} + +export function getAndRemoveAttr (el, name) { + let val + if ((val = el.attrsMap[name]) != null) { + 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 + } + } + } + return val +} + +export function addHandler (el, name, value, modifiers) { + const events = (el.events || (el.events = {})) // check capture modifier if (modifiers && modifiers.capture) { delete modifiers.capture @@ -16,3 +39,19 @@ export function addHandler (events, name, value, modifiers) { events[name] = newHandler } } + +export function addProp (el, name, value) { + (el.props || (el.props = [])).push({ name, value }) +} + +export function addAttr (el, name, value) { + (el.attrs || (el.attrs = [])).push({ name, value }) +} + +export function addDirective (el, name, value, arg, modifiers) { + (el.directives || (el.directives = [])).push({ name, value, arg, modifiers }) +} + +export function addStyle (el, name, value) { + +} diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index c9db80206..41c082fbb 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -1,8 +1,15 @@ import { decodeHTML } from 'entities' import { parseHTML } from './html-parser' import { parseText } from './text-parser' -import { addHandler } from '../helpers' import { hyphenate, makeMap } from '../../shared/util' +import { + getAndRemoveAttr, + addProp, + addAttr, + addHandler, + addDirective, + getBindingAttr +} from '../helpers' const dirRE = /^v-|^@|^:/ const bindRE = /^:|^v-bind:/ @@ -26,51 +33,6 @@ const baseWarn = msg => console.error(`[Vue parser]: ${msg}`) /** * Convert HTML string to AST. * - * There are 3 types of nodes: - * - * - Element: { - * // base info - * tag: String, - * plain: Boolean, - * attrsList: Array, - * attrsMap: Object, - * parent: Element, - * children: Array, - * - * attrs: Array - * props: Array - * directives: Array - * - * pre: Boolean - * - * if: String (expression) - * else: Boolean - * elseBlock: Element - * - * for: String - * iterator: String - * alias: String - * - * staticClass: String - * classBinding: String - * - * styleBinding: String - * - * render: Boolean - * renderName: String - * renderArgs: String - * - * slotName: String - * } - * - * - Expression: { - * expression: String (expression) - * } - * - * - Text: { - * text: String - * } - * * @param {String} template * @param {Object} options * @return {Object} @@ -295,33 +257,24 @@ function processRender (el) { function processSlot (el) { if (el.tag === 'slot') { - el.slotName = el.attrsMap.name - ? `"${el.attrsMap.name}"` - : (el.attrsMap[':name'] || el.attrsMap['v-bind:name']) + el.slotName = getBindingAttr(el, 'name') } } function processComponent (el) { if (el.tag === 'component') { - let staticName = getAndRemoveAttr(el, 'is') - el.component = staticName - ? JSON.stringify(staticName) - : (getAndRemoveAttr(el, ':is') || getAndRemoveAttr(el, 'v-bind:is')) + el.component = getBindingAttr(el, 'is') } } function processClassBinding (el) { const staticClass = getAndRemoveAttr(el, 'class') el.staticClass = parseText(staticClass) || JSON.stringify(staticClass) - el.classBinding = - getAndRemoveAttr(el, ':class') || - getAndRemoveAttr(el, 'v-bind:class') + el.classBinding = getBindingAttr(el, 'class', false /* getStatic */) } function processStyleBinding (el) { - el.styleBinding = - getAndRemoveAttr(el, ':style') || - getAndRemoveAttr(el, 'v-bind:style') + el.styleBinding = getBindingAttr(el, 'style', false /* getStatic */) } function processAttrs (el) { @@ -339,32 +292,24 @@ function processAttrs (el) { if (bindRE.test(name)) { // v-bind name = name.replace(bindRE, '') if (mustUseProp(name)) { - (el.props || (el.props = [])).push({ name, value }) + addProp(el, name, value) } else { - (el.attrs || (el.attrs = [])).push({ name, value }) + addAttr(el, name, value) } } else if (onRE.test(name)) { // v-on name = name.replace(onRE, '') - addHandler((el.events || (el.events = {})), name, value, modifiers) + addHandler(el, name, value, modifiers) } else { // normal directives name = name.replace(dirRE, '') // parse arg if ((arg = name.match(argRE)) && (arg = arg[1])) { name = name.slice(0, -(arg.length + 1)) } - ;(el.directives || (el.directives = [])).push({ - name, - value, - arg, - modifiers - }) + addDirective(el, name, value, arg, modifiers) } } else { // literal attribute - (el.attrs || (el.attrs = [])).push({ - name, - value: parseText(value) || JSON.stringify(value) - }) + addAttr(el, name, parseText(value) || JSON.stringify(value)) } } } @@ -389,21 +334,6 @@ function makeAttrsMap (attrs) { return map } -function getAndRemoveAttr (el, attr) { - let val - if ((val = el.attrsMap[attr]) != null) { - el.attrsMap[attr] = null - const list = el.attrsList - for (let i = 0, l = list.length; i < l; i++) { - if (list[i].name === attr) { - list.splice(i, 1) - break - } - } - } - return val -} - function findPrevElement (children) { let i = children.length while (i--) { diff --git a/src/runtime/instance/proxy.js b/src/runtime/instance/proxy.js index e647830f1..29bcf459e 100644 --- a/src/runtime/instance/proxy.js +++ b/src/runtime/instance/proxy.js @@ -1,8 +1,10 @@ -import { warn } from '../util/index' +import { warn, inBrowser } from '../util/index' let hasProxy, proxyHandlers, initProxy if (process.env.NODE_ENV !== 'production') { + let context = inBrowser ? window : global + hasProxy = typeof Proxy !== 'undefined' && Proxy.toString().match(/native code/) @@ -12,13 +14,14 @@ if (process.env.NODE_ENV !== 'production') { if (key === 'undefined') { return false } - if (!(key in target)) { + let has = key in target + if (!has && !(key in context)) { warn( `Trying to access non-existent property "${key}" while rendering.`, target ) } - return true + return has } }