vue3学习

vue2和vue的区别

一、基础语法

1、双向数据绑定

vue2

      //defineProperty 无法直接监听到元素的增加,删除
        let person = {
            name: 'hello'
        }
        let p = {}
       把值通过遍历做数据劫持
        Object.defineProperty(p, 'name', {
            get() {
                return person.name
            },
            set(value) {
                console.log('变更后的值为:', value)
                person.name = value
            }
        })
        p.name = 'world'

vue3

        let person = {
            name: 'hello'
        }
        let p = new Proxy(person, {
            get(target, propName) {
                return Reflect.get(target, propName)
            },
            set(target, propName, value) {
                Reflect.set(target, propName, value)
            },
            deleteProperty(target, propName) {
                Reflect.deleteProperty(target, propName)
            }
        })
        p.name = "world"
2、ref, reactive

ref:一般用在定义基本类型和引用类型,如果是引用类型底层会借助reactive形成proxy代理对象,可以直接复制整个对象,如table的数据请求回来,需要将数据整体赋值个响应对象这时如果使用的是reactive就无法进行响应。

reactive:一般用在引用类型,如{}等,不能一次性修改整个对象,如我们后端请求table的数据数据,如果想一次性赋值的整个数组的话,就行不通,此时建议使用ref来定义数组。

!!!注意
let obj=reactive([])
setTimeout(()=>{
     let arr=[1,2,3]
     //当页面初始化完成以后,这种直接赋值会改变obj的对象指向,是不会生效的
     obj=arr
},1000)

ref还可以获取元素

注意:

在vue3中私用ref 等api需要手动引入,如果不想手动引入的话可以使用setup语法糖插件
1、npm i unplugin-auto-import -D
2、在vite.config.js中配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),
    AutoImport({
      imports:['vue','vue-router'],
      dts:"src/auto-import.d.ts"//生成auto-import.d.ts全局声明
    })
  ]
})
3、toRef toRefs 把变量变成ref
<template>
  <div>{{name}}</div>
</template> 

return {
    name:toRef(person,'name')
或者
    ...toRefs(person)
}
// 如果想在setup语法糖中使用toRefs
<script setup lang="ts">
let person = reactive({
  name: "hello"
});
const { name } = toRefs(person);
</script>
4、shallowReactive,shallowRef 浅层次的响应式

shallowReactive只处理对象最外层的响应。如果一个对象数据,结构比较深,但变化时只是外层属性变化
shallowRef只处理基本数据类型的响应式,不处理对象类型的响应式。如果一个对象数据,后续不会修改该对象中的属性,而是生新的对象来替换。

5、readonly shallowReadonly

readonly:让一个响应式变为只读(深只读)(person=readonly(person))
shallowReadonly:让一个响应式变为只读(浅只读,不能更改最外层的数据)

6、toRaw markRaw

toRaw
将一个由reactive生成的响应式Proxy对象转为普通对象
适用场景:用于读取响应式对象对应的普通对象,对这个普通对象做操作不起作用
markRaw
标记一个对象使其永远不会再成为响应式对象
应用场景:
①、有些值不应被设置为响应式,例如复杂的第三方类库
②、当渲染具有不可变数据源的大列表时,跳过响应式转换可提高性能
③、当添加的元素不想做响应式,但值会正常发生改变:person.car=markRaw('ffff')

7、customRef

创建自定义的ref,并对其依赖项跟踪和更新触发进行显示控制

 let custom = myRef('hello')
    //自定义ref名称为myRef
    function myRef(value) {
      return customRef((track, trigger) => {
        return {
          get() {
            console.log('获取最新的值')
            track()//追踪当前数据
            return value
          },
          set(newValue) {
            console.log('收到最新的值', value)
            value = newValue
            //通知vue重新解析模板
            trigger()
          }
        }
      })
    }
8、provide inject

祖孙组件传值

