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 相关文章推荐
网站页面自动跳转实现方法PHP、JSP(下)
Aug 01 Javascript
js获取单选框或复选框值及操作
Dec 18 Javascript
script不刷新页面的联动前后代码
Sep 18 Javascript
使用jquery获取网页中图片高度的两种方法
Sep 26 Javascript
jquery form 加载数据示例
Apr 21 Javascript
jquery实现的Accordion折叠面板效果代码
Sep 02 Javascript
jquery ajax异步提交表单数据的方法
Oct 27 jQuery
vue左右侧联动滚动的实现代码
Jun 06 Javascript
更改BootStrap popover的默认样式及popover简单用法
Sep 13 Javascript
JS实现数组删除指定元素功能示例
Jun 05 Javascript
扫微信小程序码实现网站登陆实现解析
Aug 20 Javascript
微信小程序实现分享商品海报功能
Sep 30 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 学习路线与时间表
2010/02/21 PHP
关于Laravel参数验证的一些疑与惑
2019/11/19 PHP
js 复制或插入Html的实现方法小结
2010/05/19 Javascript
javascript 学习笔记(onchange等)
2010/11/14 Javascript
jQuery插件原来如此简单 jQuery插件的机制及实战
2012/02/07 Javascript
jquery图片延迟加载 前端开发技能必备系列
2012/06/18 Javascript
jQuery使用动态渲染表单功能完成ajax文件下载
2013/01/15 Javascript
获取offsetTop和offsetLeft值的js代码(兼容)
2013/04/16 Javascript
jquery仿搜索自动联想功能代码
2014/05/23 Javascript
基于JavaScript实现div层跟随滚动条滑动
2016/01/12 Javascript
微信小程序中hidden不生效原因的解决办法
2017/04/26 Javascript
npm国内镜像 安装失败的几种解决方案
2017/06/04 Javascript
使用jQuery实现动态添加小广告
2017/07/11 jQuery
javascript 产生随机数的几种方法总结
2017/09/26 Javascript
jquery一键控制checkbox全选、反选或全不选
2017/10/16 jQuery
vue中使用better-scroll实现滑动效果及注意事项
2018/11/15 Javascript
借助云开发实现小程序短信验证码的发送
2020/01/06 Javascript
vue.js iview打包上线后字体图标不显示解决办法
2020/01/20 Javascript
JavaScript, select标签元素左右移动功能实现
2020/05/14 Javascript
Vue 实现v-for循环的时候更改 class的样式名称
2020/07/17 Javascript
[01:07]2015国际邀请赛 中国区预选赛精彩回顾
2015/06/15 DOTA
Sublime开发python程序的示例代码
2018/01/24 Python
Python实现使用卷积提取图片轮廓功能示例
2018/05/12 Python
python多线程http压力测试脚本
2019/06/25 Python
基于python2.7实现图形密码生成器的实例代码
2019/11/05 Python
selenium+Chrome滑动验证码破解二(某某网站)
2019/12/17 Python
基于python实现检索标记敏感词并输出
2020/05/07 Python
python 从list中随机取值的方法
2020/11/16 Python
详解pycharm的python包opencv(cv2)无代码提示问题的解决
2021/01/29 Python
两种CSS3伪类选择器详细介绍
2013/12/24 HTML / CSS
加拿大最大的书店:Indigo
2017/01/01 全球购物
WatchShop法国:英国排名第一的独立手表零售商
2020/02/17 全球购物
大学生四年生活自我鉴定
2013/11/21 职场文书
房地产广告词大全
2014/03/19 职场文书
出国留学经济担保书
2014/04/01 职场文书
残联2016年全国助残日活动总结
2016/04/01 职场文书