vue3的语法

main.js中写发生变化,并不兼容vue2的写法

javascript 复制代码
//vue3
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

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


//vue2
import Vue from 'vue'
import './style.css'
import App from './App.vue'

const vm = new Vue({
    render:h=>h(app)
})
vm.$mount("#app")

vue3中template模版可以不使用根标签。


Composition API

setup:组件中所用到的数据,方法,计算属性,生命周期等,均配置在这里,返回有两种值:

  1. 对象:其属性,方法在模版中可以直接使用。
  2. 返回一个渲染函数,可自定义渲染内容,会直接代替模版内容,不常用

setup在beforeCreate之前执行一次,this是undefined

setup含有两个参数:

props:值为对象,包含组件外部传递过来的,且组件内部声明接收了的属性,想要获取props的属性,一定需要props接收参数,这点和vue2相同,倘若不使用props接收,那么数据会存储在context参数中的attrs属性中。

context:上下文对象,包括

attrs:包含组件外部传递过来,但没有在props中声明的属性,相当于替换$attrs

slots:收到的插槽内容,相当于this.$slots,

emit:分发自定义时间的函数,相当于挺好$emit(见下方代码)

emits与vue2有区别,在vue3中传递给父组件需要emits来声明已绑定在子组件的事件。

vue3不要和vue2混用,vue2可以访问到setup中的数据,但setup不能访问到vue2的数据,如果有重复数据,setup的优先级更高。setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模版会看不到return中的属性。

