首页
写过的文章
Search
1
mock的基本用法
718 阅读
2
js的数组常用方法
693 阅读
3
cnpm安装教程
565 阅读
4
能有效解决问题的提问方法2.0
554 阅读
5
Vue、git指令
490 阅读
默认分类
JavaScript
Vue
TypeScript
登录
Search
标签搜索
底部导航
tabbar
底部导航栏
vue3隐藏底部导航栏
vue3隐藏tabbar
网页
黑白
网页黑白
cnpm安装
cnpm
Z的故事
累计撰写
23
篇文章
累计收到
72
条评论
首页
栏目
默认分类
JavaScript
Vue
TypeScript
页面
写过的文章
搜索到
13
篇与
Vue
的结果
2023-01-30
vue3隐藏底部导航栏
关于vue3移动端底部TabBar隐藏方案方法一利用路由元信息meta设置一个开关按钮,控制底部导航的显示隐藏// router -> index.js { path: '/home', component: () => import('@/views/home/home.vue') },{ path: '/show', component: () => import('@/views/show/show.vue'), meta: { hideTabBar: true } } 在路由中添加meta,给一个布尔值控制导航栏显示隐藏// APP.vue <template> <div class="app"> <router-view /> <tab-bar v-if="!route.meta.hideTabBar"/> <!-- 因为其他页面没有设置所以最终得到的结果是undefined 他的取反结果是true,为true这个tabbar就会显示 --> </div> </template> <script setup> import TabBar from "@/components/tabbar/tabbar.vue" import { useRoute } from "vue-router"; const route = useRoute() <!-- 在script进行route.meta.hideTabBar 在写到template中数据会不响应 --> </script> <style scoped></style> 方法二通过css样式覆盖底部导航栏// 页面 <template> <div class="root">XXX</div> </ template> <style scoped lang="scss"> // 这一块代码可以封装到通用css样式文件中,随用随拿 .root { height: 100vh; // 因为我写的底部导航栏用了固定定位,所以要用相对定位,因为相对定位可以设置z-index position: relative; z-index: 9; background-color: #FFFFFF; //数据过多滚动以后还是会显示出底部导航,这一步是做到只在100vh内容区域进行滚动 overflow-y: auto; } </style> 可能底部导航栏写的不一样,效果会有偏差, 这一块是自己封装的导航栏。
2023年01月30日
421 阅读
0 评论
0 点赞
2022-11-14
vue TagsInput
分享这款最近在写项目用到的一个插件在开发 xxx 管理系统过程中,发现有多处需要用到在 input 输入框中输入标签的需求,于是提取关键词 vue input tags 搜索了相关组件,最后发现 @johmun/vue-tags-input 体积小,易上手,功能丰富,更能满足业务需求和后续可能的需求变更官方文档http://www.vue-tags-input.comhttps://github.com/JohMun/vue-tags-inputhttps://www.npmjs.com/package/@johmun/vue-tags-input特点无需安装其他依赖体积小:34kb minified (css included) | gzipped 9kb更容易上手操作简单可以实现编辑删除功能可以通过粘贴添加标签钩子函数:添加之前 / 删除之前自定义验证规则丰富的自定义配置自定义模版自动补全文档清晰,示例丰富安装NPMnpm install @johmun/vue-tags-input --save CDN<script src="https://unpkg.com/@johmun/vue-tags-input/dist/vue-tags-input.js"></script> 用法<template> <div> <vue-tags-input v-model="tag" :tags="tags" @tags-changed="newTags => tags = newTags" /> </div> </template> <script> import VueTagsInput from '@johmun/vue-tags-input'; export default { components: { VueTagsInput, }, data() { return { tag:'', tags: [], }; }, }; </script> <style scoped style="scss"></style>
2022年11月14日
362 阅读
0 评论
0 点赞
2022-11-03
Vue、git指令
GitGit一个开源的分布式版本控制系统,项目管理工具,git安装请输入链接描述下面是平常在项目中常用的一些指令// 初始化:创建一个git仓库 git init // 添加文件到暂存区 .全部文件添加到暂存区 git add . // 删除文件 git rm filename // 提交文件 git commit -m "提交的说明" // 切换分支 git checkout name // 查看分支 git branch // 创建分支 git branch 分支名 // 查看状态 git status // 合并分支到当前分支 git merge 分支名 // 删除分支 git branch -d 分支名 // 推送分支到远端仓库 git push // 把所有未提交的修改(包括暂存的和非暂存的)都保存起来 git stash // 恢复之前缓存的工作目录 git stash pop // 查看现有stash git stash list // 移除stash git stash drop // 拉取远端仓库分支 git pull // 克隆 git clone 还有就是vue相关的npmnpm 是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。安装完成以后可以通过node -v查看node版本,npm -v查看npmnpm的一些指令// 下载依赖 npm install // 启动项目 npm run serve // 打包 npm run build // 下载包 npm install name // 删除包 npm uninstall name 最后就是Vue的脚手架安装vue-cli(全局安装vue-cli) 命令:cnpm install vue-cli -g检查环境是否安装上:vue -V创建vue项目:在命令行里输入命令:vue init vue_demo,如果不想使用命令,vue 脚手架还可以可视化配置在命令行中输入vue ui最后运行程序:npm run serve
2022年11月03日
490 阅读
9 评论
0 点赞
2022-10-06
防抖节流的基本原理
防抖(Debounce)和节流(Throttle)都是用来控制某个函数在一定时间内触发次数,两者都是为了减少触发频率,以便提高性能或者是避免资源浪费.毕竟JS操作DOM对象的代价还是十分昂贵的.防抖防抖是指在事件触发n秒后在执行回调,如果在n秒内再次触发,则重新计算时间.(就是再触发某个事件后,在下一次触发之前,中间的间隔事件如果超过设置的时间才会发送请求,一直触发就不会发送请求)代码实现:function debounce(fn, delay) { let timeId = null return function() { const context = this // 如果在n秒内从新计算 if(timeId) { window.clearTimeout(timeId) } // 如果在n秒后调用 timeId = setTimeout(() => { fn.apply(context, arguments) timeId = null }, delay) } } 应用场景有:scroll事件滚动触发搜索框输入查询表单验证按钮提交事件浏览器窗口缩放,resize事件节流节流是指如果持续触发某个事件, 则每隔n秒执行一次代码实现:function throttle(fn, delay) { //设置一个触发开关 let canUse = true return function() { //如果为true, 就触发,否则就不触发 if(canUse) { fn.apply(this, arguments) //触发开关后,关闭开关 casUse = false //每隔delay秒执行一次 setTimeout(() => canUse = true, delay) } } } 应用场景有:DOM元素的拖拽功能实现射击游戏类计算鼠标移动的距离监听scroll事件
2022年10月06日
376 阅读
0 评论
0 点赞
2022-09-17
keep-alive
keep-alive在vue-router中使用,保活一个路由组件。一般写法vue3中,对于这个问题,写法有点不一样。<router-view>、<keep-alive> 和 <transition> transition 和 keep-alive 现在必须通过 v-slot API 在 RouterView 内部使用,下面是一个案例:<router-view v-slot="{ Component,route }"> <transition> <keep-alive> <component :is="Component" v-if="route.meta.keepalive==true" :key="route.path" /> </keep-alive> <component :is="Component" v-if="route.meta.keepalive==false" :key="route.path" /> </transition> </router-view>\ 原因: 这是一个必要的变化。详见 related RFC.所以说这里还有其他的信息,transition过度效果,现在也要用这种方式来写了。回顾一下插槽:其中v-slot="{ Component }"这种写法,是解构插槽 Prop,用来解构作用域插槽的参数,作用域插槽是用来向组件提供插槽属性的:绑定在 元素上的 attribute 被称为插槽 prop。现在,在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:// 一个todo-list组件,有一个默认的插槽 <ul> <li v-for="( item, index ) in items"> <slot :item="item" :index="index" :another-attribute="anotherAttribute"></slot> </li> </ul> // 作用域插槽 <todo-list> <template v-slot:default="slotProps"> <i class="fas fa-check"></i> <span class="green">{{ slotProps.item }}</span> </template> </todo-list> 推荐写法keep-aliveProps: include - string | RegExp | Array。只有名称匹配的组件会被缓存。 exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存。 max - number | string。最多可以缓存多少组件实例。 keepAlive本身具有的include去匹配。比如我有一个routes: onst routes = [{ path:"/", component:layout, redirect:"/AccessibleMap", children:[{ path:"page404", name:"page404", meta:{title:"page404",ismenu:false,keepalive:false}, component:()=> import("../pages/page404.vue") }, { path:"AccessibleMap", name:"AccessibleMap", meta:{title:"第一个例子",ismenu:true,keepalive:true}, component:()=> import("../pages/AccessibleMap.vue") }, { path:"tianditu", name:"tianditu", meta:{title:"加载天地图",ismenu:true,keepalive:true}, component:()=> import("../pages/tianditu.vue") }, { path:"baohuo", name:"baohuo", meta:{title:"保活组件",ismenu:true,keepalive:true}, component:()=> import("../pages/baohuo.vue") } ] }]; 在组件里面去引用,只需要过滤出一个需要保活的name数据就可以,比如说“baohuo”这个组件,组件要定义name属性(并不是指上面路由表routes里面那个name,而是baohuo.vue的name)匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称(父组件 components选项的键值)。匿名组件不能被匹配。<template> <div class="catalog"> <router-link class="mylink" active-class="my-active" v-for="item in link" :to="`/${item.path}`"> <span v-if="item.meta.ismenu == true">{{ item.meta.title }}</span> </router-link> </div> <router-view v-slot="{ Component, route }"> <keep-alive :include="keepaliveRoutes"> <component :is="Component" :key="route.path" /> </keep-alive> </router-view> </template> <script lang="ts"> import { defineComponent } from "vue"; import { routes } from "../router" interface idata { link: { path: string; meta: { title: string, ismenu: boolean, keepalive: boolean } }[]; keepaliveRoutes: Array<string>, } export default defineComponent({ data(): idata { return { link: [], keepaliveRoutes:[], } }, components: {}, created() { this.link = routes[0].children.map(e => { return { path: e.path, meta: e.meta } }) routes[0].children.forEach(e=>{ if(e.meta.keepalive == true){ this.keepaliveRoutes.push(e.path) } }) }, }); </script> 普通组件的保活跟上面说的类似。源码分析日常开发中,如果需要在组件切换时,保存组件的状态,防止它多次销毁,多次渲染,我们通常采用 <keep-alive> 组件处理,因为它能够缓存不活动的组件,而不是销毁它们。同时, <keep-alive> 组件不会渲染自己的 DOM 元素,也不会出现在组件父链中,属于一个抽象组件。当组件在 <keep-alive>内被切换时,它的 activated 和 deactivated 这两个钩子函数将会被对应执行。基础用法以下是 <keep-alive> 组件的示例用法<keep-alive :include="['a', 'b']" :max="10"> <component :is="view"></component> </keep-alive> 属性 Propsinclude 字符串或表达式。只有名称匹配的组件会被缓存。exclude 字符串或正则表达式。任务名称匹配的组件都不会被缓存。max 数字。最多可以缓存多少组件实例。注意的是,<keep-alive>组件是用在直属的子组件被开关的情况,若存在多条件性的子元素,则要求同时只能有一个元素被渲染组件源码实现上面我们了解了 <keep-alive>组件的定义、属性以及用法,下面就看下源码是如何对应实现的。抽象组件我们去掉多余的代码,看看KeepAlive组件是如何定义的const KeepAliveImpl = { __isKeepAlive: true, inheritRef: true, props: { include: [String, RegExp, Array], exclude: [String, RegExp, Array], max: [String, Number] }, setup(props: KeepAliveProps, { slots }: SetupContext){ // 省略其他代码... return()=>{ if (!slots.default) { return null } // 拿到组件的子节点 const children = slots.default() // 取第一个子节点 let vnode = children[0] // 存在多个子节点的时候,keepAlive组件不生效了,直接返回 if (children.length > 1) { current = null return children } else if ( !isVNode(vnode) || !(vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) ) { current = null return vnode } // 省略其他代码... // 返回第一个子节点 return vnode } } } 从源码可以看出 KeepAlive 组件是通过 Composition API 实现的,setup 返回的是组件的渲染函数。在渲染函数内,取组件的子节点,当存在多个子节点,则直接返回所有节点,也就 KeepAlive 组件不生效了。当仅存在一个子节点,则渲染第一个子节点的内容,也就验证了 KeepAlive 是抽象组件,不渲染本身的 DOM 元素。缓存机制了解 KeepAlive 组件缓存机制前,我们先了解下 LRU 算法概念,它正是通过该算法来处理缓存机制。LRU 算法我们常用缓存来提升数据查询的数据,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,让新数据添加进来。因此需要制定一些策略对加入缓存的数据进行管理。常见的策略有:LUR 最近最久未使用FIFO 先进先出NRU Clock 置换算法LFU 最少使用置换算法PBA 页面缓冲算法KeepAlive 缓存机制使用的是 LRU 算法(Least Recently Used),当数据在最近一段时间被访问,那么它在以后也会被经常访问。这就意味着,如果经常访问的数据,我们需要能够快速命中,而不常访问的数据,我们在容量超出限制,要将其淘汰。我们这里只讲概念,如果想深入理解 LRU 算法,可自行查找。缓存实现简化下代码,抽离出核心代码,看看缓存机制const KeepAliveImpl = { setup(props){ // 缓存KeepAlive子节点的数据结构{key:vNode} const cache: Cache = new Map() // 保存KeepAlive子节点唯一标识的数据结构 const keys: Keys = new Set() let current: VNode | null = null let pendingCacheKey: CacheKey | null = null // 在beforeMount/Update 缓存子树 const cacheSubtree = () => { if (pendingCacheKey != null) { cache.set(pendingCacheKey, instance.subTree) } } onBeforeMount(cacheSubtree) onBeforeUpdate(cacheSubtree) return ()=>{ pendingCacheKey = null const children = slots.default() let vnode = children[0] const comp = vnode.type as Component const name = getName(comp) // 解构出属性值 const { include, exclude, max } = props // key值是KeepAlive子节点创建时添加的,作为缓存节点的唯一标识 const key = vnode.key == null ? comp : vnode.key // 通过key值获取缓存节点 const cachedVNode = cache.get(key) if (cachedVNode) { // 缓存存在,则使用缓存装载数据 vnode.el = cachedVNode.el vnode.component = cachedVNode.component if (vnode.transition) { // 递归更新子树上的 transition hooks setTransitionHooks(vnode, vnode.transition!) } // 阻止vNode节点作为新节点被挂载 vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE // 让key始终新鲜 keys.delete(key) keys.add(key) } else { keys.add(key) // 属性配置max值,删除最久不用的key,这很符合LRU的思想 if (max && keys.size > parseInt(max as string, 10)) { pruneCacheEntry(keys.values().next().value) } } // 避免vNode被卸载 vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE current = vnode return vnode; } } } 从源码中可以看出 KeepAlive 声明了了个 cache 变量来缓存节点数据,它是 Map 结构。并采用 LRU 缓存算法来处理子节点存储机制,具体说明如下:声明有序集合 keys 作为缓存容器,容器内缓存组件的唯一标识 keykeys 缓存容器中的数据,越靠前的 key 值越少被访问越旧,往后的值越新鲜渲染函数执行时,若命中缓存时,则从 keys 中删除当前命中的 key,并往 keys 末尾追加 key 值,保存新鲜未命中缓存时,则 keys 追加缓存数据 key 值,若此时缓存数据长度大于 max 最大值,则删除最旧的数据,这里的值是 keys中第一个值,很符合 LRU 思想。当触发 beforeMount/update 生命周期,缓存当前激活的子树的数据挂载区别通常组件挂载、卸载都会触发各自生命周期,那 KeepAlive 子树有无缓存在挂载阶段是否存在区别呢?以下抽离下 patch 阶段中ShapeFlags.COMPONENT 类型相关核心代码看看。 const processComponent = (n1: VNode | null,n2: VNode,container: RendererElement,anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null,parentSuspense: SuspenseBoundary | null, isSVG: boolean,optimized: boolean ) => { if (n1 == null) { // 存在COMPONENT_KEPT_ALIVE ,激活n2 if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { ;(parentComponent!.ctx as KeepAliveContext).activate(n2,container,anchor,isSVG,optimized) } else { // 否则,挂载组件 mountComponent(n2,container,anchor,parentComponent,parentSuspense,isSVG,optimized) } } else { // 更新组件 updateComponent(n1, n2, optimized) } } `KeepAlive 组件在渲染函数执行时,若存在缓存,会给 vNode 赋予 `vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE `状态,因此再次渲染该子树时,会执行`parentComponent!.ctx.activate` 函数 激活子树的状态。那这里的 `activate` 函数是什么呢?看下代码` const instance = getCurrentInstance() const sharedContext = instance.ctx as KeepAliveContext sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => { const instance = vnode.component! // 挂载节点 move(vnode, container, anchor, MoveType.ENTER, parentSuspense) // 更新组件,可能存在props发生变化 patch(instance.vnode,vnode,container,anchor,instance,parentSuspense,isSVG,optimized) queuePostRenderEffect(() => { // 组件渲染完成后,执行子节点组件定义的actived钩子函数 instance.isDeactivated = false if (instance.a) {invokeArrayFns(instance.a)} const vnodeHook = vnode.props && vnode.props.onVnodeMounted if (vnodeHook) { invokeVNodeHook(vnodeHook, instance.parent, vnode) } }, parentSuspense) } 再次激活子树时,因为上次渲染已经缓存了vNode,能够从 vNode 直接获取缓存的 DOM 了,也就无需再次转次 vNode。因此可以直接执行move挂载子树,然后再执行 patch 更新组件,最后再通过queuePostRenderEffect,在组件渲染完成后,执行子节点组件定义的activate钩子函数。再看下激活/失效的实现思路,通过将渲染器传入 KeepAlive 实例的 ctx 属性内部,实现 KeepAlive 与渲染器实例的通信,并且通过 KeepAlive 暴露 activate/deactivate 两个实现。这样做的目的是,避免在渲染器直接导入 KeepAlive 产生 tree-shaking。属性实现KeepAlive 支持 3 个属性 include,exclude,max。其中 max 在上面已经讲过了,这里看下另外 2 个属性的实现setup(){ watch(() => [props.include, props.exclude], ([include, exclude]) => { include && pruneCache(name => matches(include, name)) exclude && pruneCache(name => matches(exclude, name)) }) return ()=>{ if ( (include && (!name || !matches(include, name))) || (exclude && name && matches(exclude, name)) ) { return (current = vnode) } } } 这里很好理解,当子组件名称不匹配 include 的配置值,或者子组件名称匹配了 exclude 的值,都不该被缓存,而是直接返回。而 watch 函数是监听 include、exclude 值变化时做出对应反应,即去删除对应的缓存数据。卸载过程卸载分为子组件切换时产生的子组件卸载流程,以及 KeepAlive 组件卸载导致的卸载流程子组件卸载流程组件卸载过程,会执行 unmount 方法,然后执行 parentComponent.ctx.deactivate(vnode)函数,在函数里通过 move 函数移除节点,然后通过 queuePostRenderEffect 的方式执行定义的 deactivated 钩子函数。此过程跟挂载过程类似,不过多描述。KeepAlive 组件卸载当 KeepAlive 组件卸载时,会触发onBeforeUnmount函数,现在看看该函数的实现: onBeforeUnmount(() => { cache.forEach(cached => { const { subTree, suspense } = instance if (cached.type === subTree.type) { resetShapeFlag(subTree) const da = subTree.component!.da da && queuePostRenderEffect(da, suspense) return } unmount(cached) }) }) 当缓存的 vnode 为当前 KeepAlive 组件渲染的 vnode 时,重置 vnode 的 ShapeFlag,让它不被当做是 KeepAlive 的 vNode,然后通过 queuePostRenderEffect 执行子组件的 deactivated 函数,这样就完成了卸载逻辑。否则,则执行 unmount 方法执行 vnode 的整套卸载路程。附:LRU 算法class LRUCache{ constructor(capacity){ this.capacity = capacity || 2 this.cache = new Map() } // 存值,超出最大则默认删除第一个:最近最少被用元素 put(key,val){ if(this.cache.has(key)){ this.cache.delete(key) } if(this.cache.size>=this.capacity){ this.cache.delete(this.cache.keys().next().value) } this.cache.set(key,val) } // 取值,同时刷新缓存新鲜度 get(key){ if(this.cache.has(key)){ const temp = this.cache.get(key) this.cache.delete(key) this.cache.set(key,temp) return temp } return -1 } }
2022年09月17日
416 阅读
0 评论
0 点赞
1
2
3