first commit

This commit is contained in:
2026-09-08 20:28:54 +08:00
commit 2ada8d3d5a
37380 changed files with 4886169 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
const {
ID,
isVar
} = require('./util')
const {
isComponent
} = require('../util')
let isPlatformReservedTag
function no (a, b, c) {
return false
}
function isBuiltInTag (tag) {
if (
tag === 'slot' ||
tag === 'component' ||
tag === 'keep-alive'
) {
return true
}
}
function isStatic (node) {
if (node.type === 2) {
return false
}
if (node.type === 3) {
return true
}
if (node.staticClass || node.classBinding || node.styleBinding) {
return false
}
return !!(node.pre || (
!node.hasBindings && // no dynamic bindings
!isBuiltInTag(node.tag) && // not a built-in
isPlatformReservedTag(node.tag)
))
}
function markStatic (node) {
const isStaticNode = isStatic(node)
if (isStaticNode) { // 静态节点且仅包含 ID 属性
if (
node.attrs &&
node.attrs.length === 1 &&
!node.key &&
!node.ref &&
!node.slotTarget
) {
node.plain = true
}
}
if (node.type === 1) {
// 需要保留 staticClass , selectComponent,externalClasses
// delete node.staticClass
delete node.staticStyle
const isCustomComponent = isComponent(node.tag)
if (node.attrs && !isCustomComponent && node.tag !== 'keep-alive') { // 移除静态属性
// 保留 id 属性, selectComponent 需要使用
node.attrs = node.attrs.filter(attr => {
const {
name,
value
} = attr
return name === 'id' ||
name === ID ||
// name.indexOf('data-') === 0 || // TODO dataset
isVar(value)
})
}
node.children = node.children.filter(child => { // 移除静态文本
if (child.type === 3) { // ASTText
if (!isCustomComponent) {
return false
}
child.text = '' // slot <custom>ABCD</custom>
}
return true
})
for (let i = 0, l = node.children.length; i < l; i++) {
const child = node.children[i]
markStatic(child)
}
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
const block = node.ifConditions[i].block
markStatic(block)
}
}
}
if (isStaticNode) { // 静态节点且仅包含 ID 属性
if (!node.attrsMap || !node.attrsMap.id) { // 保留 id 属性, selectComponent 需要使用
// 在 vue2.0 中 https://github.com/fxy060608/vue/blob/app-service/src/core/vdom/patch.js#L41
// sameVnode 中会对比vnode的data,如果一个有值,一个没值,会触发新增逻辑
// app端service和view编译时均会为每个节点生成_i属性,但service层为了优化包体积,性能,会对静态节点移除_i属性
// 如果app-service中优化移除了_i属性,而app-view中又保留了,就导致两者运行时的逻辑不一样
// <view v-if="true" @click="click"><custom/></view><view v-else><custom/></view>
// 上述写法,第一个view始终有data{onClick:click});第二个viewservice层如果移除_i,则没有data,而view层会保留_i,又有data
// 导致:app-service触发的是custom create逻辑、而app-view触发了custom update逻辑
// 故:service层也不应该移除_i属性,但为了影响范围小一些,目前仅在if/for等条件节点上启用此逻辑,确保此情况下service和view的data均存在
if ((node.for || node.if || node.else || node.elseif) && node.children && node.children.length) {
if (!node.plain) { // 已经包含了其他data属性,不需要attrs来激活data,可以删除
delete node.attrs
} else { // 如果plain为true,需要调整为false,否则generate时会忽略data的生成
node.plain = false
}
} else {
delete node.attrs
}
}
}
}
module.exports = function optimize (root, options) {
isPlatformReservedTag = options.isReservedTag || no
markStatic(root)
}
@@ -0,0 +1,124 @@
const {
ID,
C_IS,
C_REF,
C_NAME,
V_IF,
V_FOR,
V_ELSE_IF,
isVar
} = require('../util')
const parseTextExpr = require('./text-parser')
function parseRef (el, genVar) {
if (el.ref && isVar(el.ref)) {
el.ref = genVar(C_REF, el.ref)
}
}
function parseSlotName (el, genVar) {
if (el.slotName && isVar(el.slotName)) {
el.slotName = genVar(C_NAME, el.slotName)
}
}
function parseIs (el, genVar) {
if (!el.component) {
return
}
if (isVar(el.component)) {
el.component = genVar(C_IS, el.component)
}
}
function isProcessed (exp) {
return String(exp).indexOf('_$') === 0
}
// 当根节点是由if,elseif,else组成,会调用多次parseIf来解析root
function parseIf (el, createGenVar, isScopedSlot) {
if (!el.if) {
return
}
if (el.slotTarget && el.tag === 'template') { // new v-slot
isScopedSlot = false
}
el.ifConditions.forEach(con => {
if (!isProcessed(con.exp) && isVar(con.exp)) {
con.exp = createGenVar(con.block.attrsMap[ID], isScopedSlot)(con.block.elseif ? V_ELSE_IF : V_IF, con.exp)
}
})
if (!isProcessed(el.if)) {
el.if = createGenVar(el.attrsMap[ID], isScopedSlot)(V_IF, el.if)
}
}
function parseFor (el, createGenVar, isScopedSlot, fill = false) {
if (el.for && isVar(el.for)) {
el.for = createGenVar(el.forId, isScopedSlot)(
V_FOR,
fill
? `{forItems:${el.for},fill:true}`
: `{forItems:${el.for}}`
)
return true
}
}
function parseBinding (el, genVar) {
el.staticClass && (el.staticClass = genVar('sc', el.staticClass))
el.classBinding && (el.classBinding = genVar('c', el.classBinding))
el.styleBinding && (el.styleBinding = genVar('s', el.styleBinding))
}
function parseDirs (el, genVar, ignoreDirs = []) {
el.directives && el.directives.forEach(dir => {
if (ignoreDirs.indexOf(dir.name) === -1) {
dir.value && (dir.value = genVar('v-' + dir.name, dir.value))
dir.isDynamicArg && (dir.arg = genVar('v-' + dir.name + '-arg', dir.arg))
}
})
}
function parseAttrs (el, genVar) {
el.attrs && el.attrs.forEach(attr => {
const {
name,
value
} = attr
if (
name !== ID &&
// name.indexOf('data-') !== 0 && // TODO dataset 保留
name.indexOf('change:') !== 0 && // wxs change:prop
isVar(value) &&
value.indexOf('_$') !== 0 // 已被提前处理过了,如 wxs prop:_$gc(2,'change:prop')
) {
attr.value = genVar('a-' + name, value)
}
})
}
function parseProps (el, genVar) {
el.props && el.props.forEach(prop => {
isVar(prop.value) && (prop.value = genVar('a-' + prop.name, prop.value))
})
}
function parseText (el, parent, state) {
// fixed by xxxxxx 注意:保持平台一致性,trim 一下
el.parent && (el.parent = parent)
el.expression = parseTextExpr(el.text.trim(), false, state).expression
}
module.exports = {
parseIs,
parseRef,
parseSlotName,
parseIf,
parseFor,
parseText,
parseDirs,
parseAttrs,
parseProps,
parseBinding
}
@@ -0,0 +1,30 @@
const {
ID,
hasOwn,
addRawAttr
} = require('../util')
module.exports = function parseBlock (el, parent) {
if (el.tag === 'template' && !hasOwn(el.attrsMap, ID)) {
/**
* <current-user v-slot="{ user }">
* {{ user.firstName }}
* </current-user>
*/
addRawAttr(el, ID, parent.attrsMap[ID])
} else if (el.tag === 'block') {
el.tag = 'template'
const vForKey = el.key
if (vForKey) {
delete el.key
el.children.forEach((childEl, index) => {
const childVForKey = childEl.key
if (childVForKey) {
childEl.key = `${childVForKey}+'_'+${vForKey}+'_${index}'`
} else {
childEl.key = `${vForKey}+'_${index}'`
}
})
}
}
}
@@ -0,0 +1,20 @@
const {
ID,
elements
} = require('../util')
const {
isComponent
} = require('../../util')
// 仅限 view 层
module.exports = function parseComponent (el) {
// 需要把自定义组件的 attrs, props 全干掉
if (el.tag && !elements.includes(el.tag) && isComponent(el.tag)) {
// 仅保留 id、ID、data
el.attrs && (el.attrs = el.attrs.filter(attr => {
const name = attr.name
return name === 'id' || name === ID || name.indexOf('data-') === 0
}))
}
}
@@ -0,0 +1,29 @@
const deprecated = {
events: {
tap: 'click',
longtap: 'longpress'
}
}
module.exports = function parseEvent (el) {
if (el.events || el.nativeEvents) {
const {
events: eventsMap
} = deprecated
normalizeEvent(el.events, eventsMap)
normalizeEvent(el.nativeEvents, eventsMap)
}
}
function normalizeEvent (events, eventsMap) {
if (!events) {
return
}
Object.keys(events).forEach(name => {
// 过时事件类型转换
if (eventsMap[name]) {
events[eventsMap[name]] = events[name]
delete events[name]
// warnLogs.add(`警告:事件${name}已过时,推荐使用${eventsMap[name]}代替`)
}
})
}
@@ -0,0 +1,115 @@
/* @flow */
const validDivisionCharRE = /[\w).+\-_$\]]/
module.exports = function parseFilters (exp) {
let inSingle = false
let inDouble = false
let inTemplateString = false
let inRegex = false
let curly = 0
let square = 0
let paren = 0
let lastFilterIndex = 0
let c, prev, i, expression, filters
for (i = 0; i < exp.length; i++) {
prev = c
c = exp.charCodeAt(i)
if (inSingle) {
if (c === 0x27 && prev !== 0x5C) inSingle = false
} else if (inDouble) {
if (c === 0x22 && prev !== 0x5C) inDouble = false
} else if (inTemplateString) {
if (c === 0x60 && prev !== 0x5C) inTemplateString = false
} else if (inRegex) {
if (c === 0x2f && prev !== 0x5C) inRegex = false
} else if (
c === 0x7C && // pipe
exp.charCodeAt(i + 1) !== 0x7C &&
exp.charCodeAt(i - 1) !== 0x7C &&
!curly && !square && !paren
) {
if (expression === undefined) {
// first filter, end of expression
lastFilterIndex = i + 1
expression = exp.slice(0, i).trim()
} else {
pushFilter()
}
} else {
switch (c) {
case 0x22:
inDouble = true
break // "
case 0x27:
inSingle = true
break // '
case 0x60:
inTemplateString = true
break // `
case 0x28:
paren++
break // (
case 0x29:
paren--
break // )
case 0x5B:
square++
break // [
case 0x5D:
square--
break // ]
case 0x7B:
curly++
break // {
case 0x7D:
curly--
break // }
}
if (c === 0x2f) { // /
let j = i - 1
let p
// find first non-whitespace prev char
for (; j >= 0; j--) {
p = exp.charAt(j)
if (p !== ' ') break
}
if (!p || !validDivisionCharRE.test(p)) {
inRegex = true
}
}
}
}
if (expression === undefined) {
expression = exp.slice(0, i).trim()
} else if (lastFilterIndex !== 0) {
pushFilter()
}
function pushFilter () {
(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
lastFilterIndex = i + 1
}
if (filters) {
for (i = 0; i < filters.length; i++) {
expression = wrapFilter(expression, filters[i])
}
}
return expression
}
function wrapFilter (exp, filter) {
const i = filter.indexOf('(')
if (i < 0) {
// _f: resolveFilter
return `_f("${filter}")(${exp})`
} else {
const name = filter.slice(0, i)
const args = filter.slice(i + 1)
return `_f("${name}")(${exp}${args !== ')' ? ',' + args : args}`
}
}
@@ -0,0 +1,19 @@
const {
hasOwn,
elements
} = require('../util')
const tags = require('@dcloudio/uni-cli-shared/lib/tags')
// 仅限 view 层
module.exports = function parseTag (el) {
const tag = el.tag
const element = elements.find(element => tag === element || 'uni-' + tag === element)
if (element) {
el.tag = element
return
}
if (el.tag.indexOf('v-uni-') !== 0 && hasOwn(tags, el.tag)) {
el.tag = 'v-uni-' + el.tag
}
}
@@ -0,0 +1,62 @@
/* @flow */
const parseFilters = require('./filter-parser')
function cached (fn) {
const cache = Object.create(null)
return function cachedFn (str) {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}
}
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
const regexEscapeRE = /[-.*+?^${}()|[\]/\\]/g
const buildRegex = cached(delimiters => {
const open = delimiters[0].replace(regexEscapeRE, '\\$&')
const close = delimiters[1].replace(regexEscapeRE, '\\$&')
return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
})
module.exports = function parseText (
text,
delimiters,
state
) {
const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
if (!tagRE.test(text)) {
return
}
const tokens = []
const rawTokens = []
let lastIndex = tagRE.lastIndex = 0
let match, index, tokenValue
while ((match = tagRE.exec(text))) {
index = match.index
// push text token
if (index > lastIndex) {
rawTokens.push(tokenValue = text.slice(lastIndex, index))
if (!state.service) {
tokens.push(JSON.stringify(tokenValue))
}
}
// tag token
const exp = parseFilters(match[1].trim())
tokens.push(`(${state.genVar('t' + (state.childIndex) + '-' + (state.index++), '_s(' + exp + ')')})`)
rawTokens.push({
'@binding': exp
})
lastIndex = index + match[0].length
}
if (lastIndex < text.length) {
rawTokens.push(tokenValue = text.slice(lastIndex))
if (!state.service) {
tokens.push(JSON.stringify(tokenValue))
}
}
return {
expression: tokens.join('+'),
tokens: rawTokens
}
}
@@ -0,0 +1,56 @@
const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
function isWxsEvent (expr, filterModules) {
return !!filterModules.find(name => expr.indexOf(name + '.') === 0)
}
function parseWxsViewEvent (expr, filterModules) {
if (!simplePathRE.test(expr)) {
return expr
}
if (isWxsEvent(expr, filterModules)) {
return `$event = $handleWxsEvent($event);${expr}($event, $getComponentDescriptor())`
}
return expr
}
module.exports = function parseWxsEvents (el, {
filterModules,
isAppService,
isAppView
}) {
if (!filterModules || !filterModules.length) {
return
}
if (!el.events) {
return
}
if (isAppService) {
Object.keys(el.events).forEach(name => {
const handlers = el.events[name]
if (Array.isArray(handlers)) {
el.events[name] = handlers.filter(handler => {
return !isWxsEvent(handler.value, filterModules)
})
if (!el.events[name].length) {
delete el.events[name]
}
} else {
if (isWxsEvent(handlers.value, filterModules)) {
delete el.events[name]
}
}
})
} else if (isAppView) {
Object.keys(el.events).forEach(name => {
const handlers = el.events[name]
if (Array.isArray(handlers)) {
handlers.forEach(handler => {
handler.value = parseWxsViewEvent(handler.value, filterModules)
})
} else {
handlers.value = parseWxsViewEvent(handlers.value, filterModules)
}
})
}
}
@@ -0,0 +1,56 @@
const {
ID,
SET_DATA,
GET_CHANGE_DATA
} = require('../util')
function isWxsChangeProp (attr) {
return attr.name.indexOf('change:') === 0
}
function createSetDataGenVar (id) {
return function genVar (name, value) {
return `${SET_DATA}(${id},'${name}',${value})`
}
}
function createGetChangeDataGenVar (id) {
return function genVar (name) {
return `${GET_CHANGE_DATA}(${id},'${name}')`
}
}
module.exports = function parseWxsProps (el, {
isAppService,
isAppView
}) {
if (!el.attrs) {
return
}
const wxsChangeProps = []
el.attrs = el.attrs.filter(attr => {
if (isWxsChangeProp(attr)) {
wxsChangeProps.push(attr.name.replace('change:', ''))
if (isAppService) { // service 层移除 change:prop
return false
}
}
return true
})
if (!wxsChangeProps.length) {
return
}
const genSetVar = createSetDataGenVar(el.attrsMap[ID])
const genGetVar = createGetChangeDataGenVar(el.attrsMap[ID])
el.attrs.forEach(attr => {
if (wxsChangeProps.includes(attr.name)) {
if (isAppService) {
attr.value = genSetVar('change:' + attr.value, attr.value)
} else if (isAppView) {
attr.value = genGetVar('change:' + attr.value)
}
}
})
}
@@ -0,0 +1,15 @@
const {
ID,
hasOwn,
addRawAttr
} = require('./util')
module.exports = function preTransformNode (el, options) {
if (!hasOwn(options, 'nid')) {
options.nid = 0
}
addRawAttr(el, ID, options.nid++)
if (el.attrsMap['v-for']) {
el.forId = el.attrsMap[ID]
}
}
+210
View File
@@ -0,0 +1,210 @@
const {
ID,
V_FOR,
SET_DATA,
isVar,
getNewId,
getForEl,
processForKey,
updateForEleId,
traverseNode,
updateScopedSlotEleId
} = require('./util')
const {
isComponent
} = require('../util')
const {
parseIs,
parseRef,
parseSlotName,
parseIf,
parseFor,
parseText,
parseDirs,
parseAttrs,
parseProps,
parseBinding
} = require('./parser/base-parser')
const parseEvent = require('./parser/event-parser')
const parseBlock = require('./parser/block-parser')
const parseWxsProps = require('./parser/wxs-props-parser')
const parseWxsEvents = require('./parser/wxs-events-parser')
const preTransformNode = require('./pre-transform-node')
const optimize = require('./optimizer')
function createGenVar (id, isScopedSlot) {
if (isScopedSlot) {
return function genVar (name, value) {
return `_svm.${SET_DATA}(${id},'${name}',${value})`
}
}
return function genVar (name, value) {
return `${SET_DATA}(${id},'${name}',${value})`
}
}
function parseKey (el, isScopedSlot) {
// add default key
if (processForKey(el)) {
el = el.children[0] // 当 template 下仅文本时,处理第一个动态文本
}
if (!el.key || el.key.indexOf(SET_DATA) === 0) {
return
}
const forEl = getForEl(el)
if (!forEl) {
return isVar(el.key) && (el.key = createGenVar(el.attrsMap[ID], isScopedSlot)('a-key', el.key))
}
if (!isVar(forEl.for)) {
return
}
const forId = forEl.forId
const it = forEl.iterator2
const genVar = createGenVar(forId, isScopedSlot)
if (forEl === el) { // <view v-for="item in items" :key="item.id"></view>
el.key = genVar(V_FOR, `{forIndex:${it},key:${el.key}}`)
} else { // <template v-for="item in items"><view :key="item.id+'1'"></view><view :key="item.id+'2'"></view></template>
const keyIndex = forEl.children.indexOf(el)
el.key = genVar(V_FOR, `{forIndex:${it},keyIndex:${keyIndex},key:${el.key}}`)
}
if (el.tag === 'slot') {
el.attrs.push({ name: 'key', value: el.key })
}
}
function parseComponentAttrs (el, genVar) {
el.attrs && el.attrs.forEach(attr => {
const {
name,
value
} = attr
if (isVar(value) && (name === 'id' || name.indexOf('data-') === 0)) {
attr.value = genVar('a-' + name, value)
}
})
}
function checkAutoFill (el) {
if (
el.for &&
(
el.tag === 'template' ||
el.tag === 'block'
) &&
!el.children.find(child =>
child.type === 1 &&
child.tag !== 'template' &&
child.tag !== 'block'
)
) {
return true
}
return false
}
function transformNode (el, parent, state, isScopedSlot) {
if (el.type === 3) {
// fixed by xxxxxx 注意:保持平台一致性,trim 一下,理论上service不需要,保险起见也处理一遍
el.text = el.text.trim()
return
}
parseBlock(el, parent)
parseEvent(el)
updateForEleId(el, state)
updateScopedSlotEleId(el, state)
if (el.type === 2) {
let pid = parent.attrsMap[ID]
if (isScopedSlot && String(pid).indexOf('_si') === -1) {
pid = getNewId(pid, '_si')
}
return parseText(el, parent, {
childIndex: state.childIndex || 0,
index: 0,
service: true,
// <uni-popup>{{content}}</uni-popup>
genVar: createGenVar(pid, isScopedSlot)
})
}
const genVar = createGenVar(el.attrsMap[ID], isScopedSlot)
parseIs(el, genVar)
parseRef(el, genVar)
parseSlotName(el, genVar)
parseFor(el, createGenVar, isScopedSlot, checkAutoFill(el))
parseKey(el, isScopedSlot)
parseIf(el, createGenVar, isScopedSlot)
parseBinding(el, genVar)
parseDirs(el, genVar, ['model'])
parseWxsProps(el, {
isAppService: true
})
if (!isComponent(el.tag)) {
parseAttrs(el, genVar)
} else { // 目前的方案需要同步dataset
parseComponentAttrs(el, genVar)
}
parseProps(el, genVar)
parseWxsEvents(el, {
filterModules: state.filterModules,
isAppService: true
})
}
function postTransformNode (el, options) {
if (!el.parent) { // 从根节点开始递归处理
if (options.root) { // 当根节点是由if,elseif,else组成
parseIf(options.root, createGenVar)
} else {
options.root = el
}
traverseNode(el, false, {
createGenVar,
forIteratorId: 0,
transformNode,
filterModules: options.filterModules
})
optimize(el, options)
}
}
function genVModel (el, isScopedSlot) {
if (
(el.tag === 'input' || el.tag === 'textarea') &&
el.directives &&
el.directives.find(dir => dir.name === 'model')
) {
const prop = el.props.find(prop => prop.name === 'value')
prop.value = createGenVar(el.attrsMap[ID], isScopedSlot)('v-model', prop.value)
}
if (el.model) {
el.model.value = createGenVar(el.attrsMap[ID], isScopedSlot)('v-model', el.model.value)
}
}
function genData (el) {
delete el.$parentIterator3
genVModel(el)
return ''
}
module.exports = {
preTransformNode,
postTransformNode,
genData
}
+297
View File
@@ -0,0 +1,297 @@
const {
parseExpression
} = require('@babel/parser')
const t = require('@babel/types')
const ID = '_i'
const ITERATOR1 = '$1'
const ITERATOR2 = '$2'
const ITERATOR3 = '$3'
const SET_DATA = '_$s'
const GET_DATA = '_$g'
const SET_MP_CLASS = '_$smc'
const GET_CHANGE_DATA = '_$gc' // wxs
const C_IS = 'is'
const C_SLOT_TARGET = 'st'
const C_REF = 'ref'
const C_NAME = 'name'
const V_FOR = 'f'
const V_IF = 'i'
const V_ELSE_IF = 'e'
// web components
const elements = ['uni-view']
function isVar (str) {
if (!str) {
return false
}
const expr = parseExpression(str)
if (
t.isStringLiteral(expr) ||
t.isNumericLiteral(expr) ||
t.isBooleanLiteral(expr) ||
t.isNullLiteral(expr)
) {
return false
}
return true
}
function addRawAttr (el, name, value) {
el.attrsMap[name] = value
el.attrsList.push({
name,
value
})
}
function updateEleId (el, it, state) {
if (el.type !== 1) {
return
}
const newId = getNewId(el.attrsMap[ID], it)
addRawAttr(el, ID, newId)
if (el.attrs) {
const attr = el.attrs.find(attr => attr.name === ID)
attr.value = newId
}
el.children.forEach(child => {
if (!child.for) { // 忽略嵌套 for
updateEleId(child, it)
} else {
child.$parentIterator3 = (child.$parentIterator3 ? (child.$parentIterator3 + '+') : '') + it
child.forId = `${child.forId}+'-'+${it}`
}
})
el.ifConditions && el.ifConditions.forEach((con, index) => {
index !== 0 && updateEleId(con.block, it, state)
})
el.scopedSlots && Object.values(el.scopedSlots).forEach((slot, index) => {
updateEleId(slot, it, state)
})
}
function getBindingAttr (el, name) {
return getAndRemoveAttr(el, ':' + name) ||
getAndRemoveAttr(el, 'v-bind:' + name)
}
function getAndRemoveAttr (el, name) {
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
}
}
}
delete el.attrsMap[name]
return val
}
function updateForIterator (el, state) {
if (!el.for) {
return
}
// 简单处理,确保所有 for 循环,均包含 1,2,3
const forIteratorId = state.forIteratorId++
if (!el.iterator1) {
el.iterator1 = ITERATOR1 + forIteratorId
}
if (!el.iterator2) {
el.iterator2 = ITERATOR2 + forIteratorId
}
if (!el.iterator3) {
el.iterator3 = ITERATOR3 + forIteratorId
}
}
function updateForEleId (el, state) {
updateForIterator(el, state)
if (el.for) {
const it = el.$parentIterator3 ? (el.$parentIterator3 + '+' + "'-'" + '+' + el.iterator3) : el.iterator3
updateEleId(el, it, state)
}
}
function getNewId (id, it) {
return Number.isInteger(id) ? `("${id}-"+${it})` : `(${id}+${it})`
}
function updateScopedSlotEleId (el, state) {
// TODO 暂不考虑 scopedSlot 嵌套情况
if (el.slotScope) {
const getNewId = function (id, it) {
return Number.isInteger(id) ? `("${id}-"+${it})` : `(${id}+"-"+${it})`
}
const updateEleId = function (el) {
if (el.type !== 1) {
return
}
const it = '_si'
const newId = getNewId(el.attrsMap[ID], it)
if (el.forId) {
el.forId = getNewId(el.forId, it)
}
addRawAttr(el, ID, newId)
if (el.attrs) {
const attr = el.attrs.find(attr => attr.name === ID)
attr.value = newId
}
el.children.forEach(child => {
if (!child.slotScope) { // 忽略嵌套 scopedSlot
updateEleId(child, state)
}
})
}
if (el.tag === 'template' && el.slotTarget) { // new v-slot
el.children.forEach(child => {
if (!child.slotScope) { // 忽略嵌套 scopedSlot
updateEleId(child, state)
}
})
} else { // old slot-scope
updateEleId(el)
}
}
}
function getForEl (el) {
if (el.for) {
return el
}
if (el.parent && el.parent.for && (el.parent.tag === 'template' || el.parent.tag === 'block')) {
return el.parent
}
}
function processForKey (el) {
const forEl = getForEl(el)
if (forEl && !el.key) { // 占位的 text 标签也无需添加 key
if (!isVar(forEl.for)) { // <view v-for="10"></view>
return
}
const it = forEl.iterator3
if (forEl.tag === 'template' || forEl.tag === 'block') {
if (forEl !== el) {
const keyIndex = forEl.children.indexOf(el)
el.key = `${forEl.forId}+'-${keyIndex}'+${it}`
} else { // 当 template 下只有文本节点
if (
el.children &&
el.children.length &&
!el.children.find(child => child.type === 1)
) {
el.children[0].parent = el
if (!el.children.find(child => child.key)) {
el.children[0].key = `${forEl.forId}+'-0'+${it}`
}
return true
}
}
} else {
el.key = `${forEl.forId}+'-'+${it}`
}
}
}
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
function traverseNode (el, parent, state, isScopedSlot) {
state.transformNode(el, parent, state, isScopedSlot)
el.children && el.children.forEach((child, index) => {
state.childIndex = index
traverseNode(child, el, state, isScopedSlot)
})
el.ifConditions && el.ifConditions.forEach((con, index) => {
if (index !== 0) {
state.childIndex = index
traverseNode(con.block, el, state, isScopedSlot)
}
})
el.scopedSlots && Object.values(el.scopedSlots).forEach((slot, index) => {
state.childIndex = index
slot.slotScope = `${slot.slotScope}, _svm, _si`
if (slot.slotTargetDynamic && slot.slotTarget) {
slot.slotTarget = state.createGenVar(slot.attrsMap[ID])(C_SLOT_TARGET, slot.slotTarget)
}
traverseNode(slot, el, state, true)
})
}
function addAttr (el, name, value, dynamic) {
const attrs = dynamic
? (el.dynamicAttrs || (el.dynamicAttrs = []))
: (el.attrs || (el.attrs = []))
attrs.push({
name,
value,
dynamic
})
el.plain = false
}
function removeRawAttr (el, name) {
delete el.attrsMap[name]
const index = el.attrsList.findIndex(attr => attr.name === name)
index !== -1 && el.attrsList.splice(index, 1)
}
function removeRawBindingAttr (el, name) {
removeRawAttr(el, ':' + name)
removeRawAttr(el, 'v-bind:' + name)
}
function addHandler (el, name, value, important) {
const events = el.events || (el.events = {})
const handlers = events[name]
const newHandler = {
value: value.trim(),
dynamic: undefined
}
if (Array.isArray(handlers)) {
important ? handlers.unshift(newHandler) : handlers.push(newHandler)
} else if (handlers) {
events[name] = important ? [newHandler, handlers] : [handlers, newHandler]
} else {
events[name] = newHandler
}
el.plain = false
}
module.exports = {
C_IS,
C_REF,
C_NAME,
V_FOR,
V_IF,
V_ELSE_IF,
ID,
SET_DATA,
GET_DATA,
SET_MP_CLASS,
GET_CHANGE_DATA,
elements,
isVar,
hasOwn,
addAttr,
addRawAttr,
removeRawAttr,
removeRawBindingAttr,
getNewId,
getForEl,
addHandler,
processForKey,
updateForEleId,
updateScopedSlotEleId,
getBindingAttr,
getAndRemoveAttr,
traverseNode
}
+250
View File
@@ -0,0 +1,250 @@
const {
ID,
GET_DATA,
isVar,
getNewId,
getForEl,
updateForEleId,
updateScopedSlotEleId,
processForKey,
traverseNode
} = require('./util')
const {
parseIs,
parseRef,
parseSlotName,
parseIf,
parseFor,
parseText,
parseAttrs,
parseProps,
parseBinding
} = require('./parser/base-parser')
const parseTag = require('./parser/tag-parser')
const parseEvent = require('./parser/event-parser')
const parseBlock = require('./parser/block-parser')
const parseComponent = require('./parser/component-parser')
const parseWxsProps = require('./parser/wxs-props-parser')
const parseWxsEvents = require('./parser/wxs-events-parser')
const basePreTransformNode = require('./pre-transform-node')
function createGenVar (id, isScopedSlot) {
if (isScopedSlot) {
return function genVar (name, value) {
return `_svm.${GET_DATA}(${id},'${name}')`
}
}
return function genVar (name) {
return `${GET_DATA}(${id},'${name}')`
}
}
function parseKey (el, isScopedSlot) {
// add default key
processForKey(el)
if (el.key) { // renderList key
const forEl = getForEl(el)
if (forEl) {
if (!isVar(forEl.for)) {
return
}
if (forEl === el) { // <view v-for="item in items" :key="item.id"></view>
el.key = forEl.alias
} else { // <template v-for="item in items"><view :key="item.id+'1'"></view><view :key="item.id+'2'"></view></template>
const keyIndex = forEl.children.indexOf(el)
el.key = `${forEl.alias}['k${keyIndex}']`
}
} else {
isVar(el.key) && (el.key = createGenVar(el.attrsMap[ID], isScopedSlot)('a-key'))
}
}
}
function parseDirs (el, genVar, ignoreDirs, includeDirs = []) {
if (!el.directives) {
return
}
el.directives = el.directives.filter(dir => {
if (includeDirs.indexOf(dir.name) !== -1) {
if (ignoreDirs.indexOf(dir.name) === -1) {
dir.value && (dir.value = genVar('v-' + dir.name, dir.value))
dir.isDynamicArg && (dir.arg = genVar('v-' + dir.name + '-arg', dir.arg))
}
return true
}
})
}
const includeDirs = [
'text',
'html',
'bind',
'model',
'show',
'if',
'else',
'else-if',
'for',
'on',
'bind',
'slot',
'pre',
'cloak',
'once'
]
const ignoreDirs = ['model']
function transformNode (el, parent, state, isScopedSlot) {
if (el.type === 3) {
// fixed by xxxxxx 注意:保持平台一致性,trim 一下
el.text = el.text.trim()
return
}
parseBlock(el, parent)
parseComponent(el)
parseEvent(el)
// 更新 id
updateForEleId(el, state)
updateScopedSlotEleId(el, state)
if (el.type === 2) {
let pid = parent.attrsMap[ID]
if (isScopedSlot && String(pid).indexOf('_si') === -1) {
pid = getNewId(pid, '_si')
}
return parseText(el, parent, {
childIndex: state.childIndex || 0,
index: 0,
view: true,
// <uni-popup>{{content}}</uni-popup>
genVar: createGenVar(pid, isScopedSlot)
})
}
const genVar = createGenVar(el.attrsMap[ID], isScopedSlot)
parseIs(el, genVar)
parseRef(el, genVar)
parseSlotName(el, genVar)
if (parseFor(el, createGenVar, isScopedSlot)) {
if (el.alias[0] === '{') { // <div><li v-for=" { a, b } in items"></li></div>
el.alias = '$item'
}
}
parseKey(el, isScopedSlot)
parseIf(el, createGenVar, isScopedSlot)
parseBinding(el, genVar)
parseDirs(el, genVar, ignoreDirs, includeDirs)
parseWxsProps(el, {
isAppView: true
})
// if (el.attrs) { // TODO 过滤 dataset
// el.attrs = el.attrs.filter(attr => attr.name.indexOf('data-') !== 0)
// }
parseAttrs(el, genVar)
parseProps(el, genVar)
parseWxsEvents(el, {
filterModules: state.filterModules,
isAppView: true
})
}
function postTransformNode (el, options) {
if (!el.parent) { // 从根节点开始递归处理
if (options.root) { // 当根节点是由if,elseif,else组成
parseIf(options.root, createGenVar)
} else {
options.root = el
}
traverseNode(el, false, {
createGenVar,
forIteratorId: 0,
transformNode,
filterModules: options.filterModules
})
}
}
function handleViewEvents (events) {
Object.keys(events).forEach(name => {
const eventOpts = events[name]
// wxs
if (eventOpts.value && eventOpts.value.indexOf('$handleWxsEvent') !== -1) {
return
}
const modifiers = Object.create(null)
let type = name
const isPassive = type.charAt(0) === '&'
type = isPassive ? type.slice(1) : type
const isOnce = type.charAt(0) === '~'
type = isOnce ? type.slice(1) : type
const isCapture = type.charAt(0) === '!'
type = isCapture ? type.slice(1) : type
isPassive && (modifiers.passive = true)
isOnce && (modifiers.once = true)
isCapture && (modifiers.capture = true)
if (Array.isArray(eventOpts)) {
eventOpts.forEach(eventOpt => {
eventOpt.modifiers && Object.assign(modifiers, eventOpt.modifiers)
})
} else {
eventOpts.modifiers && Object.assign(modifiers, eventOpts.modifiers)
}
if (Object.keys(modifiers).length) {
events[name] = {
value: `$handleViewEvent($event,${JSON.stringify(modifiers)})`
}
} else {
events[name] = {
value: '$handleViewEvent($event)'
}
}
})
}
function genVModel (el, isScopedSlot) {
if (el.model) {
el.model.value = createGenVar(el.attrsMap[ID], isScopedSlot)('v-model', el.model.value)
if ((el.tag === 'v-uni-input' || el.tag === 'v-uni-textarea') && !(el.events && el.events.input)) {
el.model.callback = `function($$v){$handleVModelEvent(${el.attrsMap[ID]},$$v)}`
} else {
el.model.callback = 'function(){}'
}
}
}
function genData (el) {
delete el.$parentIterator3
genVModel(el)
// 放在 postTransformNode 中处理的时机太靠前,v-model 等指令会新增 event
el.events && handleViewEvents(el.events)
el.nativeEvents && handleViewEvents(el.nativeEvents)
return ''
}
module.exports = {
preTransformNode: function (el, options) {
parseTag(el)
return basePreTransformNode(el, options)
},
postTransformNode,
genData
}