vue2.0与vue3.0

news/2024/5/21 3:01:29

这里写自定义目录标题

  • Object.defineProperty
    • createApp
    • 自定义指令更新
    • to:body
    • data
    • 多根节点组件、函数式组件
    • 一些其他与开发者关系较为密切的更新
    • 最具有颠覆意义的响应-组合 API
    • 数据传递demo

Object.defineProperty

我们知道 Vue2 是响应式原理基于 Object.defineProperty 方法重定义对象的 getter 与 setter,vue3 则基于 Proxy 代理对象,拦截对象属性的访问与赋值过程。差异在于,前 者并不能对诸如数组长度变化、增删元素操作、对象新增属性进行感知,在 vue 层面不得不 重写一些数组方法(push、pop、unshift、shift 等),动态添加响应式属性,也要使用 $set 方法等。而 Proxy 则完美地从根上解决了这些问题,不过对于不支持 Proxy 对象的浏览器(如 IE),如果要使用 vue3 依然要进行降级兼容

createApp

import { createApp, defineAsyncComponent } from 'vue'
import App from './App.vue'
import MyApp from './MyApp.vue'

可以挂载两个实例,实现完全隔离

createApp. mount('#root')
createApp. mount('#app')

异步组件的使用,挂载到哪里就只能在注册那个地方实现

const app = createApp. mount('#root')const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComp.vue') ) app.component('async-component', AsyncComp);

1.异步组件在打包的时候分离出来了,可以缓存它,多个组件同时使用,可以复用。
2.提前用占位符代替,后面在慢慢加载

const myapp = createApp. mount('#app')

自定义指令更新

// vue2

bind - 指令绑定到元素后发生。只发生一次。
inserted - 元素插入父 DOM 后发生。
update - 当元素更新,但子元素尚未更新时,将调用此钩子。
componentUpdated - 一旦组件和子级被更新,就会调用这个钩子。
unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次。.
// vue3
bind beforeMount
insertedmounted beforeUpdate :新的!这是在元素本身更新之前调用的,很像组件生命周期钩子。
update → 移除!有太多的相似之处要更新,所以这是多余的,请改用 updated。
componentUpdated updated beforeUnmount :新的!与组件生命周期钩子类似,它将在卸载元素之前调用。
unbind -> unmounted

to:body

比如一个弹框组件,要把它放到body 标签里去渲染,避免样式混淆<pop to:body></pop>;比如路由标题每个页面都有一个标题,是页面的属性,逻辑上是全局,业务上属于模块的

data

data 声明不再接收纯 JavaScript object,而必须使用 function 声明返回。

export default { data() { return { state: '', } } }

多根节点组件、函数式组件

多根节点允许 template 标签内直接出现多个子集标签,注意默认根节点需要明确指定。

<template>
<header></header>
<main v-bind $arrtr style={color:red}></main>
<footer><footer>
</template>

如果在main组件中,如果template只有一个子节点,会继承这个样式,如果有两个子节点,就不会继承样式了,需要在继承的那个节点使用$attrs

<template><div id=""><div class="MultiNode" v-bind="$attrs">1</div><div class="" v-bind="$attrs">2</div></div>
</template>
// $attrs 默认本来绑定在唯一根节点上,多节点时需要明确指定挂在哪个节点下,用来指定继承父节点的属性

在 Vue 2 中,函数式组件有两个主要用例:

  1. 作为性能优化,因为它们的初始化速度比有状态组件快得多
  2. 返回多个根节点

Vue 3 中,函数式组件剩下的唯一用例就是简单组件。只能使用普通函数来声明。

