div滚动到指定位置 vue_【Vue 进阶】从 slot 到无渲染组件
什么是插槽
插槽(slot
)通俗的理解就是“占坑”,在组件模板中占有位置,当使用该组件的时候,可以指定各个坑的内容。也就是我们常说的内容分发
值得一提的是,插槽这个概念并不是 Vue
提出的,而是 web Components
规范草案中就提出的,具体入门可以看 使用 templates and slots[1] ,Vue
只是借鉴了这个思想罢了
在 Vue 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
,这两个目前已被废弃但未被移除且仍在文档中的 attribute
本文的例子基于 Vue 2.6.X,所以用的都是 v-slot 的语法。
本文 DEMO
已全部放到 Github[2] 和 沙箱[3] 中,供大家学习,如有问题,欢迎评论提出。
默认插槽
我们新建父组件 Parent
和子组件 Child
,结构如下:
父组件:
<h3>默认插槽h3><Child> <div class="parent-text">Hi, I am from parent.div>Child>
子组件:
<div class="child"> <slot>slot> <div>Hello, I am from Child.div>div>
父组件调用 Child
组件的时候,会在 Child
标签中将内容传入到子组件中的 标签中,如下所示
也就是最后的渲染结果如下:
<div class="child"> <div class="parent-text">Hi, I am from parent.div> <div>Hello, I am from Child.div>div>
后备内容
我们可以在子组件中的 中加入一些内容,像下面一样
<div class="child"> <slot>当父组件不传值的时候,我就展示,我只是一个后备军slot> <div>Hello, I am from Child.div>div>
当父组件调用的时候, 子组件标签内没有相关的内容时候, 标签内的内容就会生效,否则就不会渲染,可以理解就是个“备胎”
如父组件调用上面子组件:
<h3>后备内容h3> <Child1>Child1>
结果如下:
具名插槽
当然,插槽可以不止一个,这个主要是为了能够灵活的控制插槽的位置以及组件的抽象。我们可以通过在子组件的 slot
标签中设置 name
属性,然后在父组件中通过 v-slot:
(或者使用简写 #
) + 子组件 name
属性值的方式指定要插入的位置。如果是默认插槽的话,v-slot:default
即可
如下父组件:
<h3>具名插槽h3> <Child2> <template v-slot:footer><div>我是底部div>template> <template #header><div>我是头部div>template> <template v-slot:default> <div>我是内容div> template> Child2>
子组件
<div class="child"> <slot name="header">slot> <slot>slot> <div>Hello, I am from Child.div> <slot name="footer">slot> div>
需要留意的是,最后渲染的顺序是以子组件的顺序为主,也就是上面的例子,渲染出来如下:
作用域插槽
有时候,我们想在一个插槽中使用子组件的数据和事件,类似如下(注意:user
是定义在 Child3
组件中的数据):
<Child3> <template> <div>我的名字:{{user.name}}div> <div>我的年龄:{{user.age}}div> <button @click="callMe">Clicl Mebutton> template> Child3>
会直接报错:
原因在于父组件取不到子组件的数据,这里记住一个原则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
那我们怎样才能获取到子组件的数据或者事件呢?我们可以直接在子组件中通过 v-bind
的方式将数据或者事件传递给父组件中,如下所示
<div class="child"> <div>Hello, I am from Child.div>
<slot :user="user" :callMe="callMe">slot> div>
然后在父组件中的插槽内,通过类似 v-slot:default="slotProps"
接受子组件传递过来的数据
<Child3>
<template v-slot:default="slotProps"> <div>我的名字:{{slotProps.user.name}}div> <div>我的年龄:{{slotProps.user.age}}div> <button @click="slotProps.callMe">Clicl Mebutton> template> Child3>
以上 slotProps
可以自定义,而且可以使用解构赋值的语法
<template v-slot:other="{ user, callMe}"> <div>我的名字:{{user.name}}div> <div>我的年龄:{{user.age}}div> <button @click="callMe">Clicl Mebutton>template>
实例:解耦业务逻辑和视图
我们经常会遇到一个场景,就是两个组件的业务逻辑是可以复用的,但是视图却不一样,比如我们经常会有类似切换开关的需求,功能包括:
- 关闭开关
- 打开开关
- 切换开关
- 开关关闭或者打开的时候不一样的内容
我们可以很快的写出它的一个 JS
业务逻辑代码:
export default { data() { return { currentState: this.state } }, props: { state: { type: Boolean, default: false } }, methods: { openState() { this.currentState = true; }, closeState() { this.currentState = false; }, toggle() { this.currentState = !this.currentState; } }}
但是可能现在我的样式一是这样的
然而另外一个地方的样式是这样的(只是举个例子,现实可能更加的复杂,甚至有可能一些按钮直接就隐藏掉了)
这个时候,插槽就派上了用场。上面提到作用域插槽可以将数据和事件从子组件传递给父组件,这就相当于对外暴露了接口。而且可以将 HTML
中的 DOM
以及 CSS
交给父组件(调用方)去维护,子组件通过 标签插入的位置即可,主要逻辑如下:
子组件:
<template> <div class="toggle-container"> <slot :currentState="currentState" :setOn="openState" :setOff="closeState" :toggle="toggle">slot> div>template>
父组件:
<Toggle1 :state="state" class="toggle-container-two"> <template v-slot:default="{currentState, setOn, setOff, toggle }"> <button @click="toggle">切换button> <button @click="setOff">关闭button> <button @click="setOn">打开button> <div v-if="currentState">我是打开的内容div> <div v-else>我是关闭的内容div> template>Toggle1>
我们现在采用的是单文件的方式书写的,实际上子组件还是会有相关的 HTML
结构,如何做到子组件完全不需要渲染自己的 HTML
呢?那得了解下无渲染组件的实现
进阶:无渲染组件的实现
无渲染组件(renderless components)是指一个不需要渲染任何自己的 HTML
的组件。相反,它只管理状态和行为。它会暴露一个单独的作用域,让父组件或消费者完全控制应该渲染的内容。Vue
中,提供了单文件组件的写法。像上面的示例一样,我们始终还是在子组件中进行了一些渲染的操作,那如何做到真正的不渲染组件呢?
比如上面的 toggle
例子,我们已经做到了子组件暴露一个单独的作用域,让父组件或消费者完全控制应该渲染的内容。现在我们需要将单文件中的 template
结构(slot
标签外层的 div
)完全交给父组件,但单文件组件中 slot
标签是不能作为 template
的根元素的
这个时候,我们需要了解一下 Vue 渲染函数(render function
)
归根结底,Vue
及其所有的组件都只是 JavaScript
。单文件组件最后会被构建工具,如 webpack
,将 CSS
抽取形成一个文件,其他的内容会被转换成 JavaScript
,类似如下:
export default { template: <div class="mood">...div>, data: () => ({ todayIsSunny: true })}
当然,这个不是它的最终形态,模板编译器会提取 template
属性内容并将其内容编译为 JavaScript
,然后通过 render
函数添加到组件对象中。最终形态应该是如下:
render(h) { return h( 'div', { class: 'mood' }, this.todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' )}
具体的渲染函数可参见官网[4],虽然写 render
函数的成本会高一些,但是它的性能会比单文件组件好很多。
以上的例子,只有插槽的时候,我们只需要在 render
函数中,使用 this.$scopedSlots.default
代替掉 标签即可
代码如下:
export const toggle = { data() { return { currentState: this.state } }, render() { return this.$scopedSlots.default({ currentState: this.currentState, setOn: this.openState, setOff: this.closeState, toggle: this.toggle, }) }, props: { state: { type: Boolean, default: false } }, methods: { openState() { this.currentState = true; }, closeState() { this.currentState = false; }, toggle() { this.currentState = !this.currentState; } }}
以上就可以做到子组件完全不渲染自己的 HTML
了
总结
本文介绍了一些 Vue
插槽的基本知识,包括
- 默认插槽
- 后备内容
- 具名插槽
- 作用域插槽
然后介绍了一下,如何通过插槽实现业务逻辑和视图的解耦,再结合渲染函数实现真正的无渲染函数
本文 DEMO
已全部放到 Github[5] 和 沙箱[6] 中,供大家学习,如有问题,可以评论提出。
这么用心了,求个赞,哈哈
希望对大家有所帮助~
往期优秀文章推荐
- 【Vue进阶】——如何实现组件属性透传?[7]
- 前端应该知道的 HTTP 知识【金九银十必备】[8]
- 最强大的 CSS 布局 —— Grid 布局[9]
- 如何用 Typescript 写一个完整的 Vue 应用程序[10]
- 前端应该知道的web调试工具——whistle[11]
参考:
Vue 插槽(slot)使用(通俗易懂)[12]
vue 2.6 中 slot 的新用法[13]
(译)函数式组件在Vue.js中的运用[14]
Building “Renderless” Vue Components[15]
参考资料
[1]
使用 templates and slots: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_templates_and_slots
[2]
Github: https://github.com/GpingFeng/vue-slot
[3]
沙箱: https://codesandbox.io/s/hopeful-nash-id826?file=/src/main.js
[4]
官网: https://cn.vuejs.org/v2/guide/render-function.html
[5]
Github: https://github.com/GpingFeng/vue-slot
[6]
沙箱: https://codesandbox.io/s/hopeful-nash-id826?file=/src/main.js
[7]
【Vue进阶】——如何实现组件属性透传?: https://juejin.im/post/6865451649817640968
[8]
前端应该知道的 HTTP 知识【金九银十必备】: https://juejin.im/post/6864119706500988935
[9]
最强大的 CSS 布局 —— Grid 布局: https://juejin.im/post/6854573220306255880
[10]
如何用 Typescript 写一个完整的 Vue 应用程序: https://juejin.im/post/6860703641037340686
[11]
前端应该知道的web调试工具——whistle: https://juejin.im/post/6861882596927504392
[12]
Vue 插槽(slot)使用(通俗易懂): https://juejin.im/post/6844903920037281805
[13]
vue 2.6 中 slot 的新用法: https://juejin.im/post/6844903885476200461
[14]
(译)函数式组件在Vue.js中的运用: https://juejin.im/post/6844903752164442120
[15]
Building “Renderless” Vue Components: https://css-tricks.com/building-renderless-vue-components/
div滚动到指定位置 vue_【Vue 进阶】从 slot 到无渲染组件相关推荐
- div滚动到指定位置 vue_Vue列表实现滚动到指定位置样式改变
这个需求大概是这样子: 我做的一个聊天Demo,在搜索框搜索用户,可以滚动到指定的用户.然后成选中状态. 这是目前状态,我搜索南宫仆射 ,想要下面的用户列表直接滚动到 南宫仆射 并改变CSS样式. 查 ...
- js是滚动条滑到固定位置_js滚动到指定位置
序言:在网络上百度,关键字:"js div滚动到指定位置",结果基本上大同小异!各种大神都给我们总结出来了四种滚动到指定位置的办法,可惜再下愚钝,每个都不会用,所以写了一个超级简单 ...
- vue 点击div 获取位置_vue 点击元素滚动到指定位置
{{item}} {{item}} data() {return{ navgator: ['列表A','列表B','列表C','列表D', ], navgatorIndex:0, listBox: [ ...
- DIV滚动条滚动到指定位置(jquery的position()与offset()方法区别小记)
相对浏览器,将指定div滚到到指定位置,其用法如下 $("html,body").animate({scrollTop: $(obj).offset().top},speed); ...
- vue 网页滚动到指定位置显示动画效果
大部分的网页,在滚动到一定位置的时候,当前区块的文案或者图片有一些飞入,淡入淡出,向上划入的动画效果 //向上划入样式 .boxUp {transform: translateY(0%) !impor ...
- Query实现将div中滚动条滚动到指定位置的方法,html中offsetTop、clientTop、scrollTop、offsetTop各属性的解释
TML精确定位:scrollLeft,scrollWidth,clientWidth,offsetWidth scrollHeight: 获取对象的滚动高度. scrollLeft:设置或获取位于 ...
- 页面滚动到指定位置导航栏固定顶部
做一个网页时经常会用到导航栏,导航栏对于一个网站来说,地位是举足轻重的,在用到导航栏时,我们经常会用到一个效果,就是当页面滑动到一定的位置时,导航栏需要固定在页面的顶部,这是怎么实现的呢? 下面的代码 ...
- 用startSmoothScroll实现RecyclerView滚动到指定位置并置顶,含有动画。
RecyclerView滚动到指定位置并置顶 RecyclerView本身提供了几个定位的方法,除了手动滑动的scrollTo,smootScrollTo和scrollBy,smoothScrollB ...
- 监听某个区域滚动_监听页面滚动及滚动到指定位置
一.原生js通过window.onscroll监听 window.onscroll = function() { //为了保证兼容性,这里取两个值,哪个有值取哪一个 //scrollTop就是触发滚轮 ...
最新文章
- python制作缩略图
- Java基础:网络编程
- UA MATH523A 实分析3 积分理论例题 Fubini定理证明积分不等式
- RedHat 下常见系统故障及恢复方法(整理)
- AM-资产冻结与停用
- Automatic Exploit Generation:漏洞利用自动化
- 当计算机从硬盘读取数据后 将数据,当计算机从硬盘读取数据后,将数据暂时储存在于()...
- idea中配置maven仓库
- finally中关闭资源
- 细说Redirect重定向请求(情节分享)
- OpenWrt常用命令总结
- 捷联惯导系统学习7.1(捷联惯导粗对准 )
- Ubuntu下mysql可视化_ubuntu上mysql有可视化界面吗 ubuntu mysql 图形界面
- 小小串联电阻,大大的作用
- linux jq下载文件,linux 之 jq
- Web项目中手机注册短信验证码实现的全流程及代码
- echarts中改变饼图的颜色
- 2017百度之星资格赛—1003度度熊与邪恶大魔王
- 性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现
- 中国古代数学家张丘建在他的《算经》中提出了一个著名的 “ 百钱买百鸡问题”鸡翁一, 值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何
热门文章
- 第八次课作业(采购管理、信息与配置管理)
- IOSday01 连线和程序标识
- Arduino初初教程10——温湿度传感器
- 服务器无法通过系统非页面共享区来进行分配,因为共享区当前是空的解决办法.供参考....
- apache 下实现防盗链
- 100M独享带宽香港服务器有多快
- 框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)...
- 【Python问题解决】关于解决python3.x无法使用PIL库的解决方法
- 它们的定义app.config中间section节点和在执行中使用
- js 光标移动到输入框最后位置函数