异步组件(新增)
以前,异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如:
const asyncModal = () => import('./Modal.vue')
在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件需要通过将其包裹在新的 defineAsyncComponent 助手方法中来显式地定义:
<template>
<div>
-----异步组件-----
<button @click="onClick">点击显示</button>
<asyncChild v-if="isShow" />
</div>
</template>
<script setup>
import {
defineAsyncComponent } from 'vue';
let isShow = ref(false);
const asyncChild = defineAsyncComponent(() => import('./child.vue'));
const onClick = () => {
isShow.value = true;
};
</script>
子组件:
<template>
<div> Child: {
{
test }} </div>
</template>
<script setup>
let test = ref('你好!');
</script>
点击显示 后才加载child组件
$attrs 包括class&style
自定义指令
Vue 2.x & Vue 3.x
(1)Vue 2.x 自定义指令的声明周期
- bind:指令绑定到元素时触发,只触发一次;
- inserted:绑定元素被插入父DOM时触发
- update:当元素更新而子元素还没有更新时触发;
- componentUpdated:组件和子组件更新完成后触发;
- unbind:一旦指令被移除,就会调用这个钩子。也只调用一次。
<p v-highlight="'yellow'">以亮黄色高亮显示此文本</p>
Vue.directive('highlight', {
bind(el, binding, vnode) {
el.style.background = binding.value
}
})
(2)Vue 3.x 自定义指令的声明周期
- created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
- beforeMount:替代bind
- mounted:替代inserted
- beforeUpdate:移除Vue2.x 中的update,用beforeUpdate和updated来替代
- updated
- beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
- unmounted:替代unbind
<p v-highlight="'yellow'">以亮黄色高亮显示此文本</p>
const app = Vue.createApp({
})
app.directive('highlight', {
beforeMount(el, binding, vnode) {
el.style.background = binding.value
}
})
自定义指令
全局自定义指令/访问组件实例
<el-button type="primary" v-hasPermi="['system-access:list:query']" >搜索</el-button >
app.directive('hasPermi', {
mounted(el, binding, vnode) {
// 编写
}
})
局部自定义
<template>
<div>
<p v-highlight="'yellow'">以亮黄色高亮显示此文本</p>
</div>
</template>
<script>
export default {
directives: {
highlight: {
beforeMount(el, binding, vnode, prevVnode) {
el.style.background = binding.value;
}
}
}
};
</script>
is属性
vue3.0.0+ 中 禁止在 HTML 元素上使用is属性(is 已弃用)
<div is="foo"></div> 或<div :is="foo"></div> //错误
保留的 标签上使用时,它的行为将与 2.x 中完全相同
<component is="bar" />
当在不同组件标签上使用is时,is会被当做一个不同的prop;
<bar is="plastic-button" />
使用 vue: 前缀来解决 DOM 内模板解析问题
<template>
<select>
<option is="vue:optioncomp"> </option>
</select>
</template>
<script>
export default {
components:{
'optioncomp':{
template: '<option >下拉选择</option>'
}
}
};
</script>
新增v-is来实现在普通的 HTML 元素渲染组件
<template>
<div v-is="'child'">渲染 child 组件</div>
<!-- 等同于<child>渲染 child 组件</child> -->
</template>
<script>
import child from "./child.vue";
export default {
components:{
child
}
};
</script>
Data 选项
在 2.x 中,开发者可以通过 object 或者是 function 定义 data 选项
<!-- Object 声明 -->
<script>
const app = new Vue({
data: {
apiKey: 'a1b2c3'
}
})
</script>
<!-- Function 声明 -->
<script>
const app = new Vue({
data() {
return {
apiKey: 'a1b2c3'
}
}
})
</script>
3.x 中,data 选项已标准化为只接受返回 object 的 function
createApp({
data() {
return {
apiKey: 'a1b2c3'
}
}
}).mount('#app')
Mixin 合并行为变更
此外,当来自组件的 data() 及其 mixin 或 extends 基类被合并时,合并操作现在将被浅层次地执行:
const Mixin = {
data() {
return {
user: {
name: 'Jack',
id: 1
}
}
}
}
const CompA = {
mixins: [Mixin],
data() {
return {
user: {
id: 2
}
}
}
}
在 Vue 2.x 中,生成的 $data 是:
{
"user": {
"id": 2,
"name": "Jack"
}
}
在 3.0 中,其结果将会是:
{
"user": {
"id": 2
}
}
emits(新增)
emits: 列表申明从父组件继承来的事件
$emit: 抛出事件, 告诉父组件解决
<!-- 父组件 App -->
<template>
<div>
<div> 父组件:{
{
num}}</div>
<child @numchange="getNum" :num="num" ></child>
</div>
</template>
<script>
import {
ref}from 'vue'
import child from "./child.vue";
export default {
components:{
child,
},
setup() {
let num =ref(0);
const getNum = (res)=>{
num.value = res;
}
return{
num,
getNum
}
}
};
</script>
<!-- 子组件 Count -->
<template>
<div>
<p>子组件:{
{
num}}</p>
<button type="button" class="btn btn-danger" @click="add">+1</button>
</div>
</template>
<script>
export default {
props: ['num'],
emits: ['numchange'],
setup(props,ctx){
const add=()=> {
ctx.emit('numchange', props.num + 1)
}
return {
add
}
}
};
</script>
vue3 移除过滤器
在 3.x 中,过滤器已移除,且不再支持。官网建议用方法调用或计算属性来替换它们。
全局过滤器
// main.js
const app = createApp(App)
app.config.globalProperties.$filters = {
currencyUSD(value) {
return '$' + value
}
}
使用的时候: <p>{
{
$filters.currencyUSD(accountBalance) }}</p>
片段(新增)
在 3.x 中,组件可以包含多个根节点!
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
全局 API
一个新的全局 API:createApp
import {
createApp } from 'vue'
const app = createApp({
})
全局 API Treeshaking
Vue 3.x 对 部分全局 API 实现了 tree shacking 功能。
什么是 tree shaking?
tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。
新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json 的 “sideEffects” 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 “pure(纯的 ES2015 模块)”,由此可以安全地删除文件中未使用的部分。
摘抄自——《webpack——tree shaking》
作用:tree shaking 的作用是移除项目代码中上下文中的未引用代码(dead-code),已达到实现项目打包文件的精简。
前提:tree shaking 基于 ES2015模块系统。也就是项目中对模块的应用和导出需要用到import、export。
比如:在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到
import Vue from 'vue'
Vue.nextTick(() => {
})
而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果您不使用其某些功能,它们将不会包含在您的基础包中
import {
nextTick, observable } from 'vue'
nextTick(() => {
})
作用
通过脚手架vue-cli安装Vue2与Vue3项目。
vue2
<template>
<div id="app">
测{
{
count}}--{
{
double}}
</div>
</template>
<script>
export default {
name: 'App',
data(){
return{
count: 1,
question:'',
answer:''
}
},
computed: {
double: function () {
return this.count * 2;
},
},
watch: {
question: function () {
this.answer = 'xxxx'
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
vue3
<template>
<div id="app">
测{
{
state.count}}--{
{
double}}
</div>
</template>
<script>
import {
reactive ,computed, watch} from "vue";
export default {
name: 'App',
setup() {
const state = reactive({
count: 1,
});
const double = computed(() => {
return state.count * 2;
});
watch(
() => state.count,
(count, preCount) => {
console.log(count);
console.log(preCount);
}
);
return {
state,
double,
};
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
对项目进行打包,通过Tree shaking,Vue3给我们带来的好处是:
- 减少程序体积(更小)
- 减少程序执行时间(更快)
- 便于将来对程序架构进行优化(更友好)
$listeners 移除,谁来接手
Vue3 已经不支持 $listeners 了,从文档中可以知道 Vue2 的 $listeners 在 Vue3 中是合并到了 $attrs 里。
<template>
<div>--------父组件--------{
{
num}}--</div>
<Child @call="handleEmit" />
</template>
<script>
import {
ref}from 'vue'
import Child from "./Child.vue";
export default {
components:{
Child
},
setup() {
let num = ref(0)
const handleEmit =(res)=> {
num.value = res;
console.log('父组件:',res)
}
return{
num,
handleEmit
}
}
};
</script>
<template>
<div>---子组件-----</div>
<!-- 注意传参方式 -->
<Grandchild v-bind="$attrs" />
</template>
<script>
import {
ref}from 'vue'
import Grandchild from './Grandchild.vue'
export default ({
components: {
Grandchild },
setup(props, {
attrs }) {
console.log('Child 组件:',attrs) // 可以看到 Parent 传入的 call 事件,且前面加上了 on,变成了 onCall
}
})
</script>
<template>
<button @click="handleClick">孙组件:点击</button>
</template>
<script>
import {
ref}from 'vue'
export default ({
emits: ['call'],
setup(props, {
emit}) {
const handleClick= ()=> {
emit('call','1') // 成功触发 Parent 里面的 call 事件回调,在控制台1的值
}
return {
handleClick
}
}
})
</script>
在 prop 的默认函数中访问this
在vue2 中 props里的参数是可以用this.去访问的。但是vue3不行了,取而代之 组件接收到的原始 prop 将作为参数传递给默认函数。
export default{
props: {
iconClass: {
type: String,
required: true
}
},
setup(props) {
console.log(props.iconClass)
}
}
或
<script setup>
const props = defineProps({
info: {
type: Object,
default: null,
}
});
props.info
</script>
渲染函数
h() 渲染函数
在 2.x 中,render 函数会自动接收 h 函数 (它是 createElement 的惯用别名) 作为参数:
// Vue 2 渲染函数示例
export default {
render(h) {
return h('div')
}
}
在 3.x 中,h 函数现在是全局导入的,而不是作为参数自动传递。
// Vue 3 渲染函数示例
import {
h } from 'vue'
export default {
render() {
return h('div')
}
}
<h1>{
{
blogTitle }}</h1> 等于
render() {
return h('h1', {
}, this.blogTitle)
}
h() 到底会返回什么呢?其实不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为 VNode。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。
h() 参数
h(
// {
String | Object | Function | null} tag
// 一个 HTML 标签名、一个组件、一个异步组件,或者 null。
// 使用 null 将会渲染一个注释。
//
// 必需的。
'div',
// {
Object} props
// 与 attribute、prop 和事件相对应的对象。
// 我们会在模板中使用。
//
// 可选的。
{
},
// {
String | Array | Object} children
// 子 VNodes, 使用 `h()` 构建,
// 或使用字符串获取 "文本 Vnode" 或者
// 有 slot 的对象。
//
// 可选的。
[
'Some text comes first.',
h('h1', 'A headline'),
h(MyComponent, {
someProp: 'foobar'
})
]
)
约束
VNodes 必须唯一
render() {
const myParagraphVNode = Vue.h('p', 'hi')
return Vue.h('div', [
// 错误 - 重复的Vnode!
myParagraphVNode, myParagraphVNode
])
}
如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如,下面这渲染函数用完全合法的方式渲染了 20 个相同的段落:
render() {
return Vue.h('div',
Array.apply(null, {
length: 20 }).map(() => {
return Vue.h('p', 'hi')
})
)
}
注册组件
在 3.x 中,由于 VNode 是上下文无关的,不能再用字符串 ID 隐式查找已注册组件。取而代之的是,需要使用一个导入的 resolveComponent 方法:
// 3.x
import {
h, resolveComponent } from 'vue'
export default {
setup() {
const ButtonCounter = resolveComponent('button-counter')
return () => h(ButtonCounter)
}
}
Suspense作用
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
试验性:
Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。
生产环境请勿使用
使用步骤
异步引入组件
import {
defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
使用Suspense包裹组件,并配置好default 与 fallback
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
其中 fallback是由于网速或者其他原因没有加载成功时显示的组件,当加载成功后显示default(default 与 fallback 不可改名,因为Suspense相当于定义好的slot具名插槽)
Suspense搭配async函数的setup
App.vue(异步加载组件的父组件)
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child />
</template>
<template v-slot:fallback>
<h3>稍等,加载中...</h3>
</template>
</Suspense>
</div>
</template>
<script>
// import Child from './components/Child'//静态引入
import {
defineAsyncComponent } from "vue";
const Child = defineAsyncComponent(() => import("./components/Child")); //异步引入
export default {
name: "App",
components: {
Child },
};
</script>
<style>
.app {
background-color: gray;
padding: 10px;
}
</style>
定义setup为async的组件Child.vue
<template>
<div class="child">
<h3>我是Child组件</h3>
{
{
sum }}
</div>
</template>
<script>
import {
ref } from "vue";
export default {
name: "Child",
async setup() {
let sum = ref(0);
let p = new Promise((resolve) => {
setTimeout(() => {
resolve({
sum });
}, 3000);
});
return await p;
},
};
</script>
<style>
.child {
background-color: skyblue;
padding: 10px;
}
</style>
移除v-on.native修饰符
<my-component
v-on:click.native="handleNativeClickEvent"
/>
改成
<my-component
v-on:click="handleNativeClickEvent"
/>
删除 .native 修饰符的所有实例。确保所有组件都使用 emits 选项记录其事件。
转载:https://blog.csdn.net/gao_xu_520/article/details/125622292