前置导读
1. 学习 Vue3 需要什么知识?
- 需要有 HTML5 + CSS3 + JavaScript 基础
- 对 Node 的包管理器有一定的了解(比如:通过 npm 的方式安装过 Bootstrap、less 等插件)
- 了解过 Vue2 或者 React 等框架(当然,如果没有了解过问题也不大)
-
学习 Vue3 一定需要学习 TypeScript 吗?
不一定,如果有了解,自然会更好。本课程,暂定原生 JavaScript 语言,根据课程实际进度是否安排 TS 课程。
2. 通过本课程将能学到什么?
- Vite 构建工具
- Vue3 最新的 setup 语法糖
- VueRouter 路由管理
- Pinia 状态管理
- Axios 基于 promise 的 HTTP 库
- ElementPlus 一个 Vue3 的 UI 框架
- NodeJS + Express 构建后台服务(选讲)
- 实战应用(PC、移动端、桌面应用,选讲)
第一章:Vue 简介
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的渐进式 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。
Vue 采用自底向上增量开发的设计。核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。
1.1 核心关键词:构建用户界面
前端开发人员最主要的工作,就是为网站的用户构建出美观、舒适、好用的网页。
在大多数启用了构建工具的 Vue 项目中,我们可以使用一种类似 HTML 格式的文件来书写 Vue 组件,它被称为单文件组件 (也被称为 *.vue 文件,英文 Single-File Components,缩写为 SFC)。顾名思义,Vue 的单文件组件会将一个组件的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。
<script setup>
import {ref} from "vue"
const message = ref("Hello Vue3!!")
</script>
<template>
<div class="message">{{message}}</div>
</template>
<style lang="scss" scoped>
.message {
color: red;
font-weight: bold;
font-family: "楷体","MicroSoft Yahei";
}
</style>
如你所见,Vue 的单文件组件是网页开发中 HTML、CSS 和 JavaScript 三种语言经典组合的自然延伸。<template>、<script> 和 <style> 三个块在同一个文件中封装、组合了组件的视图、逻辑和样式。
1.2 核心关键词:框架
1.2.1 库和框架的区别
框架与库之间最本质区别在于控制权:you call libs, frameworks call you(控制反转)
- 库:库更多的是一个封装好的特定的集合,提供给开发者使用,而且是特定于某一方面的集合(方法和函数),库没有控制权,控制权在使用者手中,在库中查询需要的功能在自己的应用中使用,我们可以从封装的角度理解库;
- 框架:框架顾名思义就是一套架构,会基于自身的特点向用户提供一套相当于叫完整的解决方案,而且控制权的在框架本身,使用者要找框架所规定的某种规范进行开发。
在实际中,像angular、vue、react就属于框架,而jQuery、underscore、zepto就是库,在框架中我们完全可以自由的使用库,同时也可以在没有框架的基础之上使用库,库的使用是很自由的,控制权始终在我们的手中。但是使用框架时候就必须按照它的规范来进行模块化的开发。
1.2.2 Vue 框架
官方给 vue 的定位是前端框架,因为它提供了构建用户界面的一整套解决方案(俗称vue 全家桶):
- vue(核心库)
- vue-router(路由方案)
- pinia(状态管理方案)
- Element Plus(快速搭建页面UI 效果的方案)
以及辅助vue 项目开发的一系列工具:
- vite(npm 全局包:一键生成工程化的vue 项目-小而巧)
- vue-devtools(浏览器插件:辅助调试的工具)
- VSCode Vue3 插件推荐 Vue Language Features (Volar)
1.3 核心关键词:渐进式(自底向上)
渐进式:就是一步一步的拓展功能,不是说你必须一次把所有的东西都用上。
自底向上设计:是一种设计程序的过程和方法,就是先编写出基础程序段,然后再逐步扩大规模、补充和升级某些功能,实际上是一种自底向上构造程序的过程。
拿手机举例:手机最初设计的基础功能就是打电话、发短信,随着科技和网络的进步,手机慢慢的可以用来上网、拍照、拍视频、安装各种应用程序(购物、聊天、游戏等app),这就是渐进式,慢慢的去增加功能。
到了 Vue 中,保证核心功能的基础上,增加路由、状态管理、UI框架等等生态系统拓展 Vue 基础的功能,以适应指定业务场景的需求。
1.4 核心关键字:声明式
Vue 的两个核心功能:声明式渲染 和 响应性
- 声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
- 响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。
拓展:声明式编程与命令式编程
-
命令式编程(Imperative):详细的命令机器怎么(How)去处理一件事情以达到你想要的结果(What)。举例前面学习JS完成案例的过程中,每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令;每一步的指令都是按顺序执行的,称为命令式编程
-
声明式编程( Declarative):只告诉你想要的结果(What),机器自己摸索过程(How)。Vue 开发过程中,我们会在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods;称为是声明式编程;
声明式编程对程序开发人员更加友好,让其把工作重心放在核心业务实现上。
1.5 核心关键字:组件化
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。几乎任意类型的应用界面都可以抽象为一个组件树。
正是因为有了组件化的思想,前端的跨平台得到了实现。所谓组件,就是对象,每一个组件都是一个 js 对象,每一个 js 对象都可以编译成 html、Android、iOS 或者桌面端上的元素。
组件可以进行封装,实现代码复用。
界面根据Dom结构进行拆分,形成树状结构有如下优点:
- 良好的复用性:我需要哪个组件,就把哪个组件(对应图中的每一个立方体)单拎出来用,因为组件之间是独立的。
- 高效,便于比对:哪些数据数据变了可以立刻做出响应。界面中修改数据时,不需要层层寻找,找到对应的Dom元素再进行修改,我们可以根据组件的编号,找到相对应的组件进行数据的修改。
1.6 MVVM 模式
1.6.1 概述
MVVM 是一种数据驱动模式。即将数据和视图双向绑定,我们既可以通过修改视图来修改数据,也可以通过修改数据来修改视图。
Vue 使用MVVM响应式编程模型,避免直接操作DOM , 降低DOM操作的复杂性。
在MVVM(Model-View-ViewModel) 架构中,它把每个HTML 页面都拆分成了如下三个部分:
- View:视图层(UI 用户界面)
- Model:数据层(存储数据及对数据的处理如增删改查)
- ViewModel:业务逻辑层(一切 js 可视为业务逻辑),MVVM核心
1.6.2 工作原理
ViewModel 作为MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
当数据源发生变化时,会被ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
当表单元素的值发生变化时,也会被VM 监听到,VM 会把变化过后最新的值自动同步到Model 数据源中
1.6.3 优点
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
- 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
1.7 Vue 开发采用虚拟 DOM
用传统的开发模式,原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。在一次操作中,我需要更新10个DOM节点,浏览器收到第一个DOM请求后并不知道还有9次更新操作,因此会马上执行流程,最终执行10次。例如,第一次计算完,紧接着下一个DOM更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算DOM节点坐标值等都是白白浪费的性能。即使计算机硬件一直在迭代更新,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。
Web界面由DOM树(树的意思是数据结构)来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化,虚拟DOM就是为了解决浏览器性能问题而被设计出来的。如前,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象模拟DOM节点的好处:页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制,从而提高性能。
1.8 Vue3 对比 Vue2
如下图,传统的 vue2 逻辑比较分散 可读性差 可维护性差。对比 vue3 则逻辑分明可维护性高。
Vue1.x 与 Vue2.x 的 Options APl(选项式API)的设计是按照 methods、computed、data、 props这些不同的选项分类,当组件小的时候,这种分类方式一目然;但是在大型组件中,一个组件可能有多个逻辑关注点,当使用Options APl的时候,每一个关注点都有自己的Options,如果需要修改一个逻辑点关注点,就需要在单个文件中不断上下切换和寻找。
Vue3.x 的 Composition API(组合式API),它有一个很好的机制去解决这样的问题,就是将某个逻辑关注点相关的代码全都放在一个函数里。
1.9 Vue3 新特性
1.9.1 重写双向数据绑定
Vue2 基于Object.defineProperty() 实现。
把 Vue 中的核心方法 defineReactive 做一些简化如下:
function defineReactive (obj, key, val, cb) {
var dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=>{
/*....依赖收集等....*/
dep.depend()
return val
},
set:newVal=> {
val = newVal;
/*触发回调*/
dep.notify()
}
})
}
Vue通过defineReactive方法实现对需要观察的对象的每个属性进行监控。dep对象就相当于一个调度中心的作用,如果有数据用到这个属性,它就会自动收集该属性到调度中心,如果某属性发生了改变,那就会通知调度中心来更新视图。
Vue3 基于Proxy 实现
let proxyObj = new Proxy(obj,{
get : function (target,prop) {
return prop in target ? target[prop] : 0
},
set : function (target,prop,value) {
target[prop] = 888;
}
})
Proxy 与 Object.defineProperty(obj, prop, desc)方式相比有以下优势:
- 丢掉麻烦的备份数据
- 省去for in 循环
- 可以监听数组变化
- 代码更简化
- 可以监听动态新增的属性
- 可以监听删除的属性
- 可以监听数组的索引和 length 属性
1.9.2 优化 虚拟DOM
在Vue2中,每次更新diff,都是全量对比,Vue3则只对比带有标记的,这样大大减少了非动态内容的对比消耗
Vue Template Explorer 我们可以通过这个网站看到静态标记
patch flag 优化静态树
<span>Hello world!</span>
<span>Hello world!</span>
<span>Hello world!</span>
<span>Hello world!</span>
<span>{{msg}}</span>
<span>Hello world!</span>
<span>Hello world! </span>
Vue3 编译后的 Vdom 是这个样子的
export function render(_ctx,_cache,$props,$setup,$data,$options){return (_openBlock(),_createBlock(_Fragment,null,[
_createvNode( "span", null,"Hello world ! "),
_createvNode( "span",null,"Hello world! "),
_createvNode( "span",null,"Hello world! "),
_createvNode( "span", null,"Hello world! "),
_createVNode("span", null,_toDisplaystring(_ctx.msg),1/* TEXT */),
_createvNode( "span", null,"Hello world! "),
_createvNode( "span", null,"Hello world! ")],64/*STABLE_FRAGMENT */))
新增了 patch flag 标记
TEXT = 1 // 动态文本节点
CLASS=1<<1,1 // 2//动态class
STYLE=1<<2,// 4 //动态style
PROPS=1<<3,// 8 //动态属性,但不包含类名和样式
FULLPR0PS=1<<4,// 16 //具有动态key属性,当key改变时,需要进行完整的diff比较。
HYDRATE_ EVENTS = 1 << 5,// 32 //带有监听事件的节点
STABLE FRAGMENT = 1 << 6, // 64 //一个不会改变子节点顺序的fragment
KEYED_ FRAGMENT = 1 << 7, // 128 //带有key属性的fragment 或部分子字节有key
UNKEYED FRAGMENT = 1<< 8, // 256 //子节点没有key 的fragment
NEED PATCH = 1 << 9, // 512 //一个节点只会进行非props比较
DYNAMIC_SLOTS = 1 << 10 // 1024 // 动态slot
HOISTED = -1 // 静态节点
BALL = -2
我们发现创建动态 dom 元素的时候,Vdom 除了模拟出来了它的基本信息之外,还给它加了一个标记: 1 /* TEXT */,这个标记就叫做 patch flag(补丁标记)。patch flag 的强大之处在于,当你的 diff 算法走到 _createBlock 函数的时候,会忽略所有的静态节点,只对有标记的动态节点进行对比,而且在多层的嵌套下依然有效。尽管 JavaScript 做 Vdom 的对比已经非常的快,但是 patch flag 的出现还是让 Vue3 的 Vdom 的性能得到了很大的提升,尤其是在针对大组件的时候。
1.9.3 Fragments
vue3 允许我们支持多个根节点
<div>Hello World</div>
<div>Hello Vue</div>
<div :key="index" v-for="item,index in [10,20,304]">{{item}}</div>
同时支持render JSX 写法
render() {
return (
<>
{this.visable ? (
<div>{this.obj.name}</div>
) : (
<div>{this.obj.price}</div>
)}
<input v-model={this.val}></input>
{[1, 2, 3].map((v) => {
return <div>{v}-----</div>;
})}
</>
);
},
同时新增了Suspense teleport 和 多 v-model 用法
1.9.4 Tree shaking
简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码。
在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到。而 Vue3源码引入tree shaking特性,将全局 API 进行分块。如果你不使用其某些功能,它们将不会包含在你的基础包中。就是比如你要用watch 就是import {watch} from 'vue' 其他的computed 没用到就不会给你打包减少体积
1.9.5 Composition API
组合式 API (Composition API) 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语,涵盖了以下方面的 API:
响应式 API:例如 ref() 和 reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。
生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑。
依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统。
组合式 API 是 Vue 3 及 Vue 2.7 的内置功能。对于更老的 Vue 2 版本,可以使用官方维护的插件 @vue/composition-api。在 Vue 3 中,组合式 API 基本上都会配合
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 更改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`计数器初始值为 ${count.value}。`)
})
</script>
<template>
<button @click="increment">点击了:{{ count }} 次</button>
</template>
使用组合式 API 有以下几点优势:
- 更好的逻辑复用 组合式 API 最基本的优势是它使我们能够通过组合函数来实现更加简洁高效的逻辑复用。在选项式 API 中我们主要的逻辑复用机制是 mixins,而组合式 API 解决了 mixins 的所有缺陷。 组合式 API 提供的逻辑复用能力孵化了一些非常棒的社区项目,比如 VueUse,一个不断成长的工具型组合式函数集合。
- 更灵活的代码组织 同一个逻辑关注点相关的代码被归为了一组:我们无需再为了一个逻辑关注点在不同的选项块间来回滚动切换。此外,我们现在可以很轻松地将这一组代码移动到一个外部文件中,不再需要为了抽象而重新组织代码,大大降低了重构成本,这在长期维护的大型项目中非常关键。
- 更好的类型推导 组合式 API 主要利用基本的变量和函数,它们本身就是类型友好的。用组合式 API 重写的代码可以享受到完整的类型推导,不需要书写太多类型标注。大多数时候,用 TypeScript 书写的组合式 API 代码和用 JavaScript 写都差不太多!
- 更小的生产包体积
搭配
<script setup>使用组合式 API 比等价情况下的选项式 API 更高效,对代码压缩也更友好。这是由于<script setup>形式书写的组件模板被编译为了一个内联函数,和<script setup>中的代码位于同一作用域。不像选项式 API 需要依赖 this 上下文对象访问属性,被编译的模板可以直接访问<script setup>中定义的变量,无需从实例中代理。这对代码压缩更友好,因为本地变量的名字可以被压缩,但对象的属性名则不能。
1.10 vue-devtools 调试工具
vue 官方提供的vue-devtools调试工具,能够方便开发者对vue 项目进行调试与开发。Chrome 浏览器在线安装vue-devtools
-
vue 2.x 调试工具
-
vue 3.x 调试工具
注意:vue2 和vue3 的浏览器调试工具不能交叉使用!
点击Chrome 浏览器右上角的 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-clRSTwtM-1672016260783)(Vue3课程笔记导读.assets/image-20221225220407446.png)] 按钮,选择更多工具-> 扩展程序-> Vue.js devtools 详细信息,并勾选如下的两个选项:
注意:修改完配置项,须重启浏览器才能生效!
在浏览器中访问一个使用了vue 的页面,打开浏览器的开发者工具,切换到Vue 面板,即可使用vue-devtools调试当前的页面。