10个Vue开发技巧助力成为更好的工程师·二

更新 prop 在业务中是很常见的需求,但在子组件中不允许直接修改 prop,因为这种做法不符合单向数据流的原则,在开发模式下还会报出警告。因此大多数人会通过 $emit 触发自定义事件,在父组件中接收该事件的传值来更新 prop。

child.vue:

export defalut { props: { title: String }, methods: { changeTitle(){ this.$emit('change-title', 'hello') } } }

parent.vue:

<child :title="title" @change-title="changeTitle"></child> export default { data(){ return { title: 'title' } }, methods: { changeTitle(title){ this.title = title } } }

这种做法没有问题,我也常用这种手段来更新 prop。但如果你只是想单纯的更新 prop,没有其他的操作。那么 sync 修饰符能够让这一切都变得特别简单。

parent.vue:

<child :title.sync="title"></child>

child.vue:

export defalut { props: { title: String }, methods: { changeTitle(){ this.$emit('update:title', 'hello') } } }

只需要在绑定属性上添加 .sync,在子组件内部就可以触发 update:属性名 来更新 prop。可以看到这种手段确实简洁且优雅,这让父组件的代码中减少一个“没必要的函数”。

provide/inject

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

简单来说,一个组件将自己的属性通过 provide 暴露出去,其下面的子孙组件 inject 即可接收到暴露的属性。

App.vue:

export default { provide() { return { app: this } } }

child.vue:

export default { inject: ['app'], created() { console.log(this.app) // App.vue实例 } }

在 2.5.0+ 版本可以通过设置默认值使其变成可选项:

export default { inject: { app: { default: () => ({}) } }, created() { console.log(this.app) } }

如果你想为 inject 的属性变更名称,可以使用 from 来表示其来源:

export default { inject: { myApp: { // from的值和provide的属性名保持一致 from: 'app', default: () => ({}) } }, created() { console.log(this.myApp) } }

需要注意的是 provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。但是某些时候,或许它能帮助到我们。

小型状态管理器

大型项目中的数据状态会比较复杂,一般都会使用 vuex 来管理。但在一些小型项目或状态简单的项目中,为了管理几个状态而引入一个库,显得有些笨重。

在 2.6.0+ 版本中,新增的 Vue.observable 可以帮助我们解决这个尴尬的问题,它能让一个对象变成响应式数据:

// store.js import Vue from 'vue' export const state = Vue.observable({ count: 0 })

使用:

<div @click="setCount">{{ count }}</div> import {state} from '../store.js' export default { computed: { count() { return state.count } }, methods: { setCount() { state.count++ } } }

当然你也可以自定义 mutation 来复用更改状态的方法:

import Vue from 'vue' export const state = Vue.observable({ count: 0 }) export const mutations = { SET_COUNT(payload) { if (payload > 0) { state.count = payload } } }

使用:

import {state, mutations} from '../store.js' export default { computed: { count() { return state.count } }, methods: { setCount() { mutations.SET_COUNT(100) } } }

卸载watch观察

通常定义数据观察,会使用选项的方式在 watch 中配置:

export default { data() { return { count: 1 } }, watch: { count(newVal) { console.log('count 新值:'+newVal) } } }

除此之外,数据观察还有另一种函数式定义的方式:

export default { data() { return { count: 1 } }, created() { this.$watch('count', function(){ console.log('count 新值:'+newVal) }) } }

它和前者的作用一样,但这种方式使定义数据观察更灵活,而且 $watch 会返回一个取消观察函数,用来停止触发回调:

let unwatchFn = this.$watch('count', function(){ console.log('count 新值:'+newVal) }) this.count = 2 // log: count 新值:2 unwatchFn() this.count = 3 // 什么都没有发生...

$watch 第三个参数接收一个配置选项:

this.$watch('count', function(){ console.log('count 新值:'+newVal) }, { immediate: true // 立即执行watch })

巧用template

相信 v-if 在开发中是用得最多的指令,那么你一定遇到过这样的场景,多个元素需要切换,而且切换条件都一样,一般都会使用一个元素包裹起来,在这个元素上做切换。

<div v-if="status==='ok'"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </div>

如果像上面的 div 只是为了切换条件而存在,还导致元素层级嵌套多一层,那么它没有“存在的意义”。

我们都知道在声明页面模板时,所有元素需要放在 <template> 元素内。除此之外,它还能在模板内使用,<template> 元素作为不可见的包裹元素,只是在运行时做处理,最终的渲染结果并不包含它。

<template> <div> <template v-if="status==='ok'"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> </div> </template>

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgpff.html