//祖组件
setup(){
    let obj={}
    provide('obj,obj)
}
//孙组件
setup(){
    let obj=inject('obj)
}
9、props

//需要在props中定义

props: {
    msg: String,
  },
  setup(props, context) {
    //context为执行上下文
    let name = ref(props.msg)
}
//如果是使用了setup语法糖(仅限类型的 props 声明)
1、 `defineProps` 可以使用运行时声明和类型声明两种方式(详见[官方文档](https://link.juejin.cn/?target=https%3A%2F%2Fv3.cn.vuejs.org%2Fapi%2Fsfc-script-setup.html%23%25E4%25BB%2585%25E9%2599%2590-typescript-%25E7%259A%2584%25E5%258A%259F%25E8%2583%25BD "https://v3.cn.vuejs.org/api/sfc-script-setup.html#%E4%BB%85%E9%99%90-typescript-%E7%9A%84%E5%8A%9F%E8%83%BD")),但是不能同时使用。为了使 TS 更好的流转,实践中建议使用上例中的**类型声明**方式。
2、withDefaults 用来声明 props 的默认值,使用方式与运行时声明定义 default 的格式一样
interface Props {
  msg?: string
  labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})
10、emit

需要在emits中定义

 emits: ['hello'],
//setup中
 function sayHello() {
      context.emit('hello')
    }
//在setup语法糖中
// e 为 emit 的名字,第二个参数的 key 可以随意定义
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
//获取子组件的实例
defineExpose({
  sayHello
})
11、computed
   let fullName = computed(() => {
      return `${props.msg}${props.age}`
    })
12、watch watchEffect

首次加载不会监听,只会被监听数据发生变化时才监听到。(惰性)
可以拿到新的值以前的值
可以同时监听多个数据的变化
①、监听ref定义的变量 1个或多个

watch(job, (value) => {
    console.log('值变化了', value)
  })

//多个时,返回值为数组

watch([job, hobby], (value) => {
    console.log('值变化了', value)
  }, { immediate: true})

②、监听relative所定义的响应式数据的全部属性
注意1、此处无法正确获取到oldValue
2、强制开启了深度监听(deep配置无效)

watch(obj, (newValue,oldValue) => {
      console.log('值变化了', value)
    })

③、监听relative所定义的响应式数据的某些属性,props中的数据的监听同理

watch(() => props.msg, (value) => {
   console.log('值变化了', value)
 }, { immediate: true })
//监听某些具体的属性为对象,deep是起作用的
 watch([() => obj.name,() => obj.age], (value) => {
   console.log('值变化了', value)
 },{deep:true})

watchEffect的特性
watchEffect 也是一个帧听器,是一个副作用函数。
它会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听
不需要手动传入依赖(不用指定监听对象)
无法获取原始值,只能获取更新后的值
立即执行(在onMounted前调用)
一些异步操作放里面更加的合适
只能拿到最新的值
不需要指定监听的数据,组件初始化的时候就会执行一次用以收集依赖,而后收集到的依赖发生变化,这个回调才会再次执行
有点像computed,但computed更注重计算出来的值,必须写返回值。watchEffect更注重过程,不用写返回值。

 watchEffect(() => {
     //会监听在里面使用的值
      console.log('发生变化',job.value) // ref  类型的需要加.value
      })
onInvalidate 解析
①、停止监听
let stop=  watchEffect(() => {
      console.log('发生变化',job.value) // ref  类型的需要加.value
      })
stop()
②、配置项:
副作用刷新时机:flush  一般使用post
    pre:组件更新前执行
    aync:强制效果始终同步触发
    post:组件更新后执行
onTrigger 用来调试watchEffect
③、清除副作用
有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (场景:有一个页码组件里面有5个页码,点击就会异步请求数据。于是做一个监听,监听当前页码,只要有变化就请求一次。问题:如果点击的比较快,从1到5全点了一遍,那么会有5个请求,最终页面会显示第几页的内容?第5页?那是假定请求第5页的ajax响应的最晚,事实呢?并不一定。于是这就会导致错乱。还有一个问题,连续快速点5次页码,等于我并不想看前4页的内容,那么是不是前4次的请求都属于带宽浪费?这也不好。
于是官方就给出了一种解决办法:
侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。
13、生命周期及对应写入setup中的表示

生命周期           setup中
beforeCreate     setup()
created              setup()
beforeMount      onBeforeMount
mounted            onMounted
beforeUpdate    onBeforeUpdate
updated             onUpdated
beforeUnmount  onBeforeUnmounted
unmounted         onUnmounted

13、自定义hook函数

把setup中使用的composition API进行封装,类似于mixin

//hook->usePoint.ts
import {  reactive,onMounted } from 'vue';
export default function(){
    //实现鼠标打点的数据
    let point = reactive({
        x: 0,
        y: 0
      })
      onMounted(() => {
        window.addEventListener('click',(event) => {
          point.x = event.pageX
          point.y = event.pageY
        })
  
      })
      return point
}
//使用时
import usePoint from './hooks/usePoint'
let point=usePoint()
14、响应式数据的判断

isRef
isReactive
isReadonly
isProxy:检查一个对象是否由reactive或者readonly方法创建的代理
fragment(片段)
组件没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中

15、teleport(传送)

能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术

 <button>显示弹窗</button>
 <teleport to="body">
    <div>弹窗内容</div>
 </teleport>
16、defineAsyncComponent

等待异步组件时渲染一些额外的内容,让应用有更好的用户体验。
缺点:在网速慢的时候会有抖动的效果。使用Suspense进行占位(这个是实验属性)。

  <Suspense>
    <template v-slot:default>
      <Child></Child>
    </template>
    <template v-slot:fallback>占位内容</template>
  </Suspense>

import { defineAsyncComponent } from 'vue';
const Child=defineAsyncComponent(()=>import('./components/Child'))//异步引入
export default defineComponent({
  name: 'App',
  components: {
   Child
  },
}

二、差异调整

api调整

2.X全局api                       3.X实例api
Vue.config.xxx                  app.config.xxx
Vue.config.productionTip   移除
Vue.component                 app.component
Vue.directive                     app.directive
Vue.mixin                           app.mixin
Vue.use                             app.use
Vue.prototype                    app.config.globalProperties

其他调整

①、data选项应始终被生命为一个函数
②、过渡类名的更改
vue2.x

.v-enter,                             
.v-leave-to{
opacity:0;
}
.v-leave,
.v-enter-to{
opacity:1;
}

vue3.x

.v-enter-from,                             
.v-leave-to{
opacity:0;
}
.v-leave-from,
.v-enter-to{
opacity:1;
}

事件修饰符(不变此处记录一下)

.prevent:阻止默认事件
.stop:阻止事件冒泡
.once:事件只触发一次
.capture:使用事件的捕获模式
.self:只有event.target是当前操作的元素时才触发事件
.passive:事件的默认行为立即执行,无需等待事件回调执行完毕

③、移除keyCode作为v-on的修饰符,同时也不再支持config.keyCode
不能使用@keyup.13这种使用方法
Vue.config.keyCode.huiche=13 //不支持这样定义别名按键
④、移除v-on.native修饰符
父组件中绑定事件

<Child 
   v-on:close="handleClose"
   v-on:click="handleClick"
>
</Child>

子组件中声明自定义事件

export default{
  //如果不声明就是原生事件,声明了就是自定义事件
  emits:['close']
}

⑤、移除过滤器filter

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容