vue技术分享之你可能不知道的7个秘密


Posted in Javascript onApril 09, 2018

前言

本文是vue源码贡献值Chris Fritz在公共场合的一场分享,觉得分享里面有不少东西值得借鉴,虽然有些内容我在工作中也是这么做的,还是把大神的ppt在这里翻译一下,希望给朋友带来一些帮助。

一、善用watch的immediate属性

这一点我在项目中也是这么写的。例如有请求需要再也没初始化的时候就执行一次,然后监听他的变化,很多人这么写:

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

上面的这种写法我们可以完全如下写:

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>

步骤一般有三部,

第一步,引入、

第二步注册、

第三步才是正式的使用,

这也是最常见和通用的写法。但是这种写法经典归经典,好多组件,要引入多次,注册多次,感觉很烦。

我们可以借助一下webpack,使用 require.context() 方法来创建自己的(模块)上下文,从而实现自动动态require组件。

思路是:在src文件夹下面main.js中,借助webpack动态将需要的基础组件统统打包进来。

代码如下:

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

// Require in a base component context
const requireComponent = require.context(
 ‘./components', false, /base-[\w-]+\.vue$/
)

requireComponent.keys().forEach(fileName => {
 // Get component config
 const componentConfig = requireComponent(fileName)

 // Get PascalCase name of component
 const componentName = upperFirst(
  camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
 )

 // Register component globally
 Vue.component(componentName, componentConfig.default || componentConfig)
})

这样我们引入组件只需要第三步就可以了:

<BaseInput
 v-model="searchText"
 @keydown.enter="search"
/>
<BaseButton @click="search">
 <BaseIcon name="search"/>
</BaseButton>

三、精简vuex的modules引入

对于vuex,我们输出store如下写:

import auth from './modules/auth'
import posts from './modules/posts'
import comments from './modules/comments'
// ...

export default new Vuex.Store({
 modules: {
  auth,
  posts,
  comments,
  // ...
 }
})

要引入好多modules,然后再注册到Vuex.Store中~~

精简的做法和上面类似,也是运用 require.context()读取文件,代码如下:

import camelCase from 'lodash/camelCase'
const requireModule = require.context('.', false, /\.js$/)
const modules = {}
requireModule.keys().forEach(fileName => {
 // Don't register this file as a Vuex module
 if (fileName === './index.js') return

 const moduleName = camelCase(
  fileName.replace(/(\.\/|\.js)/g, '')
 )
 modules[moduleName] = {
        namespaced: true,
        ...requireModule(fileName),
       }
})
export default modules

这样我们只需如下代码就可以了:

import modules from './modules'
export default new Vuex.Store({
 modules
})

四、路由的延迟加载

这一点,关于vue的引入,我之前在 vue项目重构技术要点和总结 中也提及过,可以通过require方式或者import()方式动态加载组件。

{
 path: '/admin',
 name: 'admin-dashboard',
 component:require('@views/admin').default
}

或者

{
 path: '/admin',
 name: 'admin-dashboard',
 component:() => import('@views/admin')
}

加载路由。

五、router key组件刷新

下面这个场景真的是伤透了很多程序员的心...先默认大家用的是Vue-router来实现路由的控制。 假设我们在写一个博客网站,需求是从/post-haorooms/a,跳转到/post-haorooms/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添加一个唯一的key,这样即使是公用组件,只要url变化了,就一定会重新创建这个组件。

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

注:我个人的经验,这个一般应用在子路由里面,这样才可以不避免大量重绘,假设app.vue根目录添加这个属性,那么每次点击改变地址都会重绘,还是得不偿失的!

六、唯一组件根元素

场景如下:

(Emitted value instead of an instance of Error)
  Error compiling template:

  <div></div>
  <div></div>

  - 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.

模板中div只能有一个,不能如上面那么平行2个div。

例如如下代码:

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

会报错!

我们可以用render函数来渲染

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>

这样写很不精简,很多属性和事件都是手动定义的,我们可以如下写:

<input
  :value="value"
  v-bind="$attrs"
  v-on="listeners"
>

computed: {
 listeners() {
  return {
   ...this.$listeners,
   input: event => 
    this.$emit('input', event.target.value)
  }
 }
}

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

$listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

总结

