当前位置: 首页 > news >正文

vue3的语法

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

//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中的属性。

<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:18function 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定义的响应式数据是深层次的。

<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 = 20obj.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相对友好一点。

<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] = valueReflect.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相似。

<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:

<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定义的数据,会强制开启深度监听模式,拿不到oldValuewatch(person,(newVal,oldVal)=>{console.log(newVal,oldVal);},{immediate:true,deep:true})//监听reactive定义的数据的一个属性,多个属性用数组watch([()=>person.name,()=>person.age],(newVal,oldVal)=>{console.log(newVal,oldVal);})//特殊情况,监听reactive对象属性,这个属性是个对象,需要开启深度监听,否则无效,同时也拿不到oldValuewatch(()=>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深度监听。

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:同样需要先引入,不用指明监视的属性,监视的回调中用到哪个属性,就监视哪个属性。

<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.valueconst x2 = person.job.job1console.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

<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进行封装。

//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
}
<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出去。

<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深层次不处理,对于基本类型都处理为只读

obj = readOnly(obj)
obj = shadowReadOnly(obj)

toRaw与markRaw

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

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

let obj = toRaw(person)
obj.car = markRaw(car)

 customRef(自定义ref)

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

示例:

<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 = newValuetrigger() //通知vue去重新解析模版}, delay)}}})}//返回一个对象(常用)return {keyWord}}
}
</script><template><input type="text" v-model="keyWord"><h3>{{ keyWord }}</h3>
</template><style scoped></style>

provide与inject(需要引入)

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

祖组件:

//祖组件
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的布局。

<teleport to="body"><Demo />
<teleport/>

Suspense组件

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

//父组件
<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>
//子组件
<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.xxxapp.config.xxx
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vie.useapp.use
Vue.prototypeapp.config.globalProperties

其他改变:

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

过渡类名的更改:

<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’的假设,不仅有学习成本,而且还有实现成本,建议使用方法调用或者计算属性替换过滤器。


setup语法糖

语法糖里面的代码会被编译成组件setup()函数的内容,不需要通过return暴露声明的变量、函数以及import引入的内容,即可在<template/>使用,并且不需要些export default{}

这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行

引入组件将自动注册

