一、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参数一共可以结构出三个参数:
attrsNone-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>
`
})










