Skip to content

Commit

Permalink
Merge pull request #1405 from didi/feat-support-option-chain-simple
Browse files Browse the repository at this point in the history
feat: add wxml option chain
  • Loading branch information
hiyuki authored Feb 21, 2024
2 parents 9d2945e + 336f42d commit 4db17e8
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs-vuepress/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const sidebar = {
'basic/event',
'basic/two-way-binding',
'basic/component',
'basic/refs'
'basic/refs',
'basic/option-chain'
]
},
{
Expand Down
33 changes: 33 additions & 0 deletions docs-vuepress/guide/basic/option-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 模版内可选链表达式

Mpx提供了在模版中使用可选链`?.`访问变量属性的能力,用法/能力和JS的可选链基本一致,可以在任意被`{{}}`所包裹的模版语法内使用。
> 实现原理是在编译时将使用`?.`的部分转化为`wxs`函数调用,运行时通过`wxs`访问变量属性,模拟出可选链的效果。
使用示例:
```html
<template>
<view wx:if="{{ a?.b }}">{{ a?.c + a?.d }}</view>
<view wx:for="{{ a?.d || [] }}"></view>
<view>{{ a?.d ? 'a' : 'b' }}</view>
<view>{{ a?.g[e?.d] }}</view>
</template>

<script>
import { createComponent } from '@mpxjs/core'
createComponent({
data: {
a: {
b: true,
c: 123,
g: {
d: 321
}
},
e: {
d: 'd'
}
}
})
</script>
```
16 changes: 16 additions & 0 deletions packages/webpack-plugin/lib/runtime/oc.wxs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 可选链wxs
module.exports.g = function (val, valKeyArr) {
var res = val
if (typeof val !== 'object') {
return undefined
}
var len = valKeyArr.length
var i = 0
while (i < len) {
if ((res = res[valKeyArr[i]]) === undefined) {
break
}
i++
}
return res
}
172 changes: 172 additions & 0 deletions packages/webpack-plugin/lib/template-compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ let env
let platformGetTagNamespace
let filePath
let refId
let haveOptionChain = false

function updateForScopesMap () {
forScopes.forEach((scope) => {
Expand Down Expand Up @@ -164,6 +165,8 @@ const i18nWxsRequest = '~' + i18nWxsLoaderPath + '!' + i18nWxsPath
const i18nModuleName = '__i18n__'
const stringifyWxsPath = '~' + normalize.lib('runtime/stringify.wxs')
const stringifyModuleName = '__stringify__'
const optionsChainWxsPath = '~' + normalize.lib('runtime/oc.wxs')
const optionsChainWxsName = '__oc__'

const tagRES = /(\{\{(?:.|\n|\r)+?\}\})(?!})/
const tagRE = /\{\{((?:.|\n|\r)+?)\}\}(?!})/
Expand Down Expand Up @@ -637,6 +640,7 @@ function parse (template, options) {
forScopes = []
forScopesMap = {}
hasI18n = false
haveOptionChain = false

platformGetTagNamespace = options.getTagNamespace || no

Expand Down Expand Up @@ -761,6 +765,10 @@ function parse (template, options) {
}
}

if (haveOptionChain) {
injectWxs(meta, optionsChainWxsName, optionsChainWxsPath)
}

injectNodes.forEach((node) => {
addChild(root, node, true)
})
Expand Down Expand Up @@ -1156,6 +1164,7 @@ function parseMustacheWithContext (raw = '') {
})
}

exp = parseOptionChain(exp)
if (i18n) {
for (const i18nFuncName of i18nFuncNames) {
const funcNameRE = new RegExp(`(?<![A-Za-z0-9_$.])${i18nFuncName}\\(`)
Expand Down Expand Up @@ -2392,6 +2401,169 @@ function genNode (node) {
return exp
}

/**
* 处理可选链用法
* @param str
* @returns
*/
function parseOptionChain (str) {
const wxsName = `${optionsChainWxsName}.g`
let optionsRes
while (optionsRes = /\?\./.exec(str)) {
const strLength = str.length
const grammarMap = {
init () {
const initKey = [
{
mapKey: '[]',
mapValue: [
{
key: '[',
value: 1
},
{
key: ']',
value: -1
}
]
},
{
mapKey: '()',
mapValue: [
{
key: '(',
value: 1
},
{
key: ')',
value: -1
}
]
}
]
this.count = {}
initKey.forEach(({ mapKey, mapValue }) => {
mapValue.forEach(({ key, value }) => {
this[key] = this.changeState(mapKey, value)
})
})
},
changeState (key, value) {
if (!this.count[key]) {
this.count[key] = 0
}
return () => {
this.count[key] = this.count[key] + value
return this.count[key]
}
},
checkState () {
return Object.values(this.count).find(i => i)
}
}
let leftIndex = optionsRes.index
let rightIndex = leftIndex + 2
let haveNotGetValue = true
let chainValue = ''
let chainKey = ''
let notCheck = false
grammarMap.init()
// 查 ?. 左边值
while (haveNotGetValue && leftIndex > 0) {
const left = str[leftIndex - 1]
const grammar = grammarMap[left]
if (notCheck) {
// 处于表达式内
chainValue = left + chainValue
if (grammar) {
grammar()
if (!grammarMap.checkState()) {
// 表达式结束
notCheck = false
}
}
} else if (~[']', ')'].indexOf(left)) {
// 命中表达式,开始记录表达式
chainValue = left + chainValue
notCheck = true
grammar()
} else if (left !== ' ') {
if (!/[A-Za-z0-9_$.]/.test(left)) {
// 结束
haveNotGetValue = false
leftIndex++
} else {
// 正常语法
chainValue = left + chainValue
}
}
leftIndex--
}
if (grammarMap.checkState() && haveNotGetValue) {
// 值查找结束但是语法未闭合或者处理到边界还未结束,抛异常
throw new Error('[optionChain] option value illegal!!!')
}
haveNotGetValue = true
let keyValue = ''
// 查 ?. 右边key
while (haveNotGetValue && rightIndex < strLength) {
const right = str[rightIndex]
const grammar = grammarMap[right]
if (notCheck) {
// 处于表达式内
if (grammar) {
grammar()
if (grammarMap.checkState()) {
keyValue += right
} else {
// 表达式结束
notCheck = false
chainKey += `,${keyValue}`
keyValue = ''
}
} else {
keyValue += right
}
} else if (~['[', '('].indexOf(right)) {
// 命中表达式,开始记录表达式
grammar()
if (keyValue) {
chainKey += `,'${keyValue}'`
keyValue = ''
}
notCheck = true
} else if (!/[A-Za-z0-9_$.?]/.test(right)) {
// 结束
haveNotGetValue = false
rightIndex--
} else if (right !== '?') {
// 正常语法
if (right === '.') {
if (keyValue) {
chainKey += `,'${keyValue}'`
}
keyValue = ''
} else {
keyValue += right
}
}
rightIndex++
}
if (grammarMap.checkState() && haveNotGetValue) {
// key值查找结束但是语法未闭合或者处理到边界还未结束,抛异常
throw new Error('[optionChain] option key illegal!!!')
}
if (keyValue) {
chainKey += `,'${keyValue}'`
}
str = str.slice(0, leftIndex) + `${wxsName}(${chainValue},[${chainKey.slice(1)}])` + str.slice(rightIndex)
if (!haveOptionChain) {
haveOptionChain = true
}
}
return str
}

module.exports = {
parseComponent,
parse,
Expand Down

0 comments on commit 4db17e8

Please sign in to comment.