Vue开发中的一些常见套路和技巧(上)

21,439 阅读13分钟

简介

大家好呀,我是 wangly19 ,这次文章主要是来总结下我在使用 Vue.js 总结出来的一些套路,可以做一些查缺补漏。如果还有有趣的小技巧,也可以在评论区给我留言哦,让我也学习一下呀。本文纯文字,无图,建议在地铁坐车的时候看,大部分你可能知道,但也有没见过的。细心查缺补漏以下吧。

在项目开发中,运用好这些小技巧会让你的在开发项目的时候,对于代码的质量和阅读成都会有一个很好的提升。配合我上篇文章食用更佳:

  • 总结我对Vue项目上线做的一些基本优化:点击阅读

正文

开篇先说明下,不一定所有的知识点都是 Vue 中的,而是在Vue使用时候的一些套路,其中也包含一些 JavaScript 的小技巧奥。那么在其他的地方获取你也可以用到哦。

包管理工具

首先对于包管理工具,我现在首推 yarn 了,在使用 npm 的时候,我碰到过丢包,卡包,安装过程死板等问题,所以我毫不犹豫的使用了 yarn 代替了 npm 作为主要的包管理工具。除了上面的一些问题,yarn也给开发者主动的暴露出来了一个 yarn-erro.log 的错误堆叠文档。虽然它看起来有点鸡肋,但也说明了在某些方面还是考虑的非常周到。

package.json

大家都用过不同的脚手架,如: vue-cli create-react-app nest-cli umi-cli , taro-cli 等等开箱即用的一些定制化脚手架,我们只需要通过官方的 demo 和 快速上手 进行安装使用即可。但同样的在如此多的脚手架对 script shell 其实是不友好的,可能有些是使用 run serve 来启动项目,有些是 run start 启动,等等类似的还有很多,所以如果你使用这类脚手架,最好给自己一个统一的 script shell 。这样就不会出险混乱了。我个人使用的是运行: dev ,打包:build。(仅供参考)。

"scripts": {
    "dev": "vue-cli-service serve",
    "build": "vue-cli-service build"
  }

路径别名@...

在真实项目中,为了更好的逻辑分层,或者说进行 monorepo 的时候,项目大大小小的目录非常的嘈杂,这个时候就会因为引用资源进行消费,产生非常多不友好的路径引用。出险非常多的 ../ 等路径的引用,非常的糟糕,因此对某些资源的路径进行代理,就能够非常友好的对于开发者和协同人员进行阅读,而不是重复的套一些无用符号。我在项目中,代码的目录有以下几个。具体可以根据自己爱好进行一些合适的配置。

module.exports = {
  resolve: {
    alias: {
      // 设置路径代理
      '@': resolve('src'),
      'components': resolve('src/components'),
      'api': resolve('src/api'), 
      'icons': resolve('src/icons'),
      '#': resolve('src/assets'),
      'utils': resolve('src/utils')
    }
  }
}

全局sass文件管理

对于使用sass/scss的朋友其实都会定义一些自己个人习惯的一些global style上按涉及到开发的方方面面,例如@function@extends ckass interface@mixins全局变量等等来方便自己开发的进程,但是很多小伙伴都是在每一个component都通过@import来导入,意味着,每一个需要使用它的地方都需要添加@import .../file path来进行引入,这样来说是非常麻烦的。所以我们需要做到一次引用,终身受用,在vuecli中可以非常方便的进行全局引入

css: {
  sourceMap: false,
  loaderOptions: {
    scss: {
      additionalData: `
					@import "~@/assets/styles/norm.scss";
					@import "~@/assets/styles/mixins.scss";
				`
    },
    sass: {
      additionalData: `
					@import "~@/assets/styles/norm.scss"
					@import "~@/assets/styles/mixins.scss"
				`
    }
  }
}

打包后文件汇总

在我们打包Vue项目后,往往dist文件中目录非常杂乱,有很多个js文件都裸露在外边,非常不美观,因此需要设置assetsDir: 'static'打包文件存放目录来进行定义。通过这个方式,那么在打包后会将所有得资源文件都存放在你定义的目录下,我们只需要将index.htmlstatic放到我们nginx中进行服务部署就好了。

