目录
3.1 shallowReactive 与 shallowRef
3.2 readonly 与 shallowReadonly
尚硅谷 Vue3 技术(快速上升)
前言
1. Vue3 简介
- 2020 年 9 月 18 日,Vue.js 发布 3.0 版本,代号:One Piece(海贼王)
-
github 上的 tags 地址:https://github.com/vuejs/vue-next/releases/tag/v3.0.0
2. Vue3 带来了什么
2.1 性能的提升
-
打包大小减少 41%
-
初次渲染快 55%, 更新渲染快 133%
-
内存减少 54%
......
2.2 源码的升级
-
使用 Proxy 代替 defineProperty 实现响应式
-
重写虚拟 DOM 的实现和 Tree-Shaking
......
2.3 拥抱 TypeScript
-
Vue3 可以更好的支持 TypeScript
2.4 新的特性
2.4.1 Composition API(组合 API)
-
setup 配置
-
ref 与 reactive
-
watch 与 watchEffect
-
provide 与 inject
......
2.4.2 新的内置组件
-
Fragment
-
Teleport
-
Suspense
......
2.4.3 其他改变
-
新的生命周期钩子
-
data 选项应始终被声明为一个函数
-
移除 keyCode 支持作为 v-on 的修饰符
......
一、创建 Vue3.0 工程
1. 使用 vue-cli 创建
官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 查看 @vue/cli 版本,确保 @vue/cli 版本在4.5.0以上
打开 CMD 命令提示符,输入
vue -V
或者
vue --version
## 若 @vue/cli 版本不是4.5.0以上,则需要安装或者升级 @vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test // vue_test 是项目名称,可修改
## 启动
cd vue_test
npm run serve
2. 使用 vite 创建
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite 官网:https://vitejs.cn
-
什么是 vite?—— 新一代前端构建工具。
-
优势如下:
-
开发环境中,无需打包操作,可快速的冷启动。
-
轻量快速的热重载(HMR)。
-
真正的按需编译,不再等待整个应用编译完成。
-
-
传统构建 与 vite 构建对比图
* 传统构建 ---Webpack
* vite 构建
## 创建工程
npm init vite-app <project-name> // project-name 是项目名称,比如 vue3_test_vite
## 进入工程目录
cd <project-name> // project-name 是项目名称
## 安装依赖
npm install
## 运行
npm run dev
3. 分析工程结构
与 Vue2 脚手架的文件结构基本相同:
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
其中,入口文件 src/main.js 与 Vue2 时不同:
//引入的不再是Vue构造函数了,引入的是一个名为 createApp 的工厂函数
import { createApp } from "vue"
import App from "./App.vue"
/* createApp(App).mount('#app') */
//创建应用实例对象——app(类似于Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
//挂载
app.mount("#app")
// Vue2的写法
/* const vm = new Vue({
render:h=>h(App)
})
vm.$mount('#app') */
【注】在 Vue3 中,组件中的模板结构可以没有根标签 :
<template>
<!-- Vue3组件中的模板结构可以没有根标签 -->
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue"
export default {
name: "App",
components: {
HelloWorld,
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
二、常用 Composition API
官方文档:https://v3.cn.vuejs.org/guide/composition-api-introduction.html
2.1 拉开序幕的 setup
- src/App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ age }}</h2>
<button @click="sayHello">说话(Vue3所配置的——sayHello)</button>
<br />
<br />
<button @click="sayWelcome">说话(Vue2所配置的——sayWelcomeo)</button>
<br />
<br />
<button @click="test1">测试一下在Vue2的配置中去读取Vue3中的数据、方法</button>
<br />
<br />
<button @click="test2">
测试一下在Vue3的setup配置中去读取Vue2中的数据、方法
</button>
</template>
<script>
export default {
name: "App",
data() {
return {
sex: "男",
}
},
methods: {
sayWelcome() {
alert("欢迎来到尚硅谷学习")
},
test1() {
console.log(this.sex)
console.log(this.name)
console.log(this.age)
console.log(this.sayHello)
},
},
//此处只是测试一下setup,暂时不考虑响应式的问题。
setup() {
//数据
let name = "张三"
let age = 18
//方法
function sayHello() {
alert(`我叫${name}, 我${age}岁了, 你好啊!`)
}
function test2() {
console.log(name)
console.log(age)
console.log(sayHello)
console.log(this.sex)
console.log(this.sayWelcome)
}
//返回一个对象(常用)
return {
name,
age,
sayHello,
test2,
}
//返回一个函数(渲染函数)
// return () => h("h1", "尚硅谷")
},
}
</script>
效果:
总结:
1. 理解:Vue3.0 中一个新的配置项,值为一个函数。
2. setup 是所有 Composition API(组合 API)“ 表演的舞台 ”。
3. 组件中所用到的:数据、方法等等,均要配置在 setup 中。
4. setup 函数的两种返回值:
○ 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
○ 若返回一个渲染函数:则可以自定义渲染内容。(了解)
5. 注意点:
○ 尽量不要与 Vue2.x 配置混用
■ Vue2.x 配置(data、methos、computed...)中可以访问到 setup 中的属性、方法。
■ 但在 setup 中不能访问到 Vue2.x 配置(data、methos、computed...)。
■ 如果有重名, setup 优先。
○ setup 不能是一个 async 函数,因为返回值不再是 return 的对象, 而是 promise, 模板看不到 return 对象中的属性。(后期也可以返回一个 Promise 实例,但需要 Suspense 和异步组件的配合)
2.2 ref 函数
- src/App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ age }}</h2>
<h3>工作种类:{
{ job.type }}</h3>
<h3>工作薪水:{
{ job.salary }}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import { ref } from "vue"
export default {
name: "App",
setup() {
//数据
let name = ref("张三")
let age = ref(18)
let job = ref({
type: "前端工程师",
salary: "30K",
})
//方法
function changeInfo() {
name.value = "李四"
age.value = 48
console.log(job.value)
job.value.type = "UI设计师"
job.value.salary = "60K"
console.log(name, age)
}
//返回一个对象(常用)
return {
name,
age,
job,
changeInfo,
}
},
}
</script>
效果:
总结:
1. 作用:定义一个响应式的数据
2. 语法: const xxx = ref(initValue)
○ 创建一个包含响应式数据的引用对象(reference 对象,简称 ref 对象)
○ JS 中操作数据: xxx.value
○ 模板中读取数据:不需要.value,直接:<div>{ {xxx}}</div>
3. 备注:
○ 接收的数据可以是:基本类型、也可以是对象类型。
○ 基本类型的数据:响应式依然是靠 Object.defineProperty() 的 get 与 set 完成的。
○ 对象类型的数据:内部 “ 求助 ” 了 Vue3.0 中的一个新函数—— reactive 函数。
2.3 reactive 函数
- src/App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{
{ person.name }}</h2>
<h2>年龄:{
{ person.age }}</h2>
<h3>工作种类:{
{ person.job.type }}</h3>
<h3>工作薪水:{
{ person.job.salary }}</h3>
<h3>爱好:{
{ person.hobby }}</h3>
<h3>测试的数据c :{
{ person.job.a.b.c }}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import { reactive } from "vue"
export default {
name: "App",
setup() {
//数据
let person = reactive({
name: "张三",
age: 18,
job: {
type: "前端工程师",
salary: "30K",
a: {
b: {
c: 666,
},
},
},
hobby: ["打球", "打游戏", "游泳"],
})
//方法
function changeInfo() {
person.name = "李四"
person.age = 48
console.log(person.job)
person.job.type = "UI设计师"
person.job.salary = "60K"
person.job.a.b.c = 999
console.log(person.name, person.age)
person.hobby[0] = "学习"
}
//返回一个对象(常用)
return {
person,
changeInfo,
}
},
}
</script>
效果:
总结:
● 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用 ref 函数)
● 语法:const 代理对象 = reactive (源对象) 接收一个对象(或数组),返回一个代理对象(Proxy 的实例对象,简称 proxy 对象
● reactive 定义的响应式数据是“深层次的”
● 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
2.4 Vue3.0 中的响应式原理
2.4.1 回顾 Vue2.0 的响应式原理
- src/App.vue
<template>
<div>
<h1>我是Vue2写的效果</h1>
<h2 v-show="person.name">姓名:{
{ person.name }}</h2>
<h2>年龄:{
{ person.age }}</h2>
<h2 v-show="person.sex">性别:{
{ person.sex }}</h2>
<h2>爱好:{
{ person.hobby }}</h2>
<button @click="addSex">添加一个sex属性</button>
<button @click="deleteName">删除name属性</button>
<button @click="updateHobby">修改第一个爱好的名字</button>
</div>
</template>
<script>
import Vue from "vue"
export default {
name: "App",
data() {
return {
person: {
name: "张三",
age: 18,
hobby: ["学习", "吃饭"],
},
}
},
methods: {
addSex() {
console.log(this.person.sex)
// this.person.sex = "女" // 通过Objective.defineProperty 监测不到
// 解决方案一:
// this.$set(this.person, "sex", "女")
// 解决方案二:
Vue.set(this.person, "sex", "女")
console.log(this.person.sex)
},
deleteName() {
console.log(this.person.name)
// delete this.person.name // 监测不到
// 解决方案一:
// this.$delete(this.person, "name")
// 解决方案二:
Vue.delete(this.person, "name")
console.log(this.person.name)
},
updateHobby() {
// this.person.hobby[0] = "逛街" //监测不到,不显示
//可行,方案一:
// this.$set(this.person.hobby, 0, "逛街")
// 方案二:
this.person.hobby.splice(0, 1, "逛街")
},
},
}
</script>
效果:
总结:
vue2.x 的响应式
1. 实现原理:
● 对象类型:通过 Object.defineProperty() 对属性的读取、修改进行拦截(数据劫持)。
● 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, "count", {
get() {},
set() {},
})
2. 存在问题:
● 新增属性、删除属性, 界面不会更新。
● 直接通过下标修改数组, 界面不会自动更新。
2.4.2 Vue3.0 的响应式原理
- Vue3.0 的响应式原理——Proxy
<!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>
// 源数据
let person = {
name: "张三",
age: 18,
}
// 模拟 Vue2 中实现响应式
//#region
/* let p={}
Object.defineProperty(p,'name',{
configurable:true,
get(){ //有人读取name时调用
return person.name
},
set(value){ // 有人修改name时调用
console.log('有人修改了