mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-02 03:57:36 +08:00
wip
This commit is contained in:
parent
5c31f65684
commit
5228ccb5fd
@ -1,16 +0,0 @@
|
||||
import { getAndRemoveAttr } from './helpers'
|
||||
|
||||
export function genClass (el) {
|
||||
let ret = ''
|
||||
const classBinding =
|
||||
getAndRemoveAttr(el, ':class') ||
|
||||
getAndRemoveAttr(el, 'v-bind:class')
|
||||
if (classBinding) {
|
||||
ret += `class: ${classBinding},`
|
||||
}
|
||||
const staticClass = getAndRemoveAttr(el, 'class')
|
||||
if (staticClass) {
|
||||
ret += `staticClass: "${staticClass}",`
|
||||
}
|
||||
return ret
|
||||
}
|
16
src/compiler/codegen/directives.js
Normal file
16
src/compiler/codegen/directives.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { genModel } from './model'
|
||||
|
||||
const dirMap = {
|
||||
model: genModel
|
||||
}
|
||||
|
||||
export function genDirectives (el) {
|
||||
const dirs = el.directives
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
let dir = dirs[i]
|
||||
let gen = dirMap[dir.name]
|
||||
if (gen) {
|
||||
return gen(el, dir)
|
||||
}
|
||||
}
|
||||
}
|
@ -20,23 +20,6 @@ const modifierCode = {
|
||||
self: 'if($event.target !== $event.currentTarget)return;'
|
||||
}
|
||||
|
||||
export function addHandler (events, name, value, modifiers) {
|
||||
// check capture modifier
|
||||
if (modifiers && modifiers.capture) {
|
||||
delete modifiers.capture
|
||||
name = '!' + name // mark the event as captured
|
||||
}
|
||||
const newHandler = { value, modifiers }
|
||||
const handlers = events[name]
|
||||
if (isArray(handlers)) {
|
||||
handlers.push(newHandler)
|
||||
} else if (handlers) {
|
||||
events[name] = [handlers, newHandler]
|
||||
} else {
|
||||
events[name] = newHandler
|
||||
}
|
||||
}
|
||||
|
||||
export function genHandlers (events) {
|
||||
let res = 'on:{'
|
||||
for (let name in events) {
|
@ -1,140 +1,83 @@
|
||||
import { genHandlers, addHandler } from './on'
|
||||
import { genModel } from './model'
|
||||
import { genClass } from './class'
|
||||
import {
|
||||
checkSVG,
|
||||
parseText,
|
||||
parseModifiers,
|
||||
removeModifiers,
|
||||
getAndRemoveAttr
|
||||
} from './helpers'
|
||||
|
||||
const dirRE = /^v-|^@|^:/
|
||||
const bindRE = /^:|^v-bind:/
|
||||
const onRE = /^@|^v-on:/
|
||||
const mustUsePropsRE = /^(value|selected|checked|muted)$/
|
||||
import { genHandlers } from './events'
|
||||
import { genDirectives } from './directives'
|
||||
|
||||
export function generate (ast) {
|
||||
const code = ast ? genElement(ast) : '__h__("div")'
|
||||
return new Function(`with (this) { return ${code}}`)
|
||||
}
|
||||
|
||||
function genElement (el, key) {
|
||||
let exp
|
||||
if ((exp = getAndRemoveAttr(el, 'v-for'))) {
|
||||
return genFor(el, exp)
|
||||
} else if ((exp = getAndRemoveAttr(el, 'v-if'))) {
|
||||
return genIf(el, exp)
|
||||
function genElement (el) {
|
||||
if (el['for']) {
|
||||
return genFor(el)
|
||||
} else if (el['if']) {
|
||||
return genIf(el)
|
||||
} else if (el.tag === 'template') {
|
||||
return genChildren(el)
|
||||
} else if (el.tag === 'render') {
|
||||
return genRender(el)
|
||||
} else {
|
||||
return `__h__('${el.tag}', ${genData(el, key)}, ${genChildren(el)})`
|
||||
return `__h__('${el.tag}', ${genData(el)}, ${genChildren(el)})`
|
||||
}
|
||||
}
|
||||
|
||||
function genIf (el, exp) {
|
||||
function genIf (el) {
|
||||
const exp = el['if']
|
||||
el['if'] = false // avoid recursion
|
||||
return `(${exp}) ? ${genElement(el)} : null`
|
||||
}
|
||||
|
||||
function genFor (el, exp) {
|
||||
const inMatch = exp.match(/([a-zA-Z_][\w]*)\s+(?:in|of)\s+(.*)/)
|
||||
if (!inMatch) {
|
||||
throw new Error('Invalid v-for expression: ' + exp)
|
||||
}
|
||||
const alias = inMatch[1].trim()
|
||||
exp = inMatch[2].trim()
|
||||
let key = getAndRemoveAttr(el, 'track-by')
|
||||
if (!key) {
|
||||
key = 'undefined'
|
||||
} else if (key !== '$index') {
|
||||
key = alias + '["' + key + '"]'
|
||||
}
|
||||
return `(${exp}) && (${exp}).map(function (${alias}, $index) {return ${genElement(el, key)}})`
|
||||
function genFor (el) {
|
||||
const exp = el['for']
|
||||
const alias = el.alias
|
||||
el['for'] = false // avoid recursion
|
||||
return `(${exp}) && (${exp}).map(function (${alias}, $index) {return ${genElement(el)}})`
|
||||
}
|
||||
|
||||
function genData (el, key) {
|
||||
if (!el.attrs.length) {
|
||||
function genData (el) {
|
||||
if (el.plain) {
|
||||
return '{}'
|
||||
}
|
||||
|
||||
let data = '{'
|
||||
let attrs = 'attrs:{'
|
||||
let props = 'props:{'
|
||||
let events = {}
|
||||
let hasAttrs = false
|
||||
let hasProps = false
|
||||
let hasEvents = false
|
||||
|
||||
// key
|
||||
if (key) {
|
||||
data += `key:${key},`
|
||||
if (el.key) {
|
||||
data += `key:${el.key},`
|
||||
}
|
||||
|
||||
// check SVG namespace.
|
||||
// this has the side effect of marking all children if the tag itself is <svg>
|
||||
if (checkSVG(el)) {
|
||||
// svg
|
||||
if (el.svg) {
|
||||
data += 'svg:true,'
|
||||
}
|
||||
|
||||
// directives first.
|
||||
// directives may mutate the el's other properties before they are generated.
|
||||
if (el.directives) {
|
||||
let dirs = genDirectives(el)
|
||||
if (dirs) data += dirs + ','
|
||||
}
|
||||
// class
|
||||
// do it before other attributes becaues it removes static class
|
||||
// and class bindings from the element
|
||||
data += genClass(el)
|
||||
|
||||
// parent elements my need to add props to children
|
||||
// e.g. select
|
||||
if (el.staticClass) {
|
||||
data += `staticClass:"${el.staticClass}",`
|
||||
}
|
||||
if (el.classBinding) {
|
||||
data += `class:${el.classBinding},`
|
||||
}
|
||||
// style
|
||||
if (el.styleBinding) {
|
||||
data += `style:${el.styleBinding},`
|
||||
}
|
||||
// props
|
||||
if (el.props) {
|
||||
hasProps = true
|
||||
props += el.props + ','
|
||||
data += 'props:{' + genProps(el.props) + '},'
|
||||
}
|
||||
|
||||
// loop attributes
|
||||
for (let i = 0, l = el.attrs.length; i < l; i++) {
|
||||
let attr = el.attrs[i]
|
||||
let name = attr.name
|
||||
let value = attr.value
|
||||
|
||||
if (dirRE.test(name)) {
|
||||
// modifiers
|
||||
const modifiers = parseModifiers(name)
|
||||
name = removeModifiers(name)
|
||||
if (bindRE.test(name)) {
|
||||
name = name.replace(bindRE, '')
|
||||
if (name === 'style') {
|
||||
data += `style: ${value},`
|
||||
} else if (mustUsePropsRE.test(name)) {
|
||||
hasProps = true
|
||||
props += `"${name}": (${value}),`
|
||||
} else {
|
||||
hasAttrs = true
|
||||
attrs += `"${name}": (${value}),`
|
||||
}
|
||||
} else if (onRE.test(name)) {
|
||||
hasEvents = true
|
||||
name = name.replace(onRE, '')
|
||||
addHandler(events, name, value, modifiers)
|
||||
} else if (name === 'v-model') {
|
||||
hasProps = hasEvents = true
|
||||
props += genModel(el, events, value, modifiers) + ','
|
||||
} else {
|
||||
// TODO: normal directives
|
||||
}
|
||||
} else {
|
||||
hasAttrs = true
|
||||
attrs += `"${name}": (${JSON.stringify(attr.value)}),`
|
||||
}
|
||||
// attributes
|
||||
if (el.attrs) {
|
||||
data += 'attrs:{' + genProps(el.attrs) + '},'
|
||||
}
|
||||
if (hasAttrs) {
|
||||
data += attrs.slice(0, -1) + '},'
|
||||
}
|
||||
if (hasProps) {
|
||||
data += props.slice(0, -1) + '},'
|
||||
}
|
||||
if (hasEvents) {
|
||||
data += genHandlers(events)
|
||||
// event handlers
|
||||
if (el.events) {
|
||||
data += genHandlers(el.events)
|
||||
}
|
||||
console.log(data)
|
||||
return data.replace(/,$/, '') + '}'
|
||||
}
|
||||
|
||||
@ -154,24 +97,20 @@ function genNode (node) {
|
||||
}
|
||||
|
||||
function genText (text) {
|
||||
if (text === ' ') {
|
||||
return '" "'
|
||||
} else {
|
||||
const exp = parseText(text)
|
||||
if (exp) {
|
||||
return `(${exp}==null?'':String(${exp}))`
|
||||
} else {
|
||||
return JSON.stringify(text)
|
||||
}
|
||||
}
|
||||
return text.expression
|
||||
? `(${text.expression}==null?'':String(${text.expression}))`
|
||||
: JSON.stringify(text.text)
|
||||
}
|
||||
|
||||
function genRender (el) {
|
||||
const method = el.attrsMap.method
|
||||
const args = el.attrsMap.args
|
||||
if (process.env.NODE_ENV !== 'production' && !method) {
|
||||
console.error('method attribute is required on <render>.')
|
||||
return 'undefined'
|
||||
}
|
||||
return `${method}(${args})`
|
||||
return `${el.method}(${el.args || ''})`
|
||||
}
|
||||
|
||||
function genProps (props) {
|
||||
let res = ''
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
let prop = props[i]
|
||||
res += `"${prop.name}":${prop.value},`
|
||||
}
|
||||
return res.slice(0, -1)
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { addHandler } from './on'
|
||||
import { addHandler } from '../helpers'
|
||||
|
||||
export function genModel (el, events, value, modifiers) {
|
||||
export function genModel (el, dir) {
|
||||
const events = (el.events || (el.events = {}))
|
||||
const value = dir.value
|
||||
const modifiers = dir.modifiers
|
||||
if (el.tag === 'select') {
|
||||
if (el.attrsMap.multiple != null) {
|
||||
return genMultiSelect(events, value, el)
|
||||
@ -51,7 +54,10 @@ function genMultiSelect (events, value, el) {
|
||||
for (let i = 0; i < el.children.length; i++) {
|
||||
let c = el.children[i]
|
||||
if (c.tag === 'option') {
|
||||
c.props = `selected:(${value}).indexOf(${getInputValue(c)})>-1`
|
||||
(c.props || (c.props = [])).push({
|
||||
name: 'selected',
|
||||
value: `(${value}).indexOf(${getInputValue(c)})>-1`
|
||||
})
|
||||
}
|
||||
}
|
||||
return ''
|
||||
|
@ -1,19 +1,4 @@
|
||||
export function getAndRemoveAttr (el, attr) {
|
||||
let val
|
||||
if ((val = el.attrsMap[attr])) {
|
||||
el.attrsMap[attr] = null
|
||||
for (let i = 0, l = el.attrs.length; i < l; i++) {
|
||||
if (el.attrs[i].name === attr) {
|
||||
el.attrs.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
const modifierRE = /\.[^\.]+/g
|
||||
|
||||
export function parseModifiers (name) {
|
||||
const match = name.match(modifierRE)
|
||||
if (match) {
|
||||
@ -27,6 +12,46 @@ export function removeModifiers (name) {
|
||||
return name.replace(modifierRE, '')
|
||||
}
|
||||
|
||||
export function makeAttrsMap (attrs) {
|
||||
const map = {}
|
||||
for (let i = 0, l = attrs.length; i < l; i++) {
|
||||
map[attrs[i].name] = attrs[i].value
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
export function getAndRemoveAttr (el, attr) {
|
||||
let val
|
||||
if ((val = el.attrsMap[attr])) {
|
||||
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
|
||||
}
|
||||
|
||||
export function addHandler (events, name, value, modifiers) {
|
||||
// check capture modifier
|
||||
if (modifiers && modifiers.capture) {
|
||||
delete modifiers.capture
|
||||
name = '!' + name // mark the event as captured
|
||||
}
|
||||
const newHandler = { value, modifiers }
|
||||
const handlers = events[name]
|
||||
if (Array.isArray(handlers)) {
|
||||
handlers.push(newHandler)
|
||||
} else if (handlers) {
|
||||
events[name] = [handlers, newHandler]
|
||||
} else {
|
||||
events[name] = newHandler
|
||||
}
|
||||
}
|
||||
|
||||
const tagRE = /\{\{((?:.|\\n)+?)\}\}/g
|
||||
export function parseText (text) {
|
||||
if (!tagRE.test(text)) {
|
||||
@ -50,39 +75,3 @@ export function parseText (text) {
|
||||
}
|
||||
return tokens.join('+')
|
||||
}
|
||||
|
||||
// this map covers SVG elements that can appear as template root nodes
|
||||
const svgMap = {
|
||||
g: 1,
|
||||
defs: 1,
|
||||
symbol: 1,
|
||||
use: 1,
|
||||
image: 1,
|
||||
text: 1,
|
||||
circle: 1,
|
||||
ellipse: 1,
|
||||
line: 1,
|
||||
path: 1,
|
||||
polygon: 1,
|
||||
polyline: 1,
|
||||
rect: 1
|
||||
}
|
||||
|
||||
export function checkSVG (el) {
|
||||
if (el.tag === 'svg') {
|
||||
// recursively mark all children as svg
|
||||
markSVG(el)
|
||||
}
|
||||
return el.svg || svgMap[el.tag]
|
||||
}
|
||||
|
||||
function markSVG (el) {
|
||||
el.svg = true
|
||||
if (el.children) {
|
||||
for (var i = 0; i < el.children.length; i++) {
|
||||
if (el.children[i].tag) {
|
||||
markSVG(el.children[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,20 @@
|
||||
import { decodeHTML } from 'entities'
|
||||
import HTMLParser from './html-parser'
|
||||
import {
|
||||
parseText,
|
||||
parseModifiers,
|
||||
removeModifiers,
|
||||
makeAttrsMap,
|
||||
getAndRemoveAttr,
|
||||
addHandler
|
||||
} from './helpers'
|
||||
|
||||
const dirRE = /^v-|^@|^:/
|
||||
const bindRE = /^:|^v-bind:/
|
||||
const onRE = /^@|^v-on:/
|
||||
const modifierRE = /\.[^\.]+/g
|
||||
|
||||
const mustUsePropsRE = /^(value|selected|checked|muted)$/
|
||||
const forAliasRE = /([a-zA-Z_][\w]*)\s+(?:in|of)\s+(.*)/
|
||||
|
||||
// this map covers SVG elements that can appear as template root nodes
|
||||
const svgMap = {
|
||||
@ -44,7 +53,8 @@ export function parse (template, preserveWhitespace) {
|
||||
start (tag, attrs, unary) {
|
||||
let element = {
|
||||
tag,
|
||||
attrs,
|
||||
plain: !attrs.length,
|
||||
attrsList: attrs,
|
||||
attrsMap: makeAttrsMap(attrs),
|
||||
parent: currentParent,
|
||||
children: []
|
||||
@ -59,7 +69,10 @@ export function parse (template, preserveWhitespace) {
|
||||
svgIndex = stack.length
|
||||
}
|
||||
|
||||
processControlFlow(element)
|
||||
processFor(element)
|
||||
processIf(element)
|
||||
processRender(element)
|
||||
processSlot(element)
|
||||
processClassBinding(element)
|
||||
processStyleBinding(element)
|
||||
processAttributes(element)
|
||||
@ -105,28 +118,65 @@ export function parse (template, preserveWhitespace) {
|
||||
? ' '
|
||||
: null
|
||||
if (text) {
|
||||
currentParent.children.push(text)
|
||||
if (text !== ' ') {
|
||||
let expression = parseText(text)
|
||||
if (expression) {
|
||||
currentParent.children.push({
|
||||
expression
|
||||
})
|
||||
} else {
|
||||
currentParent.children.push({ text })
|
||||
}
|
||||
} else {
|
||||
currentParent.children.push({ text })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return root
|
||||
}
|
||||
|
||||
function processControlFlow (el) {
|
||||
function processFor (el) {
|
||||
let exp
|
||||
if ((exp = getAndRemoveAttr(el, 'v-for'))) {
|
||||
el['for'] = exp
|
||||
const inMatch = exp.match(forAliasRE)
|
||||
if (process.env.NODE_ENV !== 'production' && !inMatch) {
|
||||
console.error(`Invalid v-for expression: ${exp}`)
|
||||
}
|
||||
el.alias = inMatch[1].trim()
|
||||
el['for'] = inMatch[2].trim()
|
||||
if ((exp = getAndRemoveAttr(el, 'track-by'))) {
|
||||
el.key = exp
|
||||
el.key = exp === '$index'
|
||||
? exp
|
||||
: el.alias + '["' + exp + '"]'
|
||||
}
|
||||
}
|
||||
if ((exp = getAndRemoveAttr(el, 'v-if'))) {
|
||||
}
|
||||
|
||||
function processIf (el) {
|
||||
let exp = getAndRemoveAttr(el, 'v-if')
|
||||
if (exp) {
|
||||
el['if'] = exp
|
||||
}
|
||||
}
|
||||
|
||||
function processRender (el) {
|
||||
if (el.tag === 'render') {
|
||||
el.render = true
|
||||
el.method = el.attrsMap.method
|
||||
el.args = el.attrsMap.args
|
||||
if (process.env.NODE_ENV !== 'production' && !el.method) {
|
||||
console.error('method attribute is required on <render>.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processSlot () {
|
||||
// todo
|
||||
}
|
||||
|
||||
function processClassBinding (el) {
|
||||
el['class'] = getAndRemoveAttr(el, 'class')
|
||||
el.staticClass = getAndRemoveAttr(el, 'class')
|
||||
el.classBinding =
|
||||
getAndRemoveAttr(el, ':class') ||
|
||||
getAndRemoveAttr(el, 'v-bind:class')
|
||||
@ -139,22 +189,23 @@ function processStyleBinding (el) {
|
||||
}
|
||||
|
||||
function processAttributes (el) {
|
||||
for (let i = 0; i < el.attrs.length; i++) {
|
||||
let name = el.attrs[i].name
|
||||
let value = el.attrs[i].value
|
||||
const list = el.attrsList
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let name = list[i].name
|
||||
let value = list[i].value
|
||||
if (dirRE.test(name)) {
|
||||
name = name.replace(dirRE, '')
|
||||
// modifiers
|
||||
const modifiers = parseModifiers(name)
|
||||
if (modifiers) {
|
||||
name = name.replace(modifierRE, '')
|
||||
name = removeModifiers(name)
|
||||
}
|
||||
if (bindRE.test(name)) { // v-bind
|
||||
name = name.replace(bindRE, '')
|
||||
if (mustUsePropsRE.test(name)) {
|
||||
(el.props || (el.props = [])).push({ name, value })
|
||||
} else {
|
||||
(el.attrBindings || (el.attrBindings = [])).push({ name, value })
|
||||
(el.attrs || (el.attrs = [])).push({ name, value })
|
||||
}
|
||||
} else if (onRE.test(name)) { // v-on
|
||||
name = name.replace(onRE, '')
|
||||
@ -166,54 +217,12 @@ function processAttributes (el) {
|
||||
modifiers
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// literal attribute
|
||||
(el.attrs || (el.attrs = [])).push({
|
||||
name,
|
||||
value: JSON.stringify(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseModifiers (name) {
|
||||
const match = name.match(modifierRE)
|
||||
if (match) {
|
||||
const ret = {}
|
||||
match.forEach(m => { ret[m.slice(1)] = true })
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
function makeAttrsMap (attrs) {
|
||||
const map = {}
|
||||
for (let i = 0, l = attrs.length; i < l; i++) {
|
||||
map[attrs[i].name] = attrs[i].value
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
function getAndRemoveAttr (el, attr) {
|
||||
let val
|
||||
if ((val = el.attrsMap[attr])) {
|
||||
el.attrsMap[attr] = null
|
||||
for (let i = 0, l = el.attrs.length; i < l; i++) {
|
||||
if (el.attrs[i].name === attr) {
|
||||
el.attrs.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
function addHandler (events, name, value, modifiers) {
|
||||
// check capture modifier
|
||||
if (modifiers && modifiers.capture) {
|
||||
delete modifiers.capture
|
||||
name = '!' + name // mark the event as captured
|
||||
}
|
||||
const newHandler = { value, modifiers }
|
||||
const handlers = events[name]
|
||||
if (Array.isArray(handlers)) {
|
||||
handlers.push(newHandler)
|
||||
} else if (handlers) {
|
||||
events[name] = [handlers, newHandler]
|
||||
} else {
|
||||
events[name] = newHandler
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,10 @@ function updateClass (oldVnode, vnode) {
|
||||
let cls = staticClass
|
||||
? staticClass + (dynamicClass ? ' ' + dynamicClass : '')
|
||||
: dynamicClass
|
||||
setClass(vnode.elm, cls)
|
||||
if (cls !== oldVnode.class) {
|
||||
setClass(vnode.elm, cls)
|
||||
}
|
||||
vnode.class = cls
|
||||
}
|
||||
}
|
||||
|
||||
|
0
src/runtime/vdom/modules/directives.js
Normal file
0
src/runtime/vdom/modules/directives.js
Normal file
Loading…
Reference in New Issue
Block a user