vue3 基础-补充 ref & provide-inject
本篇主要对一些被以前内容(渲染, 传值) 等忽略的几个常用小技巧进行补充说明啦.
v-once
即对某个dom节点生效, 其会限定只会渲染一次, 不论数据是如何的变化, 演示如下:
<!DOCTYPE html>
<html lang="en">
<head>
<title>v-once</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
// v-once: 只能渲染一次哦
const app = Vue.createApp({
data () {
return { count: 1 }
},
template: `
<div @click="count += 1" v-once>
{{count}}
</div>
`
})
const vm = app.mount('#root')
</script>
</body>
</html>
只要加上了这个 v-once 指令, 就只能渲染一次啦, 这在一些输入框, 密码框校验, 性能优化等都有应用哦.
ref
其主要应用在响应式数据获取的场景, 实际功能是获取 真实 dom 节点 或 组件引用的一个语法, 后面我们会细讲, 就这里先抛砖引玉一下先.
<!DOCTYPE html>
<html lang="en">
<head>
<title>ref 获取 dom 节点</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
// ref: 获取 dom 节点
const app = Vue.createApp({
data () {
return { count: 1 }
},
mounted () {
this.$refs.count.innerHTML = 'youge'
},
template: `
<div>
<div ref="count">
{{count}}
</div>
</div>
`
})
const vm = app.mount('#root')
</script>
</body>
</html>
这里的 ref 其实就是对数据 count 的一个响应式包装, 使页面加载完后 (mounted) 能够获取到这个 count 相关的dom节点, 进行内容改变和响应式渲染.
但这种 DOM 操作其实非常不建议, 一个是会带来性能问题, 另一个是维护成本的问题.
ref 的另外一种用途是可以获取子组件的引用, 演示如下:
<!DOCTYPE html>
<html lang="en">
<head>
<title>ref 获取子组件的引用</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
// ref 获取子组件的引用
const app = Vue.createApp({
mounted () {
this.$refs.mySon.sayHi()
},
template: `
<div>
<Son ref="mySon" />
</div>
`
})
app.component('Son', {
methods: {
sayHi () {
console.log('hi~')
}
}
})
const vm = app.mount('#root')
</script>
</body>
</html>
这里父组件在调用子组件 Son 的时候, 通过 ref 就可以将子组件包裹在 mySon 这个自定义对象中, 然后在父组件中就可以通过 this.$refs.myson.xxx 的方式来调用子组件的内容啦.
当然这种方式也是不推荐的, 就有点乱和不容易维护, 同时也会带来性能的问题哦.
provice 和 inject
这个 cp 组合主要能解决多层组件传值的问题, 跨过山和大海, 一步到位. 我们先来看如果没有它, 多层组件传值应该是这样的:
<!DOCTYPE html>
<html lang="en">
<head>
<title>多层组件传值</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
// 多层组件传值
const app = Vue.createApp({
data () {
return { count: 1 }
},
template: `
<div>
<son :count=count />
</div>
`
})
// 第一层属性传值
app.component('son', {
props: ['count'],
template: `<grandson :count="count" />`
})
// 第二层属性传值
app.component('grandson', {
props: ['count'],
template: `<div>{{count}}</div>`
})
const vm = app.mount('#root')
</script>
</body>
</html>
会发现这种嵌套传值, count 的层层传递, 如果这么一直下去有点搞死人. 因此为了解决这种场景, vue 就设计了一对 cp 的语法组合, 即可 "provide -> inject".
例如我们这里想要将父组件的数据 count 直接一步到位给到 grandson , 而不通过 son 来弄:
<!DOCTYPE html>
<html lang="en">
<head>
<title>多层组件传值</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
// 多层组件传值
const app = Vue.createApp({
data () {
return { count: 1 }
},
// 通过 provide 将 data () 中的数据传递
provide () {
return {
cj: this.count
}
},
template: `
<div>
<son />
</div>
`
})
// 中间层不用管, 让 provide 跨过山河大海
app.component('son', {
template: `<grandson />`
})
// 哪一层需要用则 直接 inject 注入
app.component('grandson', {
inject: ['cj'],
template: `<div>{{cj}}</div>`
})
const vm = app.mount('#root')
</script>
</body>
</html>
其核心就是这里:
data () { return { count: 1 } },
// 通过 provide 将 data () 中的数据传递
provide () {
return {
cj: this.count
}
// 余下任何子或多层的组件想要用到这个 cj 数据通过 inject: ['cj'] 即可
需要注意的是这里遗留了一个响应式数据问题, 就如上写法只能 provide 一次, 后面当 data ( ) 数据变化时候, provide 是不会响应式的啦. 如果要解决就后面结合 ref, reacitve 等内容啦, 这里先这样吧.
小结
- v-once 指令的作用是让该 dom 只能渲染一次哦
- ref 能够获取 dom 节点和组件的引用, 同时能实现响应式数据渲染
- provide - inject 的组合能解决多层的数据传递问题, 一步到位, 没有中间商赚差价