Vue3 学习笔记梳理


Author:Gorit


Date:2021/4/24


Refer:网易云课堂


2021年发表博文:

17/30

Vue3 学习

零、Vue3.0 与 Vue2.x 的性能对比


  1. 框架内部做了大量的性能优化,包括虚拟 DOM,编译模板、Proxy 的新数据监听,更小的打包文件等
  2. 新的组合式 API (composition-api),更适合大型项目的编写方式
  3. 对 TypeScript 支持更好,去除繁琐的 this 操作,更强大的类型推导

一、搭建环境


  1. node 8.9.0 以上
  2. 安装好 npm
  3. 安装 vue
  4. 安装 Vue Cli4 脚手架

二、创建项目

  1. vue create vue3-demo

Vue3 学习笔记 —— (一)深入理解组合式 API_vue


  1. 其他的选择默认配置即可
  2. 安装 yarn :cnpm i yarn -g (提升安装速度)
  3. 测试:yarn --versin

项目结构介绍


  • package.json 项目全局管理
  • node_modules 项目依赖包,占用大量空间
  • public 入口文件
  • src:main.js 为入口文件,项目代码在这里编写

三、Vue3 Composition API

Vue3 是向下兼容 Vue2 API 的,但是 Vue3 中提供了一种全新的 Composition API

3.1 ref() or setup() ? reactive()

setup() 作为 Vue3.0 的入口函数

reactive() 作声明式渲染,用来响应数据

ref() 显示响应式数据,配合 reactve()

3.1.1 非响应式数据显示 (reactive)

直接返回数据

<template>
<div>直接返回 state【非响应式的数据】:{{count}}</div>
</template>

<script>
// 如果是 Vue2 项目,想要用 Vue3 的语法,需要安装 @vue/composition-api
// import { xxx } from '@vue/composition-api'
import {
reactive
} from 'vue'
export default {
name: 'App',
// Vue3.0 的入口函数,编写 Vue3 代码,beforeCreate之前进行触发, 需要 return
setup() {
const state = reactive({
count: 0
});
// 不具备数据响应式的效果,1s 后数据没有任何变化
setTimeout(()=> {
state.count++;
},1000)
return state;
}
}
</script>

3.1.2 响应式数据显示 (reactive)

通过对象的形式

<template>
<div>返回 state 对象 【响应式数据】{{state.count}}</div>
</template>

<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
// Vue3.0 的入口函数,编写 Vue3 代码,beforeCreate之前进行触发, 需要 return
setup() {
// 具备数据响应式, 返回对象
const state = reactive({
count: 0
});
setTimeout(()=> {
state.count++;
},1000)
return { state };

// 对返回的结果 解构,这样数据就可以只显示 {{ count }} 就可以了
// return {
// count: state.count
//}
}
}
</script>

3.1.3 响应式数据展示(整合 ref() )

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>ref 实现响应式对象 {{ count }}</div>
</template>

<script>
import {
ref
} from 'vue'
export default {
name: 'App',
setup() {
// 使用 ref() 实现响应式数据
const count = ref(0); // 这样 count 就实现了响应式的效果
setInterval(()=> {
count.value++;
},1000)
return {
count
};
}
}
</script>

因此,我们既要响应式,又要展示数据,折中的方案是直接使用 ref

3.1.4 小案例:实现一个计数器

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div class="my-app">
<!-- Vue3 无法直接拿到值 -->
<h3>{{counter}}</h3>
<button @click="increment(1)">increment</button>
<button @click="decrement(1)">decrement</button>
</div>
</template>

<script>
import { ref } from 'vue'
/**
* Options API Vue2 Class
* Composition API Vue3 Function
*/
export default {
name: 'App',
setup() {
const counter = ref(1000)
// console.log(counter.value)
const increment = () => {
counter.value += 1;
}

const decrement = () => {
counter.value -= 1;
}
// 必须 return,外部才能拿到值
return { counter, increment,decrement }
}
}
</script>

3.2 toRefs() ? toRef()

在上面的代码中,我们使用 ref() 和 reactive() 分别可以实现响应式的数据,我们是否可以两者一起使用呢?

3.2.1 ref() 和 reactive() 连用

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>ref() 和 reactive() 实现响应式对象 {{ count }}</div>
</template>

<script>
// 如果是 Vue2 项目,想要用 Vue3 的语法,需要安装 @vue/composition-api
// import { xxx } from '@vue/composition-api'
import {
ref,
reactive
} from 'vue'
export default {
name: 'App',
setup() {
// ---------------------------- ref 和 reactive 连用
const count = ref(0);
const state = reactive({
count
})
setInterval(()=> {
state.count++;
},1000)
return {
count
};
}
}
</script>

3.2.2 使用 toRefs() 和 reactive()

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>实现响应式对象 {{ count }}</div>
</template>

<script>
import {
reactive,
toRefs
} from 'vue'
export default {
name: 'App',
setup() {
// ---------------------------- //使用 toRefs() 将 reactive 转换为 Ref
const state = reactive({
count: 0
});

const { count } = toRefs(state); // toRefs() 作用:将普通类型数据,转换为 Ref 响应式数据
setInterval(()=> {
state.count++;
},1000)
return {
count
};
}
}
</script>

3.2.3 使用 toRef() 和 reactive()

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>实现响应式对象 {{ count }}</div>
</template>

<script>
import {
reactive,
toRefs
} from 'vue'
export default {
name: 'App',
setup() {
// ---------------------------- //使用 toRefs() 将 reactive 转换为 Ref
const state = reactive({
count: 0
});

const count = toRef(state, 'count'); // toRef() 作用:将普通类型数据,转换为 Ref 响应式数据,指定单个数据转换
setInterval(()=> {
state.count++;
},1000)
return {
count
};
// toRefs === {}
}
}
</script>