<script setup>
import HelloWorld from './components/HelloWorld.vue'
let msg = '你好';
function name(){return '123'
}
</script><template><div><a href="https://vitejs.dev" target="_blank"><img src="/vite.svg" class="logo" alt="Vite logo" /></a><a href="https://vuejs.org/" target="_blank"><img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /></a><p>{{ msg }}</p><p>{{ name() }}</p></div><HelloWorld msg="Vite + Vue" />
</template><style scoped>
.logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms;
}
.logo:hover {filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

组件通信必须要使用definePropsdefineEmitsAPI来代替propsemits

Props:

<template><div>父组件</div><Child :title="msg" />
</template><script setup>
import { ref } from 'vue'   // 引入ref
import Child from './child.vue'
const msg = ref('父的值')  //自动返回,在template直接解套使用
</script>
<template><div>子组件</div><div>父组件传递的值:{{title}}</div>
</template><script setup>
//import {defineProps} from 'vue'   不需要引入//语法糖必须使用defineProps替代props
const  props = defineProps({title: {type: String}
});
//script-setup 需要通过props.xx获取父组件传递过来的props
console.log(props.title) //父的值
</script>

Emits:

<template><div>子组件</div><button @click="toEmits">子组件向外暴露数据</button>
</template><script setup>
import {ref} from 'vue'
const name = ref('我是子组件')
//1、暴露内部数据
const  emits = defineEmits(['childFn']);const  toEmits = () => {//2、触发父组件中暴露的childFn方法并携带数据emits('childFn',name)
}
</script>
<template><div>父组件</div><Child  @childFn='childFn' /><p>接收子组件传递的数据{{childData}} </p>
</template><script setup>
import {ref} from 'vue'
import Child from './child.vue'const childData = ref(null)    
const childFn=(e)=>{consloe.log('子组件触发了父组件childFn,并传递了参数e')childData=e.value
}    </script>

需要主动暴露组件属性:defineExpose

使用 <script setup> 的组件是默认关闭的,即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。

<script setup>
import { ref } from 'vue'const a = 1
const b = ref(2)
//主动暴露组件属性
defineExpose({a,b
})
</script>
<template><div>父组件</div><Child  ref='childRef' /><button @click='getChildData'>通过ref获取子组件的属性 </button>
</template><script setup>
import {ref} from 'vue'
import Child from './child.vue'
const childRef= ref()  //注册响应数据  
const getChildData =()=>{//子组件接收暴露出来得值console.log(childRef.value.a) //1console.log(childRef.value.b) //2  响应式数据
}    
</script>

语法糖其他功能

useSlotsuseAttrs (少用,由于大部分人是SFC模式开发,在<template/>通过<slot/>标签就可以渲染插槽)
如果需要在script-setup中使用 slotsattrs 需要用useSlotsuseAttrs替代
需要引入:import { useSlots ,useAttrs } form 'vue'
<template/>中通过 $slots$attrs 来访问更方便(attrs用来获取父组件中非props的传递到子组件的参数/方法,attrs 用来获取父组件中非props的传递到子组件的参数/方法,attrs用来获取父组件中非props的传递到子组件的参数/方法,slots可以获取父组件中插槽传递的虚拟dom对象,在SFC模式应该用处不大,在JSX /TSX使用比较多)

//父组件
<template><Child msg="非porps传值子组件用attrs接收" ><!-- 匿名插槽 --><span >默认插槽</span><!-- 具名插槽 --><template #title><h1>具名插槽</h1></template><!-- 作用域插槽 --><template #footer="{ scope }"><footer>作用域插槽——姓名:{{ scope.name }},年龄{{ scope.age }}</footer></template></Child>
</template><script setup>
// 引入子组件
import Child from './child.vue'
</script>
//子组件
<template><!-- 匿名插槽 --><slot /><!-- 具名插槽 --><slot name="title" /><!-- 作用域插槽 --><slot name="footer" :scope="state" /><!-- $attrs 用来获取父组件中非props的传递到子组件的参数 --><p>{{ attrs.msg == $attrs.msg }}</p><!--true  没想到有啥作用... --><p>{{ slots == $slots }}</p>
</template><script setup>
import { useSlots, useAttrs, reactive, toRef } from 'vue'
const state = reactive({name: '张三',age: '18'
})const slots = useSlots()
console.log(slots.default()); //获取到默认插槽的虚拟dom对象
console.log(slots.title());   //获取到具名title插槽的虚拟dom对象
// console.log(slots.footer()); //报错  不知道为啥有插槽作用域的无法获取
//useAttrs() 用来获取父组件传递的过来的属性数据的(也就是非 props 的属性值)。
const attrs = useAttrs()
</script>

useSlots或许在JSX/TSX下更实用


<script lang='jsx'>
import { defineComponent, useSlots } from "vue";
export default defineComponent({setup() {// 获取插槽数据const slots = useSlots();// 渲染组件return () => (<div>{slots.default?slots.default():''}{slots.title?slots.title():''}</div>);},
});
</script>

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

http://www.lryc.cn/news/104691.html

相关文章:

  • 【git合并分支自定义提交消息】
  • AttributeError: module ‘PyQt5.QtGui‘ has no attribute ‘QMainWindow‘
  • 基于Java+SpringBoot+Vue前后端分离电商项目
  • Rpc服务消费者(Rpc服务调用者)实现思路
  • FANUC机器人实现2个RO输出信号互锁关联(互补)的具体方法
  • 权威认可|云畅科技再次入选中国信通院「高质量数字化转型产品及服务全景图」
  • 爬虫小白-如何调试列表页链接与详情链接不一样并三种方式js逆向解决AES-ECB
  • Ubuntu 离线部署的常见操作
  • 什么是多运行时架构?
  • 【MySQL】mysql | linux | 离线安装mysqldump
  • 中国农村程序员学习此【JavaScript教程】购买大平层,开上帕拉梅拉,迎娶白富美出任CEO走上人生巅峰
  • 【Python】Web学习笔记_flask(2)——getpost
  • RabbitMQ 教程 | 第5章 RabbitMQ 管理
  • LLM微调 | Adapter: Parameter-Efficient Transfer Learning for NLP
  • 在idea中添加try/catch的快捷键
  • 企业级开发中协同开发与持续集成持续部署
  • 九五从零开始的运维之路(其二十八)
  • iOS--Runloop
  • Doccano工具安装教程/文本标注工具/文本标注自己的项目/NLP分词器工具/自然语言处理必备工具/如何使用文本标注工具
  • windows系统之WSL 安装 Ubuntu
  • 洛谷题解 | P1046 陶陶摘苹果
  • 记一次Apache HTTP Client问题排查
  • Linux获取文件属性
  • String字符串拼接
  • 在矩池云使用Llama2-7B的具体方法
  • API教程:轻松上手HTTP代理服务!
  • 脑网络通信:概念、模型与应用——Brain network communication: concepts, models and applications
  • Docker创建tomcat容器实例后无法访问(HTTP状态 404 - 未找到)
  • oracle数据库dbLink的使用
  • Coremail中睿天下|2023年第二季度企业邮箱安全态势观察