Vue.js最佳实践(五招助你成为vuejs大师)


Posted in Javascript onMay 04, 2018

本文面向对象是有一定Vue.js编程经验的开发者。如果有人需要Vue.js入门系列的文章可以在评论区告诉我,有空就给你们写。

对大部分人来说,掌握Vue.js基本的几个API后就已经能够正常地开发前端网站。但如果你想更加高效地使用Vue来开发,成为Vue.js大师,那下面我要传授的这五招你一定得认真学习一下了。

第一招:化繁为简的Watchers

场景还原:

created(){
  this.fetchPostList()
},
watch: {
  searchInputValue(){
    this.fetchPostList()
  }
}

组件创建的时候我们获取一次列表,同时监听input框,每当发生变化的时候重新获取一次筛选后的列表这个场景很常见,有没有办法优化一下呢?

招式解析:

首先,在watchers中,可以直接使用函数的字面量名称;其次,声明immediate:true表示创建组件时立马执行一次。

watch: {
  searchInputValue:{
    handler: 'fetchPostList',
    immediate: true
  }
}

第二招:一劳永逸的组件注册

场景还原:

import BaseButton from './baseButton'
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'
export default {
 components: {
  BaseButton,
  BaseIcon,
  BaseInput
 }
}
<BaseInput
 v-model="searchText"
 @keydown.enter="search"
/>
<BaseButton @click="search">
 <BaseIcon name="search"/>
</BaseButton>

我们写了一堆基础UI组件,然后每次我们需要使用这些组件的时候,都得先import,然后声明components,很繁琐!秉持能偷懒就偷懒的原则,我们要想办法优化!

招式解析:

我们需要借助一下神器webpack,使用 require.context() 方法来创建自己的(模块)上下文,从而实现自动动态require组件。这个方法需要3个参数:要搜索的文件夹目录,是否还应该搜索它的子目录,以及一个匹配文件的正则表达式。

我们在components文件夹添加一个叫global.js的文件,在这个文件里借助webpack动态将需要的基础组件统统打包进来。

import Vue from 'vue'
function capitalizeFirstLetter(string) {
 return string.charAt(0).toUpperCase() + string.slice(1)
}
const requireComponent = require.context(
 '.', false, /\.vue$/
  //找到components文件夹下以.vue命名的文件
)
requireComponent.keys().forEach(fileName => {
 const componentConfig = requireComponent(fileName)
 const componentName = capitalizeFirstLetter(
  fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
  //因为得到的filename格式是: './baseButton.vue', 所以这里我们去掉头和尾,只保留真正的文件名
 )
 Vue.component(componentName, componentConfig.default || componentConfig)
})

最后我们在main.js中 import 'components/global.js' ,然后我们就可以随时随地使用这些基础组件,无需手动引入了。

第三招:釜底抽薪的router key

场景还原:

下面这个场景真的是伤透了很多程序员的心...先默认大家用的是Vue-router来实现路由的控制。

假设我们在写一个博客网站,需求是从/post-page/a,跳转到/post-page/b。然后我们惊人的发现,页面跳转后数据竟然没更新?!原因是vue-router"智能地"发现这是同一个组件,然后它就决定要复用这个组件,所以你在created函数里写的方法压根就没执行。通常的解决方案是监听 $route 的变化来初始化数据,如下:

data() {
 return {
  loading: false,
  error: null,
  post: null
 }
}, 
watch: {
 '$route': {
  handler: 'resetData',
  immediate: true
 }
},
methods: {
 resetData() {
  this.loading = false
  this.error = null
  this.post = null
  this.getPost(this.$route.params.id)
 },
 getPost(id){

 }
}

bug是解决了,可每次这么写也太不优雅了吧?秉持着能偷懒则偷懒的原则,我们希望代码这样写:

data() {
 return {
  loading: false,
  error: null,
  post: null
 }
},
created () {
 this.getPost(this.$route.params.id)
},
methods () {
 getPost(postId) {
  // ...
 }
}

招式解析:

那要怎么样才能实现这样的效果呢,答案是给router-view添加一个unique的key,这样即使是公用组件,只要url变化了,就一定会重新创建这个组件。(虽然损失了一丢丢性能,但避免了无限的bug)。同时,注意我将key直接设置为路由的完整路径,一举两得。

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

第四招: 无所不能的render函数

场景还原:

vue要求每一个组件都只能有一个根元素,当你有多个根元素时,vue就会给你报错

<template>
 <li
  v-for="route in routes"
  :key="route.name"
 >
  <router-link :to="route">
   {{ route.title }}
  </router-link>
 </li>
</template>

 ERROR - Component template should contain exactly one root element.
    If you are using v-if on multiple elements, use v-else-if
    to chain them instead.

招式解析:

那有没有办法化解呢,答案是有的,只不过这时候我们需要使用render()函数来创建HTML,而不是template。其实用js来生成html的好处就是极度的灵活功能强大,而且你不需要去学习使用vue的那些功能有限的指令API,比如v-for, v-if。(reactjs就完全丢弃了template)

functional: true,
render(h, { props }) {
 return props.routes.map(route =>
  <li key={route.name}>
   <router-link to={route}>
    {route.title}
   </router-link>
  </li>
 )
}

第五招:无招胜有招的高阶组件

划重点:这一招威力无穷,请务必掌握

当我们写组件的时候,通常我们都需要从父组件传递一系列的props到子组件,同时父组件监听子组件emit过来的一系列事件。举例子:

//父组件
<BaseInput 
  :value="value"
  label="密码" 
  placeholder="请填写密码"
  @input="handleInput"
  @focus="handleFocus>
