总结vue知识体系之实用技巧

开发 前端
vue 作为目前前端三大框架之一,对于前端开发者可以说是必备技能。那么怎么系统地学习和掌握 vue 呢?为此,我做了简单的知识体系体系总结。

vue 作为目前前端三大框架之一,对于前端开发者可以说是必备技能。那么怎么系统地学习和掌握 vue 呢?为此,我做了简单的知识体系体系总结,不足之处请各位大佬多多包涵和指正,如果喜欢的可以点个小赞!本文主要讲述一些vue开发中的实用技巧。

监听组件的生命周期

比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,常规的写法可能如下:

  1.  // Parent.vue 
  2. <Child @mounted="doSomething"/> 
  3.  
  4. // Child.vue 
  5. mounted() { 
  6.   this.$emit("mounted"); 

此外,还有一种特别简单的方式,子组件不需要任何处理,只需要在父组件引用的时候通过@hook 来监听即可,代码如下:

  1. <Child @hook:mounted="doSomething" /> 
  2. <Child @hook:updated="doSomething" /> 

当然这里不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以。

watch 的初始立即执行

观察和响应 Vue 实例上的数据变动。类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作。

但是当 watch 一个变量的时候,初始化时并不会执行,如下面的例子,你需要在 created 的时候手动调用一次。

  1. created() { 
  2.   this.getList(); 
  3. }, 
  4. watch: { 
  5.   keyWord: 'getList', 

上面这样的做法可以使用,但很麻烦,我们可以添加 immediate 属性,这样初始化的时候就会自动触发(不用再写 created 去调用了),然后上面的代码就能简化为:

  1. watch: { 
  2.   keyWord: { 
  3.     handler: 'getList', 
  4.     immediate: true 
  5.   } 

watch 有三个参数:

  • handler:其值是一个回调函数。即监听到变化时应该执行的函数
  • deep:其值是 true 或 false;确认是否深入监听。
  • immediate:其值是 true 或 false,确认是否以当前的初始值执行 handler 的函数

路由参数变化组件不更新

同一path的页面跳转时路由参数变化,但是组件没有对应的更新。

原因:主要是因为获取参数写在了created或者mounted路由钩子函数中,路由参数变化的时候,这个生命周期不会重新执行。

解决方案1:watch监听路由

  1. watch: { 
  2.  // 方法1 //监听路由是否变化 
  3.   '$route' (to, from) { 
  4.    if(to.query.id !== from.query.id){ 
  5.             this.id = to.query.id; 
  6.             this.init();//重新加载数据 
  7.         } 
  8.   } 
  9. //方法 2  设置路径变化时的处理函数 
  10. watch: { 
  11. '$route': { 
  12.     handler: 'init', 
  13.     immediate: true 
  14.   } 

解决方案2 :为了实现这样的效果可以给router-view添加一个不同的key,这样即使是公用组件,只要url变化了,就一定会重新创建这个组件。

  1. <router-view :key="$route.fullpath"></router-view> 

路由懒加载

Vue 项目中实现路由按需加载(路由懒加载)的 3 中方式:

  1. // 1、Vue异步组件技术: 
  2.  { 
  3.     path: '/home', 
  4.     name: 'Home', 
  5.     component: resolve => reqire(['path路径'], resolve) 
  6.   } 
  7.  
  8. // 2、es6提案的import() 
  9.   const Home = () => import('path路径') 
  10.  
  11. // 3、webpack提供的require.ensure() 
  12.   { 
  13.      path: '/home', 
  14.      name: 'Home', 
  15.      component: r => require.ensure([],() =>  r(require('path路径')), 'demo') 
  16.     } 

require.context()

require.context(directory,useSubdirectories,regExp)

  • directory:说明需要检索的目录
  • useSubdirectories:是否检索子目录
  • regExp:匹配文件的正则表达式,一般是文件名

场景:如页面需要导入多个组件,原始写法:

  1. import titleCom from '@/components/home/titleCom' 
  2. import bannerCom from '@/components/home/bannerCom' 
  3. import cellCom from '@/components/home/cellCom' 
  4. components: { 
  5.   titleCom, bannerCom, cellCom 

这样就写了大量重复的代码,利用 require.context 可以写成

  1. const path = require('path') 
  2. const files = require.context('@/components/home', false, /\.vue$/) 
  3. const modules = {} 
  4. files.keys().forEach(key => { 
  5.   const name = path.basename(key, '.vue') 
  6.   modules[name] = files(key).default || files(key) 
  7. }) 
  8. components: modules 

递归组件

递归组件: 组件在它的模板内可以递归的调用自己,只要给组件设置 name 组件就可以了。

不过需要注意的是,必须给一个条件来限制数量,否则会抛出错误: max stack size exceeded

组件递归用来开发一些具体有未知层级关系的独立组件。比如:联级选择器和树形控件

  1. <template> 
  2.   <div v-for="(item,index) in treeArr"> {{index}} <br/> 
  3.       <tree :item="item.arr" v-if="item.flag"></tree> 
  4.   </div> 
  5. </template> 
  6. <script> 
  7. export default { 
  8.   // 必须定义name,组件内部才能递归调用 
  9.   name: 'tree', 
  10.   data(){ 
  11.     return {} 
  12.   }, 
  13.   // 接收外部传入的值 
  14.   props: { 
  15.      item: { 
  16.       type:Array, 
  17.       default: ()=>[] 
  18.     } 
  19.   } 
  20. </script> 

 

清除定时器或者事件监听

由于项目中有些页面难免会碰到需要定时器或者事件监听。但是在离开当前页面的时候,定时器如果不及时合理地清除,会造成业务逻辑混乱甚至应用卡死的情况,这个时就需要清除定时器事件监听,即在页面卸载(关闭)的生命周期函数里,清除定时器。

  1. methods:{ 
  2.   resizeFun () { 
  3.     this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128 
  4.   }, 
  5.   setTimer() { 
  6.     this.timer = setInterval(() => { }) 
  7.   }, 
  8.   clearTimer() {//清除定时器 
  9.         clearInterval(this.timer) 
  10.     this.timer = null 
  11.     } 
  12. }, 
  13. mounted() { 
  14.   this.setTimer() 
  15.   window.addEventListener('resize', this.resizeFun) 
  16. }, 
  17. beforeDestroy() { 
  18.   window.removeEventListener('resize', this.resizeFun) 
  19.   this.clearTimer() 

自定义路径别名

我们也可以在基础配置文件中添加自己的路径别名

  1. resolve: { 
  2.     extensions: ['.js', '.vue', '.json'], 
  3.     alias: { 
  4.       'vue$': 'vue/dist/vue.esm.js', 
  5.       '@': resolve('src'), 
  6.       'assets': resolve('src/assets') 
  7.     } 
  8.   } 

然后我们导入组件的时候就可以这样写:

  1. // import YourComponent from '/src/assets/YourComponent' 
  2. import YourComponent from 'assets/YourComponent' 

这样既解决了路径过长的麻烦,又解决了相对路径的烦恼。

动态给修改dom的样式

原因:因为我们在写.vue文件中的样式都会追加scoped。这样针对模板dom中的样式就可以生效,但其生效后的最终样式并不是我们写的样式名,而是编码后的。比如:

  1. <template> 
  2.   <div class="box">dom</div> 
  3. </template> 
  4. <style lang="scss" scoped> 
  5.   .box { 
  6.     background: red; 
  7.   } 
  8. </style> 

 

vue 将代码转译成如下,所以我们在js中拼接上的dom结构样式并不会生效。

  1. .box[data-v-11c6864c]{ background:red; } 
  2. <template> 
  3.   <div class="box" data-v-11c6864c>dom</div> 
  4. </template> 

 

解决方法:将要改变的样式写在非scoped样式标签中。

长列表性能优化

我们应该都知道 vue 会通过 object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间。

所以,我们可以通过 object.freeze 方法来冻结一个对象,这个对象一旦被冻结,vue就不会对数据进行劫持了。

  1. export default { 
  2.   data: () => ({ 
  3.     list: [] 
  4.   }), 
  5.   async created() { 
  6.     const list = await axios.get('xxxx') 
  7.     this.list = Object.freeze(list) 
  8.   }, 
  9.   methods: { 
  10.     // 此处做的操作都不能改变list的值 
  11.   } 

另外需要说明的是,这里只是冻结了 list 的值,引用不会被冻结,当我们需要 reactive 数据的时候,我们可以重新给 list 赋值。

内容分发(slot)

插槽 slot,也是组件的一块 HTML 模板,这一块模板显示不显示、以及怎样显示由父组件来决定。实际上,一个 slot 最核心的两个问题在这里就点出来了,是显示不显示和怎样显示。

默认插槽

又名单个插槽、匿名插槽,这类插槽没有具体名字,一个组件只能有一个该类插槽。

  1. <!-- 父组件 parent.vue --> 
  2. <template> 
  3.   <div class="parent"> 
  4.     <h1>父容器</h1> 
  5.     <child> 
  6.       <div class="tmpl"> 
  7.         <span>菜单1</span> 
  8.       </div> 
  9.     </child> 
  10.   </div> 
  11. </template> 
  12.  
  13. <!-- 子组件 child.vue --> 
  14. <template> 
  15.   <div class="child"> 
  16.     <h1>子组件</h1> 
  17.     <slot></slot> 
  18.   </div> 
  19. </template> 

具名插槽

匿名插槽没有 name 属性,所以叫匿名插槽。那么,插槽加了 name 属性,就变成了具名插槽。具名插槽可以在一个组件中出现 N 次,出现在不同的位置,只需要使用不同的 name 属性区分即可。

  1. <!-- 父组件 parent.vue --> 
  2. <template> 
  3.   <div class="parent"> 
  4.     <h1>父容器</h1> 
  5.     <child> 
  6.       <div class="tmpl" slot="up"> 
  7.         <span>菜单up-1</span> 
  8.       </div> 
  9.       <div class="tmpl" slot="down"> 
  10.         <span>菜单down-1</span> 
  11.       </div> 
  12.       <div class="tmpl"> 
  13.         <span>菜单->1</span> 
  14.       </div> 
  15.     </child> 
  16.   </div> 
  17. </template> 
  18.  
  19. <!-- 子组件 child.vue --> 
  20. <template> 
  21.   <div class="child"> 
  22.     <!-- 具名插槽 --> 
  23.     <slot name="up"></slot> 
  24.     <h3>这里是子组件</h3> 
  25.     <!-- 具名插槽 --> 
  26.     <slot name="down"></slot> 
  27.     <!-- 匿名插槽 --> 
  28.     <slot></slot> 
  29.   </div> 
  30. </template> 

作用域插槽

作用域插槽可以是默认插槽,也可以是具名插槽,不一样的地方是,作用域插槽可以为 slot 标签绑定数据,让其父组件可以获取到子组件的数据。

  1. <!-- parent.vue --> 
  2. <template> 
  3.   <div class="parent"> 
  4.     <h1>这是父组件</h1> 
  5.     <child 
  6.       >> 
  7.       <template slot="default" slot-scope="slotProps"> 
  8.         {{ slotProps.user.name }} 
  9.       </template> </child 
  10.     >> 
  11.   </div> 
  12. </template> 
  13.  
  14. <!-- 子组件 child.vue --> 
  15. <template> 
  16.   <div class="child"> 
  17.     <h1>这是子组件</h1> 
  18.     <slot :user="user"></slot> 
  19.   </div> 
  20. </template> 
  21. <script> 
  22.   export default { 
  23.     data() { 
  24.       return { 
  25.         user: { 
  26.           name: '小赵' 
  27.         } 
  28.       } 
  29.     } 
  30.   } 
  31. </script> 

 

 

责任编辑:赵宁宁 来源: 前端先锋队
相关推荐

2024-03-28 08:15:09

Spring技巧配置

2015-07-16 10:15:44

web前端知识体系

2022-03-23 09:18:10

Git技巧Linux

2009-09-04 10:27:28

Linux实用技巧linux操作系统linux

2015-07-28 17:52:36

IOS知识体系

2009-12-21 15:50:39

2019-09-06 08:35:03

TCPIP算法

2012-03-08 11:13:23

企业架构

2017-06-22 13:07:21

2022-11-03 10:28:59

PandasSAC机制

2011-04-08 15:40:01

Oracle认证

2009-01-03 09:34:30

ASP.NET.NET性能优化

2022-10-11 08:00:47

多线程开发技巧

2024-05-17 08:52:43

SQL实用技巧行列转换

2017-04-03 15:35:13

知识体系架构

2017-02-27 16:42:23

Spark识体系

2011-08-11 22:35:58

投影机常见问题

2024-04-29 09:02:46

Vue页面动画样式

2009-12-09 11:21:30

Linux实用技巧

2010-10-08 15:44:17

vim
点赞
收藏

51CTO技术栈公众号