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