一、VUE中的遗漏的知识
render 中的 h 函数
h 函数可以创建虚拟 dom,通过创建的虚拟 dom 再转化为真的的 DOM,从而渲染到页面中。
<script>
// render function
// template -> render -> h -> 虚拟DOM(JS对象)-> 真实 DOM -> 展示到页面上
const app = Vue.createApp({
template: `
<my-title :level="2">
hello xiaokang
</my-title>
`
})
app.component('my-title', {
props: ['level'],
render() {
const { h } = Vue
return h('h' + this.level, {}, [
this.$slots.default(),
h('h4', {}, 'xiaokang')
])
}
})
const vm = app.mount('#root')
</script>
h 函数第一个参数为
tag name
,也就是你需要创建的标签名,第二参数为此标签上的属性,第三个参数为其内容数组。
插件
vue 中插件主要用于把一些通用性的功能封装起来
插件定义的基本语法
const myPlugin = {
install(app, options) {}
}
当使用插件时,会调用插件的 install 方法。第一个参数 app 为生成的 app 对象,第二个参数为传递的参数。
<script>
// plugin 插件, 也是把通用性的功能封装起来
const myPlugin = {
install(app, options) {
// 提供和注入
app.provide('name', 'xiaokang')
// 自定义指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 混入
app.mixin({
mounted() {
console.log('mixin')
}
})
// 全局属性
app.config.globalProperties.$sayHello = 'hello world'
}
}
const app = Vue.createApp({
template: `
<my-title />
`
})
app.component('my-title', {
inject: ['name'],
mounted() {
console.log(this.$sayHello)
},
template: `<div>{{name}}<input v-focus /></div>`
})
app.use(myPlugin, { name: 'xiaokang' })
const vm = app.mount('#root')
</script>
数据校验插件
const app = Vue.createApp({
data() {
return { name: 'dell', age: 23 }
},
rules: {
age: {
validate: (age) => age > 25,
message: 'too young, to simple'
},
name: {
validate: (name) => name.length >= 4,
message: 'name too short'
}
},
template: `
<div>name:{{name}}, age:{{age}}</div>
`
})
// 对数据做校验的插件
const validatorPlugin = (app, options) => {
app.mixin({
created() {
for (let key in this.$options.rules) {
const item = this.$options.rules[key]
this.$watch(key, (value) => {
const result = item.validate(value)
if (!result) console.log(item.message)
})
}
}
})
}
app.use(validatorPlugin)
const vm = app.mount('#root')
Teleport 传送门-vue3
主要用于将 dom 元素挂载到其他位置,例如挂载到head
里。
<body>
<div id="root"></div>
</body>
<script>
// teleport 传送门
const app = Vue.createApp({
data() {
return {
show: false,
message: 'hello'
}
},
methods: {
handleBtnClick() {
this.show = !this.show
}
},
template: `
<div class="area">
<button @click="handleBtnClick">按钮</button>
<teleport to="body">
<div class="mask" v-show="show">{{message}}</div>
</teleport>
</div>
`
})
const vm = app.mount('#root')
</script>
components API-vue3
setup 函数
该函数会在创建组件之前执行,由于在执行 setup
时尚未创建组件实例,因此在 setup
选项中没有 this
。这意味着,除了 props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
// 对数据做校验的插件
const app = Vue.createApp({
template: `
<div @click="handleClick">{{name}}</div>
`,
methods: {
test() {
console.log(this.$options.setup())
}
},
// 实例可以调用setup方法
mounted() {
this.test()
},
// 这里不能调用this
setup(props, context) {
return {
name: 'xiaokang',
handleClick: () => {
alert(123)
}
}
}
})
const vm = app.mount('#root')
setup 里返回的内容可以在实例中调用。
响应式引用与只读
主要用于将setup
中的变量转换为响应式的变量。默认变量并不是响应式的。
其中ref
处理基础类型的数据,reactive
用于处理非基础类型的数据(对象和数组)。
方法名 | 作用 |
---|---|
ref | 将基础类型数据转化为响应式数据 |
reactive | 将非基础类型的数据(对象和数组)。 |
readonly | 将数据设为只读 |
toRefs | 将非基础类型的子元素设置为响应式数据 |
基础类型引用
setup(props, context) { const { ref } = Vue let name = ref('1') setTimeout(() => { name.value = '2' }, 2000) return { name } }
通过 ref 包装后,name 实则变成了
proxy({value: '2'})
这样的引用,当修改值时需要修改 name 的 value 属性。但是调用时不需要使用value
,vue 会识别并自动调用。非基础类型引用
setup(props,context){ const { reactive, readonly, toRefs } = Vue const nameObj = reactive({ name: 'xiaokang', age: 21 }) setTimeout(() => { nameObj.age = '22' }, 2000) return { nameObj } }
通过
reactive
包装后,nameObj 就是响应式的了。组合使用
在非响应式引用里,只有整个对象是响应式的,而对象里的某个属性并不是响应式的,因此,需要将这个对象再次进行包装才可以使其属性变成响应式的。
setup(props,context){ const { reactive, readonly, toRefs } = Vue const nameObj = reactive({ name: 'xiaokang', age: 21 }) setTimeout(() => { nameObj.age = '22' }, 2000) const { name, age } = toRefs(nameObj) return { age } }
只读
const nameObj = reactive({ name: 'xiaokang', age: 22 }) const nameObjCopy = readonly(nameObj) setTimeout(() => { nameObj.age = 21 nameObjCopy.age = 23 }, 2000)
参考:https://vue3js.cn/docs/zh/guide/composition-api-introduction.html#带-ref-的响应式变量
toRef
可以用来为源响应式对象上的 property 性创建一个 ref
。然后可以将 ref 传递出去,从而保持对其源 property 的响应式连接。
setup(props, context) {
const { reactive, toRef } = Vue
const nameObj = reactive({ name: 'xiaokang' })
const age = toRef(data, 'age')
setTimeout(() => {
age.value = 21
}, 2000)
return { age }
}
setup 中 context 参数
context 参数一共可以结构出三个参数:
attrs
None-Props 属性
const app = Vue.createApp({ template: ` <child app='app' style='color:red'></child> ` }) app.component('child', { template: '<div>child</div>', setup(props, context) { const { attrs, slots, emit } = context console.log(attrs) // 接收没有被props接收的属性 } }) const vm = app.mount('#root')
slots
插槽
const app = Vue.createApp({ template: ` <child app='app' style='color:red'> 插槽内容 </child> ` }) app.component('child', { setup(props, context) { const { h } = Vue const { attrs, slots, emit } = context return () => h('div', {}, slots.default()) } }) const vm = app.mount('#root')
可以使用
jsx
进行模板渲染:https://github.com/vuejs/jsx-next#installationemit
触发自定义事件
const app = Vue.createApp({ template: ` <child @change='handleChange' app='app' style='color:red'> 插槽内容 </child> `, methods: { handleChange() { console.log(123) } } }) app.component('child', { template: "<div @click='handleClick'>123123</div>", setup(props, context) { const { h } = Vue const { attrs, slots, emit } = context function handleClick() { emit('change') } return { handleClick } } }) const vm = app.mount('#root')
计算属性
计算属性同样使用Vue
对象提供的computed
方法。computed 方法接收参数有两种类型,一种是函数,另一种是对象。
setup(props, context) {
const { ref, computed } = Vue
let number = ref(1)
let number2 = ref(0)
const handle = () => {
number.value += 1
number2.value += 1
}
// 传入函数
let cNumber = computed(() => {
return number.value + 5
})
// 传入对象
let cNumberObj = computed({
get: () => {
return number2.value + 5
},
set: (val) => {
number2.value = val - 1
}
})
return { number, cNumber, handle, cNumberObj }
}
watch 与 watchEffect
就像我们如何使用 watch
选项在组件内的 user
property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch
函数执行相同的操作。它接受 3 个参数:
- 一个响应式引用或我们想要侦听的 getter 函数
- 一个回调函数
- 可选的配置选项
侦听单个源
// 侦听一个 getter const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } ) // 直接侦听ref const count = ref(0) watch(count, (count, prevCount) => { /* ... */ })
侦听多个源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })
不同点 | watch | watchEffect |
---|---|---|
惰性 | 默认情况下是惰性的,但第三个参数传入immediate 为 true 可以立即执行 | 非惰性 |
能拿到原始值和当前值 | 能 | 只能拿到当前值 |
只可以侦听多个数据 | 可以 | 可以 |
const stop = watchEffect(() => {
// 会自动判断依赖并更新
console.log(nameObj.name)
})
stop() // 调用后会停止监听
生命周期
setup() {
const {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onRenderTracked,
onRenderTriggered
} = Vue
const name = ref('dell')
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
// 每次渲染后重新收集响应式依赖
onRenderTracked(() => {
console.log('onRenderTracked')
})
// 每次触发页面重新渲染时自动执行
onRenderTriggered(() => {
console.log('onRenderTriggered')
})
const handleClick = () => {
name.value = 'lee'
}
return { name, handleClick }
},
Provide、Inject 和 ref
提供与注入
const app = Vue.createApp({
setup() {
const { provide, ref, readonly } = Vue
const name = ref('name1')
provide('name', readonly(name))
provide('changeName', (value) => {
name.value = value
})
return {}
},
template: `
<div>
<child />
</div>
`
})
app.component('child', {
setup() {
const { inject } = Vue
const name = inject('name')
const changeName = inject('changeName')
const handleClick = () => {
changeName('name2')
}
return { name, handleClick }
},
template: '<div @click="handleClick">{{name}}</div>'
})
通过
readonly
对提供的变量进行包装,实现数据单向流(子组件不能修改父组件的值)。
ref
const app = Vue.createApp({
setup() {
const { ref, onMounted } = Vue
const hello = ref(null)
onMounted(() => {
console.log(hello.value) // dom节点
})
return { hello }
},
template: `
<div>
<div ref="hello">hello world</div>
</div>
`
})