ref 和 reactive 分别是两种响应式数据的变量风格,具体看个人情况使用

3.3 computed 计算属性

3.3.1 配合 ref() 使用 实现响应式

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>
{{count}} , {{double}}
</div>
</template>

<script>
import {
ref,
computed
} from 'vue'
export default {
name: 'App',
setup() {
// 使用计算属性
const count = ref(1)
const double = computed(()=>count.value * 2)
setTimeout(()=>{
count.value++
},1000)
return {
count,double
}
}
}
</script>

3.3.2 配合 toRefs() 和 reactive() 实现响应式

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>
{{count}} , {{double}}
</div>
</template>

<script>
import {
reactive,
toRefs,
computed
} from 'vue'
export default {
name: 'App',
setup() {
const state = reactive({
count: 1,
double: computed(()=>state.count * 2)
})
setTimeout(()=>{
state.count++
},1000)
return toRefs(state)
}
}
</script>

3.4 定义函数

3.4.1 函数与 watch

<template>
<img alt="Vue logo" src="./assets/logo.png">
<div>
事件:{{count}}
</div>
<button @click="add">添加</button>
</template>

<script>
import {
ref,
watch
} from 'vue'
export default {
name: 'App',
setup() {
// =============== 事件
const count = ref(1)
const add = () => { // 事件方法
count.value++;
}
// 配合侦听器
watch(count ,(count, prevCount) => {
console.log(count, prevCount)
})
return {count, add}
}
}
</script>

3.4.2 Vue3.0 函数与生命周期函数

​文档​


新的生命周期函数钩子要在 setup() 中使用



  1. onMounted
  2. onUpdated
  3. onUnmounted

export default {
setup() {
// mounted
onMounted(() => {
console.log('Component is mounted!')
})
}
}

四、Vue3 组件化(拆分+传值+注册)

这里主要是回顾 组件化编程


拆分的方式同 Vue2,注册 + 引入
组件拆分的案例我们沿用上面的计数器来实现 (参考 3.1.4 小节的内容)


4.1 组件拆分

预览效果:

Vue3 学习笔记 —— (一)深入理解组合式 API_响应式_02

编码如下:


  1. 定义一个新的 vue 文件,命名为 CounterView.vue 文件
  2. 使用 props 接受父组件 App.vue 穿过来的值

<template>
<div>
我是子组件 CounterView
{{counter}}
</div>
</template>

<script>
export default {
name: 'CounterView',
// props: ['counter'] vue2 一般都是这么写的
// 下面的这种写法会更加规范
props: {
counter: {
type: Number,
required: true,
default: 500
}
}
}
</script>
  1. 修改父组件 App.vue,改为引用 CounterView.vue ,并注册为组件
<template>
<div>
<img alt="Vue logo" src="./assets/logo.png" />
<!-- 属性绑定 -->
<counter-view :counter="counter"/>
<button @click="increament(1)">增加</button>
<button @click="increament(-1)">减少</button>
</div>
</template>

<script>
import { ref } from "vue";
import CounterView from '@/components/CounterView.vue'
export default {
name: "App",
components: {
CounterView
},
setup() {
const counter = ref(1000);
console.log(counter.value);
// 定义函数
const increament = (num) => {
counter.value += num;
};
return { counter,increament };
},
};
</script>

<style>
</style>

4.2 事件拆分


这里我们在上面的基础上,将 setup() 中定义的事件,拆分至另一个新的 vue 文件
首先我们需要补充一些前置概念:



  1. 在 setup() 中是没有 this 关键字的
  2. setup() 是可以接受两个参数的 (props, context),然后我们打印接受到的值如下
    Vue3 学习笔记 —— (一)深入理解组合式 API_响应式_03
  3. cotext 中,可以看到 emit 关键字,是不是很熟悉,vue2 中我们要子组件传事件给父组件,用的是 ​​this.$emit("事件名称", '值")​​, 在 Vue3 中也会用到类似的,后面会有具体的演示
  4. 编码如下:

在子组件完成事件注册

<template>
<div>
<button @click="increament(1)">增加</button>
<button @click="increament(-1)">减少</button>
</div>
</template>

<script>
export default {
name: 'CounterController',
setup(props,ctx) {
console.log(props, ctx) // 这里打印的就是我们刚刚上面看到的
const increament = (num) => {
// ctx.emit 等价于 vue2 中 this.$emit("xxx",xxx)。 在 vue3 中 setup() 函数是没有 this 的概念的
ctx.emit("onIncreament",num) // 完成事件注册,将操作的逻辑交给父组件来完成
}
return {increament}
},
}
</script>

在父组件完成事件调用

<template>
<div>
<img alt="Vue logo" src="./assets/logo.png" />
<!-- 属性绑定 -->
<counter-view :counter="counter"/>
<!-- <button @click="increament(1)">增加</button>
<button @click="increament(-1)">减少</button> -->
<!-- 子组件事件注册完毕后,交给父组件进行触发, 处理的函数需要传 $event 就可以实现和上面一样的效果了 -->
<counter-controller @onIncreament="increament($event)" />
</div>
</template>

<script>
import { ref } from "vue";
import CounterView from '@/components/CounterView.vue'
import CounterController from '@/components/CounterController.vue'
export default {
name: "App",
components: {
CounterView,
CounterController
},
setup() {
const counter = ref(1000);
// 定义函数
const increament = (num) => {
counter.value += num;
};
return { counter,increament };
},
};
</script>

五、总结

我们来回顾一下所学内容


  1. setup(props, context), setup() 是代码的入口
  2. 响应式 API:reactive() 、ref()、toRef()、toRefs()
  3. 计算属性 computed 和 watch 侦听器的使用
  4. 组件化思想