Giter Site home page Giter Site logo

baidu / san Goto Github PK

View Code? Open in Web Editor NEW
4.7K 135.0 546.0 9.68 MB

A fast, portable, flexible JavaScript component framework

Home Page: https://baidu.github.io/san/

License: MIT License

HTML 1.35% CSS 0.72% JavaScript 92.40% Smarty 3.48% TypeScript 2.06%
mvvm javascript frontend framework component san

san's People

Contributors

100pah avatar buptlhuanyu avatar cxtom avatar dafrok avatar dependabot[bot] avatar echodis avatar erik168 avatar errorrik avatar harttle avatar houyu01 avatar jiangjiu avatar jinzhan avatar jinzhubaofu avatar junmer avatar kanglover avatar kekee000 avatar leeight avatar leuisken avatar ludafa avatar meixg avatar mzh1994s avatar otakustay avatar plan-do-break-fix avatar qianliu013 avatar snailsword avatar ssddi456 avatar sw811 avatar wangshuonpu avatar x-jray avatar xtx1130 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

san's Issues

[question] 只有空白字符仍被认作有效内容被填充到 default slot

考虑以下两种情况:

<san-panel></san-panel>
<san-panel>
</san-panel>

目前 san 认识第一种情况无 default slot 内容,而第二种有填充,填充内容是一个空白字符\n;

这会影响到这种情况:

<template>
  <div>
    <slot>{{tilte}}</slot>
    <slot name="icon" />
  </div>
</template>

此时使用者换不换行就会造成两种不同的结果:

<sm-panel title="test"><sm-icon slot="icon>more</sm-icon></sm-panel>

有 title 输出

<sm-panel title="test">
  <sm-icon slot="icon>more</sm-icon>
</sm-panel>

无 title 输出,因为有两个 \n 填充给了 default slot

解析到 text-node 可以理解,但是这对使用容易造成困惑。是否有更好的解决方案?

.san 组件的 scoped css 不生效

问题

.san 组件的 scoped css 不生效

原因

san-loader 生成的 scoped id 的格式是 _s-[HASH],san 在处理 attribute 的时候会忽略下划线,于是属性变成 s-[HASH],以 s- 开头的 attribute 会被当做指令来处理,最后被 parser 干掉。

解决方案

我暂时想到以下两个解决方案,具体使用哪一个可能需要推敲一下:

1. 修改 san-loader 生成自定义属性的规则

将生成的属性 _s-[HASH] 改为 data-san-[HASH] 或其它合法属性名。
我在本地暂时用的是这个解决方案,改动和副作用较小,但会占用一个合法的属性名,留下命名冲突的隐患。如果选用这个方案,我可以把本地的改动直接发 pr 给 san-loader。

2. 修改 san 的属性名匹配规则

这个方案需要同时修改 san-loader 和 san 的源码,改动量较大。san-loader 生成一个特殊的属性名,如_s_scoped_[HASH]",san 再去优先匹配 \_s_scoped.+\ 规则,符合则当做正常 html attribute 进行处理,好处是在用户按照规范声明 prop 或 html attribute 时不存在撞名的隐患。

组件存在父子关系时 created 生命周期不符合预期

环境

  • 库版本 3.0.3
  • 浏览器版本:Chrome 58
  • 系统版本:macOS Sierra 10.12.4

描述

当组件存在父子关系时,父组件的 created 钩子函数被执行了两遍,且子组件的 created 钩子函数获取实例的 el 属性为 undefined,父子组件在相同生命周期中的表现不一致。

案例

在线演示

https://codepen.io/Dafrok/pen/oWEZYE?editors=1010

业务逻辑

创建有父子关系的两个组件并挂载,分别在 create 周期时在控制台输出当前实例的 el 属性。

预期输出

parent el: <div id=​"_san_1">​…​</div>​
children el: <div id=​"_san_2">​…​</div>​

实际输出

parent el: <div id=​"_san_1">​…​</div>​
children el: undefined
parent el: <div id=​"_san_1">​…​</div>​

