组件化示例
2020-05-14 15:01:08 0 举报
AI智能生成
前端组件化示例
作者其他创作
大纲/内容
elementui this.$message
如何实现
如何实现
将 Message 方法赋值给 Vue.prototype.$message:
Vue.prototype.$message = Message;
这样每个 Vue 组件(都继承于 Vue)都能获取到 $message 方法了
Vue.prototype.$message = Message;
这样每个 Vue 组件(都继承于 Vue)都能获取到 $message 方法了
Message 方法实现
index.js
核心代码:
import Vue from "vue";
import Main from "./main.vue";
let MessageConstructor = Vue.extend(Main); // 使用基础 Vue 构造器,创建一个“子类”
instance = new MessageConstructor({
data: options
});
instance.$mount();
document.body.appendChild(instance.$el);
instance.visible = true;
return instance;
};
export default Message;
import Vue from "vue";
import Main from "./main.vue";
let MessageConstructor = Vue.extend(Main); // 使用基础 Vue 构造器,创建一个“子类”
instance = new MessageConstructor({
data: options
});
instance.$mount();
document.body.appendChild(instance.$el);
instance.visible = true;
return instance;
};
export default Message;
完整代码:
import Vue from "vue";
import Main from "./main.vue";
import { isVNode } from "learn-element-ui/utils/vdom";
import { PopupManager } from "learn-element-ui/utils/popup";
let MessageConstructor = Vue.extend(Main); // 使用基础 Vue 构造器,创建一个“子类”
let instance;
let instances = [];
const Message = function(options) {
if (Vue.prototype.$isServer) return;
options = options || {};
if (typeof options === "string") {
options = {
message: options
};
}
instance = new MessageConstructor({
data: options
});
// vueInstance.$message({message: VNode})
if (isVNode(instance.message)) {
instance.$slots.default = [instance.message];
instance.message = null;
}
instance.$mount();
document.body.appendChild(instance.$el);
// 多个 message 同时出现,其距离顶部的高度需要叠加
let verticalOffset = options.offset || 20;
instances.forEach(item => {
// HTMLElement.offsetHeight 是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。
// 这里16是message之间的距离,verticalOffset是最上面一个message距离顶部的距离
verticalOffset += item.$el.offsetHeight + 16;
});
instance.verticalOffset = verticalOffset;
instance.visible = true;
instance.$el.style.zIndex = PopupManager.nextZIndex();
instances.push(instance);
return instance;
};
['success','warning','info','error'].forEach(type=>{
Message[type]=options=>{
if(typeof options ==='string'){
options={
message:options
}
}
options.type=type
return Message(options)
}
})
export default Message;
import Vue from "vue";
import Main from "./main.vue";
import { isVNode } from "learn-element-ui/utils/vdom";
import { PopupManager } from "learn-element-ui/utils/popup";
let MessageConstructor = Vue.extend(Main); // 使用基础 Vue 构造器,创建一个“子类”
let instance;
let instances = [];
const Message = function(options) {
if (Vue.prototype.$isServer) return;
options = options || {};
if (typeof options === "string") {
options = {
message: options
};
}
instance = new MessageConstructor({
data: options
});
// vueInstance.$message({message: VNode})
if (isVNode(instance.message)) {
instance.$slots.default = [instance.message];
instance.message = null;
}
instance.$mount();
document.body.appendChild(instance.$el);
// 多个 message 同时出现,其距离顶部的高度需要叠加
let verticalOffset = options.offset || 20;
instances.forEach(item => {
// HTMLElement.offsetHeight 是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。
// 这里16是message之间的距离,verticalOffset是最上面一个message距离顶部的距离
verticalOffset += item.$el.offsetHeight + 16;
});
instance.verticalOffset = verticalOffset;
instance.visible = true;
instance.$el.style.zIndex = PopupManager.nextZIndex();
instances.push(instance);
return instance;
};
['success','warning','info','error'].forEach(type=>{
Message[type]=options=>{
if(typeof options ==='string'){
options={
message:options
}
}
options.type=type
return Message(options)
}
})
export default Message;
其他功能点
- 支持 vueInstance.$message({message: VNode}) 的用法
- 多个 message 同时出现,其距离顶部的高度需要叠加
- 管理所有弹层组件的 index (PopupManager)
main.vue
<template>
<transition name="my-message-fade">
<div
:class="['my-message',type?`my-message--${type}`:'' ]"
v-show="visible" :style="positionStyle">
<slot>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content">{{ message }}</p>
<p v-else v-html="message" class="el-message__content"></p>
</slot>
</div>
</transition>
</template>
<script>
export default {
props:{
},
data() {
return {
visible: false,
message: "",
duration: 10000,
closed: false,
verticalOffset: 20,
dangerouslyUseHTMLString: false,
type:'info'
};
},
computed: {
positionStyle() {
return {
top: this.verticalOffset + "px"
};
}
},
watch: {
closed(newVal) {
if (newVal) {
this.visible = false;
}
}
},
mounted() {
this.startTimer();
},
methods: {
startTimer() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
!this.closed && this.close();
}, this.duration);
}
},
close() {
this.closed = true;
}
}
};
</script>
<transition name="my-message-fade">
<div
:class="['my-message',type?`my-message--${type}`:'' ]"
v-show="visible" :style="positionStyle">
<slot>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content">{{ message }}</p>
<p v-else v-html="message" class="el-message__content"></p>
</slot>
</div>
</transition>
</template>
<script>
export default {
props:{
},
data() {
return {
visible: false,
message: "",
duration: 10000,
closed: false,
verticalOffset: 20,
dangerouslyUseHTMLString: false,
type:'info'
};
},
computed: {
positionStyle() {
return {
top: this.verticalOffset + "px"
};
}
},
watch: {
closed(newVal) {
if (newVal) {
this.visible = false;
}
}
},
mounted() {
this.startTimer();
},
methods: {
startTimer() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
!this.closed && this.close();
}, this.duration);
}
},
close() {
this.closed = true;
}
}
};
</script>
代码地址
/Users/lucy/code/learn-code/learn-element-ui/src/learn_element_ui/packages/message/index.js
购物车动画组件
(将购物车的动画
单独提取出来
抽象为一个组件。
在清华大学的《软件工程》课中,
过程不能定义为对象。
这里是将某一种过程抽象成组件。)
(将购物车的动画
单独提取出来
抽象为一个组件。
在清华大学的《软件工程》课中,
过程不能定义为对象。
这里是将某一种过程抽象成组件。)
位置移动实现
js 钩子动画
/Users/lucy/IT技术教程课件/开课吧前端教程/全栈课程/2 vue/
05课 vue电商项目实战第二节 (2019.5.14)
/vue-mart(1)/src/components/CartAnim.vue:
<transition
@before-enter="beforeEnter"
@enter="enter"
@afterEnter="afterEnter">
<div class="ball" v-show="show" :style="pos">
<div class="inner">
<div class="cubeic-add"></div>
</div>
</div>
</transition>
05课 vue电商项目实战第二节 (2019.5.14)
/vue-mart(1)/src/components/CartAnim.vue:
<transition
@before-enter="beforeEnter"
@enter="enter"
@afterEnter="afterEnter">
<div class="ball" v-show="show" :style="pos">
<div class="inner">
<div class="cubeic-add"></div>
</div>
</div>
</transition>
这里用了 flip 动画的思想:
小球的初始位置是最终目的地,即购物车;
beforeEnter 钩子中将小球移动到点击事件发生的添加物品的图标位置;
enter 钩子中再将小球复原;
afterEnter 中做清理工作
小球的初始位置是最终目的地,即购物车;
beforeEnter 钩子中将小球移动到点击事件发生的添加物品的图标位置;
enter 钩子中再将小球复原;
afterEnter 中做清理工作
start
start(el) {
// 启动动画接口,传递点击按钮元素
this.el = el;
// 使.ball显示,激活动画钩子
this.show = true;
},
// 启动动画接口,传递点击按钮元素
this.el = el;
// 使.ball显示,激活动画钩子
this.show = true;
},
start 函数很简单,只是将 show 赋值为 true,这样就能触发动画钩子了,
动画逻辑都是在动画钩子函数中进行的
动画逻辑都是在动画钩子函数中进行的
beforeEnter
beforeEnter(el) {
// 把小球移动到点击的dom元素所在位置
const rect = this.el.getBoundingClientRect();
// 转换为用于绝对定位的坐标
const x = rect.left - window.innerWidth / 2;
const y = -(window.innerHeight - rect.top - 10 - 20);
// ball只移动y
el.style.transform = `translate3d(0, ${y}px, 0)`;
// inner只移动x
const inner = el.querySelector(".inner");
inner.style.transform = `translate3d(${x}px,0,0)`;
},
// 把小球移动到点击的dom元素所在位置
const rect = this.el.getBoundingClientRect();
// 转换为用于绝对定位的坐标
const x = rect.left - window.innerWidth / 2;
const y = -(window.innerHeight - rect.top - 10 - 20);
// ball只移动y
el.style.transform = `translate3d(0, ${y}px, 0)`;
// inner只移动x
const inner = el.querySelector(".inner");
inner.style.transform = `translate3d(${x}px,0,0)`;
},
enter
enter(el, done) {
// 获取offsetHeight触发重绘
// 经过实验,这里的确必须加这么一句话,并且还只能加在这个位置
// 为什么一定要触发重绘的原因还未弄清楚??
document.body.offsetHeight;
// 将内外层元素的位置通过 transform 属性的 translate3d 函数放到动画结束位置,
// 即此元素的初始位置 translate3d(0, 0, 0)
el.style.transform = `translate3d(0, 0, 0)`;
const inner = el.querySelector(".inner");
inner.style.transform = `translate3d(0,0,0)`;
el.addEventListener("transitionend", done);
},
// 获取offsetHeight触发重绘
// 经过实验,这里的确必须加这么一句话,并且还只能加在这个位置
// 为什么一定要触发重绘的原因还未弄清楚??
document.body.offsetHeight;
// 将内外层元素的位置通过 transform 属性的 translate3d 函数放到动画结束位置,
// 即此元素的初始位置 translate3d(0, 0, 0)
el.style.transform = `translate3d(0, 0, 0)`;
const inner = el.querySelector(".inner");
inner.style.transform = `translate3d(0,0,0)`;
el.addEventListener("transitionend", done);
},
afterEnter
afterEnter(el) {
// 动画结束,开始清理工作
this.show = false;
el.style.display = "none";
this.$emit("transitionend");
}
// 动画结束,开始清理工作
this.show = false;
el.style.display = "none";
this.$emit("transitionend");
}
抛物线动画
外层元素移动 y
transition: all 0.5s cubic-bezier(0.49,-0.29,0.75,0.41); // 这里的贝塞尔曲线是先往上再往下
内层元素移动 x
transition: all 0.5x linear;
不能是单例
使用起来要方便
类似 this.$alert('hh') 的使用方式
动态全局组件
开课吧前端教程/全栈课程/2 vue/05课 vue电商项目实战第二节 (2019.5.14)
/vue-mart(1)/src/utils/create.js:
import Vue from 'vue';
export default function(Component, props) {
const instance = new Vue({
// 使用 render 函数渲染
render(h) {
return h(Component, {props})
}
}).$mount();
// $mount() 方法不允许将 body 作为参数
// 手动将生成dom元素追加至body
document.body.appendChild(instance.$el);
// 获取组件实例
const comp = instance.$children[0];
// 添加 remove 方法
comp.remove = () => {
// 从body中移除dom
document.body.removeChild(instance.$el);
instance.$destroy();
}
return comp;
/vue-mart(1)/src/utils/create.js:
import Vue from 'vue';
export default function(Component, props) {
const instance = new Vue({
// 使用 render 函数渲染
render(h) {
return h(Component, {props})
}
}).$mount();
// $mount() 方法不允许将 body 作为参数
// 手动将生成dom元素追加至body
document.body.appendChild(instance.$el);
// 获取组件实例
const comp = instance.$children[0];
// 添加 remove 方法
comp.remove = () => {
// 从body中移除dom
document.body.removeChild(instance.$el);
instance.$destroy();
}
return comp;
使用
Vue.prototype.$create = create;
import CartAnim from '@/components/CartAnim.vue'
startCartAnim(el) {
const anim = this.$create(CartAnim, {
pos: {left:'46%', bottom:'30px'}
});
anim.start(el);
anim.$on('transitionend', anim.remove) // 'transitionend'事件是 CartAnim.vue 中的 afterEnter 钩子函数里触发的
}
startCartAnim(el) {
const anim = this.$create(CartAnim, {
pos: {left:'46%', bottom:'30px'}
});
anim.start(el);
anim.$on('transitionend', anim.remove) // 'transitionend'事件是 CartAnim.vue 中的 afterEnter 钩子函数里触发的
}
参考资料
/Users/lucy/IT技术教程课件/开课吧前端教程/全栈课程/2 vue/05课 vue电商项目实战第二节 (2019.5.14)
/vue-mart(1)/src/components/CartAnim.vue
/vue-mart(1)/src/components/CartAnim.vue
0 条评论
下一页