// vue2 Functional.vue
<template>
<div>{{ msg }} </div>
</template>
<script>
export default { functional: true, // 此处需要定义为ture 就可以使用函数式组件props: ['msg']}
</script>// vue3 Functional.js 个人感觉好难用
import { h } from 'vue'
export default function Functional(props, context) {
return h('div',
context.attrs,  // 属性
props.msg  // children
)
}
Functional.props = ['msg']

一些其他与开发者关系较为密切的更新

在同一元素上使用的 v-if 和 v-for 优先级已更改。即 if 比 for 的优先级更高了;

vue3渲染函数不再提供 createElement 参数(通常简写为 h),而是从依赖中导入; 标签上的属性与绑定对象的属性冲突时,以排在后面的属性为准,不再给单独属性更高 的优先级。例如:

<your-comp name="Tom" v-bind="{ name: 'Jim' }" />
// vue2 your-comp 的 props:
name:'Tom'
// vue3 your-comp 的 props:
name:'Jim' 如果顺序是 <your-comp v-bind="{ name: 'Jim' }" name="Tom" />
则 vue2 和 vue3 的 props 均为
name:'Tom
如果顺序是 <your-comp v-bind="{ name: 'Jim' }" name="Tom" />
则 vue2 和 vue3 的 props 均为
name:'Tom'

最具有颠覆意义的响应-组合 API

这也是我们学习和转向 vue3 最有意义的一部分内容,可以这么说,如果没有掌握这一 部分内容,那么即使我们底层使用了 vue3,也不过是写 vue2。就像我们拥有了一台铲车, 却还在坚持靠人力把重物装卸到车叉上,只把铲车当运输工具一样。

setup 函数
组件创建前执行的初始化函数,默认参数包含 props 和 context。context 可以理解为 组件实例挂载之前的上下文(虽然这么理解,但不是实例本身,这点要清楚),所以实例上 的 attrs,slots,emit 可以在 context 上访问到。

reactive(深层) | readonly(只读) | shallowReactive(浅层) | shallowReadonly (只读浅层)
用于创建响应式的对象(只能是对象),isReactive 可以判断对象是否为响应式的。isProxy 区分是哪 种方法创建的代理对象,isReadonly 判断对象是否为 readonly(不可被修改) 创建。shallowXXX 根据名称 可知,只将对象自身的浅层属性进行转换,深层属性保持不变。

toRaw | markRaw
前者返回代理对象的源对象,后者将普通对象转换为不可代理的对象。

ref | toRef | unref | toRefs | isRef | shallowRef | triggerRef
常用于包装基本类型值为响应式对象,例如 const box = ref(0), box.value === 0 unref: isRef(data) ? data.value : data shallowRef:创建浅层的包装对象 triggerRef:人为触发订阅了 shallowRef 的副作用。

effect | watch | watchEffect
对数据源的更新订阅,一旦订阅的数据发生变化,将自动执行副作用函数。效果与 vue2 的 watch 配置类似。

computed
基于响应式数据生成衍生数据,且衍生数据会同步响应式数据的变化,效果与 vue2 的 computed 属性一样。

provide,inject
跨组件传递数据的方案。 基于 setup 方法使用的生命周期钩子同样有对应更新

基于 setup 方法使用的生命周期钩子同样有对应更新

beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted
errorCaptured -> onErrorCaptured // 错误上报钩子,仅做了解
renderTracked -> onRenderTracked // { key, target, type } 仅做了解 renderTriggered -> onRenderTriggered // { key, target, type } 仅做了解

<template><div @click="increment">compositionVue: {{data.count}}<br /></div><span @click="pintCountRef">count: {{count}}</span><p>f: {{f}}</p><p>computedVal: {{computedVal}}</p>
</template><script>
import { reactive, toRaw, ref, effect, toRefs, watch, computed } from 'vue'
export default {setup(props, context) {const data = reactive({ count: 0, obj: {f: 2} })  // 定义了一个对象类型的 响应式对象const count = ref(0) // 定义了一个基本类型 响应式对象const pintCountRef = () => {console.log(++count.value)  // count 是一个对象, 使用count.value 来取值,标签里可以直接用,模板层面给解析出来了}const origin = toRaw(data) // 返回源对象,不用也不影响effect(() => {// pagination, search query  vue3 只关心变化了的值,变化之后,比如搜索功能,分页,查询参数,当变化的时候,不需要手动的调用请求console.log(data.count); // 副作用,如果使用了响应化数据,如果响应化数据有变化的时候就执行回调});/* watch([count, data.count], (value) => { //比vue2 有点变化// 第一个参数可以是函数,返回值变了就回调// 第一个参数可以是 ref / reactive,ref/reactive 值变了就回调// 第一个参数可以是 数组,数组每一项变了就回调alert(value)}); */watch(() => count.value > 5, (value) => { // 可以是条件,满足条件执行一次的效果// 第一个参数可以是函数,返回值变了就回调// 第一个参数可以是 ref / reactive,ref/reactive 值变了就回调// 第一个参数可以是 数组,数组每一项变了就回调alert(value)});function increment () {data.count++data.obj.f++ // 页面上的f 不会变,不是响应式变量,}const { f } = toRefs(data.obj);  // 基本值都是包装过的在解构// const f = ref(data.obj.f)const computedVal = computed(() => count.value + data.count); //计算衍生值 惰性的,只有变了才执行,不能对它进行重新赋值const computedVal2 = computed(() => count.value * data.count); // vue2里可以写多个,这里可以写一个,执行多次return { data, increment, count, pintCountRef, f, computedVal } // 在这里return 之后 template里才能获取}}
</script>

数据传递demo

// 父组件

export default {name: 'MyApp',// props: [''],data() {return {state: {value: 0}}},provide() { // 要传递给子组件的变量和方法return {state: readonly(this.state), // 使用readonly 子组件不能改onChange: () => {this.state.value = Math.random()}}},components: {  composition  }
}

// 子组件

import { h, reactive, inject, onMounted } from 'vue'export default {setup(props, context) {const data = reactive({ count: 0 })const increment = () => {data.count++}const state = inject('state')  // 注入state 获取到父组件的stateconst onChange = inject('onChange') // 获取父组件的方法/* const onChange = () => {  // 直接变父组件的state,会导致父组件里的state变化,其他引用都会变化,会出问题state.value++} */onMounted(() => { // 不是生命周期的定义,是一个注册,可以注册多个;没有thisconsole.log('task1')})onMounted(() => {console.log('task2')})return () => h('div', {onClick: onChange}, 'composition: ', data.count, 'state.value', state.value)}
}

终极简化:单文件组件 defineProps,defineEmits 直接在

// 在<script setup>内部的顶层变量,均能直接用于模板部分,
// 省略了 setup 函数及其返回值
<template>
<span @click="increment">{{count}}</span>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
</script>

下面是自定义hooks demo

// customHooks

<template>customHook: {{loading ? '下载中' : '下载完成'}}<button @click="refresh">重试</button><ul><li v-for="{name, id} in info.value" :key="name">{{name}}</li></ul>
</template><script>import useUserInfo from './hooks'import { ref, useSlots } from 'vue'export default {setup(props, { slots }) {const id = ref(0);const { info, loading } = useUserInfo(id);const refresh = () => {id.value++;}return { info, loading, refresh }}}
</script><style>
</style>

// hooks

import { ref, onMounted, watch, reactive } from 'vue'const fetch = (id) => new Promise((resolve, reject) => {setTimeout(() => {resolve([{ name: 'user-' + Math.random(), id: 1 },{ name: 'user-' + Math.random(), id: 2 },{ name: 'user-' + Math.random(), id: 3 },{ name: 'user-' + Math.random(), id: 4 },{ name: 'user-' + Math.random(), id: 5 },]);}, 200)
})export default function useUserInfo(id) { // id 为响应式数据源const info = reactive({value: []})const loading = ref(false)const getUserInfo = () => {loading.value = truefetch(id.value).then(user => {info.value = userloading.value = false})}onMounted(getUserInfo)watch(() => id.value, getUserInfo);return { info, loading }
}

http://www.mrgr.cn/p/66577688

相关文章

如何创建window11虚拟机,超详细!!!

为什么要创建window11虚拟机&#xff0c;有了虚拟机&#xff0c;可以避免一些软件的危害&#xff0c;我们可以在虚拟机里&#xff0c;测试软件的安全性。当然你也可以在虚拟机里体验下流氓软件带来的快乐。众所周知&#xff0c;流氓软件会修改默认浏览器&#xff0c;将几款流氓…

OpenWRT有线桥接部署教程

前言 之前咱们讲到OpenWRT部署WAN实现PPPoE拨号上网和自动获取IP模式上网的办法&#xff1a; OpenWRT设置PPPoE拨号教程 OpenWRT设置自动获取IP&#xff0c;作为二级路由器 这一次&#xff0c;咱们尝试用OpenWRT有线桥接上一级路由器的教程。 可能有小伙伴敏锐地发现了&am…

金丝燕智能门锁技术支持

1、如何通过金丝燕App设置门锁? 在首页App首页,您可以通过点击门锁右上角设置按钮进入门锁设置页面,进行省电模式设置,开启通知,设备解绑等操作以下是门锁设置页面:2、 如何给让我的门锁连接网络? 第一步:点击金丝燕App首页右下角“+”号: 第二步:选择设备第三步:输入Wi-Fi信…

Docker-harbor

一、搭建本地私有仓库 1.1 下载Registry镜像 1.2 添加本地私有仓库配置 1.3 重启服务并运行Registry容器 1.4.容器的操作 1.4.1 拉取Nginx镜像并为镜像打标签 1.4.2 上传到私有仓库 1.4.3 列出私有仓库所有镜像 1.4.4 列出私有仓库的镜像的所有标签 1.4.5 先删除原有…

[uniapp] 配置ts类型声明

我想引进图片,但是报错 声明一下就行 TypeScript 支持 | uni-app官网 创建tsconfig.json文件,复制官网的配置 然后在随便一个目录下写一个随便名字的.d.ts文件 例如这样 保存就行 因为ts是默认扫描全部的,所以要按照官网的写法 把不必要的排除掉就行,免得浪费性能

e语言----让信息框弹出位置跟随窗口

提醒:调试不起效果,需要编译好才行本文来自博客园,作者:__username,转载请注明原文链接:https://www.cnblogs.com/code3/p/18185605

单词反转字符串函数设计

简单设计一个以单词为个体的字符串翻转函数/*************************************************** file name:ReverseWorld.c* author :eon4051@163.com* date :2024/05/11* brief :单词反转字符串函数设计* note :None** CopyRight (c) 2024 eon…

Kettle连接Mysql数据库时报错——Driver class ‘org.gjt.mm.mysql.Driver‘ could not be found

一、问题描述 当我们使用ETL工具Kettle需要连接Mysql数据库进行数据清洗操作,在配置好Mysql的连接串内容后,点击【测试】按钮时报错【错误连接数据库 [MysqlTestConnection] : org.pentaho.di.core.exception.KettleDatabaseException: Error occurred while trying to conne…

e语言读写配置ini

两种方法本文来自博客园,作者:__username,转载请注明原文链接:https://www.cnblogs.com/code3/p/18185595

word 毕业论文格式调整

添加页眉页脚 页眉 首先在页面上端页眉区域双击&#xff0c;即可出现“页眉和页脚”设置页面&#xff1a; 页眉左右两端对齐 如果想要页眉页脚左右两端对齐&#xff0c;可以选择添加三栏页眉&#xff0c;然后将中间那一栏删除&#xff0c;即可自动实现左右两端对齐&#x…

Java毕设之学院党员管理系统的设计与实现

运行环境 环境说明: 开发语言:java 框架:springboot&#xff0c;vue JDK版本:JDK1.8 数据库:mysql5.7(推荐5.7&#xff0c;8.0也可以) 数据库工具:Navicat11 开发软件:idea/eclipse(推荐idea) Maven包:Maven3.3.9 系统实现 管理员功能实现 党员管理 管理员进入指定功能操作…

element ui的table多选

使用el-table的selection-change事件来获取选中的值&#xff1b; 例&#xff1a; html代码&#xff1a; <el-button type"primary" click"openTableSet">列表设置</el-button><!-- 列表设置弹框 --> <el-dialog :close-on-click-mo…

【专题】中国银行业2023年发展回顾及2024年展望报告合集PDF分享(附原数据表)

原文链接 :https://tecdat.cn/?p=36145 原文出处:拓端数据部落公众号 2023年,尽管面临全球经济复杂多变与国内经济多重挑战,中国银行业依然稳健前行,不仅圆满完成了社会经济发展的主要任务,还以“稳进相济,进而有为”的姿态,为实体经济的高质量发展提供了有力支撑,展望…

Docker 加持的安卓手机:随身携带的知识库(一)

这篇文章聊聊&#xff0c;如何借助 Docker &#xff0c;尝试将一台五年前的手机&#xff0c;构建成一个随身携带的、本地化的知识库。 写在前面 本篇文章&#xff0c;我使用了一台去年从二手平台购入的五年前的手机&#xff0c;K20 Pro。 为了让它能够稳定持续的运行&#xf…

力扣-题号2997

2997. 使数组异或和等于 K 的最少操作次数题目给你一个下标从 0 开始的整数数组 nums 和一个正整数 k 。 你可以对数组执行以下操作 任意次 :选择数组里的 任意 一个元素,并将它的 二进制 表示 翻转 一个数位,翻转数位表示将 0 变成 1 或者将 1 变成 0 。你的目标是让数组里…

现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障

1、前言 众所周之,通常开发一个移动端应用,会直接调用系统提供的网络请求接口去服务端请求数据,再针对返回的数据进行一些处理。 但对于追求用户体验的应用来说,还会针对移动网络的特性做进一步优化,包括: 1)速度优化:网络请求的速度怎样能进一步提升? 2)弱网适应:移…

第08章 IP分类编址和无分类编址

8.1 本章目标 了解IP地址的用途和种类了解分类编址和无分类编址区别掌握IP地址、子网掩码、网关概念及使用掌握子网划分及超网划分方法掌握无分类编址的改变和使用 8.2 IP地址的用途和种类 分类编址&#xff1a;造成地址的浪费&#xff0c;以及地址不够用&#xff1b;无分类编…

JavaScript数字分隔符

● 如果现在我们用一个很大的数字&#xff0c;例如2300000000&#xff0c;这样真的不便于我们进行阅读&#xff0c;我们希望用千位分隔符来隔开它&#xff0c;例如230,000,000; ● 下面我们使用_当作分隔符来尝试一下 const diameter 287_266_000_000; console.log(diameter)…

手机H5页面在IOS系统中无法获取Geolocation

需求 在开发H5页面的时候希望获取用户的地理位置信息,这里演示在用户上传图片的时候将用户的地理位置信息作为水印显示。 问题 在安卓手机使用vant-upload组件是没问题的,但是在IOS手机上有,报下面的提示信息。原因 苹果的IOS做了限制,如果需要使用IOS的服务,必须是HTTS协…

【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)

【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)是期刊“NEURAL COMPUTING & APPLICATIONS”&#xff08;IF 6.0&#xff09;的2021年智能优化算法 01.引言 【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)用于解决约束和全局优化问…