Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

初始化数据时ie8报错 #422

Closed
PreciousDust opened this issue Jun 20, 2019 · 15 comments
Closed

初始化数据时ie8报错 #422

PreciousDust opened this issue Jun 20, 2019 · 15 comments

Comments

@PreciousDust
Copy link

业务场景

当子组件需要根据父组件传入的数据来格式化自己的一些默认数据时,会调用一些格式化子组件数据的方法。

san版本

3.7.6

问题

1.在inited/created/attached三个生命周期钩子函数格式化数据时【就是执行 类似于this.data.set('xxx', ' xxx' )】时,ie8会报错
HTML Parsing Error: Unable to modify the parent container element before the child element is closed (KB927917)
2.格式化方法在以上三个钩子函数中放在 setTimeout 函数中就不会报错。

期望结果

在上面的钩子函数中的一个方法里面,正常的格式化数据应该在 ie8上不报错,不需要放到setTimeout里面。

@Dafrok
Copy link
Contributor

Dafrok commented Jun 21, 2019

如果不是异步,你为什么不直接把数据直接在 data 里声明呢?

@errorrik
Copy link
Contributor

上代码吧。我试了下没发现问题。这是我的代码

    var Label = san.defineComponent({
        template: '<b>{{text}}</b>',

        inited: function () {
            if (this.data.get('num') === 1) {
                this.data.set('text', 'one');
            }
        }
    });

    var MyComponent = san.defineComponent({
        template: '<div><x-l num="{{num}}"/></div>',

        components: {
            'x-l': Label
        }
    })


    var myComponent = new MyComponent({
        data: {
            num: 1
        }
    });


    myComponent.attach(document.body)

另外,你的场景感觉应该通过 computed 来做。

@PreciousDust
Copy link
Author

PreciousDust commented Jun 21, 2019

sorry,我没描述清楚,其实我遇到问题的是下面这段代码,这个直接在 ie8是没问题的,但是我用 webpack 打包时用import san from 'san'这种方式引入后相同的代码会报错,这是我真正有疑惑的地方。

没问题的代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>start - list</title>
</head>

<body>
<script src="../san.js"></script>
<script>
    var son = san.defineComponent({
        template: '<div>{{aaa}}</div>',
        initData: function () {
            return {
                aaa: 111
            }
        }
    })
    var MyApp = san.defineComponent({
        template: '<ul><son aaa="{{aaa}}"></son></ul>',
        initData: function () {
            return {
                aaa: 11
            }
        },
        attached: function () {
            this.data.set('aaa', 11111111)
        },
        components: {
            son: son
        }
    })
    var myApp = new MyApp()
    myApp.attach(document.body)
</script>
</body>
</html>

报错的代码

报错信息:HTML Parsing Error: Unable to modify the parent container element before the child element is closed (KB927917)

import san from 'san'
var son = san.defineComponent({
    template: '<div>{{aaa}}</div>',
    initData: function () {
        return {
            aaa: 111
        }
    }
})
var MyApp = san.defineComponent({
    template: '<ul><son aaa="{{aaa}}"></son></ul>',
    initData: function () {
        return {
            aaa: 11
        }
    },
    attached: function () {
        this.data.set('aaa', 11111111)
    },
    components: {
        son: son
    }
})

let myApp = new MyApp()
myApp.attach(document.body)

@Dafrok @errorrik

@errorrik
Copy link
Contributor

可能是 Object.defineProperty 的问题

@PreciousDust
Copy link
Author

是要先引入下面这段代码吗?我引入后重新打包好像还是会报错。。

(function () {
  if (!Object.defineProperty ||
    !(function () {
      try {
        Object.defineProperty({}, 'x', {});
        return true;
      } catch (e) {
        return false;
      }
    }())) {
    var orig = Object.defineProperty;
    Object.defineProperty = function (o, prop, desc) {
      // In IE8 try built-in implementation for defining properties on DOM prototypes.
      if (orig) {
        try {
          return orig(o, prop, desc);
        } catch (e) {}
      }

      if (o !== Object(o)) {
        throw TypeError("Object.defineProperty called on non-object");
      }
      if (Object.prototype.__defineGetter__ && ('get' in desc)) {
        Object.prototype.__defineGetter__.call(o, prop, desc.get);
      }
      if (Object.prototype.__defineSetter__ && ('set' in desc)) {
        Object.prototype.__defineSetter__.call(o, prop, desc.set);
      }
      if ('value' in desc) {
        o[prop] = desc.value;
      }
      return o;
    };
  }
}());

