2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Vue_插槽_自定义指令_tabbar案例

Vue_插槽_自定义指令_tabbar案例

时间:2022-11-13 14:16:52

相关推荐

Vue_插槽_自定义指令_tabbar案例

初始化建工程

vue create demo

进入demo里下载安装包

yarn add bootstrap less less-loader@5.0.0 axios

.

建立 vue.config.js

module.exports = {devServer: {open: true,port: 8088 // 自己设置,cmd 同步显示到浏览器 yarn serve 运行},lintOnSave: false // 脚手架自带的eslint代码检查工具 - 先关闭 // publicPath: './'}

组件-插槽

Pannel.vue - 组件(直接复制)

<template><div><!-- 按钮标题 --><div class="title"><h4>芙蓉楼送辛渐</h4><span class="btn" @click="isShow = !isShow">{{ isShow ? "收起" : "展开" }}</span></div><!-- 下拉内容 --><div class="container" v-show="isShow"><p>寒雨连江夜入吴,</p><p>平明送客楚山孤。</p><p>洛阳亲友如相问,</p><p>一片冰心在玉壶。</p></div></div></template><script>export default {data() {return {isShow: false,};},};</script><style scoped>h3 {text-align: center;}.title {display: flex;justify-content: space-between;align-items: center;border: 1px solid #ccc;padding: 0 1em;}.title h4 {line-height: 2;margin: 0;}.container {border: 1px solid #ccc;padding: 0 1em;}.btn {/* 鼠标改成手的形状 */cursor: pointer;}img {width: 50%;}</style>

UserSlot.vue - 使用组件(==直接复制==)

<template><div id="container"><div id="app"><h3>案例:折叠面板</h3></div></div></template><script>export default {};</script><style>#app {width: 400px;margin: 20px auto;background-color: #fff;border: 4px solid blueviolet;border-radius: 1em;box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);padding: 1em 2em 2em;}</style>

UseSlot.vue - 组件插槽使用

<template><div id="container"><div id="app"><h3>案例:折叠面板</h3><Pannel><img src="../assets/mm.gif" alt=""><span>我是内容</span></Pannel><Pannel><p>寒雨连江夜入吴,</p><p>平明送客楚山孤。</p><p>洛阳亲友如相问,</p><p>一片冰心在玉壶。</p></Pannel><Pannel></Pannel></div></div></template><script>import Pannel from "../components/03/Pannel";export default {components: {Pannel,},};</script>

组件-插槽-默认内容

口诀: <slot>夹着内容默认显示内容, 如果不给插槽slot传东西, 则使用<slot>夹着的内容在原地显示

<slot>默认内容</slot>

组件-具名插槽

Pannel.vue - 留下具名slot

<template><div><!-- 按钮标题 --><div class="title"><slot name="title"></slot><span class="btn" @click="isShow = !isShow">{{ isShow ? "收起" : "展开" }}</span></div><!-- 下拉内容 --><div class="container" v-show="isShow"><slot name="content"></slot></div></div></template>

UseSlot.vue使用

<template><div id="container"><div id="app"><h3>案例:折叠面板</h3><Pannel><template v-slot:title><h4>芙蓉楼送辛渐</h4></template><template v-slot:content><img src="../assets/mm.gif" alt=""><span>我是内容</span></template></Pannel><Pannel><template #title><span style="color: red;">我是标题</span></template><template #content><p>寒雨连江夜入吴,</p><p>平明送客楚山孤。</p><p>洛阳亲友如相问,</p><p>一片冰心在玉壶。</p></template></Pannel></div></div></template><script>import Pannel from "../components/04/Pannel";export default {components: {Pannel,},};</script>

组件-作用域插槽

口诀:

子组件, 在slot上绑定属性和子组件内的值

使用组件, 传入自定义标签, 用template和v-slot="自定义变量名"

scope变量名自动绑定slot上所有属性和值

Pannel.vue - 定义组件, 和具名插槽, 给slot绑定属性和值

<template><div><!-- 按钮标题 --><div class="title"><h4>芙蓉楼送辛渐</h4><span class="btn" @click="isShow = !isShow">{{ isShow ? "收起" : "展开" }}</span></div><!-- 下拉内容 --><div class="container" v-show="isShow"><slot :row="defaultObj">{{ defaultObj.defaultOne }}</slot></div></div></template><script>// 目标: 作用域插槽// 场景: 使用插槽, 使用组件内的变量// 1. slot标签, 自定义属性和内变量关联// 2. 使用组件, template配合v-slot="变量名"// 变量名会收集slot身上属性和值形成对象export default {data() {return {isShow: false,defaultObj: {defaultOne: "无名氏",defaultTwo: "小传同学"}};},};</script>

UseSlot.vue

<template><div id="container"><div id="app"><h3>案例:折叠面板</h3><Pannel><!-- 需求: 插槽时, 使用组件内变量 --><!-- scope变量: {row: defaultObj} --><template v-slot="scope"><p>{{ scope.row.defaultTwo }}</p></template></Pannel></div></div></template><script>import Pannel from "../components/05/Pannel";export default {components: {Pannel,},};</script>

组件-作用域插槽-使用场景

MyTable.vue - 模板(==直接复制==)

<template><div><table border="1"><thead><tr><th>序号</th><th>姓名</th><th>年龄</th><th>头像</th></tr></thead><thead><tr><td></td><td></td><td></td><td></td></tr></thead></table></div></template><script>export default {}</script>

UseTable.vue - 准备数据, 传入给MyTable.vue组件里循环使用

list: [{name: "小传同学",age: 18,headImgUrl:"/Upload/./Images/0303/603f2d2153241.jpg",},{name: "小黑同学",age: 25,headImgUrl:"/Upload/./Images/0304/6040b101a18ef.jpg",},{name: "智慧同学",age: 21,headImgUrl:"/Upload/./Images/0302/603e0142e535f.jpg",},],

MyTable.vue - ==正确代码==

<template><div><table border="1"><thead><tr><th>序号</th><th>姓名</th><th>年龄</th><th>头像</th></tr></thead><tbody><tr v-for="(obj, index) in arr" :key="index"><td>{{ index + 1 }}</td><td>{{ obj.name }}</td><td>{{ obj.age }}</td><td><slot :row="obj"><!-- 默认值给上,如果使用组件不自定义标签显示默认文字 -->{{ obj.headImgUrl}}</slot></td></tr></tbody></table></div></template><script>export default {props: {arr: Array}}</script>

在UseTable使用MyTable的时候, template上v-slot绑定变量, 传入img组件设置图片地址

<template><div><MyTable :arr="list"></MyTable><MyTable :arr="list"><!-- scope: {row: obj} --><template v-slot="scope"><a :href="scope.row.headImgUrl">{{ scope.row.headImgUrl }}</a></template></MyTable><MyTable :arr="list"><template v-slot="scope"><img style="width: 100px;" :src="scope.row.headImgUrl" alt=""></template></MyTable></div></template><script>import MyTable from "../components/06/MyTable";export default {components: {MyTable,},data() {return {list: [{name: "小传同学",age: 18,headImgUrl:"/Upload/./Images/0303/603f2d2153241.jpg",},{name: "小黑同学",age: 25,headImgUrl:"/Upload/./Images/0304/6040b101a18ef.jpg",},{name: "智慧同学",age: 21,headImgUrl:"/Upload/./Images/0302/603e0142e535f.jpg",},],};},};</script><style></style>

自定义指令-注册

UseDirective.vue - 只能在当前组件.vue文件中使用

<template><div><!-- <input type="text" v-gfocus> --><input type="text" v-focus></div></template><script>// 目标: 创建 "自定义指令", 让输入框自动聚焦// 1. 创建自定义指令// 全局 / 局部// 2. 在标签上使用自定义指令 v-指令名// 注意:// inserted方法 - 指令所在标签, 被插入到网页上触发(一次)// update方法 - 指令对应数据/标签更新时, 此方法执行export default {data(){return {colorStr: 'red'}},directives: {focus: {inserted(el){el.focus()}}}}</script><style></style>

全局注册

在main.js用 Vue.directive()方法来进行注册, 以后随便哪个.vue文件里都可以直接用v-fofo指令

// 全局指令 - 到处"直接"使用Vue.directive("gfocus", {inserted(el) {el.focus() // 触发标签的事件方法}})

v-model的本质

新建src/components/Add组件, 准备实现双向数据绑定

<template><div><p>子组件库存: {{ value }}</p><button @click="addFn">增加+1</button></div></template><script>export default {props: {value: {type: Number,default: 0}},methods: {addFn () {this.$emit('input', this.value + 1)}}}</script>

在App.vue, 准备变量传入组件中

<template><div><span>父组件库存: {{ count }}</span><hr><AddBtn :value="count" @input="val => count = val"></AddBtn><AddBtn v-model="count"></AddBtn></div></template><script>import AddBtn from '@/components/Add'export default {data () {return {count: 10}},components: {AddBtn}}</script><style></style>

上午总结

App.vue

<template><div><h1>1.组件插槽使用</h1><UseSlot></UseSlot><hr /><h1>2.作用域插槽使用场景</h1><UseTable></UseTable><hr /><h1>3.自定义指令</h1><input type="text" v-fofo /><hr /><h1>4.v-model本质</h1><!-- <input :value="count" @input="fn"> --><!-- vue变量 -> 视图 vue变量赋予给value属性 --><!-- vue变量 <- 视图 @input事件, 把值赋予给vue变量 --><input type="text" v-model="count" /><!-- 结论: v-model语法糖: vue解析这个指令时, 会把v-model转换成上面代码的样子 --><!-- 因为之前@input是原生事件, 触发时, 传递的是事件对象ev --><!-- 现在$emit自己触发的, 传递的值是具体数字 --><!-- <AddBtn :value="count" @input="(num) => { count = num }"></AddBtn> --><AddBtn v-model="count"></AddBtn><!-- 给组件内的value属性进行双向数据绑定 --></div></template><script>// template => 虚拟DOM => 真实DOM<import UseSlot from "./components/UseSlot";import UseTable from "./components/UseTable";import AddBtn from "./components/AddBtn";// 目标: 自动聚焦的命令// 场景: Vue内置的命令, 没有这样的功能// 语法:// 全局注册// import Vue from "vue";// Vue.directive("fofo", {// inserted(el) {//// 在指令所在标签, 当标签被插入到真实DOM后, inserted函数自动触发//// el就是指令所在的标签对象(js原生)//// console.log(el);//el.focus();// },// });export default {data() {return {count: 10,};},methods: {fn(ev) {this.count = ev.target.value;},},components: {UseSlot,UseTable,AddBtn,},// 局部注册directives: {fofo: {inserted(el) {el.focus();},},},};</script><style></style>

./components/UseSlot

<template><div id="container"><div id="app"><h3>案例:折叠面板</h3><Panel><template v-slot:title><h4>芙蓉楼送辛渐</h4></template><template v-slot:content><img src="../assets/mm.gif" alt="" /><span>我是文字内容</span></template></Panel><Panel><template v-slot:title><a href="">不要点我啊~~~</a></template><template #content><p>寒雨连江夜入吴,</p><p>平明送客楚山孤。</p><p>洛阳亲友如相问,</p><p>一片冰心在玉壶。</p></template><!-- scope变量的值:{row: {one: "无名氏",two: "小传同学",}} --><template v-slot:author="scope">{{ scope.row.two }}</template></Panel><Panel> </Panel></div></div></template><script>// 目标: 组件基础使用// 场景: 组件底座(基本)样式不变, 插入的标签不同// 传入数据用props, 传入标签插槽// 术语: 对组件分发不同标签的一种技术// (重要)口诀:// 1. 组件内, 使用slot占位// 2. 父组件上, 使用子组件, 标签夹着的地方传入具体标签替换到slot上// 目标: 插槽默认内容// 口诀: 在slot标签夹着的地方写入默认内容// 效果: 不给slot传入具体标签, 显示默认内容// 目标: 具名插槽// 场景: 组件内遇到多个slot// 口诀:// 1. 在slot标签上加name属性别名// 2. 在使用组件时, 标签夹着地方用template配合v-slot: 插槽名// 效果: 把template夹着的内容配合v-slot分发给对应名字的slot标签处// 目标: 作用域插槽// 场景: 使用组件插槽时, 想使用组件内变量(绑定)// 口诀:// 1. 子组件内, slot上自定义属性和组件内要传出的值(变量)// 2. template身上v-slot="临时变量名"// 3. (效果): 临时变量名就会收集对应slot身上所有属性和值import Panel from "./Panel";export default {components: {Panel,},};</script><style>#app {width: 400px;margin: 20px auto;background-color: #fff;border: 4px solid blueviolet;border-radius: 1em;box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);padding: 1em 2em 2em;}</style>

在 ./components/UseSlot里面引入 ./Panel

<template><div><!-- 按钮标题 --><div class="title"><!-- <h4>芙蓉楼送辛渐</h4> --><slot name="title"></slot><span class="btn" @click="isShow = !isShow">{{ isShow ? "收起" : "展开" }}</span></div><!-- 下拉内容 --><div class="container" v-show="isShow"><slot name="content"><p>恋恋剧中人</p><p>世界微尘里</p></slot><slot name="author" :row="defaultObj">{{ defaultObj.one }}</slot></div></div></template><script>export default {data() {return {isShow: false,defaultObj: {one: "无名氏",two: "小传同学",},};},};</script><style scoped>h3 {text-align: center;}.title {display: flex;justify-content: space-between;align-items: center;border: 1px solid #ccc;padding: 0 1em;}.title h4 {line-height: 2;margin: 0;}.container {border: 1px solid #ccc;padding: 0 1em;}.btn {/* 鼠标改成手的形状 */cursor: pointer;}img {width: 50%;}</style>

./components/UseTable

<template><div><MyTable :arr="list"><!-- scope: {row: 数据对象} --><template v-slot="scope"><img :src="scope.row.headImgUrl" alt="" /></template></MyTable><MyTable :arr="list"> </MyTable><MyTable :arr="list"><!-- scope: {row: 数据对象} --><template v-slot="scope"><a :href="scope.row.headImgUrl">点击图片</a></template></MyTable></div></template><script>import MyTable from "./MyTable";export default {data() {return {list: [{name: "小传同学",age: 18,headImgUrl:"/Upload/./Images/0303/603f2d2153241.jpg",},{name: "小黑同学",age: 25,headImgUrl:"/Upload/./Images/0304/6040b101a18ef.jpg",},{name: "智慧同学",age: 21,headImgUrl:"/Upload/./Images/0302/603e0142e535f.jpg",},],};},components: {MyTable,},};</script><style></style>

在./components/UseTable 里面引入./MyTable

<template><div><table border="1"><thead><tr><th>序号</th><th>姓名</th><th>年龄</th><th>头像</th></tr></thead><tbody><tr v-for="(obj, index) in arr" :key="index"><td>{{ index + 1 }}</td><td>{{ obj.name }}</td><td>{{ obj.age }}</td><td><slot :row="obj"><!-- 默认值给上,如果使用组件不自定义标签显示默认文字 -->{{ obj.headImgUrl }}</slot></td></tr></tbody></table></div></template><script>// 目标: 让当前表格组件, 更灵活, 适应不同项目// 需求: 头像td单元格,放入span/img/a标签// 解决: 插槽技术export default {props: {arr: Array,},};</script>

./components/AddBtn

<template><button @click="btnFn">{{ value }}</button></template><script>export default {props: {value: Number,// 只能用value属性名因为v-model转换成value属性赋值},methods: {btnFn() {this.$emit("input", this.value + 1);// 触发父亲自定义事件名字必须叫input, 因为v-model编译后@input事件名},},};</script><style></style>

显示出来的图

案例-tabbar

初始化项目

cmd命令窗口

输入下载工程

vue create tabbar-democd tabbar-demo 进入下载yarn add bootstrap less less-loader@5.0.0 axiosyarn serve 运行代码

建立 vue.config.js

module.exports = {devServer: {open: true,port: 8082},lintOnSave: false // 脚手架自带的eslint代码检查工具 - 先关闭 // publicPath: './'}

main.js

import Vue from 'vue'import App from './App.vue'import "bootstrap/dist/css/bootstrap.css"import "./assets/fonts/iconfont.css"Vue.config.productionTip = falsenew Vue({render: h => h(App),}).$mount('#app')

App.vue

<template><div><MyHeader content="TabBar案例" colorStr="white"></MyHeader><div style="padding-top: 45px; padding-bottom: 50px"><MyGoodsList></MyGoodsList></div><MyTabBar :list="tabArr"></MyTabBar></div></template><script>// 需求1:// 1.0 创建工程tabbar-demo// 1.1 下载需要的包, main.js引入字体图标和bootstrap// 1.2 创建6个需要的组件(3个复用, 3个页面), 分别填入一些内容即可// 1.3 引入到App.vue中(2个复用,1个页面), 摆好展示// 需求2: 底部导航铺设// 2.0 MyTabBar里一套标签和样式(弹性布局)// 2.1 App 里面准备底部使用字体图标名和显示导航名字, 手动形成一个数组// 2.2 App.vue -> MyTabBar.vue 底部导航的渲染数据// 2.3 在MyTabBar.vue使用v-for循环展示数据和光标// 需求3: 底部导航高亮// 3.0 定义highIndex变量-保存高亮索引值// 3.1 每一项点击事件, 传递索引值下来, 保存到highIndex身上// 3.2 再给class进行判断(用循环自己的索引===高亮highIndex做对比)// 需求5: 插槽技术// 目的: MyTable 组件更加灵活// 5.0 列标题, 抽离出来, slot占位 name="header"// 5.1 template #header传入具体th标签// 5.2 tbody里, slot占位(tr和v-for放在里面)// 5.3 template #tbody传入具体td标签// 需求6: 添加tab// 6.0 准备input和botton按钮(md里复制)// 6.1 点击按钮, 实现input出现, button消失(v-if和v-else)// 6.2 自定义fofo指定-实现输入框自动聚焦// 6.3 监听input失去焦点, 让按钮出现// 6.4 v-model绑定输入框 对象.inputValue属性收集用户的值// 6.5 监听回车按键, 无值提示, 有值加到tag数组里// 6.6 监听esc按键, 清空输入框内容import MyHeader from "./components/MyHeader";import MyTabBar from "./components/MyTabBar";import MyGoodsList from "./views/MyGoodsList";export default {data() {return {tabArr: [{iconName: "icon-home",tabName: "商品列表",},{iconName: "icon-sousuo",tabName: "商品搜索",},{iconName: "icon-user",tabName: "我的信息",},],};},components: {MyHeader,MyTabBar,MyGoodsList,},};</script><style></style>

./components/MyHeader

<template><div class="my-header" :style="{ backgroundColor: bgc, color: colorStr }">{{ content }}</div></template><script>export default {// props:[]// 数组的方式只能起名, 对象可以详细的修饰限制props: {// 格式:/*** 变量名:类型* 变量名:{* type: 类型,* default:默认值* required:true // 此变量必须传入* }*/bgc: String, // 背景颜色的字符串colorStr: {// 文字颜色字符串type: String,default: "black",},content: {// 标题文字type: String,required: true, // 必须传入},},};</script><style lang="less" scoped>.my-header {height: 45px;line-height: 45px;text-align: center;background-color: #1d7bff;color: #fff;position: fixed;top: 0;left: 0;width: 100%;z-index: 2;}</style>

./components/MyTabBar

<template><div class="my-tab-bar"><!-- <div v-if="list.length >= 2 && list.length <= 5"></div> --><divclass="tab-item"v-for="(obj, index) in list":key="index"@click="tabItemFn(index)":class="{ current: index === highIndex }"><!-- 图标 --><span class="iconfont" :class="obj.iconName"></span><!-- 文字 --><span>{{ obj.tabName }}</span></div></div></template><script>export default {props: {list: {type: Array,// defaule:'默认值',// required: true // 必须传值// ... 查阅vue官方文档validator(val) {// 自定义校验规则// 目标: 数组长度只能是2-5个// console.log(val);if (val.length >= 2 && val.length <= 5) {return true; // 通过校验返回true} else {console.error(new Error("底部导航只能2-5个"));return false; // 通不过返回false}},},},data() {return {highIndex: 0,};},methods: {tabItemFn(ind) {this.highIndex = ind;},},};</script><style lang="less" scoped>.my-tab-bar {position: fixed;left: 0;bottom: 0;width: 100%;height: 50px;border-top: 1px solid #ccc;display: flex;justify-content: space-around;align-items: center;background-color: white;.tab-item {display: flex;flex-direction: column;align-items: center;}}.current {color: #1d7bff;}</style>

./views/MyGoodsList

<template><div><MyTable :list="arr"><template #header><th>#</th><th>商品名称</th><th>价格</th><th>标签</th><th>操作</th></template><template #body="scope"><td>{{ scope.row.id }}</td><td>{{ scope.row.goods_name }}</td><td>{{ scope.row.goods_price }}</td><td><inputclass="tag-input form-control"style="width: 100px"type="text"v-if="scope.row.inputVisible"v-fofo@blur="scope.row.inputVisible = false"v-model="scope.row.inputValue"@keyup.enter="enterFn(scope.row)"@keyup.esc="scope.row.inputValue = ''"/><buttonstyle="display: block"class="btn btn-primary btn-sm add-tag"v-else@click="scope.row.inputVisible = true">+Tag</button><spanclass="badge bg-warning text-dark my_tags"v-for="(str, index) in scope.row.tags":key="index">{{ str }}</span></td><td><button class="btn btn-danger btn-sm" @click="delFn(scope.row.id)">删除</button></td></template></MyTable></div></template><script>import axios from "axios";import MyTable from "../components/MyTable.vue";export default {data() {return {arr: [], // 商品数组};},created() {axios({url: "/api/goods",}).then((res) => {this.arr = res.data.data;});},methods: {delFn(id) {let index = this.arr.findIndex((obj) => obj.id === id);this.arr.splice(index, 1);},enterFn(obj) {if (obj.inputValue.length === 0) {return alert("请输入内容");}// 有数据, 加入到数组里obj.tags.push(obj.inputValue);obj.inputValue = "";obj.inputVisible = false;},},components: {MyTable,},directives: {fofo: {inserted(el) {el.focus();},},},};</script><style scoped>.my_tags {margin: 0 5px;}</style>

../components/MyTable.vue

<template><table class="table table-bordered table-stripped"><!-- 表格标题区域 --><thead><tr><!-- <th>#</th><th>商品名称</th><th>价格</th><th>标签</th><th>操作</th> --><slot name="header"></slot></tr></thead><!-- 表格主体区域 --><tbody><tr v-for="obj in list" :key="obj.id"><!-- <td>{{ obj.id }}</td><td>{{ obj.goods_name }}</td><td>{{ obj.goods_price }}</td><td>{{ obj.tags }}</td><td><button class="btn btn-danger btn-sm">删除</button></td> --><slot name="body" :row="obj"></slot></tr></tbody></table></template><script>export default {name: "MyTable",props: {list: Array,},};</script><style scoped lang="less">.my-goods-list {.badge {margin-right: 5px;}}</style>

图例显示

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。