</BaseInput>
//子组件
<template>
 <label>
  {{ label }}
  <input
   :value="value"
   :placeholder="placeholder"
   @focus=$emit('focus', $event)"
   @input="$emit('input', $event.target.value)"
  >
 </label>
</template>

有下面几个优化点:

1.每一个从父组件传到子组件的props,我们都得在子组件的Props中显式的声明才能使用。这样一来,我们的子组件每次都需要申明一大堆props, 而类似placeholer这种dom原生的property我们其实完全可以直接从父传到子,无需声明。方法如下:

<input
   :value="value"
   v-bind="$attrs"
   @input="$emit('input', $event.target.value)"
  >

$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定,并且可以通过 v-bind="$attrs" 传入内部组件——在创建更高层次的组件时非常有用。

2.注意到子组件的 @focus=$emit('focus', $event)" 其实什么都没做,只是把event传回给父组件而已,那其实和上面类似,我完全没必要显式地申明:

<input
  :value="value"
  v-bind="$attrs"
  v-on="listeners"
>
computed: {
 listeners() {
  return {
   ...this.$listeners,
   input: event => 
    this.$emit('input', event.target.value)
  }
 }
}

$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

3.需要注意的是,由于我们input并不是BaseInput这个组件的根节点,而默认情况下父作用域的不被认作 props 的特性绑定将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。所以我们需要设置 inheritAttrs:false ,这些默认行为将会被去掉, 以上两点的优化才能成功。

结尾

以上所述是小编给大家介绍的Vue.js最佳实践(五招助你成为vuejs大师),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Jquery Select操作方法集合脚本之家特别版
May 17 Javascript
JavaScript实现生成GUID(全局统一标识符)
Sep 05 Javascript
js 左右悬浮对联广告代码示例
Dec 12 Javascript
微信小程序 开发之滑块视图容器(swiper)详解及实例代码
Feb 22 Javascript
Angular2使用Angular-CLI快速搭建工程(二)
May 21 Javascript
基于vue的短信验证码倒计时demo
Sep 13 Javascript
jQuery实现通过方向键控制div块上下左右移动的方法【测试可用】
Apr 26 jQuery
jQuery实现的点击标题文字切换字体效果示例【测试可用】
Apr 26 jQuery
Vue中android4.4不兼容问题的解决方法
Sep 04 Javascript
vue中el-upload上传图片到七牛的示例代码
Oct 19 Javascript
用js限制网页只在微信浏览器中打开(或者只能手机端访问)
Dec 24 Javascript
JS定时器如何实现提交成功提示功能
Jun 12 Javascript
详解使用jQuery.i18n.properties实现js国际化
May 04 #jQuery
node实现的爬虫功能示例
May 04 #Javascript
基于jQuery.i18n实现web前端的国际化
May 04 #jQuery
JavaScript实现百度搜索框效果
Mar 26 #Javascript
JS与CSS3实现图片响应鼠标移动放大效果示例
May 04 #Javascript
Vue 父子组件数据传递的四种方式( inheritAttrs + $attrs + $listeners)
May 04 #Javascript
JS模拟实现哈希表及应用详解
May 04 #Javascript
You might like
php中通过smtp发邮件的类,测试通过
2007/01/22 PHP
php数组函数序列之end() - 移动数组内部指针到最后一个元素,并返回该元素的值
2011/10/31 PHP
PHP中require和include路径问题详解
2014/12/25 PHP
在Mac上编译安装PHP7的开发环境
2015/07/28 PHP
PHP常见的6个错误提示及解决方法
2016/07/07 PHP
PHP使用mysqli同时执行多条sql查询语句的实例
2019/03/22 PHP
php+redis实现消息队列功能示例
2019/09/19 PHP
取得传值的函数
2006/10/27 Javascript
从jquery的过滤器.filter()方法想到的
2013/09/29 Javascript
jquery实现点击弹出层效果的简单实例
2014/03/03 Javascript
Javascript基础教程之关键字和保留字汇总
2015/01/18 Javascript
JavaScript原型及原型链终极详解
2016/01/04 Javascript
js剪切板应用clipboardData实例解析
2016/05/29 Javascript
JavaScript中this的用法及this在不同应用场景的作用解析
2017/04/13 Javascript
jQuery实现简单的滑动导航代码(移动端)
2017/05/22 jQuery
vue利用better-scroll实现轮播图与页面滚动详解
2017/10/20 Javascript
Servlet3.0与纯javascript通过Ajax交互的实例详解
2018/03/18 Javascript
微信小程序实现左滑动删除效果
2020/03/30 Javascript
Vue的v-model的几种修饰符.lazy,.number和.trim的用法说明
2020/08/05 Javascript
[02:00]最后,我终于出了辉耀
2018/03/27 DOTA
Python跳出循环语句continue与break的区别
2014/08/25 Python
python实现快速排序的示例(二分法思想)
2018/03/12 Python
python-docx修改已存在的Word文档的表格的字体格式方法
2018/05/08 Python
Python实现的NN神经网络算法完整示例
2018/06/19 Python
python3 深浅copy对比详解
2019/08/12 Python
Parfumdreams英国:香水和化妆品
2019/05/10 全球购物
小学安全教育材料
2014/02/17 职场文书
前处理班长职位说明书
2014/03/01 职场文书
聘用意向书范本
2014/04/01 职场文书
《动手做做看》教学反思
2014/04/09 职场文书
幼儿园教研活动总结
2014/04/30 职场文书
城市创卫标语
2014/06/17 职场文书
大学生联谊活动策划书(光棍节)
2014/10/10 职场文书
秋菊打官司观后感
2015/06/03 职场文书
写给医护人员的一封感谢信
2019/09/16 职场文书
一文搞清楚MySQL count(*)、count(1)、count(col)区别
2022/03/03 MySQL