Render函数
基本使用
render函数用于渲染html元素,用于利用JavaScript实现在template标签中做不了的事情。render提供一个h函数来构造元素,h函数的第一个参数是元素类型,第二个参数是元素的属性,第三个参数是元素的子组件:
<script>
import {h} from 'vue';
export default {
render() {
return h("h2",{class:"title"},"hello render");
},
}
</script>render函数实现计数器
<script>
import {h} from 'vue';
export default {
data() {
return {
counter:0
}
},
render() {
return h("div",{class:"app"},[
h("h2",null,`当前计数:${this.counter}`),
h("button",{
onClick:()=>this.counter++
},"+1"),
h("button",{
onClick:()=>this.counter--
},"-1"),
]);
},
}
</script>setup函数也可以完成替代render函数的功能:
<script>
import {h,ref} from 'vue';
export default {
setup(props) {
const counter=ref(0);
return ()=>{
return h("div",{class:"app"},[
h("h2",null,`当前计数:${counter.value}`),
h("button",{
onClick:()=>counter.value++
},"+1"),
h("button",{
onClick:()=>counter.value--
},"-1"),
]);
}
},
}
</script>插槽的使用
// HelloWorld.vue
<script>
import { h } from 'vue';
export default {
render() {
return h("div",null,[
h("h2",null,"hello world"),
this.$slots.default? this.$slots.default({name:"sk"}):h("span",null,"我是hello world的默认插槽")
]);
},
}
</script>// App.vue
<script>
import {h} from 'vue';
import HelloWorld from './HelloWorld.vue';
export default {
render() {
return h(HelloWorld,{},{
default: props=>h("span",null,`App传入HelloWorld的内容: ${props.name}`)
});
},
}
</script>JSX的使用
太怪了
实现上面的例子
// HelloWorld.vue
<script>
export default {
render() {
return(
<div>
<h2>hello world</h2>
{ this.$slots.default? this.$slots.default():<span>我是hello world的默认插槽</span>}
</div>
)
},
}
</script>// App.vue
<script>
import HelloWorld from './HelloWorld.vue';
export default {
data() {
return {
counter:0
}
},
render() {
const increment=()=>this.counter++;
const decrement=()=>this.counter--;
return (
<div>
<h2>当前计数值:{this.counter}</h2>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<HelloWorld>
{{default:props=><button>我是按钮</button>}}
</HelloWorld>
</div>
)
},
}
</script>自定义指令
基本使用
局部指令如同局部组件需要在App内部使用directives option,directives是一个对象,里面定义了一些自定义的指令(缺省v-):
<script>
export default {
// 局部指令
directives:{
focus:{
mounted(el,bindings,vnode,preVnode) {
console.log("focus mounted");
el.focus();
},
}
}
}
</script>全局指令如同全局组件,需要调用App的directive方法来注册:
const app=createApp(App);
app.directive("focus",{
mounted(el,bindings,vnode,preVnode) {
console.log("focus mounted");
el.focus();
},
});
app.mount('#app')生命周期函数
生命周期函数中传来四个参数:1.element:当前的元素;2.bindings:传入的修饰符、值等;3.vnode;4.前一个vnode:
directives:{
sk:{
created(el,bindings,vnode,preVnode) {
console.log("sk created",el,bindings,vnode,preVnode);
},
beforeMount() {
console.log("sk beforeMount");
},
mounted() {
console.log("sk mounted");
},
beforeUpdate() {
console.log("sk beforeUpdate");
},
updated() {
console.log("sk updated");
},
beforeUnmount() {
console.log("sk beforeUnMount");
},
unmounted() {
console.log("sk unmounted");
},
}
},对自定义指令添加修饰符和值,可以在bindings中接收到:
<template>
<div>
<button v-if="counter<2" v-sk.aaa.bbbb="'123'" @click="increment">当前计数:{{counter}}</button>
</div>
</template>bindings使用方式:
created(el,bindings,vnode,preVnode) {
console.log("sk created",el,bindings,vnode,preVnode);
console.log(bindings.value);
console.log(bindings.modifiers);
},时间格式化的练习:
// format-time.js
import dayjs from "dayjs"
export default function(app){
let formatString="YYYY-MM-DD HH:mm";
app.directive("format-time",{
created(el,bindings) {
if (bindings.value){
formatString=bindings.value;
}
},
mounted(el,bindings) {
const textContent = el .textContent;
let timestamp = parseInt(textContent);
if (textContent.length===10){
timestamp*=1000;
}
el.textContent=dayjs(timestamp).format(formatString);
},
})
}
// App.vue
<template>
<div>
<h2 v-format-time="'YYYY-MM-DD'">{{timestamp}}</h2>
</div>
</template>通过函数注册到App中:
// directives/index.js
import registerFormat from "./format-time";
export default function registerDirectives(app){
registerFormat(app)
}
// main.js
registerDirectives(app);teleport
可以内容挂载到别的标签下:
<template>
<div class="app">
<teleport to='#sk'>
<h2>当前计数</h2>
<button>+1</button>
<hello-world/>
</teleport>
<teleport to='#sk'>
<span>呵呵呵</span>
</teleport>
</div>
</template>VUE插件
以对象方式定义一个插件,对象内部必须有install方法,并可以接收app的入参:
export default{
install(app){
app.config.globalProperties.$name="shopkeeper";
}
}在app中使用use注册:
app.use(pluginObject);使用方式,created等生命周期、methods等可以获取this的方法中可以直接获取;setup中需要通过getCurrentInstance获取:
mounted() {
console.log(this.$name);
},
setup(props) {
const instance = getCurrentInstance();
console.log("setup",instance.appContext.config.globalProperties.$name);
}以函数方式定义一个插件,就相当于定义一个install函数,并接收app入参,以函数方式定义一个注册time-format的directive的功能为例:
// plugins/plugin_function.js
import registerFormat from "../directives/format-time";
export default function(app){
registerFormat(app);
}// main.js
mport pluginFunction from './plugins/plugin_function';
const app=createApp(App);
app.use(pluginFunction);
app.mount('#app')nextTick
nextTick类似于Android组件的post方法,即让组件更新完ui之后再执行所要求的操作:
<template>
<div>
<h2 class="title" ref="titleRef">{{message}}</h2>
<button @click="addMessageContent">添加内容</button>
</div>
</template>
<script>
import {ref, onUpdated, nextTick} from 'vue'
export default {
setup(props) {
const message = ref("");
const titleRef =ref(null)
const addMessageContent = ()=>{
message.value+="哈哈哈哈哈哈";
nextTick(()=>{
console.log("addMessageContent",titleRef.value.offsetHeight);
})
};
// 其它组件的更新也会调用这个回调
onUpdated(() => {
console.log("onUpdated",titleRef.value.offsetHeight);
});
return {
message,
titleRef,
addMessageContent
}
}
}
</script>
<style scoped>
.title{
width: 120px;
}
</style>