\n")
595 | }
596 |
597 | return s
598 | }
599 |
600 | func nicePrintProps(p Props) string {
601 | var s strings.Builder
602 | s.WriteString("[")
603 | for _, i := range p {
604 | s.WriteString(fmt.Sprintf("%+v, ", i))
605 | }
606 | s.WriteString("]")
607 |
608 | return s.String()
609 | }
610 |
--------------------------------------------------------------------------------
/statement.go:
--------------------------------------------------------------------------------
1 | package vpl
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/robertkrimen/otto/ast"
8 | "github.com/zbysir/vpl/internal/lib/log"
9 | "github.com/zbysir/vpl/internal/parser"
10 | "github.com/zbysir/vpl/internal/util"
11 | "strings"
12 | "sync"
13 | )
14 |
15 | // 执行每一个块的上下文
16 | type Scope struct {
17 | Parent *Scope
18 | Value map[string]interface{}
19 | }
20 |
21 | func (s *Scope) Get(k string) interface{} {
22 | return s.GetDeep(k)
23 | }
24 |
25 | func NewScope(outer *Scope) *Scope {
26 | return &Scope{
27 | Parent: outer,
28 | Value: nil,
29 | }
30 | }
31 |
32 | // 获取作用域中的变量
33 | // 会向上查找
34 | func (s *Scope) GetDeep(k ...string) (v interface{}) {
35 | var rootExist bool
36 | var ok bool
37 |
38 | curr := s
39 | for curr != nil {
40 | v, rootExist, ok = ShouldLookInterface(curr.Value, k...)
41 | // 如果root存在, 则说明就应该读取当前作用域, 否则向上层作用域查找
42 | if rootExist {
43 | if !ok {
44 | return nil
45 | } else {
46 | return
47 | }
48 | }
49 |
50 | curr = curr.Parent
51 | }
52 |
53 | return
54 | }
55 |
56 | func (s *Scope) Extend(data map[string]interface{}) *Scope {
57 | return &Scope{
58 | Parent: s,
59 | Value: data,
60 | }
61 | }
62 |
63 | // 设置暂时只支持在当前作用域设置变量
64 | // 避免对上层变量造成副作用
65 | func (s *Scope) Set(k string, v interface{}) {
66 | if s.Value == nil {
67 | s.Value = map[string]interface{}{}
68 | }
69 | (s.Value)[k] = v
70 | }
71 |
72 | // 在渲染中的上下文, 用在function和directive
73 | type RenderCtx struct {
74 | Scope *Scope // 当前作用域, 用于向当前作用域声明一个值
75 | Store Store // 用于共享数据, 此Store是RenderParam中传递的Store
76 | }
77 |
78 | type Directive func(ctx *RenderCtx, nodeData *NodeData, binding *DirectivesBinding)
79 |
80 | type DirectivesBinding struct {
81 | Value interface{}
82 | Arg string
83 | Name string
84 | }
85 |
86 | // 编译之后的Prop
87 | // 将js表达式解析成AST, 加速运行
88 | type propC struct {
89 | CanBeAttr bool
90 | Key string
91 | Val expression
92 | IsStatic bool
93 | ValStatic string // 如果Prop是静态的, 那么会在编译时优化为字符串
94 | }
95 |
96 | type propsC []*propC
97 |
98 | // for nicePrint
99 | func (r propsC) String() string {
100 | str := "["
101 | for _, v := range r {
102 | beAttr := ""
103 | if v.CanBeAttr {
104 | beAttr = "(attr)"
105 | }
106 | var val interface{} = v.Val
107 |
108 | if v.IsStatic {
109 | val = v.ValStatic
110 | }
111 |
112 | str += fmt.Sprintf("%+v%v: %+v, ", v.Key, beAttr, val)
113 | }
114 | str = strings.TrimSuffix(str, ", ")
115 | str += "]"
116 | return str
117 | }
118 |
119 | func (r propsC) execTo(ctx *RenderCtx, ps *Props) {
120 | if len(r) == 0 {
121 | return
122 | }
123 |
124 | for _, p := range r {
125 | c := CanNotBeAttr
126 | if p.CanBeAttr {
127 | c = CanBeAttr
128 | }
129 |
130 | ps.append(&PropKeys{
131 | AttrWay: c,
132 | Key: p.Key,
133 | }, p.exec(ctx).Val)
134 | }
135 | return
136 | }
137 |
138 | func (r *propC) exec(ctx *RenderCtx) *Prop {
139 | if r == nil {
140 | return &Prop{}
141 | }
142 | if r.IsStatic {
143 | return &Prop{Key: r.Key, Val: r.ValStatic}
144 | } else {
145 | return &Prop{Key: r.Key, Val: r.Val.Exec(ctx)}
146 | }
147 | }
148 |
149 | // 数值Prop
150 | // 执行PropC会得到PropR
151 | type Prop struct {
152 | Key string
153 | Val interface{}
154 | }
155 |
156 | //type PropKey struct {
157 | // AttrWay AttrWay // 能否被当成Attr输出
158 | // Key string
159 | //}
160 |
161 | type AttrWay uint8
162 |
163 | const (
164 | MayBeAttr AttrWay = 0 // 无法在编译时确定, 还需要在运行时判断
165 | CanBeAttr AttrWay = 1 // 在编译时就确定能够当做attr
166 | CanNotBeAttr AttrWay = 2 // 在编译时就确定不能够当做attr
167 | )
168 |
169 | type PropKeys struct {
170 | AttrWay AttrWay // 能否被当成Attr输出
171 | Key string
172 | Last *PropKeys // 如果LinkKey只有一个元素, 则last是自己
173 | Next *PropKeys
174 | }
175 |
176 | func (l *PropKeys) Append(a *PropKeys) {
177 | if l.Key == "" {
178 | l.Key = a.Key
179 | l.Last = l
180 | l.Next = nil
181 | l.AttrWay = a.AttrWay
182 | return
183 | }
184 |
185 | if l.Last == nil {
186 | l.Last = l
187 | }
188 | l.Last.Next = a
189 | l.Last = a
190 | }
191 |
192 | type Props struct {
193 | keys *PropKeys // 在生成attr时会用到顺序
194 | data map[string]interface{} // 存储map有利于快速取值
195 | }
196 |
197 | func NewProps() *Props {
198 | return &Props{
199 | keys: nil,
200 | data: nil,
201 | }
202 | }
203 |
204 | func (r *Props) ForEach(cb func(index int, k *PropKeys, v interface{})) {
205 | if r == nil {
206 | return
207 | }
208 | i := 0
209 | h := r.keys
210 | for h != nil {
211 | cb(i, h, r.data[h.Key])
212 | h = h.Next
213 | i++
214 | }
215 | return
216 | }
217 |
218 | func (r *Props) ToMap() map[string]interface{} {
219 | if r == nil {
220 | return nil
221 | }
222 | return r.data
223 | }
224 |
225 | func (r *Props) append(k *PropKeys, v interface{}) {
226 | if r.data == nil {
227 | r.data = map[string]interface{}{}
228 | }
229 | if r.keys == nil {
230 | r.keys = &PropKeys{}
231 | }
232 |
233 | // 合并class/style
234 | ve, exist := r.data[k.Key]
235 | if !exist {
236 | r.keys.Append(k)
237 | } else {
238 | switch k.Key {
239 | case "class":
240 | v = margeClass(ve, v)
241 | case "style":
242 | v = margeStyle(ve, v)
243 | }
244 | }
245 |
246 | r.data[k.Key] = v
247 | }
248 |
249 | // AppendAttr 向当前tag添加属性
250 | func (r *Props) AppendAttr(k, v string) {
251 | r.append(&PropKeys{
252 | AttrWay: CanBeAttr,
253 | Key: k,
254 | }, v)
255 | }
256 |
257 | func (r *Props) Append(k string, v interface{}) {
258 | r.append(&PropKeys{
259 | AttrWay: MayBeAttr,
260 | Key: k,
261 | }, v)
262 | }
263 |
264 | func (r *Props) AppendClass(c ...string) {
265 | cla := make([]interface{}, len(c))
266 | for i, v := range c {
267 | cla[i] = v
268 | }
269 | r.append(&PropKeys{
270 | AttrWay: CanBeAttr,
271 | Key: "class",
272 | }, cla)
273 | }
274 |
275 | func (r *Props) AppendStyle(st map[string]string) {
276 | stm := make(map[string]interface{}, len(st))
277 | for k, v := range st {
278 | stm[k] = v
279 | }
280 | r.append(&PropKeys{
281 | AttrWay: CanBeAttr,
282 | Key: "style",
283 | }, stm)
284 | }
285 |
286 | func margeClass(a interface{}, b interface{}) (d interface{}) {
287 | ar := []interface{}{a, b}
288 | return ar
289 | }
290 |
291 | func margeStyle(a interface{}, b interface{}) (d interface{}) {
292 | var ar map[string]interface{}
293 | if at, ok := a.(map[string]interface{}); ok {
294 | ar = at
295 | } else {
296 | ar = map[string]interface{}{}
297 | }
298 |
299 | if bt, ok := b.(map[string]interface{}); ok {
300 | for k, v := range bt {
301 | ar[k] = v
302 | }
303 | }
304 |
305 | return ar
306 | }
307 |
308 | // 无序添加多个props
309 | func (r *Props) AppendMap(mp map[string]interface{}) {
310 | keys := util.GetSortedKey(mp)
311 |
312 | for _, k := range keys {
313 | v := mp[k]
314 | r.append(&PropKeys{
315 | AttrWay: MayBeAttr,
316 | Key: k,
317 | }, v)
318 | }
319 | }
320 |
321 | // 有序添加多个props
322 | func (r *Props) appendProps(ps *Props) {
323 | if ps == nil {
324 | return
325 | }
326 |
327 | ps.ForEach(func(index int, k *PropKeys, v interface{}) {
328 | r.append(k, v)
329 | })
330 | }
331 |
332 | func (r *Props) Get(key string) (interface{}, bool) {
333 | v, exist := r.data[key]
334 | return v, exist
335 | }
336 |
337 | // 如果 style和class动态与静态不冲突 ,并且沒有指令, 则可以将静态style/class优化为 string
338 | func compileProps(p parser.Props, staticProp bool) (propsC, error) {
339 | pc := make(propsC, len(p))
340 | hasBindStyle := false
341 | hasBindClass := false
342 | for _, v := range p {
343 | if !v.IsStatic && v.Key == "class" {
344 | hasBindClass = true
345 | }
346 | if !v.IsStatic && v.Key == "style" {
347 | hasBindStyle = true
348 | }
349 | }
350 | for i, v := range p {
351 | static := staticProp
352 | if staticProp {
353 | if v.Key == "class" && v.IsStatic && hasBindClass {
354 | static = false
355 | }
356 | if v.Key == "style" && v.IsStatic && hasBindStyle {
357 | static = false
358 | }
359 | }
360 |
361 | p, err := compileProp(v, static)
362 | if err != nil {
363 | return nil, err
364 | }
365 | pc[i] = p
366 | }
367 | return pc, nil
368 | }
369 |
370 | func compileProp(p *parser.Prop, staticProp bool) (*propC, error) {
371 | if p == nil {
372 | return nil, nil
373 | }
374 |
375 | pc := &propC{
376 | Key: p.Key,
377 | CanBeAttr: p.CanBeAttr,
378 | }
379 | if p.IsStatic {
380 | // 如果是静态的, 并且需要优化为字符串, 则修改为字符串
381 | if staticProp {
382 | switch p.Key {
383 | case "style":
384 | pc.ValStatic = getStyleFromProps(p.StaticVal).ToAttr()
385 | case "class":
386 | pc.ValStatic = getClassFromProps(p.StaticVal).ToAttr()
387 | default:
388 | pc.ValStatic = p.StaticVal.(string)
389 | }
390 | pc.IsStatic = true
391 | } else {
392 | pc.Val = newRawExpression(p.StaticVal)
393 | }
394 | } else {
395 | if p.ValCode != "" {
396 | node, err := compileJS(p.ValCode)
397 | if err != nil {
398 | return nil, fmt.Errorf("parseJs err: %w", err)
399 | }
400 | pc.Val = &jsExpression{node: node, code: p.ValCode}
401 | } else {
402 | pc.Val = &nullExpression{}
403 | }
404 | }
405 |
406 | return pc, nil
407 | }
408 |
409 | func compileVBind(v *parser.VBind) (*vBindC, error) {
410 | if v == nil {
411 | return nil, nil
412 | }
413 |
414 | if v.Val == "" {
415 | return nil, nil
416 | }
417 |
418 | node, err := compileJS(v.Val)
419 | if err != nil {
420 | return nil, fmt.Errorf("parseJs err: %w", err)
421 | }
422 |
423 | return &vBindC{val: &jsExpression{node: node, code: v.Val}}, nil
424 | }
425 |
426 | func compileDirective(ds parser.Directives) (directivesC, error) {
427 | if len(ds) == 0 {
428 | return nil, nil
429 | }
430 |
431 | pc := make(directivesC, len(ds))
432 | for i, v := range ds {
433 | node, err := compileJS(v.Value)
434 | if err != nil {
435 | return nil, fmt.Errorf("parseJs err: %w", err)
436 | }
437 | pc[i] = directiveC{
438 | Name: v.Name,
439 | Value: &jsExpression{node: node, code: v.Value},
440 | Arg: v.Arg,
441 | }
442 | }
443 | return pc, nil
444 | }
445 |
446 | // 作用在tag的所有属性
447 | type tagStruct struct {
448 | // Props: 无论动态还是静态, 都是Props(包括class与style, 这是为了实现v-bind='$props'语法).
449 | // 静态的attr也处理成Props是为了保持顺序, 当然也是为了减少概念
450 | //
451 | // 如:
452 | // 其中 Props 值为: id=string, data-id=string, style=map[string]interface
453 | //
454 | // 另外tag上的 Props 会根据CanBeAttrKey设置被转为html attr.
455 | Props propsC
456 | VBind *vBindC
457 |
458 | Directives directivesC
459 | Slots *SlotsC
460 | }
461 |
462 | // 编译时的指令
463 | type directiveC struct {
464 | Name string // v-animate
465 | Value expression // {'a': 1}
466 | Arg string // v-set:arg
467 | }
468 |
469 | type directivesC []directiveC
470 |
471 | // 组件的属性
472 | type ComponentStruct = tagStruct
473 |
474 |
475 | // VBind 语法, 一次传递多个prop
476 | // v-bind='{id: id, 'other-attr': otherAttr}'
477 | // 有一个特殊用法:
478 | // v-bind='$props': 将父组件所有的 props(不包括class和style) 一起传给子组件
479 | type vBindC struct {
480 | useProps bool
481 | val expression
482 | }
483 |
484 | func (v *vBindC) execTo(ctx *RenderCtx, ps *Props) {
485 | if v == nil {
486 | return
487 | }
488 | var b interface{}
489 | if v.useProps {
490 | b = ctx.Scope.Get("$props")
491 | } else {
492 | b = v.val.Exec(ctx)
493 | }
494 | switch t := b.(type) {
495 | case map[string]interface{}:
496 | ps.AppendMap(t)
497 | case skipMarshalMap:
498 | ps.AppendMap(t)
499 | case *Props:
500 | ps.appendProps(t)
501 | default:
502 | panic(fmt.Sprintf("bad Type of Vbind: %T", b))
503 | }
504 | }
505 |
506 | func (v *vBindC) exec(ctx *RenderCtx) interface{} {
507 | if v == nil {
508 | return nil
509 | }
510 | if v.useProps {
511 | return ctx.Scope.Get("$props")
512 | } else {
513 | return v.val.Exec(ctx)
514 | }
515 |
516 | }
517 |
518 | // 表达式, 所有js表达式都会被预编译成为expression
519 | type expression interface {
520 | // 根据scope计算表达式值
521 | Exec(ctx *RenderCtx) interface{}
522 | }
523 |
524 | // 原始值
525 | type rawExpression struct {
526 | raw interface{}
527 | }
528 |
529 | func (r *rawExpression) String() string {
530 | return fmt.Sprintf("%v", r.raw)
531 | }
532 |
533 | func (r *rawExpression) Exec(*RenderCtx) interface{} {
534 | return r.raw
535 | }
536 |
537 | func newRawExpression(raw interface{}) *rawExpression {
538 | return &rawExpression{raw: raw}
539 | }
540 |
541 | type jsExpression struct {
542 | node ast.Node
543 | code string
544 | }
545 |
546 | func (r *jsExpression) Exec(ctx *RenderCtx) interface{} {
547 | v, err := runJsExpression(r.node, ctx)
548 | if err != nil {
549 | log.Warningf("runJsExpression err:%v", err)
550 | return err
551 | }
552 |
553 | return v
554 | }
555 |
556 | func (r *jsExpression) String() string {
557 | return r.code
558 | }
559 |
560 | type nullExpression struct {
561 | }
562 |
563 | func (r *nullExpression) Exec(*RenderCtx) interface{} {
564 | return nil
565 | }
566 |
567 | // vue语法会被编译成一组Statement
568 | // 为了避免多次运行造成副作用, 所有的 运行时代码 都不应该修改 编译时
569 | type Statement interface {
570 | Exec(ctx *StatementCtx, o *StatementOptions) error
571 | }
572 |
573 | type FuncStatement func(*StatementCtx, *StatementOptions) error
574 |
575 | func (f FuncStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
576 | return f(ctx, o)
577 | }
578 |
579 | type Writer interface {
580 | // 如果需要实现异步计算, 则需要将span存储, 在最后统一计算出string.
581 | WriteSpan(Span)
582 | // 如果是同步计算, 使用WriteString会将string结果直接存储或者拼接
583 | WriteString(string)
584 | Result() string
585 | }
586 |
587 | type Span interface {
588 | Result() string
589 | }
590 |
591 | // 静态字符串块
592 | type StrStatement struct {
593 | Str string
594 | }
595 |
596 | func (s *StrStatement) Exec(ctx *StatementCtx, _ *StatementOptions) error {
597 | ctx.W.WriteString(s.Str)
598 | return nil
599 | }
600 |
601 | type EmptyStatement struct {
602 | }
603 |
604 | func (s *EmptyStatement) Exec(_ *StatementCtx, _ *StatementOptions) error {
605 | return nil
606 | }
607 |
608 | // tag块
609 | type tagStatement struct {
610 | tag string
611 | tagStruct tagStruct
612 | }
613 |
614 | // 执行map格式的props(来至v-bind语法)
615 | func execBindProps(t map[string]interface{}, ctx *StatementCtx, attrKeys *[]string, attr *map[string]string, class *strings.Builder, style *map[string]interface{}) {
616 | keys := util.GetSortedKey(t)
617 |
618 | for _, k := range keys {
619 | v := t[k]
620 | if k == "class" {
621 | writeClass(v, class)
622 | if _, exist := (*attr)["class"]; !exist {
623 | *attrKeys = append(*attrKeys, "class")
624 | (*attr)["class"] = ""
625 | }
626 | } else if k == "style" {
627 | switch t := v.(type) {
628 | case map[string]interface{}:
629 | if *style == nil {
630 | *style = t
631 | } else {
632 | for k, v := range t {
633 | (*style)[k] = v
634 | }
635 | }
636 | }
637 | if _, exist := (*attr)["style"]; !exist {
638 | *attrKeys = append(*attrKeys, "style")
639 | (*attr)["style"] = ""
640 | }
641 | } else {
642 | if ctx.CanBeAttrsKey(k) {
643 | if _, exist := (*attr)[k]; !exist {
644 | *attrKeys = append(*attrKeys, k)
645 | }
646 |
647 | switch v := v.(type) {
648 | case string:
649 | (*attr)[k] = v
650 | default:
651 | (*attr)[k] = util.InterfaceToStr(v, true)
652 | }
653 | }
654 | }
655 | }
656 | }
657 |
658 | // 如果tag没有指令, 则也不需要生成props, 而是将propsC直接运行成为attr.
659 | func (t *tagStruct) ExecAttr(ctx *StatementCtx, rCtx *RenderCtx) error {
660 | var style map[string]interface{}
661 | var class strings.Builder
662 |
663 | // 使用attr来解决attr会合并的问题.
664 | // 如组件外传递的attr会覆盖与根组件相同的attr
665 | attrKeys := make([]string, 0, len(t.Props))
666 | attr := make(map[string]string, len(t.Props))
667 |
668 | if len(t.Props) != 0 {
669 | for _, p := range t.Props {
670 | if p.Key == "class" {
671 | // 如果class是静态的, 则不会发生合并的情况, 直接写入到write.
672 | if p.IsStatic {
673 | ctx.W.WriteString(` class="`)
674 | ctx.W.WriteString(p.ValStatic)
675 | ctx.W.WriteString(`"`)
676 | } else {
677 | writeClass(p.Val.Exec(rCtx), &class)
678 | if _, exist := attr["class"]; !exist {
679 | attrKeys = append(attrKeys, "class")
680 | attr["class"] = ""
681 | }
682 | }
683 | } else if p.Key == "style" {
684 | if p.IsStatic {
685 | ctx.W.WriteString(` style="`)
686 | ctx.W.WriteString(p.ValStatic)
687 | ctx.W.WriteString(`"`)
688 | } else {
689 | switch t := p.Val.Exec(rCtx).(type) {
690 | case map[string]interface{}:
691 | if style == nil {
692 | style = t
693 | } else {
694 | for k, v := range t {
695 | style[k] = v
696 | }
697 | }
698 | }
699 | if _, exist := attr["style"]; !exist {
700 | attrKeys = append(attrKeys, "style")
701 | attr["style"] = ""
702 | }
703 | }
704 | } else {
705 | if p.IsStatic {
706 | if p.ValStatic != "" {
707 | if _, exist := attr[p.Key]; !exist {
708 | attrKeys = append(attrKeys, p.Key)
709 | }
710 | attr[p.Key] = p.ValStatic
711 | }
712 | } else {
713 | v := p.Val.Exec(rCtx)
714 | if _, exist := attr[p.Key]; !exist {
715 | attrKeys = append(attrKeys, p.Key)
716 | }
717 |
718 | switch v := v.(type) {
719 | case string:
720 | attr[p.Key] = v
721 | default:
722 | attr[p.Key] = util.InterfaceToStr(v, true)
723 | }
724 | }
725 | }
726 | }
727 | }
728 |
729 | // 可能需要将 props和vBind中重复的attr去重
730 | if t.VBind != nil {
731 | var b = t.VBind.exec(rCtx)
732 | switch t := b.(type) {
733 | case nil:
734 | case map[string]interface{}:
735 | execBindProps(t, ctx, &attrKeys, &attr, &class, &style)
736 | case skipMarshalMap:
737 | execBindProps(t, ctx, &attrKeys, &attr, &class, &style)
738 | case *Props:
739 | execBindProps(t.ToMap(), ctx, &attrKeys, &attr, &class, &style)
740 | default:
741 | panic(fmt.Sprintf("bad Type of Vbind: %T", b))
742 | }
743 | }
744 |
745 | // 保证attr排序和写的一致
746 | // style和class会出现合并的情况, 所以在运行完所有props和vBind之后处理.
747 | for i := range attrKeys {
748 | switch attrKeys[i] {
749 | case "style":
750 | ctx.W.WriteString(` style="`)
751 | sortedKeys := util.GetSortedKey(style)
752 | var st strings.Builder
753 | for _, k := range sortedKeys {
754 | v := style[k]
755 | if st.Len() != 0 {
756 | st.WriteByte(' ')
757 | }
758 |
759 | st.WriteString(k)
760 | st.WriteString(": ")
761 | switch v := v.(type) {
762 | case string:
763 | st.WriteString(util.EscapeStyle(v))
764 | default:
765 | bs, _ := json.Marshal(v)
766 | st.WriteString(util.Escape(string(bs)))
767 | }
768 | st.WriteByte(';')
769 | }
770 | ctx.W.WriteString(st.String())
771 | ctx.W.WriteString(`"`)
772 | case "class":
773 | ctx.W.WriteString(` class="`)
774 | ctx.W.WriteString(class.String())
775 | ctx.W.WriteString(`"`)
776 | default:
777 | ctx.W.WriteString(` `)
778 | ctx.W.WriteString(attrKeys[i])
779 | v := attr[attrKeys[i]]
780 | if v != "" {
781 | ctx.W.WriteString(`="`)
782 | ctx.W.WriteString(v)
783 | ctx.W.WriteString(`"`)
784 | }
785 | }
786 | }
787 |
788 | return nil
789 | }
790 |
791 | func (t *tagStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
792 | rCtx := ctxPool.Get().(*RenderCtx)
793 | rCtx.Store = ctx.Store
794 | rCtx.Scope = o.Scope
795 | defer ctxPool.Put(rCtx)
796 |
797 | ctx.W.WriteString("<" + t.tag)
798 |
799 | // 如果没有指令, 则优化props执行流程
800 | // - 只执行CanBeAttr的props
801 | // - 优化class与style
802 | // - 直接写入Writer, 减少props声明
803 |
804 | // 如果没有指令, 则不需要闭包slot作用域
805 | var slots *Slots
806 |
807 | if len(t.tagStruct.Directives) != 0 {
808 | // 处理attr
809 | // 计算Props
810 | var props *Props
811 | if len(t.tagStruct.Props) != 0 || t.tagStruct.VBind != nil {
812 | props = NewProps()
813 |
814 | if len(t.tagStruct.Props) != 0 {
815 | t.tagStruct.Props.execTo(rCtx, props)
816 | }
817 |
818 | // v-bind="{id: 1}" 语法, 将计算出整个PropsR
819 | if t.tagStruct.VBind != nil {
820 | t.tagStruct.VBind.execTo(rCtx, props)
821 | }
822 | }
823 |
824 | // 只有指令有修改slots的需求, 如果没有指令, 则不需要闭包slot作用域
825 | slots = t.tagStruct.Slots.WrapScope(o)
826 |
827 | // 执行指令
828 | // 指令可以修改scope/props/style/class/children
829 | data := &NodeData{
830 | Props: props,
831 | Slots: slots,
832 | }
833 | execDirectives(t.tagStruct.Directives, ctx, o.Scope, data)
834 | props = data.Props
835 | slots = data.Slots
836 |
837 | if props != nil {
838 | props.ForEach(func(index int, k *PropKeys, v interface{}) {
839 | // 如果在编译期就确定了不能被转为attr, 则始终不能
840 | // 如果无法在编译期间确定(如 通过props.AppendMap()的方式添加的props/通过v-bind="$props"方式而来的props), 则还需要再次调用函数判断
841 | if k.AttrWay == CanNotBeAttr {
842 | return
843 | }
844 | if k.AttrWay == MayBeAttr {
845 | // style和class字段始终会作为attr
846 | if k.Key != "style" && k.Key != "class" {
847 | if !ctx.CanBeAttrsKey(k.Key) {
848 | return
849 | }
850 | }
851 | }
852 |
853 | if k.Key == "style" {
854 | if v != nil {
855 | ctx.W.WriteString(` style="`)
856 | var s strings.Builder
857 | writeStyle(v, &s)
858 | ctx.W.WriteString(s.String())
859 | ctx.W.WriteString(`"`)
860 | }
861 | } else if k.Key == "class" {
862 | if v != nil {
863 | ctx.W.WriteString(` class="`)
864 | var s strings.Builder
865 | writeClass(v, &s)
866 | ctx.W.WriteString(s.String())
867 | ctx.W.WriteString(`"`)
868 | }
869 | } else {
870 | ctx.W.WriteString(" ")
871 | ctx.W.WriteString(k.Key)
872 |
873 | if v != nil {
874 | ctx.W.WriteString(`="`)
875 |
876 | switch v := v.(type) {
877 | case string:
878 | ctx.W.WriteString(v)
879 | default:
880 | ctx.W.WriteString(util.InterfaceToStr(v, true))
881 | }
882 | ctx.W.WriteString(`"`)
883 | }
884 | }
885 |
886 | })
887 | }
888 |
889 | } else {
890 | err := t.tagStruct.ExecAttr(ctx, rCtx)
891 | if err != nil {
892 | return err
893 | }
894 | }
895 |
896 | ctx.W.WriteString(">")
897 |
898 | // 如果没有指令, 则不需要闭包slot作用域
899 | if slots != nil {
900 | // 子节点
901 | children := slots.Default
902 | if children != nil {
903 | err := children.Exec(ctx, nil)
904 | if err != nil {
905 | return err
906 | }
907 | }
908 | } else if t.tagStruct.Slots != nil {
909 | // 直接执行children, 而不当做slots
910 | children := t.tagStruct.Slots.Default
911 | if children != nil && children.Children != nil {
912 | err := children.Children.Exec(ctx, o)
913 | if err != nil {
914 | return err
915 | }
916 | }
917 | }
918 |
919 | ctx.W.WriteString("" + t.tag + ">")
920 | return nil
921 | }
922 |
923 | func execDirectives(ds directivesC, ctx *StatementCtx, scope *Scope, o *NodeData) {
924 | rCtx := ctxPool.Get().(*RenderCtx)
925 | rCtx.Store = ctx.Store
926 | rCtx.Scope = scope
927 | defer ctxPool.Put(rCtx)
928 |
929 | for _, v := range ds {
930 | val := v.Value.Exec(rCtx)
931 | d, exist := ctx.Directives[v.Name]
932 | if exist {
933 | d(
934 | rCtx,
935 | o,
936 | &DirectivesBinding{
937 | Value: val,
938 | Arg: v.Arg,
939 | Name: v.Name,
940 | },
941 | )
942 | }
943 |
944 | }
945 | }
946 |
947 | type Class = parser.Class
948 | type Styles = parser.Styles
949 |
950 | type NodeData struct {
951 | Props *Props // 给组件添加attr
952 | Slots *Slots
953 | }
954 |
955 | // 支持的格式: map[string]interface{}
956 | func getStyleFromProps(styleProps interface{}) Styles {
957 | if styleProps == nil {
958 | return Styles{}
959 | }
960 | st := Styles{}
961 | switch t := styleProps.(type) {
962 | case map[string]interface{}:
963 | for k, v := range t {
964 | switch v := v.(type) {
965 | case string:
966 | st.Add(k, util.EscapeStyle(v))
967 | default:
968 | bs, _ := json.Marshal(v)
969 | st.Add(k, util.EscapeStyle(string(bs)))
970 | }
971 | }
972 | }
973 |
974 | return st
975 | }
976 |
977 | // 支持的格式: map[string]interface{}
978 | func writeStyle(styleProps interface{}, w *strings.Builder) {
979 | if styleProps == nil {
980 | return
981 | }
982 |
983 | switch t := styleProps.(type) {
984 | case map[string]interface{}:
985 | sortedKeys := util.GetSortedKey(t)
986 | for _, k := range sortedKeys {
987 | v := t[k]
988 | if w.Len() != 0 {
989 | w.WriteByte(' ')
990 | }
991 | w.WriteString(k + ": ")
992 |
993 | switch v := v.(type) {
994 | case string:
995 | w.WriteString(util.Escape(v))
996 | default:
997 | bs, _ := json.Marshal(v)
998 | w.WriteString(util.Escape(string(bs)))
999 | }
1000 |
1001 | w.WriteString(";")
1002 | }
1003 | }
1004 |
1005 | return
1006 | }
1007 |
1008 | type ifStatement struct {
1009 | conditionCode string
1010 | condition expression
1011 | ChildStatement Statement
1012 | ElseIf []*elseStatement
1013 | }
1014 |
1015 | type elseStatement struct {
1016 | conditionCode string
1017 | // 如果condition为空 则说明是else节点, 否则是elseif节点
1018 | condition expression
1019 | ChildStatement Statement
1020 | }
1021 |
1022 | func (i *ifStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
1023 | rCtx := ctxPool.Get().(*RenderCtx)
1024 | rCtx.Store = ctx.Store
1025 | rCtx.Scope = o.Scope
1026 | defer ctxPool.Put(rCtx)
1027 | r := i.condition.Exec(rCtx)
1028 | if util.InterfaceToBool(r) {
1029 | err := i.ChildStatement.Exec(ctx, o)
1030 | if err != nil {
1031 | return err
1032 | }
1033 | } else {
1034 | // 如果if没有判断成功, 则循环执行elseIf
1035 | for _, ef := range i.ElseIf {
1036 | // 如果condition为空 则说明是else节点
1037 | if ef.condition == nil {
1038 | err := ef.ChildStatement.Exec(ctx, o)
1039 | if err != nil {
1040 | return err
1041 | }
1042 | break
1043 | }
1044 | if util.InterfaceToBool(ef.condition.Exec(rCtx)) {
1045 | err := ef.ChildStatement.Exec(ctx, o)
1046 | if err != nil {
1047 | return err
1048 | }
1049 | break
1050 | }
1051 | }
1052 | }
1053 |
1054 | return nil
1055 | }
1056 |
1057 | type forStatement struct {
1058 | ArrayKey string
1059 | Array expression
1060 | ItemKey string
1061 | IndexKey string
1062 | ChildChunks Statement
1063 | }
1064 |
1065 | func (f forStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
1066 | rCtx := ctxPool.Get().(*RenderCtx)
1067 | rCtx.Store = ctx.Store
1068 | rCtx.Scope = o.Scope
1069 | array := f.Array.Exec(rCtx)
1070 | ctxPool.Put(rCtx)
1071 |
1072 | return util.ForInterface(array, func(index int, v interface{}) error {
1073 | scope := o.Scope.Extend(map[string]interface{}{
1074 | f.IndexKey: index,
1075 | f.ItemKey: v,
1076 | })
1077 |
1078 | err := f.ChildChunks.Exec(ctx, &StatementOptions{
1079 | Scope: scope,
1080 | })
1081 | if err != nil {
1082 | return err
1083 | }
1084 |
1085 | return nil
1086 | })
1087 | }
1088 |
1089 | type groupStatement struct {
1090 | s []Statement
1091 | strBuffer strings.Builder
1092 | }
1093 |
1094 | // 调用GroupStatement.Append之后还必须调用Finish才能保证GroupStatement中的数据是正确的
1095 | func (g *groupStatement) Finish() Statement {
1096 | if g.strBuffer.Len() != 0 {
1097 | g.s = append(g.s, &StrStatement{Str: g.strBuffer.String()})
1098 | g.strBuffer.Reset()
1099 | }
1100 |
1101 | if len(g.s) == 0 {
1102 | return &EmptyStatement{}
1103 | }
1104 |
1105 | if len(g.s) == 1 {
1106 | return g.s[0]
1107 | }
1108 | return g
1109 | }
1110 |
1111 | func (g *groupStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
1112 | for i := range g.s {
1113 | err := g.s[i].Exec(ctx, o)
1114 | if err != nil {
1115 | return err
1116 | }
1117 | }
1118 |
1119 | return nil
1120 | }
1121 |
1122 | // Append 拼接一个新的语句到组里, 如果有连续的字符串语句 则会合并成为一个字符串语句.
1123 | func (g *groupStatement) Append(st Statement) {
1124 | if st == nil {
1125 | return
1126 | }
1127 | switch appT := st.(type) {
1128 | case *EmptyStatement:
1129 | return
1130 | case *StrStatement:
1131 | g.strBuffer.WriteString(appT.Str)
1132 | case *groupStatement:
1133 | for _, v := range appT.s {
1134 | g.Append(v)
1135 | }
1136 | default:
1137 | if g.strBuffer.Len() != 0 {
1138 | g.s = append(g.s, &StrStatement{Str: g.strBuffer.String()})
1139 | g.strBuffer.Reset()
1140 | }
1141 | g.s = append(g.s, st)
1142 | }
1143 | }
1144 |
1145 | // 调用组件
1146 | type ComponentStatement struct {
1147 | ComponentKey string
1148 | ComponentStruct ComponentStruct
1149 | }
1150 |
1151 | // 调用组件语句
1152 | // o是上层options.
1153 | // 根据组件attr拼接出新的scope, 再执行组件
1154 | // 处理slot作用域
1155 | func (c *ComponentStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
1156 | rCtx := ctxPool.Get().(*RenderCtx)
1157 | rCtx.Store = ctx.Store
1158 | rCtx.Scope = o.Scope
1159 | defer ctxPool.Put(rCtx)
1160 |
1161 | // 计算Props
1162 | var props *Props
1163 | if c.ComponentStruct.VBind != nil || c.ComponentStruct.Props != nil {
1164 | props = NewProps()
1165 | // v-bind="{id: 1}" 语法, 将计算出整个PropsR
1166 | if c.ComponentStruct.VBind != nil {
1167 | c.ComponentStruct.VBind.execTo(rCtx, props)
1168 | }
1169 |
1170 | // 如果还传递了其他props, 则覆盖
1171 | if c.ComponentStruct.Props != nil {
1172 | c.ComponentStruct.Props.execTo(rCtx, props)
1173 | }
1174 | }
1175 |
1176 | // 处理slot作用域
1177 | slots := c.ComponentStruct.Slots.WrapScope(o)
1178 |
1179 | cp, exist := ctx.Components[c.ComponentKey]
1180 | // 没有找到组件时直接渲染自身的子组件
1181 | if !exist {
1182 | ctx.W.WriteString(fmt.Sprintf(`<%s data-err="not found component"`, c.ComponentKey))
1183 | c.ComponentStruct.ExecAttr(ctx, rCtx)
1184 | ctx.W.WriteString(`>`)
1185 |
1186 | if slots != nil {
1187 | child := slots.Default
1188 | if child != nil {
1189 | err := child.Exec(ctx, nil)
1190 | if err != nil {
1191 | return nil
1192 | }
1193 | }
1194 | }
1195 |
1196 | ctx.W.WriteString(fmt.Sprintf("%s>", c.ComponentKey))
1197 | return nil
1198 | }
1199 |
1200 | // 执行指令
1201 | // 指令可以修改scope/props/style/class/children
1202 | if len(c.ComponentStruct.Directives) != 0 {
1203 | data := &NodeData{
1204 | Props: props,
1205 | Slots: slots,
1206 | }
1207 | execDirectives(c.ComponentStruct.Directives, ctx, o.Scope, data)
1208 | props = data.Props
1209 | slots = data.Slots
1210 | }
1211 |
1212 | //json.Marshal()
1213 |
1214 | // 运行组件应该重新使用新的scope
1215 | // 和vue不同的是, props只有在子组件中申明才能在子组件中使用, 而vtpl不同, 它将所有props放置到变量域中.
1216 | scope := ctx.NewScope()
1217 | if props != nil {
1218 | propsMap := props.ToMap()
1219 | scope = scope.Extend(propsMap)
1220 | // 使用skipMarshalMap解决循环引用时Marshal报错的问题
1221 | scope.Set("$props", skipMarshalMap(propsMap))
1222 | }
1223 |
1224 | return cp.Exec(ctx, &StatementOptions{
1225 | // 此组件在声明时拥有的所有slots
1226 | Slots: slots,
1227 | // 此组件上的props
1228 | // 用于:
1229 | // - root组件使用props转为attr,
1230 | // - slot组件收集所有的props实现作用域插槽(https://cn.vuejs.org/v2/guide/components-slots.html#%E4%BD%9C%E7%94%A8%E5%9F%9F%E6%8F%92%E6%A7%BD)
1231 | //
1232 | Props: props,
1233 |
1234 | // 此组件和其下的子组件所能访问到的所有变量(包括了当前组件的props)
1235 | Scope: scope,
1236 | // 父级作用域
1237 | // 只有组件有父级作用域, 用来执行slot
1238 | Parent: o,
1239 | })
1240 | }
1241 |
1242 | // 声明Slot的语句(编译时)
1243 | //
1244 | type SlotC struct {
1245 | Name string
1246 | propsKey string
1247 | Children Statement
1248 | }
1249 |
1250 | // Slot的运行时
1251 | type Slot struct {
1252 | *SlotC
1253 |
1254 | // 在运行时被赋值
1255 | // Declarer 存储在当slot声明时的组件数据
1256 | Declarer *StatementOptions
1257 | }
1258 |
1259 | type ExecSlotOptions struct {
1260 | // 语法传递的slot props.
1261 | SlotProps *Props
1262 | }
1263 |
1264 | func (s *Slot) Exec(ctx *StatementCtx, o *StatementOptions) error {
1265 | if s.Children == nil {
1266 | return nil
1267 | }
1268 |
1269 | var no *StatementOptions
1270 | // 将申明时的scope和传递的slot-props合并
1271 | if s.Declarer != nil {
1272 | no = &StatementOptions{}
1273 | no.Slots = s.Declarer.Slots
1274 |
1275 | scope := s.Declarer.Scope
1276 | if o != nil && o.Props != nil && s.propsKey != "" {
1277 | scope = scope.Extend(map[string]interface{}{
1278 | s.propsKey: o.Props.ToMap(),
1279 | })
1280 | }
1281 |
1282 | no.Scope = scope
1283 | }
1284 | return s.Children.Exec(ctx, no)
1285 | }
1286 |
1287 | // 胡子语法: {{a}}
1288 | // 会进行html转义
1289 | type mustacheStatement struct {
1290 | exp expression
1291 | }
1292 |
1293 | var ctxPool sync.Pool
1294 |
1295 | func init() {
1296 | ctxPool = sync.Pool{New: func() interface{} {
1297 | return &RenderCtx{}
1298 | }}
1299 | }
1300 |
1301 | func (i *mustacheStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
1302 | rCtx := ctxPool.Get().(*RenderCtx)
1303 | rCtx.Store = ctx.Store
1304 | rCtx.Scope = o.Scope
1305 | defer ctxPool.Put(rCtx)
1306 |
1307 | //rCtx := &RenderCtx{
1308 | // Store: ctx.Store, Scope: o.Scope,
1309 | //}
1310 |
1311 | r := i.exp.Exec(rCtx)
1312 |
1313 | ctx.W.WriteString(util.InterfaceToStr(r, true))
1314 | return nil
1315 | }
1316 |
1317 | // 不会转义的html语句
1318 | // 用于v-html
1319 | type rawHtmlStatement struct {
1320 | exp expression
1321 | }
1322 |
1323 | func (i *rawHtmlStatement) Exec(ctx *StatementCtx, o *StatementOptions) error {
1324 | rCtx := ctxPool.Get().(*RenderCtx)
1325 | rCtx.Store = ctx.Store
1326 | rCtx.Scope = o.Scope
1327 | defer ctxPool.Put(rCtx)
1328 |
1329 | r := i.exp.Exec(rCtx)
1330 |
1331 | ctx.W.WriteString(util.InterfaceToStr(r, false))
1332 | return nil
1333 | }
1334 |
1335 | // https://cn.vuejs.org/v2/guide/components-slots.html
1336 | // SlotsC 存放传递给组件的所有Slot(默认与具名)(编译时), vue语法:
1337 | type SlotsC struct {
1338 | Default *SlotC
1339 | NamedSlot map[string]*SlotC
1340 | }
1341 |
1342 | func (s *SlotsC) marge(x *SlotsC) {
1343 | if x == nil {
1344 | return
1345 | }
1346 |
1347 | if x.Default != nil {
1348 | s.Default = x.Default
1349 | }
1350 |
1351 | if x.NamedSlot != nil {
1352 | if s.NamedSlot == nil {
1353 | s.NamedSlot = make(map[string]*SlotC, len(x.NamedSlot))
1354 | }
1355 | for k, xs := range x.NamedSlot {
1356 | s.NamedSlot[k] = xs
1357 | }
1358 | }
1359 | }
1360 |
1361 | // WrapScope 设置在slot声明时的scope, 用于在运行slot时使用声明slot时的scope
1362 | func (s *SlotsC) WrapScope(o *StatementOptions) (sr *Slots) {
1363 | if s == nil {
1364 | return nil
1365 | }
1366 | if s.Default != nil {
1367 | sr = &Slots{
1368 | Default: &Slot{
1369 | SlotC: s.Default,
1370 | Declarer: o,
1371 | },
1372 | }
1373 | }
1374 | if s.NamedSlot != nil {
1375 | if sr == nil {
1376 | sr = &Slots{}
1377 | }
1378 |
1379 | sr.NamedSlot = make(map[string]*Slot, len(s.NamedSlot))
1380 | for k, v := range s.NamedSlot {
1381 | sr.NamedSlot[k] = &Slot{
1382 | SlotC: v,
1383 | Declarer: o,
1384 | }
1385 | }
1386 | }
1387 |
1388 | return
1389 | }
1390 |
1391 | type Slots struct {
1392 | Default *Slot // 大多数都是Default插槽,放入map里会有性能损耗,所以优化为单独一个字段
1393 | NamedSlot map[string]*Slot
1394 | }
1395 |
1396 | func (s *Slots) Get(key string) *Slot {
1397 | if s == nil {
1398 | return nil
1399 | }
1400 | if key == "default" {
1401 | return s.Default
1402 | }
1403 | return s.NamedSlot[key]
1404 | }
1405 |
1406 | func ParseHtmlToStatement(tpl string, options *parser.ParseVueNodeOptions) (Statement, *SlotsC, error) {
1407 | nt, err := parser.ParseHtml(tpl)
1408 | if err != nil {
1409 | return nil, nil, err
1410 | }
1411 | vn, err := parser.ToVueNode(nt, options)
1412 | if err != nil {
1413 | return nil, nil, fmt.Errorf("parseToVue err: %w", err)
1414 | }
1415 | statement, slots, err := toStatement(vn)
1416 | if err != nil {
1417 | return nil, nil, err
1418 | }
1419 | return statement, slots, nil
1420 | }
1421 |
1422 | // 执行语句(组件/Tag)所需的参数
1423 | type StatementOptions struct {
1424 | Slots *Slots
1425 |
1426 | // 渲染组件时, 组件上的props
1427 | // 如
1428 | //