Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wychl
uni-app
提交
82c5a914
U
uni-app
项目概览
wychl
/
uni-app
与 Fork 源项目一致
Fork自
DCloud / uni-app
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-app
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
82c5a914
编写于
4月 09, 2021
作者:
Q
qiang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix(mp): 增加新的解构插槽编译选项 betterScopedSlots,解构插槽内支持使用作用域外部值和复杂表达式
上级
bd35b9ad
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
205 addition
and
8 deletion
+205
-8
packages/uni-template-compiler/__tests__/compiler-mp-weixin.spec.js
...ni-template-compiler/__tests__/compiler-mp-weixin.spec.js
+36
-1
packages/uni-template-compiler/lib/constants.js
packages/uni-template-compiler/lib/constants.js
+3
-0
packages/uni-template-compiler/lib/script/traverse/index.js
packages/uni-template-compiler/lib/script/traverse/index.js
+8
-2
packages/uni-template-compiler/lib/script/traverse/render-slot.js
.../uni-template-compiler/lib/script/traverse/render-slot.js
+34
-0
packages/uni-template-compiler/lib/script/traverse/resolve-scoped-slots.js
...late-compiler/lib/script/traverse/resolve-scoped-slots.js
+54
-0
packages/uni-template-compiler/lib/script/traverse/visitor.js
...ages/uni-template-compiler/lib/script/traverse/visitor.js
+10
-0
packages/uni-template-compiler/lib/template/traverse.js
packages/uni-template-compiler/lib/template/traverse.js
+3
-3
packages/vue-cli-plugin-uni/lib/env.js
packages/vue-cli-plugin-uni/lib/env.js
+4
-0
packages/webpack-uni-mp-loader/lib/template.js
packages/webpack-uni-mp-loader/lib/template.js
+3
-2
src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js
src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js
+50
-0
未找到文件。
packages/uni-template-compiler/__tests__/compiler-mp-weixin.spec.js
浏览文件 @
82c5a914
...
@@ -82,6 +82,41 @@ describe('mp:compiler-mp-weixin', () => {
...
@@ -82,6 +82,41 @@ describe('mp:compiler-mp-weixin', () => {
)
)
})
})
it
(
'
generate scoped slot with filter
'
,
()
=>
{
assertCodegen
(
'
<my-component><template v-slot="{item}">{{getValue(item)}}<template></my-component>
'
,
'
<my-component vue-id="551070e6-1" bind:__l="__l" vue-slots="{{[
\'
default
\'
]}}"><block><block wx:if="{{$root.m0}}">{{$root.m1}}</block></block></my-component>
'
,
'
with(this){var m0=$hasScopedSlotsParams("551070e6-1");var m1=m0?getValue($getScopedSlotsParams("551070e6-1","default","item")):null;$mp.data=Object.assign({},{$root:{m0:m0,m1:m1}})}
'
,
{
betterScopedSlots
:
true
}
)
assertCodegen
(
'
<my-component><template v-slot="item">{{getValue(item.text)}}<template></my-component>
'
,
'
<my-component vue-id="551070e6-1" bind:__l="__l" vue-slots="{{[
\'
default
\'
]}}"><block><block wx:if="{{$root.m0}}">{{$root.m1}}</block></block></my-component>
'
,
'
with(this){var m0=$hasScopedSlotsParams("551070e6-1");var m1=m0?getValue($getScopedSlotsParams("551070e6-1","default").text):null;$mp.data=Object.assign({},{$root:{m0:m0,m1:m1}})}
'
,
{
betterScopedSlots
:
true
}
)
assertCodegen
(
'
<view><slot :item="item"><slot></view>
'
,
'
<view><block wx:if="{{$slots.default}}"><slot></slot></block><block wx:else><slot></slot></block></view>
'
,
'
with(this){$setScopedSlotsParams("default",{"item":item})}
'
,
{
betterScopedSlots
:
true
}
)
assertCodegen
(
'
<view><slot v-bind="object"><slot></view>
'
,
'
<view><block wx:if="{{$slots.default}}"><slot></slot></block><block wx:else><slot></slot></block></view>
'
,
'
with(this){$setScopedSlotsParams("default",object)}
'
,
{
betterScopedSlots
:
true
}
)
})
it
(
'
generate scoped slot
'
,
()
=>
{
it
(
'
generate scoped slot
'
,
()
=>
{
assertCodegen
(
assertCodegen
(
'
<slot v-bind:user="user"></slot>
'
,
'
<slot v-bind:user="user"></slot>
'
,
...
@@ -158,4 +193,4 @@ describe('mp:compiler-mp-weixin', () => {
...
@@ -158,4 +193,4 @@ describe('mp:compiler-mp-weixin', () => {
'
<test data-custom-hidden="{{!(shown)}}" vue-id="551070e6-1" bind:__l="__l" vue-slots="{{[
\'
default
\'
]}}">hello world</test>
'
'
<test data-custom-hidden="{{!(shown)}}" vue-id="551070e6-1" bind:__l="__l" vue-slots="{{[
\'
default
\'
]}}">hello world</test>
'
)
)
})
})
})
})
packages/uni-template-compiler/lib/constants.js
浏览文件 @
82c5a914
...
@@ -106,6 +106,9 @@ module.exports = {
...
@@ -106,6 +106,9 @@ module.exports = {
METHOD_TO_STRING
,
METHOD_TO_STRING
,
METHOD_RENDER_LIST
,
METHOD_RENDER_LIST
,
METHOD_RESOLVE_FILTER
,
METHOD_RESOLVE_FILTER
,
METHOD_RENDER_SLOT
,
METHOD_CREATE_EMPTY_VNODE
,
METHOD_RESOLVE_SCOPED_SLOTS
,
PREFIX_GLOBAL
:
'
g
'
,
PREFIX_GLOBAL
:
'
g
'
,
PREFIX_ATTR
:
'
a
'
,
PREFIX_ATTR
:
'
a
'
,
PREFIX_METHOD
:
'
m
'
,
PREFIX_METHOD
:
'
m
'
,
...
...
packages/uni-template-compiler/lib/script/traverse/index.js
浏览文件 @
82c5a914
...
@@ -90,6 +90,7 @@ module.exports = function traverse (ast, state) {
...
@@ -90,6 +90,7 @@ module.exports = function traverse (ast, state) {
const
blockStatementBody
=
[]
const
blockStatementBody
=
[]
const
objectPropertyArray
=
[]
const
objectPropertyArray
=
[]
const
initExpressionStatementArray
=
[]
const
initExpressionStatementArray
=
[]
const
renderSlotStatementArray
=
[]
// TODO 待重构,至少 filter,method 等实现方式要调整
// TODO 待重构,至少 filter,method 等实现方式要调整
babelTraverse
(
ast
,
visitor
,
undefined
,
{
babelTraverse
(
ast
,
visitor
,
undefined
,
{
scoped
:
[],
scoped
:
[],
...
@@ -100,7 +101,8 @@ module.exports = function traverse (ast, state) {
...
@@ -100,7 +101,8 @@ module.exports = function traverse (ast, state) {
identifierArray
:
identifierArray
,
identifierArray
:
identifierArray
,
propertyArray
:
objectPropertyArray
,
propertyArray
:
objectPropertyArray
,
declarationArray
:
blockStatementBody
,
declarationArray
:
blockStatementBody
,
initExpressionStatementArray
:
initExpressionStatementArray
initExpressionStatementArray
:
initExpressionStatementArray
,
renderSlotStatementArray
})
})
if
(
initExpressionStatementArray
.
length
)
{
if
(
initExpressionStatementArray
.
length
)
{
...
@@ -111,10 +113,14 @@ module.exports = function traverse (ast, state) {
...
@@ -111,10 +113,14 @@ module.exports = function traverse (ast, state) {
blockStatementBody
.
push
(
getDataExpressionStatement
(
objectPropertyArray
))
blockStatementBody
.
push
(
getDataExpressionStatement
(
objectPropertyArray
))
}
}
if
(
renderSlotStatementArray
.
length
)
{
blockStatementBody
.
push
(...
renderSlotStatementArray
)
}
reIdentifier
(
identifierArray
)
reIdentifier
(
identifierArray
)
return
t
.
withStatement
(
return
t
.
withStatement
(
t
.
thisExpression
(),
t
.
thisExpression
(),
t
.
blockStatement
(
blockStatementBody
)
t
.
blockStatement
(
blockStatementBody
)
)
)
}
}
packages/uni-template-compiler/lib/script/traverse/render-slot.js
0 → 100644
浏览文件 @
82c5a914
const
t
=
require
(
'
@babel/types
'
)
module
.
exports
=
function
getRenderSlot
(
path
,
state
)
{
const
name
=
path
.
get
(
'
arguments.0
'
)
const
arg2
=
path
.
get
(
'
arguments.2
'
)
const
arg3
=
path
.
get
(
'
arguments.3
'
)
let
valueNode
if
(
arg3
)
{
// v-bind:object
valueNode
=
arg3
.
node
}
else
if
(
arg2
&&
!
arg2
.
isNullLiteral
())
{
if
(
arg2
.
isObjectExpression
())
{
const
propertiesPath
=
arg2
.
get
(
'
properties
'
)
const
oldProperties
=
[]
const
newProperties
=
[]
propertiesPath
.
forEach
(
path
=>
{
const
properties
=
path
.
get
(
'
key
'
).
isStringLiteral
({
value
:
'
SLOT_DEFAULT
'
})
?
oldProperties
:
newProperties
properties
.
push
(
path
.
node
)
})
if
(
!
newProperties
.
length
)
{
return
}
valueNode
=
t
.
objectExpression
(
newProperties
)
arg2
.
replaceWith
(
t
.
objectExpression
(
oldProperties
))
}
else
{
valueNode
=
arg2
.
node
}
}
if
(
valueNode
)
{
state
.
renderSlotStatementArray
.
push
(
t
.
expressionStatement
(
t
.
callExpression
(
t
.
identifier
(
'
$setScopedSlotsParams
'
),
[
t
.
stringLiteral
(
name
.
node
.
value
),
valueNode
])))
}
// TODO 组件嵌套
path
.
skip
()
}
packages/uni-template-compiler/lib/script/traverse/resolve-scoped-slots.js
0 → 100644
浏览文件 @
82c5a914
const
t
=
require
(
'
@babel/types
'
)
const
{
METHOD_CREATE_EMPTY_VNODE
}
=
require
(
'
../../constants
'
)
function
replaceId
(
path
,
ids
)
{
let
replaced
path
.
traverse
({
noScope
:
true
,
Identifier
(
path
)
{
const
name
=
path
.
node
.
name
if
(
name
in
ids
&&
path
.
key
!==
'
key
'
&&
(
path
.
key
!==
'
property
'
||
path
.
parent
.
computed
))
{
path
.
replaceWith
(
ids
[
name
])
replaced
=
true
}
}
})
return
replaced
}
module
.
exports
=
function
getResolveScopedSlots
(
parent
,
state
)
{
const
properties
=
parent
.
get
(
'
arguments.0.elements.0.properties
'
)
const
fn
=
properties
.
find
(
path
=>
path
.
get
(
'
key
'
).
isIdentifier
({
name
:
'
fn
'
}))
const
params
=
fn
.
get
(
'
value.params.0
'
)
if
(
!
params
)
{
return
}
const
vueId
=
parent
.
parentPath
.
parentPath
.
get
(
'
properties
'
).
find
(
path
=>
path
.
get
(
'
key
'
).
isIdentifier
({
name
:
'
attrs
'
})).
get
(
'
value
'
).
get
(
'
properties
'
).
find
(
path
=>
path
.
get
(
'
key
'
).
isStringLiteral
({
value
:
'
vue-id
'
})).
get
(
'
value
'
).
node
.
value
const
slot
=
properties
.
find
(
path
=>
path
.
get
(
'
key
'
).
isIdentifier
({
name
:
'
key
'
})).
get
(
'
value
'
).
node
.
value
const
ids
=
{}
function
updateIds
(
vueId
,
slot
,
value
,
key
)
{
const
array
=
[
t
.
stringLiteral
(
vueId
),
t
.
stringLiteral
(
slot
)]
if
(
key
)
{
array
.
push
(
t
.
stringLiteral
(
key
))
}
ids
[
value
]
=
t
.
callExpression
(
t
.
identifier
(
'
$getScopedSlotsParams
'
),
array
)
}
if
(
params
.
isObjectPattern
())
{
params
.
get
(
'
properties
'
).
forEach
(
prop
=>
{
updateIds
(
vueId
,
slot
,
prop
.
get
(
'
value
'
).
node
.
name
,
prop
.
get
(
'
key
'
).
node
.
name
)
})
}
else
if
(
params
.
isIdentifier
())
{
updateIds
(
vueId
,
slot
,
params
.
node
.
name
)
}
const
fnBody
=
fn
.
get
(
'
value.body
'
)
if
(
replaceId
(
fnBody
,
ids
))
{
const
orgin
=
fnBody
.
get
(
'
body.0.argument
'
)
const
elements
=
orgin
.
get
(
'
elements
'
)
const
node
=
(
elements
.
length
===
1
?
elements
[
0
]
:
orgin
).
node
const
test
=
t
.
callExpression
(
t
.
identifier
(
'
$hasScopedSlotsParams
'
),
[
t
.
stringLiteral
(
vueId
)])
orgin
.
replaceWith
(
t
.
arrayExpression
([
t
.
conditionalExpression
(
test
,
node
,
t
.
callExpression
(
t
.
identifier
(
METHOD_CREATE_EMPTY_VNODE
),
[]))]))
}
}
packages/uni-template-compiler/lib/script/traverse/visitor.js
浏览文件 @
82c5a914
...
@@ -6,6 +6,8 @@ const {
...
@@ -6,6 +6,8 @@ const {
METHOD_RENDER_LIST
,
METHOD_RENDER_LIST
,
METHOD_BUILT_IN
,
METHOD_BUILT_IN
,
METHOD_RESOLVE_FILTER
,
METHOD_RESOLVE_FILTER
,
METHOD_RENDER_SLOT
,
METHOD_RESOLVE_SCOPED_SLOTS
,
IDENTIFIER_FILTER
,
IDENTIFIER_FILTER
,
IDENTIFIER_METHOD
,
IDENTIFIER_METHOD
,
IDENTIFIER_GLOBAL
IDENTIFIER_GLOBAL
...
@@ -26,6 +28,8 @@ const traverseData = require('./data')
...
@@ -26,6 +28,8 @@ const traverseData = require('./data')
const
traverseRenderList
=
require
(
'
./render-list
'
)
const
traverseRenderList
=
require
(
'
./render-list
'
)
const
getMemberExpr
=
require
(
'
./member-expr
'
)
const
getMemberExpr
=
require
(
'
./member-expr
'
)
const
getRenderSlot
=
require
(
'
./render-slot
'
)
const
getResolveScopedSlots
=
require
(
'
./resolve-scoped-slots
'
)
function
addStaticClass
(
path
,
staticClass
)
{
function
addStaticClass
(
path
,
staticClass
)
{
const
dataPath
=
path
.
get
(
'
arguments.1
'
)
const
dataPath
=
path
.
get
(
'
arguments.1
'
)
...
@@ -217,6 +221,12 @@ module.exports = {
...
@@ -217,6 +221,12 @@ module.exports = {
this
this
)
)
)
)
}
else
if
(
this
.
options
.
betterScopedSlots
)
{
if
(
methodName
===
METHOD_RESOLVE_SCOPED_SLOTS
)
{
getResolveScopedSlots
(
path
,
this
)
}
else
if
(
methodName
===
METHOD_RENDER_SLOT
)
{
getRenderSlot
(
path
,
this
)
}
}
}
break
break
}
}
...
...
packages/uni-template-compiler/lib/template/traverse.js
浏览文件 @
82c5a914
...
@@ -304,7 +304,7 @@ function traverseRenderSlot (callExprNode, state) {
...
@@ -304,7 +304,7 @@ function traverseRenderSlot (callExprNode, state) {
const
slotName
=
callExprNode
.
arguments
[
0
].
value
const
slotName
=
callExprNode
.
arguments
[
0
].
value
let
deleteSlotName
=
false
// 标记是否组件 slot 手动指定了 name="default"
let
deleteSlotName
=
false
// 标记是否组件 slot 手动指定了 name="default"
if
(
callExprNode
.
arguments
.
length
>
2
)
{
// 作用域插槽
if
(
!
state
.
options
.
betterScopedSlots
&&
callExprNode
.
arguments
.
length
>
2
)
{
// 作用域插槽
const
props
=
{}
const
props
=
{}
callExprNode
.
arguments
[
2
].
properties
.
forEach
(
property
=>
{
callExprNode
.
arguments
[
2
].
properties
.
forEach
(
property
=>
{
props
[
property
.
key
.
value
]
=
genCode
(
property
.
value
)
props
[
property
.
key
.
value
]
=
genCode
(
property
.
value
)
...
@@ -369,7 +369,7 @@ function traverseResolveScopedSlots (callExprNode, state) {
...
@@ -369,7 +369,7 @@ function traverseResolveScopedSlots (callExprNode, state) {
})
})
const
slotName
=
keyProperty
.
value
.
value
const
slotName
=
keyProperty
.
value
.
value
const
returnExprNodes
=
fnProperty
.
value
.
body
.
body
[
0
].
argument
const
returnExprNodes
=
fnProperty
.
value
.
body
.
body
[
0
].
argument
if
(
!
proxyProperty
)
{
if
(
!
state
.
options
.
betterScopedSlots
&&
!
proxyProperty
)
{
const
resourcePath
=
state
.
options
.
resourcePath
const
resourcePath
=
state
.
options
.
resourcePath
const
ownerName
=
path
.
basename
(
resourcePath
,
path
.
extname
(
resourcePath
))
const
ownerName
=
path
.
basename
(
resourcePath
,
path
.
extname
(
resourcePath
))
...
@@ -510,4 +510,4 @@ function traverseCreateTextVNode (callExprNode, state) {
...
@@ -510,4 +510,4 @@ function traverseCreateTextVNode (callExprNode, state) {
function
traverseCreateEmptyVNode
(
callExprNode
,
state
)
{
function
traverseCreateEmptyVNode
(
callExprNode
,
state
)
{
return
''
return
''
}
}
packages/vue-cli-plugin-uni/lib/env.js
浏览文件 @
82c5a914
...
@@ -258,6 +258,10 @@ if (platformOptions.usingComponents === true) {
...
@@ -258,6 +258,10 @@ if (platformOptions.usingComponents === true) {
}
}
}
}
if
(
platformOptions
.
betterScopedSlots
)
{
process
.
env
.
BETTER_SCOPED_SLOTS
=
true
}
if
(
if
(
process
.
env
.
UNI_USING_COMPONENTS
||
process
.
env
.
UNI_USING_COMPONENTS
||
process
.
env
.
UNI_PLATFORM
===
'
h5
'
process
.
env
.
UNI_PLATFORM
===
'
h5
'
...
...
packages/webpack-uni-mp-loader/lib/template.js
浏览文件 @
82c5a914
...
@@ -65,7 +65,8 @@ module.exports = function (content, map) {
...
@@ -65,7 +65,8 @@ module.exports = function (content, map) {
const
filterModules
=
parseFilterModules
(
params
&&
params
[
'
filter-modules
'
])
const
filterModules
=
parseFilterModules
(
params
&&
params
[
'
filter-modules
'
])
Object
.
assign
(
vueLoaderOptions
.
options
.
compilerOptions
,
{
Object
.
assign
(
vueLoaderOptions
.
options
.
compilerOptions
,
{
mp
:
{
mp
:
{
platform
:
process
.
env
.
UNI_PLATFORM
platform
:
process
.
env
.
UNI_PLATFORM
,
betterScopedSlots
:
process
.
env
.
BETTER_SCOPED_SLOTS
},
},
filterModules
,
filterModules
,
filterTagName
,
filterTagName
,
...
@@ -84,4 +85,4 @@ module.exports = function (content, map) {
...
@@ -84,4 +85,4 @@ module.exports = function (content, map) {
throw
new
Error
(
'
vue-loader-options parse error
'
)
throw
new
Error
(
'
vue-loader-options parse error
'
)
}
}
this
.
callback
(
null
,
content
,
map
)
this
.
callback
(
null
,
content
,
map
)
}
}
src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js
浏览文件 @
82c5a914
...
@@ -41,11 +41,61 @@ function initEventChannel () {
...
@@ -41,11 +41,61 @@ function initEventChannel () {
}
}
}
}
function
initScopedSlotsParams
()
{
const
center
=
{}
const
parents
=
{}
Vue
.
prototype
.
$hasScopedSlotsParams
=
function
(
vueId
)
{
const
has
=
center
[
vueId
]
if
(
!
has
)
{
parents
[
vueId
]
=
this
this
.
$on
(
'
hook:destory
'
,
()
=>
{
delete
parents
[
vueId
]
})
}
return
has
}
Vue
.
prototype
.
$getScopedSlotsParams
=
function
(
vueId
,
name
,
key
)
{
const
data
=
center
[
vueId
]
if
(
data
)
{
const
object
=
data
[
name
]
||
{}
return
key
?
object
[
key
]
:
object
}
else
{
parents
[
vueId
]
=
this
this
.
$on
(
'
hook:destory
'
,
()
=>
{
delete
parents
[
vueId
]
})
}
}
Vue
.
prototype
.
$setScopedSlotsParams
=
function
(
name
,
value
)
{
const
vueId
=
this
.
$options
.
propsData
.
vueId
const
object
=
center
[
vueId
]
=
center
[
vueId
]
||
{}
object
[
name
]
=
value
if
(
parents
[
vueId
])
{
parents
[
vueId
].
$forceUpdate
()
}
}
Vue
.
mixin
({
destroyed
()
{
const
propsData
=
this
.
$options
.
propsData
const
vueId
=
propsData
&&
propsData
.
vueId
if
(
vueId
)
{
delete
center
[
vueId
]
delete
parents
[
vueId
]
}
}
})
}
export
default
function
parseBaseApp
(
vm
,
{
export
default
function
parseBaseApp
(
vm
,
{
mocks
,
mocks
,
initRefs
initRefs
})
{
})
{
initEventChannel
()
initEventChannel
()
initScopedSlotsParams
()
if
(
vm
.
$options
.
store
)
{
if
(
vm
.
$options
.
store
)
{
Vue
.
prototype
.
$store
=
vm
.
$options
.
store
Vue
.
prototype
.
$store
=
vm
.
$options
.
store
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录