动态多级表达式的绑定未生效

    it("dynamic expr long path", function (done) {

        var wrap = document.createElement('div');
        document.body.appendChild(wrap);

        var MyComponent = san.defineComponent({
            template: ''
                + '<div>'
                +   '<div san-for="item, index in config">'
                +       '<div>{{item.name}}</div>'
                +       '<div san-for="child, ci in item.children">'
                +           '{{index}} - {{ci}} : <input value="{=result[child.name]=}"/>'
                +       '</div>'
                +   '</div>'
                + '</div>',

            initData: function() {
                return {
                    result: {},
                    config: [
                        {
                            name: 'erik',
                            children: [
                                {
                                    name: 'erik-child-0'
                                },
                                {
                                    name: 'erik-child-1'
                                }
                            ]
                        }
                    ]
                }
            },

            attached: function() {
                var input = wrap.getElementsByTagName('input')[0];
                triggerEvent('#' + input.id, 'input', '666');
                setTimeout(this.doneSpec.bind(this), 600);
            },

            doneSpec: function() {

                var result = this.data.get('result');
                expect(result.erik).toBe('666');

                this.dispose();
                document.body.removeChild(wrap);
                done();
            },
        });

        var myComponent = new MyComponent();
        myComponent.attach(wrap);

    });

input双绑时的中文输入法兼容问题

先上测试代码:

var MyApp = san.defineComponent({
    template: ''
        + '<div>'
        +   '<input value="{= name =}" placeholder="please input">'
        +   'Hello {{name}}!'
        + '</div>'
});
var myApp = new MyApp();
myApp.attach(document.body);

3.0.0-rc.3在双绑text类型的input元素时,输入中文会出问题:

image

翻了下代码,应该是因为直接绑定了input事件,但没处理compositionstartcompositionend的状态导致的。

Data变更之后,视图没有更新

  var Issue = san.defineComponent({
    template: '<template><div><slot/></div></template>'
  });

  var Foo = san.defineComponent({
    components: {
      'x-issue': Issue
    },
    template: ''
    + '<template>'
      + '<div san-for="item, index in items">'
        + '<div class="row">'
          + '<label>{{item.label}}</label>'
          + '<x-issue>'
            + '<button on-click="addText(index)">Add</button>'
            + '<table border="1px"><tr san-for="o in item.datasource"><td>{{o.label}}</td></tr></table>'
          + '</x-issue>' // end of x-issue
        + '</div>'   // end of row
      + '</div>'
    + '</template>',

    addText(index) {
      this.data.push('items[' + index + '].datasource', {label: Date.now()});
    },

    initData() {
      return {
        items: [
          {
            label: 'A',
            datasource: []
          }
        ]
      }
    }
  });

  var foo = new Foo();
  foo.attach(document.body);

如果把 <x-issue> 换成 <div> 就没有问题了,所以有点儿奇怪。

在多维数组下使用 push 时,视图和模型没有同步。

环境

  • 库版本 3.0.3
  • 浏览器版本:Chrome 58
  • 系统版本:macOS Sierra 10.12.4

案例

在线展示

在线展示

逻辑说明

点击 'Add Item' 按钮时,向 cols[0].list push 了一个对象,然后在控制台里打印 cols

期望输出

一个 <li> 标签被插入视图,并且 cols 字段打印结果为 [{list: [{title: 'title'}]}]

实际输出

一个 <li> 标签被插入视图,cols 字段打印结果为 [{list: []}]

原因

推测可能是 this.data.get 获取模型的引用有偏差。

一个组件模板顶层声明另一个组件无法正常渲染

比如在todo-amd的基础上,声明一个App组件:

define(function (require) {
    var san = require('san');

    return san.defineComponent({
        template: '<ui-list></ui-list>',

        components: {
            'ui-list': require('./List')
        }
    });
});

这个组件仅渲染一个List组件,是无法使用的,如果声明为<div><ui-list></ui-list></div>就能使用