module.exports = {
    publicPath: '/',
    outputDir: 'dist',
    lintOnSave: true,
    assetsDir: 'static'
}

Proxy跨域

对于代理跨域都应该不陌生吧,在Vue中,通过配置Proxy能够非常方便的进行域名的代理,当然这需要后台进行一定的配合。如果你的项目涉及到了多方域名,那么也可以设置多个代理。只需要在devServe中加入以下配置。

proxy: {
  "/api": {
    target: 'http://.......',
    changeOrigin: true, // 是否改变域名
    ws: true, // socket
    pathRewrite: {
      // 路径重写
      "/api": '' // 对拼接内容进行重写
    }
  },
  ....
}

对developer和build的打包进行不同配置

大部分开发者都喜欢将Vueconfig写在一个文件中,看起来是没有问题,但是随着环境的变化,项目优化,WebPack插件,等等具有针对性的配置进来后,就会显得稍微杂乱了,这个时候就可以考虑做单独的配置,通过process.dev分别对不同的环境进行一个config的引入,下面贴出我的配置方式。我在项目根目录下新建了一个config目录,里面将公共的方法进行拆包成一个public.js其他的根据生产环境和线下环境进行一个区分的编译。

-- config
--- dev.js
--- build.js
--- public.js
vue.config.js

# 代码 vue.config.js
const devConfig = require('./config/dev')
const buildConfig = require('./config/build')
module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig

全局方法用还是不用

适当的将一些常用的方法挂载在Vueprototype中,也无可厚非。紧紧建议谨慎使用,至今挂在上面的也依旧只有为数不多的localstorage方法。所以说,这个看人喜好吧,我不喜欢用的原因是我喜欢写typeScript,如果在Vue的原型上挂载太多东西,同样的需要进去进行一些类型声明,来声明这个挂载方法的一些凭证。

vue.prototype.local = localStorageUtils

避免组件样式冲突

为了避免在开发过程中产生的冲突,在写Vue Component的时候,有两种方法能够为组件创建独立的样式风格,一种是cssModule,另一种是scoped。大多数时候,我们都是采用scoped的方式来避免组件样式之间相互冲突的,使用方式也非常简单,只需要在style上面绑定scoped就可以了。而cssModule用起来就有点抽象了,当你创建某个css文件的时候,只需要在后面添加.module它就会自动识别为一个cssModule模块,如:global.module.scss

<style scoped>
</style>

保持路由页面名称和组件名称一直

当我们在声明vue-router的时候,我个人建议将name属性和需要挂载的组件name保持一直,因为后续中,可能会涉及到页面数据缓存,页面数据排除等工作,这样就非常方便的可以在keepalive上面去进行一些路由组件和数据的缓存,而不必后续除了问题进行补救。

// component
export default {
	name: 'Home',
    data: () => ({})
    ...
}

// router
{
	path: '/home',
    name: 'Home',
    component: () => import('...')
}

计算属性实用

计算属性的作用非常强大。在开发过程中,个人非常不推荐进行一些复杂的逻辑运算,不仅破坏了template的可阅读性,对于代码扩展也变得非常的艰难。尽量把一些逻辑运算写入到计算属性中去。下面就是个常见的例子:

DEMO:数据过滤

这是一个非常常见的过滤数据的计算属性,主要的作用就是将vFor中需要进行vIf判断的属性先过滤掉,来达到我们需要渲染的正确DOM元素,优化性能的同时对于数据也有很好的管理和扩展性。