以上所述是小编给大家介绍的vue技术分享之你可能不知道的7个秘密,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
extjs表格文本启用选择复制功能具体实现
Oct 11 Javascript
JS图像无缝滚动脚本非常好用
Feb 10 Javascript
Actionscript与javascript交互实例程序(修改)
Sep 22 Javascript
jQuery实现复选框的全选和反选
Feb 02 Javascript
Vue.js 2.0 移动端拍照压缩图片上传预览功能
Mar 06 Javascript
详解axios在node.js中的post使用
Apr 27 Javascript
webpack配置打包后图片路径出错的解决
Apr 26 Javascript
用POSTMAN发送JSON格式的POST请求示例
Sep 04 Javascript
layer.alert自定义关闭回调事件的方法
Sep 27 Javascript
JavaScript实现图片轮播特效
Oct 23 Javascript
AngularJS动态生成select下拉框的方法实例
Nov 17 Javascript
vue 使用 sortable 实现 el-table 拖拽排序功能
Dec 26 Vue.js
一步步教会你微信小程序的登录鉴权
Apr 09 #Javascript
vue组件详解之使用slot分发内容
Apr 09 #Javascript
vue组件中使用props传递数据的实例详解
Apr 08 #Javascript
Vue入门之animate过渡动画效果
Apr 08 #Javascript
vue组件与复用详解
Apr 08 #Javascript
vue使用vue-i18n实现国际化的实现代码
Apr 08 #Javascript
Vue中的无限加载vue-infinite-loading的方法
Apr 08 #Javascript
You might like
重置版游戏视频
2020/04/09 魔兽争霸
PHP 裁剪图片成固定大小代码方法
2009/09/09 PHP
浅谈PHP与C#的值类型指向区别的详解
2013/05/21 PHP
yii的CURD操作实例详解
2014/12/04 PHP
php操作redis缓存方法分享
2015/06/03 PHP
JQuery下的Live方法和$.browser方法使用代码
2010/06/02 Javascript
Jquery CheckBox全选方法代码附js checkbox全选反选代码
2010/06/09 Javascript
Bootstrap基本样式学习笔记之图片(6)
2016/12/07 Javascript
bootstrap实现每隔5秒自动轮播效果
2016/12/20 Javascript
解决jQuery使用append添加的元素事件无效的问题
2018/08/30 jQuery
vue 使用axios 数据请求第三方插件的使用教程详解
2019/07/05 Javascript
vue路由教程之静态路由
2019/09/03 Javascript
解决layui表格内文本超出隐藏的问题
2019/09/12 Javascript
Vue基础配置讲解
2019/11/29 Javascript
node.js中stream流中可读流和可写流的实现与使用方法实例分析
2020/02/13 Javascript
javascript实现倒计时效果
2020/02/17 Javascript
浅谈vue中$event理解和框架中在包含默认值外传参
2020/08/07 Javascript
微信小程序以7天为周期连续签到7天功能效果的示例代码
2020/08/20 Javascript
Vue 实现监听窗口关闭事件,并在窗口关闭前发送请求
2020/09/01 Javascript
js实现鼠标切换图片(无定时器)
2021/01/27 Javascript
[52:52]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第三局
2016/02/27 DOTA
Python实例分享:快速查找出被挂马的文件
2014/06/08 Python
Python中tell()方法的使用详解
2015/05/24 Python
python简单实现基于SSL的IRC bot实例
2015/06/15 Python
Python 新建文件夹与复制文件夹内所有内容的方法
2018/10/27 Python
使用pycharm在本地开发并实时同步到服务器
2019/08/02 Python
Python+Tensorflow+CNN实现车牌识别的示例代码
2019/10/11 Python
python实现随机加减法生成器
2020/02/24 Python
Django admin 实现search_fields精确查询实例
2020/03/30 Python
利用python+request通过接口实现人员通行记录上传功能
2021/01/13 Python
Stuart Weitzman美国官网:美国奢华鞋履品牌
2016/08/18 全球购物
Sephora丝芙兰印尼官方网站:购买化妆品和护肤品
2018/07/02 全球购物
优秀技术工人先进材料
2014/02/17 职场文书
习近平在党的群众路线教育实践活动总结大会上的讲话全文
2014/10/25 职场文书
使用 Docker Compose 构建复杂的多容器App
2022/04/30 Servers
Python 一键获取电脑浏览器的账号密码
2022/05/11 Python