提供⾃定义渲染器,可以在⾮DOM环境中使⽤Vue的运⾏时。
虚拟dom的优点?
平时我们一般并不需要使用这个api,只是为了扩展渲染能力才会使用,如用vue写微信小程序之类的需求。
这个api主要就是可以自定义渲染方式。
说明:
vue2中没createRenderer的写法。
import { createRenderer, h, render } from "vue"
const vnode = h('div', 'hello-vue2的render')
render(vnode, app); // 这个render方法,createElemet() el.textContent = 'hello'
// // appendChild
vue3中有createRenderer自定义浏览器的写法。
import { createRenderer, h } from "vue"
const { render } = createRenderer({
// 创建一个元素
createElement(type) {
if (type === "div") {
return document.createElement("p")
}
},
setElementText(el, text) {
el.textContent = text
},
insert(el, parent) {
parent.appendChild(el)
}
// 给元素设置属性,方法
// 给元素设置内容
})
const vnode = h("div", "vue3-hello-自定义render")
render(vnode, app)
v-bind绑定样式CSS变量-绑定样式
在scoped中自定义深度选择、插槽、全局样式的可以采用::deep()
、:slotted()
、:global()
这些选择器函数。
修改子组件中的样式:
vue3-7-15/src/App.vue
vue3-7-15/src/components/scoped.vue
子组件的h1标签----hello
样式已经生效。
vue3-7-15/src/App.vue
vue3-7-15/src/components/scoped.vue
子组件的h1标签----hello
样式没有生效。
:slotted()
在子组件中捕获修改来自于父组件插槽中的类名,并可根据预定的类让插槽的css样式有预设:
示例:
vue3-7-15/src/App.vue
这是来自于父组件的插槽内容
vue3-7-15/src/components/scoped.vue
样式生效。
示例:
vue3-7-15/src/App.vue
这是来自于父组件的插槽内容
vue3-7-15/src/components/scoped.vue
样式不生效。
示例:
vue3-7-15/src/App.vue
这是来自于父组件的插槽内容
vue3-7-15/src/components/scoped.vue
直接在父组件中设置插槽的样式,生效。
:global()
在组件的scoped中设置全局样式。
示例:
vue3-7-15/index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite Apptitle>
head>
<body>
<div id="app">div>
<div id="root">rootdiv>
<script type="module" src="/src/main.js">script>
body>
html>
vue3-7-15/src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
vue3-7-15/src/App.vue
在组件的scoped中设置的全局样式生效了。
vue3于父组件中直接上子组件上设置的事件直接会绑定到子组件的根节点上。
vue3-7-15/src/App.vue
vue3-7-15/src/components/emit.vue
子组件上的根节点-我是一个按钮
vue3中父组件上绑定的事件会触发。
vue3设置上emits可以设置在当前组件中那些组件是自定义事件,需要基于$emit(“自定义事件名”)才能向让父组件在其自身组件实例上绑定的该同名方法被触发。
vue3-7-15/src/App.vue
vue3-7-15/src/components/emit.vue
子组件上的根节点-我是一个按钮
此时如果子组件不使用this.$emit(“click”)触发自定义的click事件,在父组件上监听不到它的执行。
vue3-7-15/src/App.vue
vue3-7-15/src/components/emit.vue
子组件上的根节点-我是一个按钮
此时如果子组件先执行handleClick(),之后使用this.$emit(“click”)触发自定义的click事件,在父组件上监听到才执行handleClick1()。
vue3-7-15/src/App.vue
vue3-7-15/src/components/emit.vue
子组件上的根节点-我是一个按钮
由于没基于emits标识哪些事件是自定义的,此时如果子组件先执行handleClick(),之后使用this.$emit(“click”)触发自定义的click事件,在父组件上监听到才执行handleClick1(),此时handleClick1()的入参是子组件传递的值"hello"。而由于没基于emits标识哪些事件是自定义的,父组件依旧会监听到子组件根节点上执行了click事件,于是父组件的handleClick1(),此时handleClick1()的入参是子组件根节点的事件对象。
Suspense组件主要的作⽤优雅地处理异步组件的加载状态。
可以支持渲染异步组件 并且添加loading,我们需要先有一个异步组件。异步组件可以是懒加载的组件,也可以在setup被异步执行的组件。我们也可以自己写一个异步组件。
异步组件示例:
这是一个异步加载到的的组件---哈哈
等价于
这是一个异步加载到的的组件---哈哈
Suspense组件的示例:
vue3-7-15/src/App.vue
正在加载异步组件的loading文字
vue3-7-15/src/components/async-demo.vue
这是一个异步加载到的的组件---哈哈
整个性能比以前高了
整个体积小了。
$set
、$delete
、.native
、$listeners
、eventBus
、$on
、$emit
、$off
、$once
都移除了;keyCode修饰符
。过滤器
干掉。inline-template
直接删除。Vue.component
、Vue.directive
等全局静态方法,全部移除。.sync
移除,如@xxx.sync="xxx"
不再支持。provide
、inject
实例上的api也进行了转移。需要使用import {provide,inject} from 'vue'
这类写法。vue3 采用ts来编写提示好, vue2.7 也是用ts来编写的
以前vue2把代码都放在一起管理,想扩展或者单独使用不方便, vue3在一个仓库下管理了多个项目,每个模块可以单独使用
@vue/compiler-dom
- @compiler-core
(将我们的模版变成render函数)。@vue/runtime-dom
- @vue/runtime-core
- @vue/reactivity
方便管理可以单独使用。const isObject = (val) => val !== null && typeof val === "object"
const proxyMap = new WeakMap()
function createReactiveObject(target) {
if (!isObject(target)) {
console.warn(`value cannot be madereactive: ${String(target)}`)
return target
}
// 经过劫持处理过的,就不在重复处理了
const existingProxy = proxyMap.get(target)
if (existingProxy) return existingProxy
// 进⾏数据劫持
const proxy = new Proxy(target, {
get: function get(target, key, receiver) {
const res = Reflect.get(target, key)
if (isObject(res)) {
return reactive(res)
}
return res
},
set: function set(target, key, value, receiver) {
let oldValue = target[key]
if (oldValue === value) return
const result = Reflect.set(target, key, value, receiver)
console.log("渲染")
return result
}
})
proxyMap.set(target, proxy)
return proxy
}
function reactive(target) {
return createReactiveObject(target)
}
const state = reactive({ name: "jw", arr: [1, 2, 3] })
state.name = "哈哈"
state.arr[0] = 100
const 以函数作为属性的代理配置对象 = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : 37;
}
};
const 源对象 = {}
const 代理对象 = new Proxy(源对象, 以函数作为属性的代理配置对象);
代理对象.a = 1;
代理对象.b = undefined;
console.log(代理对象.a, 代理对象.b); // 1, undefined
console.log('c' in 代理对象, 代理对象.c); // false, 37
代理对象
,对该返回的代理对象
执行操作才会触发以函数作为属性的代理配置对象
中的配置属性。以函数作为属性的代理配置对象
也监听不到源对象上的直接修改。源对象
一般是常被作为代理对象
的存储后端。
代理对象
一般根据目标验证关于源对象
不可扩展性或不可配置属性的不变量。代理对象
对应的以函数作为属性的代理配置对象
中的函数里去修改它。// 以前我们劫持的是属性(重写set和get)(新增的不行) $set $delete
// proxy 劫持的是对象 (代理,并没有增添额外的属性)(劫持的范围变大了)
function isObject(value) {
return value !== null && typeof value === "object"
}
const state = { name: "zf", age: { n: { n: 100 } } }
// 最终返回的是代理对象,后续我们使用代理对象访问属性
function reactive(state) {
const proxy = new Proxy(state, {
get(target, key, receiver) {
// target指代的是被代理源对象
// key 是取值的属性
// receiver 代理对象
// 依赖收集
// return target[key]
let res = Reflect.get(target, key, receiver)
if (isObject(res)) {
// 懒代理
return reactive(res)
}
return res
},
set(target, key, value, receiver) {
console.log("用户设置值了")
return Reflect.set(target, key, value, receiver)
}
})
return proxy
}
// proxy支持数组和对象的新增以及删除,而且没有给属性重新定义。 性能好,是懒代理的
const proxy = reactive(state)
// 当我取到proxy.age 的时候发现他是一个对象,那我就把这个对象在进行代理
// Reflect 以后会将所有的Object的api全部放到reflect中
// Object.defineProperty Reflect.defineProperty
// Object.setPrototypeof Reflect.setPrototypeof
Vue.component
, Vue.directive
Vue.use
Vue.prototype
-> app.config.globalProperties
, app.use
app.mixin
;Vue3 composition Api
:reactive
、computed
、watch
、watchEffect
、ref
、toRef
、toRefs
。defineOption()
;setup(){}
, setup语法糖
(新的语法);let proxy = { name: "zf" }
with (proxy) {
console.log(proxy.name) //zf
}
// Object.defineProperty({getter})
const total = {
// ref的实现原理
_value: null,
get value() {
console.log("依赖收集")
return this._value
},
set value(val) {
this._val = val
}
}
console.log(total.value)
total.value = "abc"
console.log(total.value)
const props = defineProps()
const emit = defineEmits()
defineExpose()
const attrs = useAttrs()
const slots = useSlots()