写Vue有很长一段时间了,除了常规的业务开发之外,也应该思考和反思一下封装组件的正确方式。以弹窗组件为例,一种实现是在需要模板中引入需要弹窗展示的组件,然后通过一个flag变量来控制弹窗的组件,在业务代码里面会充斥着冗余的弹窗组件逻辑,十分不优雅。
本文整理了开发Vue组件的一些技巧,包含大量代码示例。
开发环境
vue-cli3提供了非常方便的功能,可以快速编写一些测试demo,是开发组件必备的环境。下面是安装使用步骤
// 全局安装vue-cli3
npm install -g @vue/cli
vue -V // 查看版本是否为3.x
// 安装扩展,此后可以快速启动单个vue文件
npm install -g @vue/cli-service-global
// 快速启动demo文件
vue serve demo.vue
如果需要scss,则还需要在目录下安装sass-loader等。
下面是使用vue-cli3可能会遇见的几个问题,更多使用教程可以参考:一份超级详细的Vue-cli3.0使用教程[赶紧来试试!]
自定义入口文件
如果需要(比如需要开发移动端的组件),可以在使用vue serve
时自定义html入口文件,在根目录下编写index.html
,并确保页面包含#app
的dom即可。
引入公共混合文件
通过style-resources-loader
在每个文件引入公共样式混合等,参考自动化导入
需要访问Vue全局对象
在某些时候需要放问全局Vue对象,如开发全局指令、插件时
import Vue from "vue"
import somePlugin from "../src/somePlugin"
Vue.use(somePlugin)
上面这种写法并不会生效,这是因为vue serve xxx.vue
仅仅只能作为快速原型开发的方案,使用的Vue与 import引入的Vue不是同一个对象。一种解决办法是手动指定vue serve
的入口文件
// index.js
import Vue from "../node_modules/vue/dist/vue.min"
import placeholder from "../src/placeholder/placeholder"
Vue.use(placeholder)
new Vue({
el: "#app",
template: ``,
created(){},
})
Vue的组件系统
Vue组件的API主要包含三部分:prop、event、slot
- props 表示组件接收的参数,最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,此外还可以通过type、validator等方式对输入进行验证
- slot可以给组件动态插入一些内容或组件,是实现高阶组件的重要途径;当需要多个插槽时,可以使用具名slot
- event是子组件向父组件传递消息的重要途径
单向数据流
参考:单向数据流-官方文档。
父级 prop 的更新会向下流动到子组件中,但是反过来则不行
单向数据流是Vue组件一个非常明显的特征,不应该在子组件中直接修改props的值
- 如果传递的prop仅仅用作展示,不涉及修改,则在模板中直接使用即可
- 如果需要对prop的值进行转化然后展示,则应该使用computed计算属性
- 如果prop的值用作初始化,应该定义一个子组件的data属性并将prop作为其初始值
从源码/src/core/vdom/create-component.js
和/src/core/vdom/helpers/extract-props.js
里可以看见,在处理props的取值时,首先从
function extractPropsFromVNodeData(){
const res = {}
const { attrs, props } = data
// 执行浅拷贝
checkProp(res, props, key, altKey, true) || checkProp(res, attrs, key, altKey, false)
return res
}//在此我向大家推荐一个前端全栈开发交流圈:582735936 突破技术瓶颈,提升思维能力
在子组件修改props,却不会修改父组件,这是因为extractPropsFromVNodeData
中是通过浅复制将attrs传递给props的。
浅复制意味着在子组件中对对象和数组的props进行修改还是会影响父组件,这就违背了单向数据流的设计。因此需要避免这种情况出现。
组件之间的通信
这里可以参考:vue组件通信全揭秘,写的比较全面
- 父子组件的关系可以总结为 prop 向下传递,事件event向上传递
- 祖先组件和后代组件(跨多代)的数据传递,可以使用provide和inject来实现
此外,如果需要跨组件或者兄弟组件之间的通信,可以通过eventBus或者vuex等方式来实现。