我觉得这是一个问题,主要原因是:

  1. 对于使用者来说,DOM Element和Component应该尽可能保持概念上的一致性
  2. 有些组件的作用就是细化另一个组件,比如MoneyInput可能就是<ui-text-box type="number" postfix="$">,这个组件的唯一作用就是指定一堆属性的固定值,作为组件系统中模板的复用方案很常见
  3. 有些组件会使用一个容器组件作为自己的顶层,如AuthFail可能是<ui-dialog on-ok="backToLogin" on-cancel="backToLogin">您登录超时,请重新登录</ui-dialog>,这些组件是基于容器对业务的封装,使得模板看上去能更简洁地表达业务,也具备更好的复用性

如果在这些场景下,都要求再多套一层DOM Element会造成开发的不便和最终DOM结构的复杂,特别对于使用Dialog这样的场景,多套一层可能整个定位、拖动都会被破坏

因此我们在基于aNode渲染的时候,对根节点最好也采取和子节点一样的策略,随着Element和Component的不同进行不同的渲染(其实把Element和Component归一化后就不会有这问题了),然后利用递归不断创建子元素会好些

貌似 <br> 会重复输出?

var HelloWorld = Component.defineComponent({ 
    template: '<div>{{name}}<br></div>',
    initData: function() {
        return {
            name: 'hello world'
        };
    }
});

var foo = new HelloWorld();
foo.attach(document.body);

image

表单元素checkbox 上数据双绑失效和radio元素条件性无法默认选中态判定

如题:
1, checkbox表单元素点击后只有html层面的选中/取消选中效果, 双绑的数据值无更新(但默认选中态可判定)
2, radio 元素如果在传递的直接data值中数据类型为number(data: 1), 然后直接转换为string类型(data: '1'), 无法判定默认选中态. 但如果双绑的值不为直接的data值, 比如为转换过的 新设置的newData: "1", 则可以判定默认选中态

san单文件组件样式目前不支持less等预处理器解析

san 单文件组件目前不支持 less 解析,单文件样式代码如下:

<style lang="less">
    @brand-color: blue;
    .hello {
        color: @brand-color;
    }
</style>

webpack 2 配置如下:

    module: {
        rules: [
            {
                test: /\.san$/,
                use: 'san-loader'
            },
            {
                test: /\.js$/,
                use: 'babel-loader',
                include: [appPath]
            },
            {
                test: /\.less$/,
                use: 'css-loader!less-loader'
            },
            {
                test: /\.css$/,
                use: 'css-loader'
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                use: 'url-loader?limit=10000'
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                use: 'url-loader?limit=10000'
            }
        ]
    }

是配置有误,还是目前不支持呢?后续会提供支持吗?

子组件数据更新时粒度更精细

    san.defineComponent({
            components: {
                'ui-user': UserInfo
            },
            template: '<div><ui-user info="{{md.user}}"></ui-user></div>'
        });

data.set('md.user.name') 时,更新user组件内部数据项的info.name,不要灌入整个info

[feature request] 组件隔代递归

问题

定义 Grandpa Father Son 三个组件,假设每个组件都是上一个的父组件,此时如果在 Son 中注册 Grandpa 组件会失效。目前看来 san 组件似乎只有自递归的能力,但是如果一个需要递归的组件过大,逻辑过于复杂,为可维护性考虑就避免不了拆分问题,于是就产生了隔代递归的需求。

复现用例

在线演示

https://codepen.io/Dafrok/pen/qjmWdg?editors=1010

场景概述

这是一个简单的 JSON 可视化组件,注册了 Leaf Branch JsonNode 三个组件,每一个 JSON 的节点对应一个 JsonNode 组件实例。
JsonNode 中注册了 LeafBranch,两个子组件,如果当前属性是对象或数组,则渲染 Branch 组件,如果是其它值类型则渲染 Leaf 组件。
Branch 组件上会注册一个 JsonNode 组件用于渲染当前 JSON 节点的所有成员。
根据这个逻辑,JsonNodeBranch 再回到 JsonNode 形成了一个隔代递归,san 无法处理。

期望输出