// 计算属性
computed: {
  filterList: function () {
  return this.showData.filter(function (data) {
  	// 返回需要显示的数据
    return data.isShow
  })
}
  
// DOM
  
<ul>
  <li v-for="item in filterList" :key="item.id">
  {{ item.name }}
  </li>
</ul>

集合方法

对于option API来说当涉及到表格的增查删改的时候,如果不处理好会有以下情况出现,在我看来完全是有点阅读疲劳,因为这种方法往往并不是很多,且变动的可能性并不是很大。那么我们就可以进行一个工厂式的包装进行统一管理,在出现纰漏的时候可以快速的定位到出现问题的地方。

getData() {}
createRow() {}
updateRow() {}
deleteRow() {}

通过一个公共的方法来管理表格数据的增查删改。当然了,如果对于操作本身的复杂度大的话,那么依旧是考虑进行拆分出代码数多的操作,防止当前工厂出现阅读阻碍。

 tableFactory(action) {
  switch (action) {
    case 'update':
      ...
      break;
    
    case 'create':
      ...
      break;
      
    case 'delete':
      ...
      break;
  
    default:
      // ...默认获取列表
      break;
  }
}

mixin混入

我很早之前我就表明我个人不太喜欢mixin,但无奈但是得用啊。所以取了个折中得办法,对混入mixin定义的变量都带固定的格式。我的命名规则如下。对所有声明都以大写的M作为标记,同样的混入的Methods也是一样的。

Mip: 'xxxx',
Mmsg: 'xxxxxxxxxxxxxxx'

保持对Props的数据验证规范

我个人非常建议对Props进行一些基本的约束,如数据类型,是否必填默认值验证规则等等进行选择,而不是直接通过一个数组,注册一个Porps的命名空间。

props: {
	test: {
    	type: String,
        default: ''
    },
    test2: {
    	type: [Number, String],
        default: 1
    },
    test3: {
    	required: false,
        type: Object
    }
}

v-model 和 sync修饰符

在大多数的时候,在组件中会有很多的小组件,大家都知道,在Vue中对于直接修改props的值是会出现警告的,属于非法行为,虽然它看上去是有效的。当然,按照流程走的话依旧是通过$emit抛出事件和参数,然后父组件接收参数赋值给传递到组件中的值,这样一个父子组件传值的流程就结束了,但这样的步骤是非常嘈杂的,所以在一些简单的数据格式下,我选择使用v-modelsync修饰符来进行数据的传输管理。

自定义组件v-model

首先通过props中注册的value属性,注意必须为value,其实就是使用$emit抛出一个名字为input(必须)的事件,将需要修改的值传递进去,那么就可以在负组件中通过v-model指令进行数据的绑定了。

porps: {
	value: {
    	type: [String, Number]
    }
}

$emit('input', 回馈的内容)

sync修饰符

这个修饰符非常有意思,通过$emit抛出时使用update:绑定的props名称就可以进行父组件值得更新,是不是非常方便。

this.$emit('update:title', 'newTitle')

组件名称使用

大多时候,我们在组件中定义的name都是作为在template模板中使用的名称,这里建议使用驼峰命名,因为在vue中对驼峰命名做出了很好的解析。

// GanMessage.vue组件
export default {
	name: 'GanMessage'
    .....
}

// 引入后使用
components: {
	[GanMessage.name]: GanMessage
}

// 模板中使用
<template>
	<gan-message/>
</template>

slot插槽默认内容

当你在slot中添加内容元素的时候会当作一个默认的内容进行存在,当你使用插槽的时候,会将slot内的东西覆盖掉,作为一个默认的而插内容元素存在。

<slot>
	<p>默认内容</p>
</slot>

模板引擎调试

大多数时候,在template上面写一些逻辑非常难调试,都是直接看效果的,对于一些值来说,变得无法掌控,所以说在开发环境中,我都会在原型上挂一个全局的console.log方法进行调试。

vue.prototype.$logs = window.console.log;

// 使用
<template>
	{{$logs('1111')}}
</template>

过滤器使用

对于过滤器,其实一直都是非常鸡肋的东西,在很多时候都没多少人去使用它,在全局符号和时间格式上我都是使用过滤器来进行约束的,比如需要对时间进行格式花,对数字添加金钱符号,对金钱进行千分逗号问题都可以使用过滤器进行解决。所以在适合的场景下合理的去使用它也是非常方便的。

filters: {
    $time (timeText) {
      return ...
    }
}

获取数据的生命周期

对于数据的获取一直都是又存在争议的,大部分同学都是在created中获取的吧,我个人是在beforeMount中进行后台数据请求获取的

async beforeMount(){
	const data = await getUserInfo();
}

使用async 和 await

大多数时候,在使用Promise的时候都是通过.then.catch.finally来进行处理的。但其实我更加的推荐使用async异步函数的方式来进行Pormise的处理,我们只需要进行数据的获取就好了,通过try异常捕获可以快速的对错误进行一个好的排查和抛出。参考上面获取数据的生命周期可以看到

async beforeMount(){
	try {
      const data = await getUserInfo()
    } catch (error) {
      console.log(error)
    } finally {}
}

管理请求加载状态

在大多数进行数据请求的时候都会添加一个loading的状态显示,友好的给用户或者使用者一些有好的提示,大多数新手前端都是在Promise中成功和失败都进行一次状态的修改。这样无疑是增加了不必要的开销。其实只需要在finally中进行一次更改就好了。既不会多出无用代码,也不会对处理数据产生不必要的干扰。

async beforeMount(){
	// 开始加载
	this.loading = true
	try {
      const data = await getUserInfo()
    } catch (error) {
      console.log(error)
    } finally {
    	// 停止加载
    	this.loading = false
    }
}

尽量避免频繁watch

对于监听器的话尽量一个组件内别去频繁的使用它,如果说你在某个组件频繁的需要使用watch监听多个数据源的变化来进行业务开发,那么是不是说明这个组件或者说这个需求本身存在非常不合理的地方,大多数需求都是主动触发,而不是依赖某个条件作为本次任务的必要条件。所以说,对于监听器尽量减少使用,但如果真的有需要那么使用它也是非常棒的体验。一切都是开发者自主的行为,尝试思考是否需要使用监听器作为最优的解决方案。

开启性能追踪

在vue中有一个非常有意思的东西,那就是Vue.config.performance这个新增的API,按照官方的说法如下:

设置为 true 以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪。只适用于开发模式和支持 performance.mark API 的浏览器上。 @vue.js官方

建议是在开发环境下开启,线上环境就没必要了。在main.js中修改下状态就好了。

vue.config.performance = process.env.NODE_ENV !== "production";

env配置

在脚手架项目中都知道有.env文件,但它们究竟是什么?其实它们也只是一存储静态常量的地方,在使用webpack时,我们所处于的一个过程其实是处于Node.js当中的因此,当中存放的变量多数为一个配置信息。都是属于个人私密信息,所以为了项目安全,上传存储库的时候切记需要删除,避免违法人员通过该账号的信息对你的造成一些困扰。(在Vue中使用需要遵守它的规范添加VUE_APP

NDOE_ENV=development
APP_KEY=***********
HOST_URL=**********

属性排放

对于Vue的Option API排放的问题一直让人咂舌,一千个人有不同的风格,很难做到统一,建议总结一份自己觉得方便的模板做成模板格式,下次通过快捷键自动生成这个架构。在写组件就会得心应手起来了。附上一份我的基础模板

export default {
	name: '名称',
    components: { // 组件挂载a},
    created(){} // 数据获取
    beforeMount() {}, // 数据获取
    data: () => ({}), //响应式数据
    computed: {} // 计算属性集合
    methods: {} // 方法集合
    ... // 销毁页面不要的资源
}

总结

我现在大多数时候,都觉得当使用 Vue 后,很容易触摸到瓶颈。当我想在进行深层次的时候却发现前方的路非常的窄小。大部分时间都是在做一些熟能生巧的东西。技巧虽好,终究离不开成长,起脚讲究熟能生巧,千万不要迷恋于一些技巧性的东西,对于成长毫无作用,甚至会让自己心态觉得自己很强,有很大自信点,其实终究只是一名P0程序员。看过太多的二年程序员使用vue时的一把梭,根本没有任何的逻辑性,类似于为了完成任务而进进行任务,忽略了自身的成长。

这篇文章是一个上篇, 里面涉及的都是非常简单的套路合技巧,并没有涉及到vueRouterVuex ,这个会有下集更新哦。一手信息可以管抓我获取文章推送。

作者现在准备求职了,大专学历。工作也1年多。准备换个环境学习,更新文章慢了,如果文章有用,学到东西了,那就随手点个赞吧。有内推机会也可以滴滴我哦。