阅读 1854

手写Vue组件踩坑与心得

原文链接: github.com

记得以前看过一句话,说市面上任何的UI库都无法满足一个产品的所有需求。

事实上的确如此,产品需求总是千奇百怪。正如我公司现在的产品,引用的是elemen-ui的库,但是无法级联多选,下拉多选的展现形式不对,穿梭框无法上下移动等各种需求逼迫我们只能自己去写组件实现了。

自己手写实现了两个组件之后,先写一篇记录一些坑和学会的新东西

1、子组件不允许修改父组件数据

之前在写angular的父子组件传递数据,子组件可以修改父组件传递进来的数据。不过在Vue中子组件不允许修改父组件穿进来的值,以vue举例来说

<!--父组件HTML内容-->
<children-component :value="data"></children-component>

<!--父组件的js内容-->
export default({
    data(){
        return {
            data : [1,2,3,4]
        }
    }
})

<!--子组件的js内容-->
export default({
    props : ["value"],
    created : {
        this.value = [5,6,7,8];
    }
})
复制代码

此举会引发一个非常常见的报错,
image

那么解决方法是什么呢,那就要看你需求了。比如传进来的是一个渲染列表,我需要的只是修改渲染的数据,那么可以emit出去,然后在父组件重新赋值,通过双向绑定,触发子组件的再次渲染。

<!--父组件HTML内容-->
<children-component :value="data" @changeProp="changeData"></children-component>


<!--父组件的js内容-->
export default({
    data(){
        return {
            data : [1,2,3,4]
        }
    },
    methods : {
        changeData(value){
            this.data = value;
        }
    }
})

<!--子组件的js内容-->
export default({
    props : ["value"],
    created : {
        this.$emit("changeProp",[5,6,7,8])
    }
})
复制代码

例子写的比较简单,其实原理就是emit一个数据到父组件上去,然后在父组件中接受到这个传递上来的新值,将data赋予新值,然后重新传递到了子组件,起到一个变向修改子组件的效果。


被人提醒到一个方法是用.sync,这个方法也是可以的。之前一直以为是被废弃就没用,才发现它其实是在的。看了一下.sync文档,发现这其实是一个语法糖的形式展现出来。如文档所示

<!--日常使用语法糖形态-->
<comp :foo.sync="bar"></comp>
<!--↓↓↓↓↓真实形态↓↓↓↓↓↓-->
<comp :foo="bar" @update:foo="val => bar = val"></comp>
<!--子组件js代码-->
this.$emit('update:foo', newValue)
复制代码

个人感觉Vue框架其实还是不赞同直接修改数据,但是它帮你定义了一个update事件,让你在子组件可以直接显式调用,不需要自己去定义事件这么麻烦了。

2.Vue给每个组件提供了一个默认的父子传递事件v-model。

但是你每次父子组件传递数据时,都要父子处定义一个事件是很麻烦的,vue则为每个组件提供了一个默认v-model的语法糖。

<!--父组件HTML内容-->
<children-component v-mode="data" ></children-component>

<!--父组件的js内容-->
export default({
    data(){
        return {
            data : [1,2,3,4]
        }
    },
    watch : {
        //可以在此处监听子组件传递上来的数据
        data(n,o){
            console.log(n,o);   //[5,6,7,8],[1,2,3,4]
        }
    }
})

<!--子组件的js内容-->

export default({
    <!--大家注意我下面这行代码是打了注释的,说明我在子组件没有定义任何属性-->
    //  props : ["value"]
    <!--并且我接下去直接在代码中使用了this.value(一个完全没有定义过的value)。-->
    created : {
        console.log(this.value);
        this.$emit("input",[5,6,7,8]);  //当我想改变传进来值的时候
    }
})
复制代码

因为之前一直使用ng-model来用于表单组件的传递,所以开始对于v-mode也是这个印象,不过后面看了element-ui的源码才发现我想简单了,然后网上搜了一下对于这个语法糖的解释。

//注意,该组件不是表单组件
<children-component :value="data" @input="data = arguments[0]"></children-component>
复制代码

看了这行代码大家心里估计也能明白的差不多了,其实Vue只是帮我们把父组件上的两段声明合二为一了,同时再帮我们在子组件处直接省略了定义。语法糖说到底就是帮我们省力的嘛。

3.全局一次性引用写好的组件

假如我们写好了一些组件,接下去肯定还要引入和使用吧。但是你写了这么多组件,在每个地方一个个引用想要的是一件很麻烦的事情。我们最好是在一个初始的地方一次性全部引入,然后在用的地方直接使用

(当然全部引入无可避免的会引入和打包不需要的东西,不过这是公共组件库的烦恼,我们自己写的肯定会全部用到)。

// 在文件开头初始引入所有的组件文件
import b from "./components/common/b.vue"
import c from "./components/common/c.vue"

const components = [b,c];

const install = function (Vue, opts = {}) {
	components.map(component => {
		Vue.component(component.name, component);
	});
};

export default install
复制代码

然后直接在启动的main.js文件里面引入就好

import ui from "install.js"
Vue.use(ui);
复制代码

然后你就可以随意在任何一个组件里面直接调用了,比自己之前在每个组件里重复调用要方便一点。

<b v-model="data1"></b>
<c v-model="data2"></c>
复制代码

好了,暂时到这里,都是自己的踩得坑和心得,希望对大家有所帮助吧,接下去还有的话再补充好了。干货不多,烂笔头为主。

文章分类
前端