缩写记忆:
tv code2&tf spa
tv code2: defineProps和defineEmits、defineExpose、useSlots和useAttrs)
event 缓存
template,vite,composition ApI,生命周期都加on,diff算法优化(添加标记,增量diff),dom优化、expose
typescript,function编程vue3新增了一个名为setup的入口函数,value, computed, watch, onMounted等方法都需要从外部import。 setup pinia api
expose: 子组件是
1.data侦听从object.defineproperty=> proxy
缺点增加对象key,不能响应,需要$set(this.obj,‘key’,‘value’)
const person = {
name: '炫H5',
age: 20
};
Object.keys(person).forEach(function(key) {
Object.defineProperty(person, key, {
enumerable: true,
configurable: true,
get: function() {
console.log('get');
},
set: function(newVal) {
// 当属性值发生变化时我们可以进行额外操作
console.log(`欢迎大家来到${newVal}`);
},
});
});
person.name = '炫H5前端博客';
//欢迎大家来到炫H5前端博客
//"炫H5前端博客"
proxy劫持方法更多,有11种,比如apply,has,construct等。
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
const person = observable({
name: '炫H5',
age: 5
});
function print() {
console.log(`${person.name}, ${person.age}年了`)
}
observe(print);
person.name = '炫H5前端博客已经有';
2.setup中生命周期加了on,destroy=> onunmount、create=>setup
setup外可以用vue2的生命周期。
3.vite代替webpack
自动注册
vue2:require.context()
const requireComponent = require.context(
// 其组件目录的相对路径
'@/components',
// 是否查询其子目录
true,
// 匹配基础组件文件名的正则表达式
/index.vue$/
)
vue3:import.meta.globEager
// 自动导入公共组件
const modulesFiles:any = import.meta.globEager('./components/*/*.vue')
// console.log(modulesFiles,"看看是什么东西")
Vite 用 esbuild 替代 Rollup 进行预打包,速度也非常快,上面的第三点可以看到生产可以用 esbuild 作为压缩器。生产打包还是用的Rollup, esbuild 目前对生产包支持不够健壮,很多配置无法通过 esbuild 实现
4.template只能有一个根节点改为多个
5.diff算法优化
tag:‘div’,
props:{
className:‘container’,
id:‘div1’
},
children:[//没有children就是text
{
tag:‘p’,
children:‘text’
}
]
所以vue3 针对此问题,在此基础上做出了优化:
标记模板中的静态内容,区别了模板中的静态和动态节点
更新时,只diff操作动态的内容
Vue3中会首先区分出以上模板的静态节点和动态节点,当视图更新时,只对动态节点部分进行diff运算,减少了资源的损耗
参考链接
6.dom创建优化,静态提升 hoistStatic
参考:https://juejin.cn/post/6898503749351047175
vue2的基于snabbdom优化,diff
在Vue2中无论元素是否参与更新,每次都会重新创建,然后再渲染。如下图所示,每次都会createVNode。
但是在Vue3中使用了静态提升后,对于不参与更新的元素,只会被创建一次,在渲染时直接复用即可:
7.event事件侦听器缓存 cacheHandlers
1 vue2.x中,绑定事件每次触发都要重新生成全新的function去更新
2 cacheHandlers 是Vue3中提供的事件缓存对象
3 当 cacheHandlers 开启,会自动生成一个内联函数,同时生成一个静态节点。当事件再次触发时,只需从缓存中调用即可,无需再次更新
8.ssr
Vue2 中也是有 SSR 渲染的,但是 Vue3 中的 SSR 渲染相对于 Vue2 来说,性能方面也有对应的提升。
9.composition &setup
vue2的Options API 和 vue3的Composition API
Options API 约定:
我们需要在 props 里面设置接收参数
我们需要在 data 里面设置变量
我们需要在 computed 里面设置计算属性
我们需要在 watch 里面设置监听属性
我们需要在 methods 里面设置事件方法
你会发现 Options APi 都约定了我们该在哪个位置做什么事,这反倒在一定程度上也强制我们进行了代码分割。
现在用 Composition API,不再这么约定了,于是乎,代码组织非常灵活,我们的控制代码写在 setup 里面即可。
setup函数里面是没有this对象、必须return、在beforeCreate之前执行。
在vue2中,watch、computed、data、method等API都是直接作为对象的属性
vue3中Composition API,用setup包裹,响应式用ref(),reactive()
在此之前,Vue2.x 的 Mixins 以及 react 的高阶组件等模式都可以实现逻辑的组合与复用,但它们都存在数据来源不清晰、命名空间冲突以及性能的问题。写到这里,要向 react 致敬,因为 React Hooks 的出现是革命性的,而 Vue3 的 composition-api 提供的全新的逻辑复用方案也是受到了 React Hooks 的启发。
但是要return方法和data
const data = reactive({
orderTab:[{
icon:"send-gift-o",
text:"待付款"
},{
icon:"send-gift-o",
text:"待发货"
},{
icon:"todo-list-o",
text:"待收货"
},{
icon:"point-gift-o",
text:"待评价"
}]
})
let methodsMap = {
// 设置
setting:()=>{
router.push({path:'setting'})
}
}
return {
...methodsMap,
...toRefs(data)
}
}
但是vue3也支持option API
<template>
<div>
<p>{{ text1 }}</p>
<button @click="myFun1">按钮1</button>
<p>{{ age }}</p>
<button @click="myfun2">按钮2</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "App",
data() {
return {
text1: "option api",
};
},
// setu函数是组合api的入口函数
setup() {
let age = ref(20);
function myfun2() {
alert("llooppp");
}
return { age, myfun2 };
},
components: {},
methods: {
myFun1() {
alert("abcdefg");
},
},
};
</script>
9.1暴露给模版的属性来源清晰(从函数返回);
9.2不存在命名空间冲突,返回值可以被任意重命名,并且组件可以用点语法;
9.3受到了 React Hooks 的启发,没有创建额外的组件实例所带来的性能损耗。
可读性强,不用跳跃式查找
importuseAfrom'./a';
importuseBfrom'./b';
importuseCfrom'./c';
exportdefault{
setup (props) {
let{ a, methodsA } = useA();
let{ b, methodsB } = useA();
let{ c, methodsC } = useC();
return{
a,
methodsA,
b,
methodsB,
c,
methodsC
}
}
}
<template>
<div ref="div">
这个是div
</div>
</template>
<script lang="ts">
import { defineComponent,onMounted,ref } from 'vue'
export default defineComponent({
setup() {
const div=ref(null) //本质是reactive({value:null})
// 错误用法
// this.$refs.div
//正确用法
// 需要再onMountd 生命周期内使用
onMounted(()=>{
// 界面挂载完后 会执行
console.log(div.value)
})
//接受的是null,原因是setup执行时机比mounted早,dom还没形成 注意vue3的生命周期
console.log(div.value);// 执行早于 onMounted
return {
div
}
},
})
</script>
<template>
<div>
<button @click="click"></button>
// 直接引用变量获取值
<p>{{num}}</p>
</div>
</template>
<script lang="ts">
import { defineComponent,ref } from 'vue'
export default defineComponent({
setup() {
// 声明双向数据ref
const num=ref()
// js 通过.value 获取值
num.value=123;
const click = ()=>{
num.value+=1;
}
return {
num,
click
}
},
})
</script>
<template>
<div>
<p>{{ user }}</p>
<button @click="increase">click me! one year later</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "reactive",
setup() {
const user = reactive({ name: "Alice", age: 12 });
function increase() {
++user.age
}
return { user, increase };
},
};
</script>
如果觉得return麻烦,可以用script setup,还提供3个方法:
defineProps、defineEmits、useContext
参考连接
vue3函数式编程思路,支持ts更丝滑
使用方法:创建vue-cli4.5以上版本,支持vue3
vue3实例化,函数式编程
createApp(App).use(store).use(router).use(Vant).use(global).mount(‘#app’)
vue2面向对象方式
new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount(‘#app’)
10.ts类型检查,从flow改为ts
支持tsx对标react jsx
但是不能用v-for、v-if、@click,要用map,三目运算符,onClick
如何使用tsx:
import * as vue from 'vue';
const React = { createElement: vue.h, Fragment: vue.Fragment }
export const Hello = vue.defineComponent({
props: {
message: String
},
setup(props) {
return () => <div>{ props.message }</div>
}
})
11.1fun函数式编程思想,借鉴react
函数作为第一对象
闭包,return对象
没用new 类名。
函数作为参数传递,柯里化
不需要new 对象,直接调用函数。
vue2
new Vue({
router,
store,
render: h => h(App)
}).$mount(‘#app’)
vue3
createApp(App)
app.use(router)
app.use(store,key)
app.mount(‘#app’)
11.1 使用import来按需引入
vue2是将mounted,data,computed,watch之类的方法作为一个对象的属性进行导出。
vue3新增了一个名为setup的入口函数,value, computed, watch, onMounted等方法都需要从外部import。
使用自定义方法(ref、state、reactive、toRef、toRefs等)前需要引入
import { useStore } from “@/store”;
import { useRoute } from “vue-router”;
import { ref, reactive,computed, toRef, toRefs } from “vue”;
优点是:
首先就是我们需要写的代码量少了,
其次就是我们可以封装更多的子函数、引用更多的公共函数去维护我们的代码,
第三就是代码的可读性变高了。(当然,我们的打包体积也会变小)
参考
12.vue2用eventbus,vue3用mitt.js跨组件传值
Vue3 从实例中完全删除了
o
n
、
on、
on、off 和 $once 方法
那么官方推荐的做法是使用第三方库:
mitt.js
tiny-emitter
@ai-zen/event-bus -S
npm i @ai-zen/event-bus -S
import { eventBus } from "@ai-zen/event-bus";
const handler = () => console.log("handle");
eventBus.on("test on/off", handler);
eventBus.once("test once", () => console.log("I only be triggered once!"));
eventBus.emit("test on/off");
eventBus.emit("test once");
eventBus.off("test on/off", handler); // 关闭监听
13 element ui =>element plus
组件增加了(缩写seats):
Skeleton-骨架屏
Empty-空状态
Affix -固钉
TimeSelect 时间选择
Space 间距
14.pinia
状态持久化替代vuex(vue2和vue3都适用)
扁平化管理,不用mutation
参考链接
说明 | vue2 | vue3.0 | vue3.2 |
---|---|---|---|
数据结构 | data() | setup(){} | <script setup /> |
变量使用 | data(){return{} | setup(){return{…data}} | 不需要return 和export default{} |
顶级 await 的支持 | 要写async | 要写async | 不需要写async,直接await |
ref、parent | this.$parent | child组件return expose、emits | ref、 defineExpose、defineEmits |
this | this.$parent | getCurrentInstance 《h2 ref=“root”>姓名</h2》 const {proxy} = getCurrentInstance() console.log(proxy.$refs.root); | ref、 defineExpose、defineEmits |
父子组件传值 | props、emit | props、emit | defineProps、defineEmits |
插槽 | slot | slot、attrs | useSlots(在jsx/tsx中更实用)和useAttrs() |
vuex | slot | slot、attrs | useSlots和useAttrs() |
watch事件 | watch | watch | watch、watchEffect |
v-memo | 没有 | 没有 | 有。在v-for中使用v-memo时,确保它们被用在了同一个元素上。v-memo在v-for内部是无效的 |
component | 需要注册 | 需要注册 | 不需要注册 |
vscode插件 | 需要注册 | vetur | volar |
第三方插件 | 需要注册 | 需要注册 | 一般在 mounted中初始化(注:1) |
this | this | getCurrentInstance() | getCurrentInstance() |
beforeRouteEnter | beforeRouteEnter | 放在setup(){}前面。 | 单独建一个script放beforeRouteEnter |
emit | this.$emit | setup(props,context){context.emit(‘fun name’, var)} | import defineEmits from 'vue' ;const emit = defineEmits([“changeMsg”]);emit(“changeMsg”, “is son”); |
listener | this.$listener | 合并到attrs | 合并到attrs |
useRef和useEffect | 无 | 有 | 有 |
15.增加了很多API
姓名
import { onMounted, ref, getCurrentInstance } from 'vue' const {proxy} = getCurrentInstance() console.log(proxy.$refs.root);shallowReactive
shallowRef
watch
watchEffect
getCurrentInstance
isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
useStore 、 useRouter 和 useRoute 都是只能在 setup中才能使用
const store = useStore()
const route = useRoute() // vue2 this.
r
o
u
t
e
.
n
a
m
e
r
o
u
t
e
.
n
a
m
e
c
o
n
s
t
r
o
u
t
e
r
=
u
s
e
R
o
u
t
e
r
(
)
/
/
v
u
e
2
t
h
i
s
.
route.name route.name const router = useRouter() //vue2 this.
route.nameroute.nameconstrouter=useRouter()//vue2this.router.push(‘/’)
router.push(‘/’)
15.1 withDefaults 包裹defineProps泛类型
withDefaults(
defineProps<{
size?: number;
labels?: string[];
}>(),
{
size: 3,
labels: () => [“default label”],
}
);
15.2 withAsyncContext
const post = await withAsyncContext(
fetch(/api/post/1
).then(® => r.json())
);
15.3 teleport
让组件脱离固定的组件位置,可以挂载在逻辑上最优的位置, 其它使用都跟组件一样,只有位置改变
比如遮罩图层,to=“body”
15.4 defineComponent
defineAsyncComponent针对ts语法使用,可以有代码提示。
使用方法一:
import { defineAsyncComponent } from “vue”;
const Child = defineAsyncComponent(() => import(‘./child.vue’))
使用方法二:
defineCustomElement
import { defineCustomElement } from ‘vue’
const MyVueElement = defineCustomElement({
props: {},
emits: {},
template: ...
,
// defineCustomElement only: CSS to be injected into shadow root
styles: [/* inlined css */
]
customElements.define(‘my-vue-element’, MyVueElement)
document.body.appendChild(
new MyVueElement({
// initial props (optional)
})
)
15.5 defineExpose
子暴露给父,父用ref访问。
15.6 v-memo:
注1:
<script>
import { onMounted, getCurrentInstance } from 'vue'
export default {
setup () {
onMounted(() => {
const self = getCurrentInstance()
console.log(self.$moment().format('HH:mm'))
})
}
}
</script>
15.6减少了api
为了减少副作用 V3 移除了什么 off on
15.7 mixins->composition
1.溯源查找方便
2.命名空间不会冲突,可以用import{ *** as 别名 }
参考链接:
https://www.yisu.com/zixun/721560.html
16.deep样式穿透写法
vue2 /deep/
vue3 必须有一个根节点
<style lang="less" scoped>
:deep(.title3){
background-color:antiquewhite;
}
</style>