// pug template
.item
  .leaf foo: 233
  .leaf bar: bar
  .branch baz:
    .item
      .leaf a: 1
      .leaf b: 2
  .branch qux:
    .item
      .leaf 0: 6
      .leaf 1: 6
      .leaf 2: 6

实际输出

// pug template
.item
  .leaf foo: 233
  .leaf bar: bar
  .item baz:
    json-node
  .item qux:
    json-node

一个在双向绑定动态属性时遇到的问题

环境

简述

由于在一个项目中使用了由 store 控制的无限嵌套组件,所以在操作 store 的时候只能在一个计算属性中使用递归的方式来寻找某一个层级的组件所对应 store 中的 key string。而这样的操作使表单元素的绑定产生了三个问题:

  1. 双向绑定时,如果被绑定的属性不存在,input 元素初始值为 'undefined'。
  2. 双向绑定时,如果更改 input 值,可以发现 store 已经正确更新,当前组件的视图也正确更新,而其它平级组件的视图却没有更新。
  3. 单向绑定时,在 on-input 回调中 dispatch action,组件被重绘,input 光标会跳至末尾,无法正常地插输入字符。

示例

https://codepen.io/Dafrok/pen/LyqQOo?editors=0110

问题

递归生成的 input 元素没有办法顺利地更新 store。虽然我也可以在单向绑定的情况下记录光标的位置,更新视图后重置光标,但是显然这么做是非常没有效率的。如果这些问题是 feature 所致,还望指教正确的使用姿势。

组件attached状态后查找san-for指令渲染的dom的问题

请看一下下面这种栗子的情况:

<template>
    <div class="container">
        <div class="items" san-for="item,index in list"></div>
    </div>
</template>

<script>
    // 假设引了jq
        
    export default {
        attached() {
            let doms = $(this.el).find('.items');
            // doms啥也没有
        }
    }

</script>

匿名 Boolean 类型变量在属性、指令、事件参数中被当做变量名

复现用例

Codepen

期望输出

  • Boolean Prop: true
  • Named Variable Prop: false
  • Number Prop: 666
true //  <- log(true)
false // <- log(false)

实际输出

  • Boolean Prop:
  • Named Variable Prop: It's a variable named 'false'
  • Number Prop: 666

You can never see me if san-if="true".
You can see me although san-if="false".

undefined <- log(true)
It's a variable named 'false' <- log(false)

补充说明

san-if 指令中使用匿名变量没有任何意义,这里只是用于列举一种情况。

源码

const Children = san.defineComponent({
  template: `<ul>
    <li>Boolean Prop: {{booleanProp}}</li>
    <li>Named Variable Prop: {{namedVariableProp}}</li>
    <li>Number Prop: {{numberProp}}</li>
  </ul>`
})

const Parent = san.defineComponent({
  components: {
    'child-comp': Children
  },
  template: `<div>
    <child-comp
      booleanProp="{{true}}"
      namedVariableProp="{{false}}"
      numberProp="{{666}}">
    </child-comp>
    <p san-if="!true">You can never see me if \`san-if="true"\`.</p>
    <p san-if="false">You can see me although \`san-if="false"\`.</p>
    <button on-click="log(true)">console.log(true)</button>
    <button on-click="log(false)">console.log(false)</button>
  </div>`,
  initData () {
    return {
      false: `It's a variable named 'false'`
    }
  },
  log (msg) {
    console.log(msg)
  }
})

const comp = new Parent()

comp.attach(document.body)

[Feature Request] 加入 beforeDetach 生命周期,并允许生命周期钩子中手动控制生命周期向下执行。

原因

在实现 san 的过渡动画 HOC 的过程中,进入阶段可以在 created 和 attached 生命周期中给实例的 el 添加 css hook 来实现淡入效果。但当实例的 san-if 指令变为 false 时,将会自动进行 detach 和 dispose,此时 el 已同步销毁,无法通过插入 css hook 的方法执行淡出动画。

理想 API 设计

