- vue3.0 相比vue2来说性能方面提升了
- vue3.0更好的兼容了TypeScript语法
- 增加了许多新特性
1. Composition (组合) API
2. setup
ref 和 reactive
computed 和 watch
新的生命周期函数
provide 与 inject
-
新组件
Fragment - 文档碎片
Teleport - 瞬移组件的位置
Suspense - 异步加载组件的 loading 界面
其它 API 更新 -
全局 API 的修改
将原来的全局 API 转移到应用对象
模板语法变化
安装
- vue3的安装及安装选项跟vue2的基本相同
-
使用cli创建项目
安装或者升级npm install -g @vue/cli
-
注意:保证 vue cli 版本在 4.5.0 以上
-
查看版本
vue --version
-
创建项目
-
my-project是自己定义的项目的名字
vue create my-project -
安装选项
- Please pick a preset - 选择 Manually select features
- Check the features needed for your project - 选择上 TypeScript ,特别注意点空格是选择,点回车是下一步
- Choose a version of Vue.js that you want to start the project with - 选择 3.x (Preview)
- Use class-style component syntax - 直接回车
- Use Babel alongside TypeScript - 直接回车
- Pick a linter / formatter config - 直接回车
- Use history mode for router? - 直接回车
- Pick a linter / formatter config - 直接回车
- Pick additional lint features - 直接回车
- Where do you prefer placing config for Babel, ESLint, etc.? - 直接回车
- Save this as a preset for future projects? - 直接回车
vue3.0中的文件的介绍
- node_modules: 项目的依赖库
- public: 公共目录项目开发环境不参与打包构建
- src 文件夹:我们主要操作的地方,组件的增加修改等都在这个文件夹里操作
- eslintrc: 配置 eslint 的检测规则,强制按照规则书写代码;
- gitignore: 指定 git 忽略的文件,所有 git 操作均不会对其生效;
- babel.config.js:关于babel的相关配置
- package-lock.json: 当 node_modules 或 package.json 发生变化时自动生成的文件。
- package.json: 指定项目开发和生成环境中需要使用的依赖库;
- tsconfig.js: 关于typescript的相关配置
src文件的中的文件介绍
- main.ts:程序主的入口文件
里面的内容介绍
// 引入createApp函数,创建对应的应用,产生应用的示实例对象
import { createApp } from 'vue'
// 引入App组件(所有组件的父级组件)
import App from './App.vue'
// 创建App引用返回对应的实例对象,调用mount方法进行挂在
createApp(App).mount('#app')
- 在App.vue中如果出现以下报错
解决方案
App.vue文件介绍
<template>
<!-- vue2中的html模板中必须要有一对根标签,vue3组件中的html模板中可以没有根标签 -->
<img alt="Vue logo" src="./assets/logo.png">
<!-- 使用这个子级组件 -->
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</template>
<script lang="ts">
// 这里可以写ts的代码
// defineComponent函数,目的是定义一个组件,内部可以传入一个配置对象
import { defineComponent } from 'vue';
// 引入一个子级组件
import HelloWorld from './components/HelloWorld.vue';
// 暴露出去一个定义好的组件
export default defineComponent({
// 当前组件的名字是App
name: 'App',
components: {
// 注册一个子组件
HelloWorld
}
});
</script>
基本使用
Composition中的第一个函数方法
- setup
-
新的 option, 所有的组合 API 函数都在此使用, 只在初始化时执行一次
-
函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
app.vue代码
这个会在页面上显示 (法外狂徒zs)
<template>
{{name}}
</template>
<script lang="ts">
import { defineComponent } from 'vue';
// 暴露出去一个定义好的组件
export default defineComponent({
name: 'App',
setup(){
return {
name:'法外狂徒zs'
}
}
});
</script>
- ref
- 作用: 定义一个数据的响应式
- 语法: const xxx = ref(initValue):
创建一个包含响应式数据的引用(reference)对象
js 中操作数据: xxx.value
模板中操作数据: 不需要.value - 一般用来定义一个基本类型的响应式数据
ref和setup的基本使用
<template>
{{count}}
<button @click="updataconst">更新数据</button>
</template>
<script lang="ts">
// 这边也要引入ref
import { defineComponent, ref} from 'vue';
// 暴露出去一个定义好的组件
export default defineComponent({
name: 'App',
// 实现点击按钮让页面上的数据发生变化
// vue2.0写法
// data(){
// return{
// count:0
// }
// },
// methods:{
// updataconst(){
// this.count++
// }
// },
// vue3.0 写法
// 程序执行会先进入setup函数中
setup(){
// let count=0 //此时的数据不是响应式的数据所以页面的数据不会跟着变
// 给ref一个值
// ref是一个函数作用是:定义一个响应式数据
const count=ref(0)
console.log(count);
function updataconst(){
// count++这个会报错 :因为count是一个ref对象,对象不能进行++操作
count.value++
}
return {
// es6语法 调用
// count:count
count,
updataconst
}
}
});
</script>
- reactive
- 作用: 定义多个数据的响应式
- const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
- 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
<template>
<h3>名字:{{user.name}}</h3>
<h3>年龄:{{user.age}}</h3>
<h3>信息:{{user.wife}}</h3>
<button @click="updataconst">更新数据</button>
</template>
<script lang="ts">
// 这边也要引入ref
import { defineComponent, reactive} from 'vue';
// 暴露出去一个定义好的组件
export default defineComponent({
name: 'App',
setup(){
// 把数据变为响应式数据
const obj = reactive({
name:'牛夫人',
age:'20',
wife:{
name:'勇敢牛牛',
while:"不怕困难"
}
})
//返回的是一个proxy的代理对象,被代理的目标对象就是obj对象
// user现在是代理对象,obj是目标对象
const user =reactive(obj)
console.log(user);
// 方法
const updataconst=()=>{
// 直接使用目标对象的方式来更新目标的对象中的值,是不可能的,只能使用代理对象的方式来更新数据(响应式数据)
// obj.name+='猴子'
user.name+='呢'
user.age+='其实是18'
user.wife.while+='哦耶'
}
return{
user,
updataconst
}
}
});
</script>
vue3.0中响应式数据原理
- 你可以先创建一个文件夹然后在文件夹中创建一个html文件 , 下面是代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const user={
name:'张',
age:18,
wife:{
name:'王',
age:18
}
}
//把目标对象变成代理对象
// 参数1:user---》target目标对象
// 参数2:handler---》处理器对象,用来监视数据及数据的操作
const proxyUser=new Proxy(user,{
// 获取目标对象的某个值
get(target,prop){
console.log('get方法调用了');
// Reflect中也有一个get对象 他的参数就是get的返回值
return Reflect.get(target,prop)
},
// 修改目标对象的属性/为目标对象添加新的值
set(target,prop,val){
console.log('set方法调用了');
// 这里也需要返回Reflect
return Reflect.set(target,prop,val)
},
// 删除目标对象上的某个属性
deleteProperty(target,prop){
console.log('deletef调用了');
return Reflect.set(target,prop)
}
})
// 通过代理对象来获取目标对象中的某个属性值
console.log(proxyUser.name);
// 通过代理对象来更新目标对象上的某个值
proxyUser.name='我是李呀'
console.log(user);
// 通过代理对象向目标对象中添加一个新的属性
proxyUser.sex='男'
// 删除
delete proxyUser.name
console.log(user);
// 更新目标对象中的某个属性对象中的属性值
proxyUser.wife.name='牛夫人'
console.log(user);
</script>
</body>
</html>
vue3.0中响应式数据测试
- 这里是在vue.app中的代码
<template>
<h3>名字:{{user.name}}</h3>
<h3>年龄:{{user.age}}</h3>
<h3>信息:{{user.wife}}</h3>
<button @click="updataconst">更新数据</button>
</template>
<script lang="ts">
// 这边也要引入ref
import { defineComponent, reactive} from 'vue';
// 暴露出去一个定义好的组件
export default defineComponent({
name: 'App',
setup(){
// 把数据变为响应式数据
const obj = reactive({
name:'牛夫人',
age:'20',
wife:{
name:'勇敢牛牛',
while:"不怕困难"
}
})
//返回的是一个proxy的代理对象,被代理的目标对象就是obj对象
// user现在是代理对象,obj是目标对象
const user =reactive(obj)
console.log(user);
// 方法
const updataconst=()=>{
// 直接使用目标对象的方式来更新目标的对象中的值,是不可能的,只能使用代理对象的方式来更新数据(响应式数据)
// obj.name+='猴子'
user.name+='呢'
user.age+='其实是18'
user.wife.while+='哦耶'
// 通过代理对象来找到该对象中的某个属性,更改该对象中某个数组的数据
user.wife.while='你是猪'
// 通过当前的代理对象把目标对象中的某个数组属性中添加一个新的属性
user.wife.car[2]='猪3'
}
return{
user,
updataconst
}
}
});
</script>
- setup 详解
执行时机
- 在 beforeCreate 之前执行(一次), 此时组件对象还没有创建
- this 是 undefined, 不能通过 this 来访问 data/computed/methods / props
实验 以下
父组件代码
<template>
<h2>我是App父级组件</h2>
<h3>mes:{{msg}}</h3>
<button @click="msg+='==='">更新数据</button>
<!-- 父传子 -->
<child :msg='msg'/>
</template>
<script lang="ts">
// 这边也要引入ref
import { defineComponent, ref} from 'vue';
// 引入子级组件
import child from './components/child.vue'
// 暴露出去一个定义好的组件
export default defineComponent({
name: 'App',
// 注册组件
components:{
child
},
setup(){
const msg=ref('我出来了')
return{
msg,
}
}
});
</script>
子组件代码
<template>
<h2>我是子组件</h2>
<h3>msg:{{msg}}</h3>
</template>
<script lang='ts'>
import {defineComponent} from 'vue'
export default defineComponent({
name:'child',
// 接收父组件传过来的属性
// setup是在beforeCreate声明周期回调之前就执行了,而且就执行一次
// 可以推断出:setup在执行的时候,当前组件还没有创建出来,也就意味着:组件实例对象this更本就不能使用
props:['msg'],
// 数据初始化的生命周期
beforeCreate(){
console.log('beforeCerate执行了');
},
setup(){
//this是undefined说明就不能通过this调用data/computed/methods / props
console.log('setup执行了',this);
return {
// setup中一般都是返回一个对象,对象中的属性和方法都可以在html模板中直接使用
}
}
})
</script>
返回值
- 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与 data 函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与 methods 中的方法合并成功组件对象的方法
- 一般不要混合使用: methods 中可以访问 setup 提供的属性和方法, 但在 setup 方法中不能访问 data 和 methods :因为ithis是undefined
<template>
<h2>我是子组件</h2>
<h3>msg:{{ msg }}</h3>
<h3>{{ name }}</h3>
</template>
<script lang='ts'>
import { defineComponent } from "vue";
export default defineComponent({
name: "child",
//setup中的返回值是一个对象,为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
props: ["msg"],
// setup 不能是一个 async 函数: 因为返回值不再是 return 的对象, 而是 promise, 模板看不到 return 对象中的属性数据
setup() {
const showMsg1 = () => {
console.log("我是showMsg1");
};
return {
// setup中一般都是返回一个对象,对象中的属性和方法都可以在html模板中直接使用
showMsg1,
};
},
//setup中的对象中的属性和data函数返回对象的属性合并成为组件对象的属性
data() {
return {
name: "王",
};
},
mounted() {
console.log(this);
},
methods: {
// setup中的对象中的方法和methods 中的方法合并为组件对象的方法
// 在vue3中不推荐一起使用 methods , setup 及 data 和 methods
// 一般不要混合使用: methods 中可以访问 setup 提供的属性和方法, 但在 setup 方法中不能访问 data 和 methods
showMsg2() {
console.log("methods中的showMsg2");
},
},
});
</script>
<style>
</style>
参数
setup中有两个参数(props,context)
<template>
<h2>我是子组件</h2>
<h3>msg:{{ msg }}</h3>
<h3>{{ name }}</h3>
</template>
<script lang='ts'>
import { defineComponent } from "vue";
export default defineComponent({
name: "child",
props: ["msg"],
setup(props,context) {
console.log(props); //是一个对象,里面有父级组件向子组件传递的参数,并且是在子组件中使用props接收到的所有属性 ,包含props配置声明且传入了的所有属性对象
// console.log(props.msg);
console.log(context); //是一个对象,里面有attrs对象,emit,slots
// attrs:获取当前组件标签上的属性(eg:vue.app中的标签里的mes),但是该属性是在props中没有声明接收的所有的属性对象
// emit:分发事件
// slots对象:插槽
// console.log(context.attrs);
// console.log(context.emit);
}
})
</script>
-
reactive与ref详解
- reactive 与 ref是 Vue3 的 composition API 中 2 个最重要的响应式 API
- ref 用来处理基本类型数据, reactive 用来处理对象(递归深度响应式)
app.vue中的代码
<template>
<h1>m1:{{m1}}</h1>
<h1>m2:{{m2}}</h1>
<h1>m3:{{m3}}</h1>
<button @click="updata">跟新数据</button>
</template>
<script lang="ts">
// 这边也要引入ref
import { defineComponent, reactive, ref} from 'vue';
// 引入子级组件
import child from './components/child.vue'
// 暴露出去一个定义好的组件
export default defineComponent({
name: 'App',
setup(){
// ref 用来处理基本类型数据, reactive 用来处理对象(递归深度响应式)
// 通过ref的方式设置的数据
const m1=ref('abc')
// 通过reactive的方式设置的数据
const m2=reactive({
name:'小明',
wife:{
name:'小红'
}
})
// ref能传入对象吗
const m3=ref({
name:'小明',
wife:{
name:'小红'
}
})
// 更新数据
const updata=()=>{
// 如果用 ref 对象/数组, 内部会自动将对象/数组转换为 reactive 的代理对象(proxy类型的对象)
console.log(m3)
// ref 的数据操作: 在 js 中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
m1.value+='==='
m2.wife.name+='==='
m3.value.wife.name+='==='
console.log(m3.value.wife);//proxy对象
}
return{
m1,
m2,
m3,
updata
}
}
});
</script>
总结:
- vue3相对vue2来说性能快了1.2-2倍
- 比如说:
- diff方法优化:
vue2中的虚拟dom是全量的对比(每个节点不论写死的还是动态的都会比较)
vue3新增了静态标记(patchflag)与上次虚拟节点对比时,只对比带有patch flag的节点(动态数据所在的节点);可通过flag信息得知当前节点要对比的具体内容 - 静态提升:
vue2无论元素是否参与更新,每次都会重新创建然后再渲染
vue3对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可