@errorrik
Copy link
Contributor

我可能猜错了。

你看看你最终输出的html,script是不是位于没闭合标签内

@PreciousDust
Copy link
Author

这是生成的 html

<!DOCTYPE html>
<html>
  <head>
    <meta charset=UTF-8>
    <title>Webpack App</title>
  </head>
  <body>
  <script type=text/javascript src=static/js/main.c12fe78d35e4e7ee4664.js></script></body>
</html>

另外我刚才把import 换成了require,打包也是一样的结果

let san = require('san')
var son = san.defineComponent({
    template: '<div>{{aaa}}</div>',
    initData: function () {
        return {
            aaa: 111
        }
    }
})
var MyApp = san.defineComponent({
    template: '<ul><son aaa="{{aaa}}"></son></ul>',
    initData: function () {
        return {
            aaa: 11
        }
    },
    attached: function () {
        this.data.set('aaa', 11111111)
    },
    components: {
        son: son
    }
})

let myApp = new MyApp()
myApp.attach(document.body)

但是我只要把 set 放到 setTimeout 里面,就不会报错

let san = require('san')
var son = san.defineComponent({
    template: '<div>{{aaa}}</div>',
    initData: function () {
        return {
            aaa: 111
        }
    }
})
var MyApp = san.defineComponent({
    template: '<ul><son aaa="{{aaa}}"></son></ul>',
    initData: function () {
        return {
            aaa: 11
        }
    },
    attached: function () {
        setTimeout(() => {
            this.data.set('aaa', 11111111)
        }, 50)
    },
    components: {
        son: son
    }
})

let myApp = new MyApp()
myApp.attach(document.body)

@errorrik

@errorrik
Copy link
Contributor

到这步,信息量太少了,我无法判断出来。

你只能根据错误信息定位到static/js/main.c12fe78d35e4e7ee4664.js的运行位置,再根据当前dom的状态,跟踪到底是哪里出问题了

@PreciousDust
Copy link
Author

1111111
这个报错无从查起。。
额 要不我提交一个demo项目到 git 上面?

@errorrik
Copy link
Contributor

@PreciousDust
Copy link
Author

https://github.com/sjiangbei/san-test @errorrik 传上去了,如果有时间辛苦看一下 0.0

@errorrik
Copy link
Contributor

IE9- 环境下,不支持 webpack4 打包代码中的 getter/setter 访问器属性。建议更换其他构建工具来进行打包构建。

@PreciousDust
Copy link
Author

额 好像和 webpack4没关系把? 我降级成 webpack3和2 还是同样的问题。例子也上传了@errorrik https://github.com/sjiangbei/san-test

@errorrik
Copy link
Contributor

@sjiangbei

webpack打包出来的代码里,有这么一段,对setImmediate进行模拟实现:在html element上append一个script

function installReadyStateChangeImplementation() {
        var html = doc.documentElement;
        registerImmediate = function(handle) {
            // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
            // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
            var script = doc.createElement("script");
            script.onreadystatechange = function () {
                runIfPresent(handle);
                script.onreadystatechange = null;
                html.removeChild(script);
                script = null;
            };
            html.appendChild(script);
        };
    }

san 的 nextTick 是这么实现的

    if (typeof setImmediate === 'function') {
        setImmediate(nextHandler);
    }
    // 用MessageChannel去做setImmediate的polyfill
    // 原理是将新的message事件加入到原有的dom events之后
    else if (typeof MessageChannel === 'function') {
        var channel = new MessageChannel();
        var port = channel.port2;
        channel.port1.onmessage = nextHandler;
        port.postMessage(1);
    }
    // for native app
    else if (isNativePromise) {
        Promise.resolve().then(nextHandler);
    }
    else {
        setTimeout(nextHandler, 0);
    }

你的代码 myApp.attach(document.body) 执行时,html标签还没闭合,所以往html上append script会报这个错误。

解决办法很简单,把你初始化的代码放到 window.onload 里就好了。反正你的页面比较空白,window.onload时机并不晚

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants