组件以插件的形式引入使用,当然,也可以直接在页面引入组件文件,两者按需使用。
安装插件:
import Button from './oyButton';
Button.install = function (Vue) {
Vue.component(Button.name, Button);
}
export default Button;
vue.install源码:
export function initUse (Vue: Globalapi) {
Vue.use = function (plugin: Function | Object) {
# /*检测该插件是否已经被安装*/
if (plugin.installed) {
return
}
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
# /*install执行插件安装*/
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
plugin.installed = true
return this
}
}
通过源码可知,vue不会重复安装同一个插件。以第一次安装为准
现在,可以在代码中使用组件啦~
<oy-button>我是按钮按钮</oy-button>
以上,是一个非常简单的组件库实现。现在来看看element组件库是如何实现的。
这里重点说下packages目录和src目录
|-- packages # 组件源码目录
|-- button # button组件目录,一个组件一个文件,方便管理
|-- src # 组件实现代码
|-- button-group.vue
|-- button.vue
|-- index.js # 组件入口文件
|-- src
|--directives # 实现滚轮优化,鼠标点击优化
|--locale # 国际化
|--mixins # 公用逻辑代码
|--transitions # 样式过度效果
|--utils # 工具类包
|--index.js # 源码入口文件
整个目录结构非常清晰。
button模块目录,有一个index.js作为模块入口
import ElButton from './src/button';
ElButton.install = function(Vue) {
Vue.component(ElButton.name, ElButton);
};
export default ElButton;
在index.js文件中,对组件进行拓展,添加Install方法。
import Button from '../packages/button/index.js';
const components = [Button]
# 定义一个install方法
const install = function(Vue, opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);
# 将所有的功能模块进行注册。
components.map(component => {
Vue.component(component.name, component);
});
# 注册插件
Vue.use(Loading.directive);
const ELEMENT = {};
ELEMENT.size = opts.size || '';
# 绑定Vue实例方法
Vue.prototype.$message = Message;
};
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
# 最后,将所有功能模块和install方法一起导出。
# 这样当引入element-ui时,便可以使用vue.use(element-ui)进行注册,即将所有的功能组件进行全局注册。
module.exports = {
version: '2.3.8',
locale: locale.use,
i18n: locale.i18n,
install,
Button,
}
module.exports.default = module.exports;
element组件实现时,html基本实现了语义化标签。
标记组件。
Badge 标记组件部分源码:
<!-- sup标签语义:上标文本 -->
<transition name="el-zoom-in-center">
<sup
v-show="!hidden && (content || content === 0 || isDot)"
v-text="content"
class="el-badge__content"
:class="{ 'is-fixed': $slots.default, 'is-dot': isDot }">
</sup>
</transition>
ps: 自己写代码都是div span
element组件基本都兼容了v-model绑定值,组件使用起来更加舒适~
兼容v-model需要做一下几点:
(如text元素使用input事件来改变value属性 和 checkbox使用的change事件来改变check属性)
input组件源码:
export default {
props: {
# 定义value
value: [String, Number],
},
methods: {
handleInput(event) {
if (this.isOnComposition) return;
const value = event.target.value;
# 变更数据以后通过input去更新父组件数据
this.$emit('input', value);
this.setCurrentValue(value);
},
}
}
vue中,存在几种组件之间数据传递的方案:
在日常开发中,父子组件之间数据传递用到比较多的方案是props。当组件层次比较深,就使用attrs来透传数据:
<el-select
v-model="selectValue"
v-bind="$attrs"
v-on="$listeners">
<template v-if="label && keyValue">
<el-option
v-for="(item, index) in selectList"
:key="index"
:label="item[label]"
:value="item[keyValue]"></el-option>
</template>
</el-select>
element组件,在父子组件传递数据也是使用props,但是当组件层次比较深,或者不清楚组件层次时,使用的是:provide / inject
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
关于provide / inject:
“这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效” --vue文档简单来说,就是父组件通过provide来提供变量,子组件通过inject来引用变量。
vue的inject源码:
# src/core/instance/inject.js
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
provide是向下传递数据,先获取provide内容,然后传递给vm._provided设置成全局数据。inject会根据选项的 key 数组一层层向上遍历,拿到结果。
provide 相对于props,实现了跨层级提供数据。需要注意的是provide不是响应式的。
方法 | 解释 | 适用场景 |
---|---|---|
props | 用于接收来自父组件的数据 | 父子组件之间传递数据 |
provide | 以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效 | 替代嵌套过深的props,可以理解为一个bus,但只做父组件通知子组件的单向传递的一个属性 |
attrs | 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外) | 父组件传向子组件传的,子组件没有通过prop接受的数据都会放在$attrs中 |
parent/child | 获取父/子组件实例 |
两者都是通知父组件执行事件的方法,但是有一定的区别:
对于组件嵌套过深,element自己实现了一个简易版的发布订阅方式:
function broadcast(componentName, eventName, params) {
# 组件名称,事件名称,参数
# 当前组件下的子组件循环
this.$children.forEach(child => {
# 获取组件名称
var name = child.$options.componentName;
# 如果组件名称和要触发的事件组件名称相同
if (name === componentName) {
# 当前子组件,调用$emit方法
child.$emit.apply(child, [eventName].concat(params));
} else {
# 如果没有相等,那就继续查找当前子组件的子组件
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
name: 'ElButton',
props: {
type: {
type: String,
default: 'default'
},
},
};
组件在使用过程中,会不断的优化添加功能,但是组件的内部变更不能影响组件的使用,这就需要组件有很好的扩展性,在一开始,能够提供足够比较友好的接口。
在组件中预留一些“插槽”,使用组件的时候,可以再“插槽”中注入自定义的内容,从而改变组件渲染结果。element组件库在这方面做得很好。
input组件部分源码:
<div>
<template v-if="type !== 'textarea'">
<!-- 前置元素 -->
<div class="el-input-group__prepend" v-if="$slots.prepend">
<slot name="prepend"></slot>
</div>
<input>
<!-- 前置内容 -->
<span class="el-input__prefix" v-if="$slots.prefix || prefixIcon" :style="prefixOffset">
<slot name="prefix"></slot>
</span>
<!-- 后置内容 -->
<span>
<span class="el-input__suffix-inner">
<template v-if="!showClear">
<slot name="suffix"></slot>
</template>
</span>
</span>
<!-- 后置元素 -->
<div class="el-input-group__append" v-if="$slots.append">
<slot name="append"></slot>
</div>
</template>
</div>
Input组件预留了四个“插槽”,允许使用者在前后位置都可以插入内容。
element组件提供了丰富的钩子函数:
focus() {
(this.$refs.input || this.$refs.textarea).focus();
},
blur() {
(this.$refs.input || this.$refs.textarea).blur();
},
组件要能接受一定的错误使用,能针对可预知的错误使用进行处理。
focus() {
# 先判断this.$refs.input是否存在,才进行接下来操作,避免数据为空报错情况。
(this.$refs.input || this.$refs.textarea).focus();
}
来自:https://segmentfault.com/a/1190000018707985
Vuetify 支持SSR(服务端渲染),SPA(单页应用程序),PWA(渐进式Web应用程序)和标准HTML页面。 Vuetify是一个渐进式的框架,试图推动前端开发发展到一个新的水平。
通过给组件传递参数, 可以让组件变得更加可扩展, 组件内使用props接收参数,slot的使用就像它的名字一样, 在组件内定义一块空间。在组件外, 我们可以往插槽里填入任何元素。slot-scope的作用就是把组件内的数据带出来
函数子组件(FaCC )与高阶组件做的事情很相似, 都是对原来的组件进行了加强,类似装饰者。FaCC,利用了react中children可以是任何元素,包括函数的特性,那么到底是如何进行增强呢?
在现代的三大框架中,其中两个Vue和React框架,组件间传值方式有哪些?组件间的传值是灵活的,可以有多种途径,父子组件同样可以使用EventBus,Vuex或者Redux
自定义指令:以v开头,如:v-mybind。bind的作用是定义一个在绑定时执行一次的初始化动作,观察bind函数,它将指令绑定的DOM作为一个参数,在函数体中,直接操作DOM节点为input赋值。
prop的定义:在没有状态管理机制的时候,prop属性是组件之间主要的通信方式,prop属性其实是一个对象,在这个对象里可以定义一些数据,而这些数据可以通过父组件传递给子组件。 prop属性中可以定义属性的类型,也可以定义属性的初始值。
Web组件由三个独立的技术组成:自定义元素。很简单,这些是完全有效的HTML元素,包含使用一组JavaScript API制作的自定义模板,行为和标记名称(例如,<one-dialog>)。
web组件可以直接或间接的调用其他web资源。一个web组件通过内嵌返回客户端内容的另一个web资源的url来间接调用其他web资源。在执行时,一个web资源通过包含另一个资源的内容或者转发请求到另一个资源直接调用。
在实际开发项目中,有时我们会用到自定义按钮;因为一个项目中,众多的页面,为了统一风格,我们会重复用到很多相同或相似的按钮,这时候,自定义按钮组件就派上了大用场,我们把定义好的按钮组件导出,在全局引用,就可以在其他组件随意使用啦,这样可以大幅度的提高我们的工作效率。
Vue中子组件调用父组件的方法,这里有三种方法提供参考,第一种方法是直接在子组件中通过this.$parent.event来调用父组件的方法,第二种方法是在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!