html 复制代码
<script>
import { h } from 'vue';
import HelloWorld from './components/HelloWorld.vue'
export default{
  name:"app",
  props:['name','age'],
  emits:['test']
  compponents:{HelloWorld},
  setup(props,context) {
    //非响应式数据
    let name = props.name?props.name:'张三'
    let age = props.age?props.agge:18
    
    function Greeting(){
      console.log(name,age);
    }
    //返回一个对象(常用)
    return {
      name,
      age,
      Greeting
    }

    function test(){
        context.emit(test,'123')
    }

    //返回渲染函数
    // return ()=> h("h1",'你好')
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{name }}-{{ age }}</p>
</template>

<style scoped>
</style>

响应式数据

ref函数 适用于基本类型数据,也可以是对象类型,创建一个reference的引用对象,修改数据需要.value的值,模版中读取数据不需要.value,直接使用即可。

接收的数据可以是基本类型也可以是对象类型,基本类型的书记是靠数据劫持Object.defineProperty()完成的,而对象类型会转为Proxy对象,借助了reactive函数。

reactive函数 定义一个对象类型的响应式数据(基本类型不需要它,使用ref),包括数组类型也支持响应式。

内部基于es6的proxy实现,返回一个代理对象Proxy,通过代理对象内部数据进行操作,reactive定义的响应式数据是深层次的。

html 复制代码
<script>
import {ref,reactive} from 'vue'
import HelloWorld from './components/HelloWorld.vue'
export default{
  name:"app",
  props:['name','age'],
  compponents:{HelloWorld},
  setup(props) {
    //定义响应式数据
    let name = ref('张三')
    let age = ref(19)
    let obj = reactive({type:"ui",salary:"100k"})
    function msgChange(){
      name.value = "李四"
      age.value = 20
      obj.type = 'web'
      obj.salary = '200k'
    }
    //返回一个对象(常用)
    return {
      name,
      age,
      msgChange,
      obj
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{name }}-{{ age }}</p>
  <p>{{obj.type }}-{{ obj.salary }}</p>
  <button @click="msgChange">修改信息</button>
</template>

<style scoped>
</style>

proxy的响应式实现原理:

通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写,属性的添加,属性的删除等,比起vue2多出了增加和删除属性的监听。

通过Reflect(反射):对源对象的属性进行操作。类似于objec的操作方法。

使用Reflect的原因:在封装框架的过程中,Object.defineProperty()如果添加了相同的属性名,那么会直接报错,导致js线程阻塞,如果要解决那么就要写很多的 try{} catch(){} ,Reflect.defineProperty()会有个返回值,可以通过判断决定是否即系往下走,所以Reflect相对友好一点。

html 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive} from 'vue'

export default{
  name:"app",
  props:['name','age'],
  compponents:{HelloWorld},
  setup(props) {
    let person = {name:"张三",age:18}
    const p = new Proxy(person,{
      //target就是person本身

      //以下两种方式都可以,vue3主要采用Reflect的方式
      get(target,propName){
        // return target[propName]
        return Reflect.get(target,propName)
      },
      //读取和新增属性,都会调用这个方法
      set(target,propName,value){
        // target[propName] = value
        Reflect.set(target,propName,value)
      },
      deleteProperty(target,propName){
        // return delete target[propName]
        return Reflect.defineProperty(target,propName)
      }
    })

    function msgChange(){
      person.name='李四'
    }
    //返回一个对象(常用)
    return {
      person
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{person.name }}-{{ person.age }}</p>
  <button @click="msgChange">修改信息</button>
</template>

<style scoped>
</style>

计算属性computed:

需要引入computed函数,配置与vue相似。

html 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive,computed} from 'vue'

export default{
  name:"app",
  setup(props) {
    //定义响应式数据
    let name = ref('张三')
    let age = ref(19)
    //简写
    let fullName = computed(()=>{
      return name.value
    })
    //完整写法
    let fullName2 = computed({
      get(){
        return name.value
      },
      set(value){
        name.value = value
      }
    })

    //返回一个对象(常用)
    return {
      name,
      age,
      fullName,
      fullName2
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{fullName}}</p>
  <p>{{fullName2}}</p>
</template>

<style scoped>
</style>

监听watch:

html 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive,computed,watch} from 'vue'

export default{
  name:"app",
  setup(props) {
    //定义响应式数据
    let name = ref('张三')
    let age = ref(19)
    let person = reactive({
      name:"李四",
      age:19,
      job:{
        job1:'20k'
      }
    })
    //监听ref定义的数据,多个使用数组
    watch([name,age],(newVal,oldVal)=>{
      console.log(111);
    },{immediate:true,deep:true})
    //监听reactive定义的数据,会强制开启深度监听模式,拿不到oldValue
    watch(person,(newVal,oldVal)=>{
      console.log(newVal,oldVal);
    },{immediate:true,deep:true})
    //监听reactive定义的数据的一个属性,多个属性用数组
    watch([()=>person.name,()=>person.age],(newVal,oldVal)=>{
      console.log(newVal,oldVal);
    })
    //特殊情况,监听reactive对象属性,这个属性是个对象,需要开启深度监听,否则无效,同时也拿不到oldValue
    watch(()=>person.job,(newVal,oldVal)=>{
      console.log(newVal,oldVal);
    },{deep:true})
    function nameChange(e){
      name.value = '333'
    }
    function nameChange(e){
      name.value = '333'
      person.name='222'
    }
    //返回一个对象(常用)
    return {
      name,
      age,
      nameChange,
      person
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{name}}</p>
  <button @click="nameChange">修改</button>
</template>

<style scoped>
</style>

监听reactive对象时,不能获取到oldvalue,并且强制开启了deep深度监听配置,而且配置无效。

监听reactive对象的某个属性(为对象)时,必须deep配置才有效。

监听ref对象时,需要添加.value,实际上就是监听proxy代理的reactive的对象数据,强制开启了深度监听。

监听ref对象时,也可以直接使用对象名称,但是需要自己开启deep深度监听。

javascript 复制代码
let person = ref({
      name:"李四",
      age:19,
      job:{
        job1:'20k'
      }
    })
    //监听ref定义的数据,深度监听要自己手动添加deep深度监听
    watch(person,(newVal,oldVal)=>{
      console.log(111);
    },{deep:true})
    //实际是reactive数据,强制开启了deep深度监听
    watch(person.value,(newVal,oldVal)=>{
      console.log(newVal,oldVal);
    })

watchEffect:同样需要先引入,不用指明监视的属性,监视的回调中用到哪个属性,就监视哪个属性。

javascript 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive,computed,watch,watchEffect} from 'vue'

export default{
  name:"app",
  setup(props) {
    //定义响应式数据
    let name = ref('张三')
    let age = ref(19)
    let person = reactive({
      name:"李四",
      age:19,
      job:{
        job1:'20k'
      }
    })
    watchEffect((()=>{
      const x1 = name.value
      const x2 = person.job.job1
      console.log('watchEffect');
    }))
    function nameChange(e){
      name.value = '333'
    }
    function nameChange(e){
      name.value = '333'
      person.name='222'
    }
    //返回一个对象(常用)
    return {
      name,
      age,
      nameChange,
      person
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{name}}</p>
  <button @click="nameChange">修改</button>
</template>

<style scoped>
</style>

生命周期

vue2通过new Vue()的形式创建vue实例,在没有el的情况下需要等$mount函数调用才可以继续往下走,但已经执行了beforeCreate created生命周期,其实只创建了vm,并没有被挂载,会造成资源浪费;vue3通过createApp并且执行了mount函数之后,再执行生命周期函数,并且减少了判断次数,提高执行效率。

beforeDestroy替换为beforeUnmount

destroyed替换为unmounted

组合式生命周期api:

beforeCreate => setup()

created => setup()

beforeMount => onBeforeMount

mounted => onMounted

beforeUpdate => onBeforeUpdate

updated => onUpdated

beforeUnmount => onBeforeUnmount

unmounted => onUnmounted

html 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive,computed,watch,watchEffect,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'

export default{
  name:"app",
  setup(props) {
    onBeforeMount(() => {
      
    }),
    onMounted(() => {
      
    }),
    onBeforeUpdate(() => {
      
    }),
    onUpdated(() => {
      
    }),
    onBeforeUnmount(() => {
      
    }),
    onUnmounted(() => {
      
    }),
    //返回一个对象(常用)
    return {}
  }
}
</script>

<template>
  <div>
    app
  </div>
</template>

<style scoped>
</style>

自定义hook函数把setup函数中使用的组合式api进行封装。

javascript 复制代码
//useMounted.js
import {onMounted, reactive,} from 'vue'
export default function (){
    let obj = reactive({
        name:null,
        age:null
    })
    onMounted(()=>{
        obj.name = '789'
        obj.age=19
    })
    return obj
}
javascript 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive,computed,watch,watchEffect} from 'vue'
import useMounted from './hooks/useMounted'
export default{
  name:"app",
  setup(props) {
    let obj = useMounted()
    //返回一个对象(常用)
    return {
      obj
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{obj.name}}-{{ obj.age }}</p>
</template>

<style scoped>
</style>

toRef和toRefs:使对象里所有的属性都变为ref响应式数据。toRef智能操作一个属性,toRefs类似于浅拷贝,可以直接将reactive响应式对象放入,实现多个属性的转变,将数据拆散了return出去。

javascript 复制代码
<script>
import HelloWorld from './components/HelloWorld.vue'
import {toRefs,reactive,computed,watch,watchEffect,toRef} from 'vue'
export default{
  name:"app",
  setup(props) {
    let person = reactive({
      name:"李四",
      age:19,
      job:{
        job1:'20k'
      }
    })

    let student = reactive({
      grade:3,
      class:4
    })
    function nameChange(e){
      person.name = '333'
      person.age=99
    }
    //返回一个对象(常用)
    return {
      name:toRef(person,'name'),
      age:toRef(person,'age'),
      ...toRefs(student),
      nameChange,
    }
  }
}
</script>

<template>
  <div>
    app
  </div>
  <p>{{name}}-{{ age }}</p>
  <p>{{grade}}-{{ classes }}</p>
  <button @click="nameChange">修改</button>
</template>

<style scoped>
</style>

其他组合API(都需要在顶部引入对应的api)

shallowReactive与shallowRef

用法和ref相同,shallowReactive只会将对象最外层的属性作为响应式数据,内层的不是响应式。

shallowRef 如果传入是个基本类型,ref和shallowRef没有区别,如果传入是个对象,shallowRef不再进行响应式处理,但是ref会转为proxy的响应式。

readOnly和shallowReadOnly

readOnly将传入的对象类型的响应式数据全部变为只读,不可更改值,shallowReadOnly将响应式对象类型的数据第一层处理为readOnly深层次不处理,对于基本类型都处理为只读

javascript 复制代码
obj = readOnly(obj)
obj = shadowReadOnly(obj)

toRaw与markRaw

toRaw 将一个由reactive生成的响应式对象转为普通对象,不会引起页面更新。

markRaw 标记一个对象,使其永远不会再成为响应式对象(有些值不应该被设为响应式,例如第三方的库等等),跳过响应式转换可以提高性能。

javascript 复制代码
let obj = toRaw(person)
obj.car = markRaw(car)

customRef(自定义ref)

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

示例:

javascript 复制代码
<script>
import { customRef } from 'vue'
export default {
  name: "app",
  setup(props) {
    let keyWord = myRef('hello',1000)
    function myRef(value,delay) {
      let timer;
      return customRef((track, trigger) => {
        return {
          get() {
            track() //通知vue追踪数据变化
            return value
          },
          set(newValue) {
            clearTimeout(timer)
            timer = setTimeout(() => {
              value = newValue
              trigger() //通知vue去重新解析模版
            }, delay)
          }
        }
      })
    }
    //返回一个对象(常用)
    return {
      keyWord
    }
  }
}
</script>

<template>
  <input type="text" v-model="keyWord">
  <h3>{{ keyWord }}</h3>
</template>

<style scoped></style>

provide与inject(需要引入)

实现祖孙组件之间的通信:

祖组件:

javascript 复制代码
//祖组件
setup(){
    let car = reactive({name:'张三',age:9})
    provide('car',car)
}

//孙组件
setup(){
    const car = inject('car')
    return{
        car
    }
}

响应式数据的判断

isRef:是否为ref对象

isReactive:是否为reactive创建的响应式

isReadonly:是否是由readonly创建的只读代理

isProxy:是否由reactive或者readonly方法创建的代理


一些新的组件

Fragment

vue2中组件必须有一个根标签,vue3中可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中,减少了标签层级,减少内存占用。

Teleport

是一种能将我们组件html结构移动到指定位置的技术

to:指定所要传送的位置(body / html /css选择器),会改变页面的html的布局。

html 复制代码
<teleport to="body">
    <Demo />
<teleport/>

Suspense组件

等待异步组件时渲染一些额外内容,用户体验变好。

html 复制代码
//父组件
<Suspense>
    <template v-slot:default>
        <Child />
    <template />
    <template v-slot:fallback> //有个加载中的画面提示
        <h3>加载中。。。<h3/>
    <template />
<Suspanse/>


//异步引入
<script>
   import {defineAsyncCompponent} from 'vue'
   const Child = defineAsyncCompponent(()=>import('./Child')) //动态引入
   export default{
       components:{Child}
   }
</script>
html 复制代码
//子组件
<template>
    <h3>子组件<h3/>
<template/>

//异步引入
<script>
   import {defineAsyncCompponent,ref} from 'vue'
   const Child = defineAsyncCompponent(()=>import('./Child')) //动态引入
   export default{
       setup(){
         let sum = ref(0)
         let p = new Promise((resolve,reject)=>{
           setTimeout(()=>{
               resolve(sum)
           },3000)
         }) 
         return await p
       }
   }
</script>

全局api的转移

全部调整带应用实例app上了

|--------------------------|-----------------------------|
| 2.x全局API(Vue) | 3.x全局API(app) |
| Vue.config.xxx | app.config.xxx |
| Vue.config.productionTip | 移除 |
| Vue.component | app.component |
| Vue.directive | app.directive |
| Vue.mixin | app.mixin |
| Vie.use | app.use |
| Vue.prototype | app.config.globalProperties |

其他改变:

data选项应始终被声明为一个函数,防止组件在被复用的时候产生数据的关联关系,造成干扰。

过渡类名的更改:

html 复制代码
<style>
    //vue2
    .v-enter,
    .v-leave-to{
        opacity:0;
    };
    .v-leave,
    .v-enter-to{
        opacity:1;
    }

    //vue3
    .v-enter-from,
    .v-leave-to{
        opacity:0;
    };
    .v-leave-from,
    .v-enter-to{
        opacity:1;
    }
<style/>

移除keyCode作为v-on的修饰符,会存在一定的兼容性问题,所以移除。

已不被支持keyUp.13

不支持config.keyCodes自定义别名按键(Vue.config.keyCodes.huiche=13)

移除v-on.native修饰符:子组件中emits不接受的click事件为原生事件,否则为自定义事件。

父组件:

<div>

<Child @click = "handleAdd" @close="handleClose" />

</div>

子组件:

export default{

emits:['close']

}

移除过滤器filter

过滤器虽然看起来很方便,但需要一个自定义语法,打破了大括号内表达式是'只是javascript'的假设,不仅有学习成本,而且还有实现成本,建议使用方法调用或者计算属性替换过滤器。

其他的可以参考官网: 快速上手 | Vue.js

相关推荐
前端Hardy10 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu108301891113 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
天天进步20153 小时前
Vue+Springboot用Websocket实现协同编辑
vue.js·spring boot·websocket
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
疯狂的沙粒3 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪4 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背4 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M4 小时前
node.js第三方Express 框架
前端·javascript·node.js·express
weiabc4 小时前
学习electron
javascript·学习·electron