vue3细节的改变
Posted 面条请不要欺负汉堡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3细节的改变相关的知识,希望对你有一定的参考价值。
异步组件(新增)
以前,异步组件是通过将组件定义为返回 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 reffrom '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 reffrom '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 reffrom '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 reffrom '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组件</h以上是关于vue3细节的改变的主要内容,如果未能解决你的问题,请参考以下文章