Lison《vue技术栈开发实战》(四)

  • 从SplitPane组件谈Vue中“操作”DOM
    • 简单两列布局
    • 如何让两个div改变宽度
    • 鼠标拖动效果
    • v-model和.sync的用法
  • 渲染函数和JSX快速掌握
    • render函数
    • 函数式组件
    • JSX
    • 作用域插槽
  • 递归组件的使用
    • 封装简单Menu组件
    • 递归组件

从SplitPane组件谈Vue中“操作”DOM

在以往的前端开发中,我们习惯了使用jQuery来操作DOM,比如修改一个div的宽度,需要获取这个div的DOM,然后修改他的style;但是在Vue中,我们是不应该这样做的,而是要换一种思路,即“数据驱动视图”。

简单两列布局

将左边的元素宽度百分比进行设定,右边的元素绝对定位后设置left,就可以实现两个div分别放置。

如何让两个div改变宽度

改变宽度只需要将上述的widthleft变成计算属性即可。

鼠标拖动效果

<template><div class="split-pane-wrapper" ref="outer"><div class="pane pane-left" :style="{ width: leftOffsetPercent, paddingRight: `${this.triggerWidth / 2}px` }"><slot name="left"></slot></div><div class="pane-trigger-con" @mousedown="handleMousedown" :style="{ left: triggerLeft, width: `${triggerWidth}px` }"></div><div class="pane pane-right" :style="{ left: leftOffsetPercent, paddingLeft: `${this.triggerWidth / 2}px` }"><slot name="right"></slot></div></div>
</template>
<script>
export default {name: 'SplitPane',props: {value: {type: Number,default: 0.5},triggerWidth: {type: Number,default: 8},min: {type: Number,default: 0.1},max: {type: Number,default: 0.9}},data () {return {// leftOffset: 0.3,canMove: false,initOffset: 0}},computed: {leftOffsetPercent () {return `${this.value * 100}%`},triggerLeft () {return `calc(${this.value * 100}% - ${this.triggerWidth / 2}px)`}},methods: {handleClick () {this.leftOffset -= 0.02},handleMousedown (event) {document.addEventListener('mousemove', this.handleMousemove)document.addEventListener('mouseup', this.handleMouseup)this.initOffset = event.pageX - event.srcElement.getBoundingClientRect().leftthis.canMove = true},handleMousemove (event) {if (!this.canMove) returnconst outerRect = this.$refs.outer.getBoundingClientRect()let offsetPercent = (event.pageX - this.initOffset + this.triggerWidth / 2 - outerRect.left) / outerRect.widthif (offsetPercent < this.min) offsetPercent = this.minif (offsetPercent > this.max) offsetPercent = this.max// this.$emit('input', offsetPercent)this.$emit('update:value', offsetPercent)},handleMouseup () {this.canMove = false}}
}
</script>
<style lang="less">
.split-pane-wrapper{height: 100%;width: 100%;position: relative;.pane{position: absolute;top: 0;height: 100%;&-left{// width: 30%;background: palevioletred;}&-right{right: 0;bottom: 0;background: paleturquoise;}&-trigger-con{height: 100%;background: red;position: absolute;top: 0;z-index: 10;user-select: none;cursor: col-resize;}}
}
</style>

看下几个注意点:

  • handleMousedown的时候是给body绑定mousemove事件,以便鼠标在整个页面挪动的过程中完成监听
  • initOffset的值是指点下去那一瞬间,鼠标相对于竖杠的偏移,需要进行初始化的记录
  • user-select: none;实现选中不显示,让页面展示的效果相对更加友好

v-model和.sync的用法

v-model我们之前都用过,看下.sync的用法:

//父组件给子组件传入一个函数<MyFooter :age="age" @setAge="(res)=> age = res"></MyFooter>//子组件通过调用这个函数来实现修改父组件的状态。mounted () {console.log(this.$emit('setAge',1234567));}

渲染函数和JSX快速掌握

render函数

看下render能够设置的部分函数值:

 render: h => {return h(CountTo, {'class': [],attrs: {},style: {},props: {endVal: 100},// domProps: {//   innerHTML: '123'// },on: {'on-animation-end': (val) => {console.log('animation end!')}},nativeOn: {'click': () => {console.log('click!')}},directives: [],scopedSlots: {},slot: '',key: '',ref: ''})}render: h => h('div', [h('ul', {on: {'click': handleClick}}, getLiEleArr(h))])

函数式组件

函数式组件是只传递给它数据,不监听传递给它的状态,没有生命周期和钩子函数,只是作为一个接收参数的函数。

<template><ul><li @mousemove="handleMove" v-for="(item, index) in list" :key="`item_${index}`"><!-- <span v-if="!render">{{ item.number }}</span><render-dom v-else :render-func="render" :number="item.number"></render-dom> --><slot name="aa" :number="item.number"></slot><slot :number="item.number"></slot></li></ul>
</template>
<script>
import RenderDom from '_c/render-dom'
export default {name: 'List',components: {RenderDom},props: {list: {type: Array,default: () => []},render: {type: Function,default: () => {}}},methods: {handleMove (event) {event.preventDefault()}}
}
</script>

renderPage

<template><div><list :list="list" :style="{color: 'red'}"><count-to slot="aa" slot-scope="count" :end-val="count.number"></count-to></list></div>
</template>
<script>
import List from '_c/list'
import CountTo from '_c/count-to'
export default {data () {return {list: [{ number: 100 },{ number: 45 }]}},components: {List,CountTo},methods: {renderFunc (h, number) {return (<CountTo nativeOn-click={this.handleClick} on-on-animation-end={this.handleEnd} endVal={number} style={{color: 'pink'}}></CountTo>)},handleClick (event) {// console.log(event)},handleEnd () {// console.log('end!')}}
}
</script>

JSX

看上述page页面的返回值

  <CountTo nativeOn-click={this.handleClick} on-on-animation-end={this.handleEnd} endVal={number} style={{color: 'pink'}}></CountTo>

作用域插槽

我们在渲染list的时候都要进行function导入,很不直观,这里可以使用作用域插槽
看下作用域插槽的取值:

 <slot :number="item.number"></slot>
<count-to slot="aa" slot-scope="count" :end-val="count.number"></count-to>

递归组件的使用

在我们日常开发中,常有一些用到嵌套使用的组件,而且嵌套的深度有时候是根据数据来定的,是在编写程序的时候未知的,所以这样在写页面的时候是没法固定写死的,而是需要通过像类似于递归函数那样,逐层遍历。递归组件和递归函数的使用思想相似,都需要有能够停止递归的条件,通过本节我们将详细学习如何使用递归组件。

封装简单Menu组件

<template><ul class="a-submenu"><div class="a-submenu-title" @click="handleClick"><slot name="title"></slot><span class="shrink-icon" :style="{ transform: `rotateZ(${showChild ? 0 : 180}deg)` }">^</span></div><div v-show="showChild" class="a-submenu-child-box"><slot></slot></div></ul>
</template><script>
export default {nmae: 'ASubmenu',data () {return {showChild: false}},methods: {handleClick () {this.showChild = !this.showChild}}
}
</script><style lang="less">
.a-submenu{background: rgb(33, 35, 39);&-title{color: #fff;position: relative;.shrink-icon{position: absolute;top: 4px;right: 10px;}}&-child-box{overflow: hidden;padding-left: 20px;}li{background: rgb(33, 35, 39);}
}
</style>

递归组件

submenu.vue

<template><a-submenu><div slot="title">{{ parent.title }}</div><template v-for="(item, i) in parent.children"><a-menu-item v-if="!item.children" :key="`menu_item_${index}_${i}`">{{ item.title }}</a-menu-item><re-submenu v-else :key="`menu_item_${index}_${i}`" :parent="item"></re-submenu></template></a-submenu>
</template>
<script>
import menuComponents from '_c/menu'
const { AMenuItem, ASubmenu } = menuComponents
export default {name: 'ReSubmenu',components: {AMenuItem,ASubmenu},props: {parent: {type: Object,default: () => ({})},index: Number}
}
</script>

memuPage

<template><div class="menu-box"><!-- <a-menu><a-menu-item>1111</a-menu-item><a-menu-item>2222</a-menu-item><a-submenu><div slot="title">3333</div><a-menu-item>3333-11</a-menu-item><a-submenu><div slot="title">3333-22</div><a-menu-item>3333-22-11</a-menu-item><a-menu-item>3333-22-22</a-menu-item></a-submenu></a-submenu></a-menu> --><a-menu><template v-for="(item, index) in list"><a-menu-item v-if="!item.children" :key="`menu_item_${index}`">{{ item.title }}</a-menu-item><re-submenu v-else :key="`menu_item_${index}`" :parent="item" :index="index"></re-submenu></template></a-menu></div>
</template>
<script>
import menuComponents from '_c/menu'
import ReSubmenu from './re-submenu.vue'
const { AMenu, AMenuItem, ASubmenu } = menuComponents
export default {name: 'menu_page',components: {AMenu,AMenuItem,ASubmenu,ReSubmenu},data () {return {list: [{title: '1111'},{title: '2222'},{title: '3333',children: [{title: '3333-1'},{title: '3333-2',children: [{title: '3333-2-1'},{title: '3333-2-2'},{title: '3333-2-3',children: [{title: '3333-2-3-1'},{title: '3333-2-3-2'}]}]}]}]}}
}
</script>
<style lang="less">
.menu-box{width: 300px;height: 400px;
}
</style>

递归组件有两个注意点:

  • 一定要写好终止条件
  • 一定要进行命名,以便于继续调用

Lison《vue技术栈开发实战》(四)相关推荐

  1. Lison《vue技术栈开发实战》(二)

    Lison<vue技术栈开发实战>(二) 状态管理bus的使用 父子组件通信 v-model语法糖 使用bus通信 状态管理Vuex(一) state和getter 辅助函数的使用 模块中 ...

  2. Lison《vue技术栈开发实战》(三)

    Lison<vue技术栈开发实战>(三) Ajax请求实战 解决跨域问题 封装axios 请求拦截 响应拦截 队列管理 使用Mock模拟Ajax请求 响应模拟 Mock用法精讲 数据模板 ...

  3. Lison《vue技术栈开发实战》(一)

    Lison<vue技术栈开发实战>(一) 第01章 使用vue-cli3创建项目 使用Vue UI创建.管理项目 项目结构目录整理 初始文件添加 基本配置详解 使用代理解决跨域 第02章 ...

  4. 读书笔记《Spring Boot+Vue全栈开发实战》(下)

    本书将带你全面了解Spring Boot基础与实践,带领读者一步步进入 Spring Boot 的世界. 前言 第九章 Spring Boot缓存 第十章 Spring Boot安全管理 第十一章 S ...

  5. ehcache springboot_阿里内部进阶学习SpringBoot+Vue全栈开发实战文档

    前言 Spring 作为一个轻量级的容器,在JavaEE开发中得到了广泛的应用,但是Spring 的配置烦琐臃肿,在和各种第三方框架进行整合时代码量都非常大,并且整合的代码大多是重复的,为了使开发者能 ...

  6. 《Spring Boot+Vue全栈开发实战》读书笔记

    写在前面 嗯,回家处理一些事,所以离职了,之前的公司用开源技术封装了一套自己的低代码平台,所以之前学的spring Boot之类的东西都忘了很多,蹭回家的闲暇时间复习下. 笔记整体以 Spring B ...

  7. Spring Boot+Vue全栈开发实战——花了一个礼拜读懂了这本书

    很幸运能够阅读王松老师的<Spring Boot+Vue全栈开发实战>这本书!之前也看过Spring Boot与Vue的相关知识,自己也会使用了Spring Boot+Vue进行开发项目. ...

  8. 《SpringBoot+vue全栈开发实战项目》笔记

    前言 Spring 作为一个轻量级的容器,在JavaEE开发中得到了广泛的应用,但是Spring 的配置繁琐臃肿,在和各种第三方框架进行整合时代码量都非常大,并且整合的代码大多是重复的,为了使开发者能 ...

  9. SpringBoot+vue全栈开发实战笔记太香了

    Spring 作为一个轻量级的容器,在JavaEE开发中得到了广泛的应用,但是Spring 的配置繁琐臃肿,在和各种第三方框架进行整合时代码量都非常大,并且整合的代码大多是重复的,为了使开发者能够快速 ...

最新文章

  1. python数据分析常用的算法_萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (上)理解篇...
  2. mark一个subList的坑
  3. windows环境下bat和python调用rysnc的方式
  4. spring boot处理请求返回值的格式(自定义消息转换器)
  5. 全国计算机等级考试题库二级C操作题100套(第88套)
  6. 怎样从Linux终端管理进程:10个你必须知道的命令
  7. 同步工具之Semaphore信号量
  8. python机械_10分钟掌握Python-机器学习小项目
  9. vscode python 远程调试_vscode 远程调试python的方法
  10. 安全行业最全防火墙产品全家福
  11. @程序员,你知道大厂是如何招聘到优秀项目经理的吗?
  12. 移动政务中的小程序技术
  13. 无人驾驶学习笔记-NDT 配准
  14. 数据分析入门之2012美国大选政治献金项目
  15. access统计班级人数_Excel中怎么快速统计成绩表中分数段人数 - 卡饭网
  16. DTI | Drug-target interaction | 基础知识
  17. 上帝视角看 TypeScript
  18. 人民币纸币采用防伪油墨
  19. 蓝牙功率放大器系统性能
  20. Google大数据三大论文

热门文章

  1. 数字逻辑芯片(三态门)
  2. 数组积木问题 c语言,全排列算法及解决数字搭积木问题
  3. Android的无边界程序设计理念
  4. Memory-Based One-Class Collaborative Filtering
  5. MICCAI 2019 参会总结
  6. Windows更新 (KB3045318)过程会暂停sql server相关服务
  7. 《土豆荣耀》重构笔记(三)创建游戏场景
  8. 【Python自动化Excel】Python与pandas字符串操作
  9. SQL中inner join、left join、right join、outer join之间的区别
  10. 集中抄表系统前景分析