此段代码仅作抛砖引玉,如果能有更合理的设计或直接将过度动画集成到 core 是缀吼的。

  • 增加 beforeDetach 生命周期,用于在 detach 之前进行一些*操作。
  • 增加 stopLifeCycle 实例方法,用于阻止当前 life cycle 向下执行。
  • 拟增加 nextLifeCycle 实例方法,手动控制 life cycle 向下执行。
class SanComponent extends Component {
  async beforeDetach () {
    this.stopLifeCyle()
    await doSomethingAsync()
    this.nextLifeCycle() // 或 this.detach() 
  }
}

可能引发的问题

引入了异步的生命周期,可能引发 model 层和视图层不同步的问题。如 san-if 值改变两次后,第一个生命周期还没有结束,导致生命周期混乱。

san-for和san-if 不能同时使用

var MyApp = san.defineComponent({
    template: '<select value="{=v=}">'+
              '  <option></option>'+
              '  <option  san-for="item in list" value="{{item}}" san-if="item==2">{{item}}</option>'+
              '</select>',
    initData: function () {
        var data ={
            list:[],
            v:20
        };
        for(var i=0;i<21;i++){
            data.list.push(i);
        }
        return  data;
    }
});
var myApp = new MyApp();
myApp.attach(document.body);

在vuejs里是可以这么使用的

script 标签做 stump 会影响 CSS selector,建议使用 comment tag 代替。

描述

san 在某些场景(通常是使用 s-if / s-for 指令时)下会在父节点的第一个节点或最后一个节点中插入一个 script 标签占位,使 CSS selector 误判。

复现用例

输入

Stylus

ul
  li
    color green
  li:first-child
    color blue
  li:last-child
    color red

JavaScript

const App = san.defineComponent({
  template: `<ul>
    <li s-for="item in list">{{item}}</li>
  </ul>`,
  initData () {
    return {
      list: [1, 2, 3]
    }
  }
})

const app = new App()
app.attach(document.body)

期望输出

<ul>
  <li>1</li><!-- 蓝色 -->
  <li>2</li><!-- 绿色 -->
  <li>3</li><!-- 红色 -->
</ul>

实际输出

<ul>
  <li>1</li><!-- 蓝色 -->
  <li>2</li><!-- 绿色 -->
  <li>3</li><!-- 绿色 -->
  <script type="text/san"></script>
</ul>

在线演示

https://codepen.io/Dafrok/pen/LLQexv

建议

使用特殊 comment tag 代替 script 标签做占位符。

用了 Slot 之后,Data 和 View 不同步

var BoxGroup = san.defineComponent({
  template: '<template><div class="box-group">'
    + '<div san-for="item in datasource">'
    + '<label><input type="checkbox" value="{{item.value}}" checked="{=value=}" /><span>{{item.text}}</span></label>'
    + '</div>'
    + '</div></template>',
  initData: function() {
    return {
      datasource: [],
      value: []
    };
  }
});

var Issue = san.defineComponent({
  template: '<div><slot/></div>'
});

var App = san.defineComponent({
  components: {
    'ui-boxgroup': BoxGroup,
    'x-issue': Issue
  },
  template: '<template>'
  + '<button on-click="onClick">Clear</button><hr/>'
  + '<x-issue san-for="p in groups">'
    + '<div>value: {{p.value}}</div>'
    + '<ui-boxgroup datasource="{{p.datasource}}" value="{=p.value=}" /><hr/>'
  + '</x-issue>'
  + '</template>',
  initData: function() {
     return {
       groups: [
         {
           datasource: [
             {text: 'foo', value: 'foo'},
             {text: 'bar', value: 'bar'}
           ],
           value: []
         },
         {
           datasource: [
             {text: 'abc', value: 'abc'},
             {text: '123', value: '123'}
           ],
           value: []
         }
       ]
     }
  },
  onClick: function() {
    this.data.set('groups[0].value', []);
    this.data.set('groups[1].value', []);
  }
});

var app = new App();
app.attach(document.body)

如果把 <x-issue 换成 <div 就一切正常了

[feature request] 错误的 template 提供更好的错误提示

