比较
大约 6 分钟
应用配置项
config 是一个包含 Vue 应用程序全局配置的对象。可以在挂载应用程序之前修改下面列出的属性。
- devtools 类型: boolean 默认值: true 如何使用:
app.config.devtools = true;
// 是否开启 vue-devtools 工具的检测,默认情况下开发环境是 true,生产环境下则为 false。
- errorHandler 类型: Function 默认值: undefined 如何使用:
app.config.errorHandler = (err, vm, info) => {
// info 为 Vue 在某个生命周期发生错误的信息
};
// 为组件渲染功能和观察程序期间的未捕获错误分配处理程序。
- globalProperties 🌟 类型: [key: string]: any 默认值: undefined 如何使用:
app.config.globalProperties.userName = 'decade';
app.component('c-component', {
mounted() {
console.log(this.userName); // 'decade'
},
// 若是组件内也有 name 属性,则组建内的属性权限比较高。
});
还有一个知识点很重要,在 Vue2.x 中,我们定义一个全局属性或者方法都是如下所示:
Vue.prototype.$md5 = () => {};
在 Vue3.0 中,我们便可这样定义:
const app = Vue.createApp({});
app.config.globalProperties.$md5 = () => {};
- performance 类型: boolean 默认值: false 如何使用:
app.config.performance = true;
// 将其设置为 true 可在浏览器 devtool 性能/时间线面板中启用组件初始化,编译,渲染和补丁性能跟踪。 仅在开发模式和支持 Performance.mark API的浏览器中工作。
Application API
全局改变的动作,都在 createApp 所创建的应用实例中,如下所示:
import { createApp } from 'vue';
const app = createApp({});
那么 app 下这些属性:
- component 参数: 第一个参数 string 类型表示组件名,第二个参数 Function 或 Object 返回值: 只传第一个参数,返回组建。带上第二个参数则返回应用程序实例 如何使用:
import { createApp } from 'vue';
const app = createApp({});
// 注册一个 options 对象
app.component('shisan-component', {
/* ... */
});
// 检索注册的组件
const DecadeComponent = app.component('decade-component');
- config(上面第一段讲过了)
- directive 自定义指令变化不大,还是之前那些东西,如下:
app.directive('my-directive', {
// 挂载前
beforeMount() {},
// 挂载后
mounted() {},
// 更新前
beforeUpdate() {},
// 更新后
updated() {},
// 卸载前
beforeUnmount() {},
// 卸载后
unmounted() {},
});
composition api
composition api 为 vue 应用提供更好的逻辑复用和代码组织。
<template>
<div>
<p>counter: {{ counter }}</p>
<p>doubleCounter: {{ doubleCounter }}</p>
<p ref="desc"></p>
</div>
</template>
<script>
import {
reactive,
computed,
watch,
ref,
toRefs,
onMounted,
onUnmounted,
} from 'vue';
export default {
setup() {
const data = reactive({
counter: 1,
doubleCounter: computed(() => data.counter * 2),
});
let timer;
onMounted(() => {
timer = setInterval(() => {
data.counter++;
}, 1000);
});
onUnmounted(() => {
clearInterval(timer);
});
const desc = ref(null);
watch(
() => data.counter,
(val, oldVal) => {
// console.log(`counter change from ${oldVal} to ${val}`);
desc.value.textContent = `counter change from ${oldVal} to ${val}`;
},
);
return { ...toRefs(data), desc };
},
};
</script>
Teleport
传送门组件提供一种简洁的方式可以指定它里面内容的父元素。
<template>
<button @click="modalOpen = true">弹出一个全屏模态窗口</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
这是一个模态窗口! 我的父元素是"body"!
<button @click="modalOpen = false">Close</button>
</div>
</div>
</teleport>
</template>
<script>
export default {
data() {
return {
modalOpen: true,
};
},
};
</script>
<style scoped>
.modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modal div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
width: 300px;
height: 300px;
padding: 5px;
}
</style>
Fragments
vue3 中组件可以拥有多个根。
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
Emits Component Option
vue3 中组件发送的自定义事件需要定义在 emits 选项中:
- 原生事件会触发两次,比如
click
- 更好的指示组件工作方式
- 对象形式事件校验
<template>
<div @click="$emit('click')">
<h3>自定义事件</h3>
</div>
</template>
<script>
export default {
emits: ['click'],
};
</script>
自定义渲染器 custom renderer
Vue3.0 中支持 自定义渲染器 (Renderer):这个 API 可以用来自定义渲染逻辑。比如下面的案例我们可以把数据渲染到 canvas 上。
首先创建一个组件描述要渲染的数据,我们想要渲染一个叫做 piechart 的组件,我们不需要单独声明该组件,因为我们只是想把它携带的数据绘制到 canvas 上。创建 CanvasApp.vue
<template> <piechart @click="handleClick" :data="state.data" :x="200" :y="200" :r="200" ></piechart> </template> <script> import { reactive, ref } from 'vue'; export default { setup() { const state = reactive({ data: [ { name: '大专', count: 200, color: 'brown' }, { name: '本科', count: 300, color: 'yellow' }, { name: '硕士', count: 100, color: 'pink' }, { name: '博士', count: 50, color: 'skyblue' }, ], }); function handleClick() { state.data.push({ name: '其他', count: 30, color: 'orange' }); } return { state, handleClick, }; }, }; </script>
下面我们创建自定义渲染器,main.js
import { createApp, createRenderer } from 'vue' import CanvasApp from './CanvasApp.vue' const nodeOps = { insert: (child, parent, anchor) => { // 我们重写了insert逻辑,因为在我们canvasApp中不存在实际dom插入操作 // 这里面只需要将元素之间的父子关系保存一下即可 child.parent = parent; if (!parent.childs) { parent.childs = [child] } else { parent.childs.push(child); } // 只有canvas有nodeType,这里就是开始绘制内容到canvas if (parent.nodeType == 1) { draw(child); // 如果子元素上附加了事件,我们给canvas添加监听器 if (child.onClick) { ctx.canvas.addEventListener('click', () => { child.onClick(); setTimeout(() => { draw(child) }, 0); }) } } }, remove: child => {}, createElement: (tag, isSVG, is) => { // 创建元素时由于没有需要创建的dom元素,只需返回当前元素数据对象 return {tag} }, createText: text => {}, createComment: text => {}, setText: (node, text) => {}, setElementText: (el, text) => {}, parentNode: node => {}, nextSibling: node => {}, querySelector: selector => {}, setScopeId(el, id) {}, cloneNode(el) {}, insertStaticContent(content, parent, anchor, isSVG) {}, patchProp(el, key, prevValue, nextValue) { el[key] = nextValue; }, }; // 创建一个渲染器 let renderer = createRenderer(nodeOps); // 保存画布和其上下文 let ctx; let canvas; // 扩展mount,首先创建一个画布元素 function createCanvasApp(App) { const app = renderer.createApp(App); const mount = app.mount app.mount = mount(selector) { canvas = document.createElement('canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; document.querySelector(selector).appendChild(canvas); ctx = canvas.getContext('2d'); mount(canvas); } return app } createCanvasApp(CanvasApp).mount('#demo')
编写绘制逻辑
const draw = (el, noClear) => { if (!noClear) { ctx.clearRect(0, 0, canvas.width, canvas.height); } if (el.tag == 'piechart') { let { data, r, x, y } = el; let total = data.reduce((memo, current) => memo + current.count, 0); let start = 0, end = 0; data.forEach((item) => { end += (item.count / total) * 360; drawPieChart(start, end, item.color, x, y, r); drawPieChartText(item.name, (start + end) / 2, x, y, r); start = end; }); } el.childs && el.childs.forEach((child) => draw(child, true)); }; const d2a = (n) => { return (n * Math.PI) / 180; }; const drawPieChart = (start, end, color, cx, cy, r) => { let x = cx + Math.cos(d2a(start)) * r; let y = cy + Math.sin(d2a(start)) * r; ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(x, y); ctx.arc(cx, cy, r, d2a(start), d2a(end), false); ctx.fillStyle = color; ctx.fill(); ctx.stroke(); ctx.closePath(); }; const drawPieChartText = (val, position, cx, cy, r) => { ctx.beginPath(); let x = cx + (Math.cos(d2a(position)) * r) / 1.25 - 20; let y = cy + (Math.sin(d2a(position)) * r) / 1.25; ctx.fillStyle = '#000'; ctx.font = '20px 微软雅黑'; ctx.fillText(val, x, y); ctx.closePath(); };
reactive
import { reactive, computed } from 'vue';
export default {
setup() {
const state = reactive({
a: 0,
});
function increment() {
state.a++;
}
return {
state,
increment,
};
},
};
// reactive 相当于 Vue2.x 的 Vue.observable () API,经过 reactive 处理后的函数能变成响应式的数据,类似之前写模板页面时定义的 data 属性的值。
watchEffect
import { reactive, computed, watchEffect } from 'vue';
export default {
setup() {
const state = reactive({ a: 0 });
const double = computed(() => state.a * 3);
function increment() {
state.count++;
}
const wa = watchEffect(() => {
// 使用到了哪个 ref/reactive 对象.value, 就监听哪个
console.log(double.value);
});
// 可以通过 wa.stop 停止监听
return {
state,
increment,
};
},
};
// watchEffect 被称之为副作用,立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
computed
import { reactive, computed } from 'vue';
export default {
setup() {
const state = reactive({
a: 0,
});
const double = computed(() => state.a * 3);
function increment() {
state.a++;
}
return {
double,
state,
increment,
};
},
};
ref 和 toRefs
toRefs 提供了一个方法可以把 reactive 的值处理为 ref,也就是将响应式的对象处理为普通对象。
hooks
与 2.x 版本相对应的生命周期钩子
Vue2.x 的生命周期 | Vue3.x 的生命周期 |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
Vue3.0 在 Composition API 中另外加了两个钩子,分别是 onRenderTracked
和 onRenderTriggered
,两个钩子函数都接收一个 DebuggerEvent
:
export default {
onRenderTriggered(e) {
debugger;
// 检查哪个依赖性导致组件重新渲染
},
};