x-chat icon indicating copy to clipboard operation
x-chat copied to clipboard

不太明白你封装的toast组件为什么分开写了一个.vue和.js文件

Open fantasy525 opened this issue 7 years ago • 9 comments

  • 你封装的组件为什么是分开的呢?这样有什么好处?
  • 我也不明白这里为什么要这么写?
import Vue from "vue";
const ToastConstructor = Vue.extend(require('./toast.vue'));
let instance = new ToastConstructor().$mount('');

document.body.appendChild(instance.$el);

翻看文档知道这里意思是想渲染在app元素之外,为什么要这么弄呢?

fantasy525 avatar Feb 06 '18 03:02 fantasy525

我这样写的好处就是既实现了事件管理又易于复用。其实怎么写应该根据你的项目和习惯去做。可参考知乎上的回答:https://www.zhihu.com/question/35820643

ermu592275254 avatar Feb 06 '18 05:02 ermu592275254

全部写在一个不是也可以复用吗?这样写在什么情况下易于复用呢?

fantasy525 avatar Feb 06 '18 07:02 fantasy525

如果是按照vue组件的规范写在一个vue文件里面,注册为全局组件,此时就需要通过vue所提倡的状态管理,而不是事件管理了。同时为了规范需要把状态保存到vuex的store中,我不太喜欢这样的形式去管理这种多处使用的小组件。这样写,如果别的项目也用到直接copy就好了,不用再去store增加状态管理

ermu592275254 avatar Feb 06 '18 08:02 ermu592275254

emmm,让我理解下,

import Vue from "vue";
const ToastConstructor = Vue.extend(require('./toast.vue'));
let instance = new ToastConstructor().$mount('');

document.body.appendChild(instance.$el);

这样的写法,貌似这个toast就是一个单独的vm实例,跟app下的那个实例还不一样?这个实例挂载在toast相关的html上?唯一的联系就是这个实例时app vm原型的一个方法?是这个意思么? 另外还有那里为什么$mount(''),里面是空的是什么意思?

fantasy525 avatar Feb 06 '18 08:02 fantasy525

上面代码其实就是做了下面3步:

  1. 创建VUE子类
  2. 将实例挂载。vm.$mount('')代表elementOrSelector参数为空,模板将被渲染为文档之外的的元素,必须使用原生 DOM API 把它插入文档中
  3. 用原生插入到body中

如果写成一个VUE文件:

全局注册 Vue.component(tagName, options) => 放在根组件内 => 使用状态管理 或者$refs去获取元素实例

第一种使用:

store.commit('showModel','some message...',2000);

第二种使用

this.$refs.model.show('some message...',2000);

emmmmm,写成一个VUE文件我能想到的就这两种方式。个人觉得这两种方式都不太好。

ermu592275254 avatar Feb 06 '18 11:02 ermu592275254

不好意思,这两天都在看这方面的东西,那个知乎看了几遍了,现在才想起你这里的回复,不过现在再来看你的回复能看懂了。 像这个vm.$mount('')是我没仔细看文档,文档都有,我看了知乎那篇文章后,觉得像toast这种只是简单提示的,是可以放在body下的。 按照你说的写成一个vue文件,然后放在app组件下,之后用你说的两种方式调用,第一种我觉得要引入vuex或者eventbus是不太好, 但是第二种在某个子组件使用的话使用this.$refs没法直接获取跟组件下的弹窗组件吧?第二种的不太好指的哪里呢? 另外像你的做法如果想引入slot来定制内容怎么做?异步插入没法引入slot吧?我想的就是这种情况需要注册为全局组件然后引入到子组件中定制slot,接着在这个全局组件绑定事件和传入需要的prop,不知道是不是这样做呢?

fantasy525 avatar Feb 09 '18 04:02 fantasy525

第二种方式的确不能获取根组件下的,我之前描述错了。需要在每个用到的组件下引用。这样就不能确保单例。所以不太好使。 如果想引入slot,模版渲染就成了一个难题,知乎的回答上已经有人做了回答——参考Jim Liu 和agileago的回答。 个人认为需要slot的组件都是难以复用的组件,最好还是特殊对待。像alert,propmt这样的组件在业务逻辑不是很复杂的情况下 可以考虑放在根组件下。可参考https://segmentfault.com/a/1190000011650003

ermu592275254 avatar Feb 09 '18 05:02 ermu592275254

segmentfault那个也是你写的吧?知乎那俩大神写的方案没给出具体事例不知道具体怎么实现, 你的推荐那篇文章中

return new Promise(function(resolve,reject) {
        instance.$refs.cancelBtn.addEventListener('click',function() {
            instance.show = false;
            resolve(false);
        });
        instance.$refs.okBtn.addEventListener('click',function() {
            instance.show = false;
            resolve(true);
        })
    })

这里我是用instance.$on('')来监听组件中$emit的事件的,你这样写貌似也可以,不过这两者是有什么深层的区别?或者优缺点?另外你有没有写过需要引入slot的?

fantasy525 avatar Feb 09 '18 06:02 fantasy525

emmmmm,我之前写这个组件的时候没有想到用instance.$on('');哈哈,经验不够,看来还得努力学习。我觉得用instance.$on('')更好一些. 知乎说的插件vue-dom-portal,其实就是一个slot。你可以在任何地方写一个slot——它的指令v-dom-portal就相当于slot。然后指定一个位置插入就可以了。 比如我在test.vue中写了一个

<template>
    <div class="bg-grey">
        <div v-dom-portal="'body'" style="background-color: blue;position: fixed; z-index:100;width: 200px;height:200px;">
            <p style="text-align: center;color:#fff;">我可以被插入当DOM的任何位置</p>
        </div>
    </div>
</template>

我给它传的portal是body,所以他会被渲染到body中;

<body class="half-border">
    <div id="app">
        <div class="bg-grey child-view"><!----></div> 
    </div>
    <div style="background-color: blue; position: fixed; z-index: 100; width: 200px; height: 200px;">
       <p style="text-align: center; color: rgb(255, 255, 255);">我可以被插入当DOM的任何位置</p>
    </div>
</body>

这样就实现了全局的slot,你把portal换成全局的alert,propmt这种组件,便可以实现你想要得效果啦

ermu592275254 avatar Feb 10 '18 02:02 ermu592275254