目前如果 san template 编写错误,会得到很笼统的错误提示。

比如在 template 很深的位置漏写属性的双引号 <xxx style={{xxx}} />,san 给出的错误提示是:template 需要一个根元素。

这个错误提示很难帮忙开发者快速定位到问题所在的位置。

建议能够优化。

试用了下有几个问题(Data.set, san-ref, hook, binding)

  1. Data.set 只能设置一个数据,不太方便,像react的setState可以传一个对象,同时设置好几个值
  2. san-ref用在原生元素上不起作用,看了下代码只有Compnent才能用san-ref,所以是不是以后要像virtual-dom一样把dom都转成Component?
  3. 没有beforeUpdate的hook,无法在update之前对data进行处理
  4. 绑定数据的时候,只有value可以(value={{value}}可以),其他的属性都不可以(id={{id}}不行)
  5. 对于已经有id的元素,加了san属性会覆盖原来的id,esui中是使用data-ui-id
  6. checkbox和san-if放一起有点纠结
<input san-if="checked" type="checkbox" checked >
<input san-else type="checkbox" >

数据双向绑定的问题

var Foo = san.defineComponent({
  template: ''
  + '<template>'
    + '<div san-for="item in items">'
      + '<div class="row">'
        + '<label>{{item.label}} - {{item.values|join}}</label>'
        + '<div class="boxes">'
          + '<span san-for="box in item.datasource">'
            + '<input type="checkbox" value="{{box.value}}" checked="{=item.values=}"/> {{box.title}}'
          + '</span>'
        + '</div>' // end of boxes
      + '</div>'   // end of row
    + '</div>'
  + '</template>',
  
  filters: {
    join(value) {
      return value.join(',')
    }  
  },
  
  initData() {
    return {
      items: [
        {
          label: 'A',
          datasource: [
            {
              title: 'foo',
              value: 'foo'
            },
            {
              title: 'bar',
              value: 'bar'
            }
          ],
          values: ['foo']
        },
        {
          label: 'B',
          datasource: [
            {
              title: 'foo',
              value: 'foo'
            },
            {
              title: 'bar',
              value: 'bar'
            }
          ],
          values: ['bar', 'foo']
        }
      ]
    }
  }
});

var foo = new Foo();
foo.attach(document.body);

这个地方 checked="{=item.values=}" 是我用错了吗?

数据更新之后,View没有变化

  var Block = san.defineComponent({
    template: '<template><div class="x-block"><slot/></div></template>'
  });
  var Foo = san.defineComponent({
    components: {
      'x-block': Block
    },
    template: ''
    + '<template>'
      + '<x-block san-if="f">{{foo}}</x-block>'
      + '<x-block san-else>{{bar}}</x-block>'
    + '</template>',

    initData() {
      return {
        f: false,
        foo: 'foo',
        bar: 'bar'
      }
    }
  });

  var foo = new Foo();
  foo.attach(document.body);

页面打开之后,如果在控制台里面执行 foo.data.set('bar', '123'),页面应该更新,但是没有。

[question]static property 的支持

ESNext 是无法声明 prototype property 的。所以,对于 template / filters / components 等属性,San 提供了 static property 的支持。

实际上不嫌难看的话,可以用ESNext这么搞:

class HelloComponent {
    get template() {
        return '<p>Hello {{name}}!</p>';
    }
    initData() { 
        return {name: 'San'} 
    }
}

// test
HelloComponent.prototype.template && console.log(HelloComponent.prototype.template)

input type 属性如果是变量的话,数据同步存在问题

var Foo = san.defineComponent({
  template: '<template><div>value is: {{value}}</div><input type="{{boxType}}" value="bj" checked="{=value=}" /></template>',
  initData: function() {
    return {
      boxType: 'radio',
      value: ''
    };
  }
});

var Bar = san.defineComponent({
  template: '<template><div>value is: {{value}}</div><input type="radio" value="bj" checked="{=value=}" /></template>',
  initData: function() {
    return {
      value: ''
    };
  }
});

var App = san.defineComponent({
  components: {
    'ui-foo': Foo,
    'ui-bar': Bar
  },
  template: '<template><ui-foo/><ui-bar/></template>'
});

var app = new App();
app.attach(document.body)

对于 Foo 这个组件,bindInfo.raw{{boxType}},不是 radio 或者 checkbox

多次 set 导致的视图更新出错

san 版本

3.0.3-rc.6

描述:

模板

<span class="page-num" san-if="current - 2 < totalPage">{{ current - 2 }}</span>
<span class="page-num" san-if="current - 1 < totalPage">{{ current - 1 }}</span>
<span class="page-num" san-if="current < totalPage">{{ current }}</span>
<button on-click="changeVal()">change</button>

初始值

{
  totalPage: 5,
  current: 5
}

事件处理

{
    changeVal() {
        this.data.set('current', 19);
        this.data.set('totalPage', 19);
    }
}

操作

点击 button

错误结果

期待结果

<span>17</span><span>18</span>

问题定位

在第一个 set 产生了两个 dispose child task,来移除第 1 和2 个 span;
而第二个 set 产生了两个 update child task,来更新第 1 和 2 个 span;

根本原因是 tasks 是异步执行的,view 在 tasks 执行完成前与 model 不一致。两次 set 都对基础状态的 view 做了 diff。第二次 set 应该基于上一次 set 后的 view 来计算 diff。

解决方案

如果每次 set 都是同步做全量 diff 的话,将 task queue 清空即可。

san-if 的判断逻辑貌似有问题

  var Foo = san.defineComponent({
    template: ''
    + '<template>'
      + '<input type="text" value="{=value=}" />'
      + '<div san-if="{{isFoo}}">is foo</div>'
      + '<div san-if="{{!isFoo}}">is not foo</div>'
    + '</template>',

    computed: {
        isFoo() {
            const value = this.data.get('value');
            return value === 'foo';
        }
    },

    initData() {
      return {
        value: 'xxoo'
      }
    }
  });

  var foo = new Foo();
  foo.attach(document.body);

如果把 <div san-if="{{!isFoo}}">is not foo</div> 改成 <div san-else>is not foo</div>,那么是符合预期的。

或者改成 <div san-if="{{value !== 'foo'}}">is not foo</div> 也是符合预期的。

支持标签自闭合

除去使用slot等场景,自定义的组件都不会有内容,此时如果还要写闭合标签太累,希望在所有标签上都能支持自闭合,即<ui-calendar />可以,<input />可以,<div />也可以

关于dataType的支持

@jinzhubaofu 讨论后,确定:

  1. 只做validate,不做类型转换先
  2. dev版本支持,release版本忽略

然后是形式:

json schema

太大了,而且只能支持json定义的类型(我没好好读spec,不知道对date这种常用的复合类型支持的怎样)

react propType

https://github.com/facebook/prop-types

这种方式,除了支持基本的类型外,还:

  • 支持required
  • 支持多类型
  • 支持枚举
  • 变相支持range
  • 支持深层次结构
  • 写起来挺烦的,比如定义一个数组,其中每项包含id,age,貌似就要 PropTypes.arrayOf(PropTypes.shape({id, age})) ?

类似vue,贴近JS

dataType= {
  name: String,
  age: Number,
  addr: {
    provider: String,
    city: String
  }
}

这种方式,除了支持基本的类型外,最多能支持深层次结构,但是很多值相关的就做不了了。当然,可以开放支持function来定制校验规则。如果内置校验规则,和 react propType 也没毛区别。

来讨论下

@otakustay @jinzhubaofu 你俩觉得选哪种好

san的生命周期问题

最近在使用san的时候发现,san的component的created方法会调用两遍
于是clone了最新版的san, 使用example里的todos_esnext在list.san里进行验证 同样发现created执行了两遍 生命周期的顺序是正常的

下面是控制台打印的内容

List.san?7f39:67 compiled
List.san?7f39:63 inited
List.san?7f39:59 created
List.san?7f39:59 created
List.san?7f39:55 attached

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.