目录
4.1、shallowReactive 与 shallowRef
4.2、readonly 与 shallowReadonly
5.1 、shallowReactive 与 reactive
5.3 、shallowReadonly 与 readonly
5.4 、isRef, isReactive 与 isReadonly
1、认识Vue3
1.1、了解相关信息
- Vue.js3.0"One Piece"正式版在2020年9月份发布。
- Vue3支持Vue2的大多数特性
- 更好的支持TypeScript
- 现在主流组件库都已经发布了支持vue3.0的版本,其他生态也在不断地完善中,这是趋势。
- element-plus (opens new window)基于 Vue 3.0 的桌面端组件库。
- vant (opens new window)vant3.0版本,有赞前端团队开源移动端组件库。
- ant-design-vue (opens new window)Ant Design Vue 2.0版本,社区根据蚂蚁 ant design 开发。
1.2、性能提升
- 打包大小减少41%。
- 初次渲染快55%,更新渲染快133%。
- 内存减少54%。
- 使用Proxy代替defineProperty实现数据响应式。
- 重写虚拟DOM的实现和Tree-Shaking。
1.3、新增特性
- Composition(组合)API
- setup:
1、 ref和reactive。
2、 computed和watch。
3、 新的生命周期函数。
4、 provide与inject。
5、 ……
- 新组件:
1、Fragment - 文档碎片。
2、Teleport - 瞬移组件的位置。
3、Suspense - 异步加载组件的loading界面。
- 其它API更新:
1、全局API的修改。
2、将原来的全局API转移到应用对象。
3、模板语法变化。
2、创建Vue3项目
2.1、使用vue-cli创建
1、文档:创建一个项目 | Vue CLI
## 安装或者升级
npm install -g @vue/cli
## 保证 vue cli 版本在 4.5.0 以上
vue --version
## 创建项目
vue create my-project
2、然后的步骤:
- 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 additional lint features - 直接回车。
- Where do you prefer placing config for Babel, ESLint, etc.? - 直接回车。
- Save this as a preset for future projects? - 直接回车。
2.2、使用vite创建
- 文档:https://v3.cn.vuejs.org/guide/installation.html
- vite是一个由原生ESM驱动的Web开发构建工具。在开发环境下基于浏览器原生ES important开发。
- 它做到了本地快速开发启动,在生产环境下基于Rollup打包。
- 快速的冷启动,不需要等待打包操作;
- 即时的热模块更新,替换性能和模块数量的解耦让更新起飞;
- 真正的按需编译,不再等待整个应用编译完成,这是一个巨大的改变。
# npm 6.x
$ npm init vite@latest <project-name> --template vue
// 上面的vue可改为 vanilla,vanilla-ts,vue,vue-ts,react,react-ts,preact,preact-ts,lit,lit-ts,svelte,svelte-ts
# npm 7+,需要加上额外的双短横线
$ npm init vite@latest <project-name> -- --template vue
$ cd <project-name>
$ npm install
$ npm run dev
或
1、创建项目 npm init vite-app 项目名称 或者 yarn create vite-app 项目名称
2、安装依赖 npm i 或者 yarn
3、启动项目 npm run dev 或者 yarn dev
3、Composition API (常用部分)
3.1、setup
- 新的option,所有的组合API函数都在此使用,只在初始化时执行一次。
- 函数如果返回对象,对象中的属性或方法,模板中可以直接使用。
3.2、ref
- 作用:定义一个数据的响应式。
- 语法:const xxx = ref(initValue);
- 创建一个包含响应式数据的引用(reference)对象。
- js中操作数据:xxx.value。
- 模板中操作数据:不需要.value。
- 一般用来定义一个基本类型的响应式数据。
<template>
<h2>{
{count}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
import { ref } from 'vue'
export default {
// 在vue3中依然可以使用data和methods配置,但建议使用其新语法实现
/**
data() {
return {
count: 0
}
}
methods: {
update() {
this.count++
}
}
*/
// 使用vue3的composition API
setup() {
// 定义响应式数据
const count = ref(0)
// 更新响应式数据
/**
const update = () {
count.value++
}
*/
function update() {
count.value = count.value + 1
}
return {
count,
update
}
}
}
</script>
3.3、reactive
- 作用:定义多个数据的响应式。
- const proxy = reactive(obj):接受一个普通对象然后返回该普通对象的响应式代理器对象。
- 响应式转换是“深层的”:会影响对象内部所有嵌套的属性。
- 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据都是响应式的
<template>
<h2>name: {
{state.name}}</h2>
<h2>age: {
{state.age}}</h2>
<h2>wife: {
{state.wife}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
import { reactive } from 'vue';
export default {
// 定义响应式数据对象
const state = reactive({
name: 'tom',
age: 20,
wife: {
name: 'marry',
age: 19
}
})
const update = () => {
state.name += '--'
state.age += 1
state.wife.name += '++'
state.wife.age += 2
}
return {
state,
update
}
</script>
3.4、比较Vue2与Vue3的响应式(重要)
3.4.1、Vue2的响应式
- 核心:
- 对象:通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组:通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持。
Object.defineProperty(data, 'count', {
get() {}
set() {}
})
- 问题:
- 对象直接新添加的属性或删除已有属性,界面不会自动更新。
- 直接通过下标替换元素或更新length,界面不会自动更新arr[1] = {}
3.4.2、Vue3的响应式
- 核心:
- 通过Proxy(代理):拦截对data任意属性的任意(13种)操作,包括属性值的读写,属性的添加、删除等……
- 通过Reflect(反射):动态对被代理对象的相应属性进行特定的操作。
- 文档:
1)https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
2)https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
new Proxy(data, {
// 拦截读取属性值
get(target, prop) {
return Reflect.get(target, prop)
}
// 拦截设置属性值或添加新属性
set(target, prop, value) {
return Reflect.set(target, prop, value)
}
// 拦截删除属性
deleteProperty(target, prop) {
retrun Reflect.deleteProperty(target,prop)
}
})
proxy.name = 'lgg'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proxy 与 Reflect</title>
</head>
<body>
<script>
const user = {
name: 'lgg',
age: 3
}
/**
proxyUser是代理对象,user是被代理对象
后面所有的操作都是通过代理对象来操作被代理对象内部属性。
*/
const proxyUser = new Proxy(user, {
get(target, prop) {
// 劫持get()
return Reflect.get(target, prop)
}
// 劫持set()
set(target, prop, val) {
return Reflect.set(target, prop, val)
}
// 劫持delete属性
return Reflect.deleteProperty(target, prop)
})
// 读取属性
console.log(proxyUser === user)
console.log(proxyUser.name, proxyUser.age)
// 设置属性值
proxyUser.name = 'l'
proxyUser.age = 18
console.log(user)
// 添加属性
proxyUser.sex = '男'
console.log(user)
// 删除属性
delete proxyUser.sex
console.log(user)
</script>
</body>
</html>
3.5、setup细节
setup执行的时机
- 在beforeCreate之前执行(一次),此时组件对象还没有创建。
- this是undefined,不能通过this来访问data/computed/methods/props。
- 其实所有的composition API相关回调函数中也都不可以。
setup的返回值
- 一般都返回一个对象:为模板提供数据,也就是模板中可以直接使用此对象中的所有属性/方法。
- 返回对象中的属性会与data函数返回对象的属性合并成为组件对象。
- 返回对象中的方法会与methods中的方法合并成为组件对象的方法。
- 如果有重名,setup优先。
- 注意:
- 一般不要混合使用:methods中可以访问setup提供的属性和方法,但在setup方法中不能访问data和methods。
- setup不能是一个async函数:因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性数据(但测试也可以)。
setup的参数
- setup(props, context)/setup(props, { attrs, slots, emit })
- props:包含props配置声明且传入了的所有属性的对象。
- attrs:包含没有在props配置中声明的属性的对象,相当于this.$attrs。
- slots:包含所有传入的插槽内容的对象,相当于this.$slots。
- emit:用来分发自定义事件的函数,相当于this.$emit
<template>
<h2>App</h2>
<p>msg: {
{msg}}</p>
<button @click="fn('--')">更新</button>
<child :msg="msg" msg2="cba" @fn="fn"/>
</template>
<script lang="ts">
import {
reactive,
ref,
} from 'vue'
import child from './child.vue'
export default {
components: {
child
},
setup () {
const msg = ref('abc')
function fn (content: string) {
msg.value += content
}
return {
msg,
fn
}
}
}
</script>
<template>
<div>
<h3>{
{n}}</h3>
<h3>{
{m}}</h3>
<h3>msg: {
{msg}}</h3>
<h3>msg2: {
{$attrs.msg2}}</h3>
<slot name="xxx"></slot>
<button @click="update">更新</button>
</div>
</template>
<script lang="ts">
import {
ref,
defineComponent
} from 'vue'
ex