vue3的选项式API详解?(看这一篇就够了)
vue3的选项式API详解?(看这一篇就够了)
🌈个人主页:前端青山
🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来vue篇专栏内容:vue3-选项式API
目录
1.1.1 MVC(Model-View-Controller)
1.1.2 MVP(Model-View-Presenter)
1.1.3 MVVM(Model-View-ViewModel)
1.vue3初探
1.1 MVX
目标:理解MVVM、MVC、MVP
MV系列框架中,M和V分别指Model层和View层,但其功能会因为框架的不同而变化。
Model层是数据模型,用来存储数据;
View层是视图,展示Model层的数据。
虽然在不同的框架中,Model层和View层的内容可能会有所差别,但是其基础功能不变,变的只是 数据的传输方式
。
1.1.1 MVC(Model-View-Controller)
MVC是模型-视图-控制器,它是MVC、MVP、MVVM这三者中最早产生的框架,其他两个框架是以它为基础发展而来的。
MVC的目的就是将M和V的代码分离,且MVC是单向通信,必须通过Controller来承上启下。
$ npx express express-app --view=ejs # npx 项目生成器的管理工具 # express node项目的生成器 # epxress-app 项目名称-自己起名 # --view=ejs 前端的模版 # 如果npx 创建不成 cnpm i express-generator -g # 如果npx 创建不成 express express-app --view=ejs # 如果还不行 找一个创建的项目,拷贝过来,安装依赖 $ cd express-app $ cnpm i # npm i $ cnpm run start # http://localhost:3000
Model:模型层,数据模型及其业务逻辑,是针对业务模型建立的数据结构,Model与View无关,而与业务有关。
View:视图层,用于与用户实现交互的页面,通常实现数据的输入和输出功能。
Controller:控制器,用于连接Model层和View层,完成Model层和View层的交互。还可以处理页面业务逻辑,它接收并处理来自用户的请求,并将Model返回给用户。
// express-app/mysql/db.js -- 可替换自己熟悉的写法
// 链接数据库
const mongoose = require('mongoose')
const DB_URL = "mongodb://localhost:27017/ty2206"
mongoose.connect(DB_URL)
mongoose.connection.on('connected', () => {
console.log('数据库连接成功')
})
mongoose.connection.on('disconnected', () => {
console.log('数据库连接断开')
})
mongoose.connection.on('error', (err) => {
console.log('数据库连接失败' + err)
})
module.exports = mongoose
// express-app/mysql/collections/User.js
const mongoose = require('../db')
const Schema = mongoose.Schema
// MVC 中 M
const userSchema = new Schema({
userId: {
required: true,
type: String
},
userName: {
type: String
},
password: String
})
// users 数据库中的集合名称
module.exports = mongoose.model(userSchema, 'users')
// express-app/routes/index.js
var express = require('express');
var router = express.Router();
// var User = require('../mysql/collections/User')
/* GET home page. */
// MVC 中的 C
router.get('/', function(req, res, next) {
// User.find().exec((err, data) => {
// if (err) throw err
// res.render 渲染哪一个页面
// res.render('index', { title: 'Express', data });
// })
// 模拟数据库操作
res.render('index', { title: 'Express', data: [
{ userId: 'user1', userName: '吴大勋' },
{ userId: 'user2', userName: '纪童伟' },
] });
});
module.exports = router;
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<h1>用户列表如下</h1>
<table>
<tr>
<th>序号</th>
<th>id</th>
<th>姓名</th>
</tr>
<% for(var i = 0; i < data.length; i++) { %>
<tr>
<td><%= i + 1 %></td>
<td><%= data[i].userId %></td>
<td><%= data[i].userName %></td>
</tr>
<% } %>
</table>
</body>
</html>
遇到条件控制语句 使用 <% %>包裹,遇到变量 使用 <%= %> 或者 <%- %>包裹
<%= %> 原样输出 - 转义输出。---- innerText
<%- %> 解析输出。 ---- innerHTML
上图可以看出各部分之间的通信是单向的,呈三角形状。
具体MVC框架流程图如图
从上图可以看出,Controller层触发View层时,并不会更新View层中的数据,View层的数据是通过监听Model层数据变化自动更新的,与Controller层无关。换言之,Controller存在的目的是确保M和V的同步,一旦M改变,V应该同步更新。
同时,我们可以看到,MVC框架大部分逻辑都集中在Controller层,代码量也集中在Controller层,这带给Controller层很大压力,而已经有独立处理事件能力的View层却没有用到;而且,Controller层与View层之间是一一对应的,断绝了View层复用的可能,因而产生了很多冗余代码。
MVC 房东 -房客 -中介
为了解决上述问题,MVP框架被提出
1.1.2 MVP(Model-View-Presenter)
MVP是模型-视图-表示器,它比MVC框架大概晚出现20年,是从MVC模式演变而来的。它们的基本思想有相同之处:Model层提供数据,View层负责视图显示,Controller/Presenter层负责逻辑的处理。将Controller改名为Presenter的同时改变了通信方向。
Model:模型层,用于数据存储以及业务逻辑。
View:视图层,用于展示与用户实现交互的页面,通常实现数据的输入和输出功能。
Presenter:表示器,用于连接M层、V层,完成Model层与View层的交互,还可以进行业务逻辑的处理。
上图可以看出各部分之间的通信是双向的。
在MVC框架中,View层可以通过访问Model层来更新,但在MVP框架中,View层不能再直接访问Model层,必须通过Presenter层提供的接口,然后Presenter层再去访问Model层。
从上图可以看出,View层和Model层互不干涉,View层也自由了很多,所以View层可以抽离出来做成组件,在复用性上就比MVC框架好很多。
但是,由于View层和Model层都需要经过Presenter层,导致Presenter层比较复杂,维护起来也会有一定的问题;而且,因为没有绑定数据,所有数据都需要Presenter层进行“手动同步”,代码量较大,虽然比起MVC框架好很多,但还是有比较多冗余部分。
为了让View层和Model层的数据始终保持一致,MVVM框架出现了。
1.1.3 MVVM(Model-View-ViewModel)
MVVM是模型-视图-视图模型。MVVM与MVP框架区别在于:MVVM采用双向绑定:View的变动,自动反映在ViewModel,反之亦然。
Model:数据模型(数据处理业务),指的是后端传递的数据。
View:视图,将Model的数据以某种方式展示出来。
ViewModel:视图模型,数据的双向绑定(当Model中的数据发生改变时View就感知到,当View中的数据发生变化时Model也能感知到),是MVVM模式的核心。ViewModel 层把 Model 层和 View 层的数据同步自动化了,解决了 MVP 框架中数据同步比较麻烦的问题,不仅减轻了 ViewModel 层的压力,同时使得数据处理更加方便——只需告诉 View 层展示的数据是 Model 层中的哪一部分即可。
上图可以看出各部分之间的通信是双向的,而且我们可以看出,MVVM框架图和MVP框架图很相似,两者都是从View层开始触发用户的操作,之后经过第三层,最后到达Model层。而关键问题就在于这第三层的内容,Presenter层是采用手动写方法来调用或修改View层和Model层;而ViewModel层双向绑定了View层和Model层,因此,随着View层的数据变化,系统会自动修改Model层的数据,反之同理。
具体MVVM框架流程图如图
从上图可以看出,View层和Model层之间的数据传递经过了ViewModel层,ViewModel层并没有对其进行“手动绑定”,不仅使速度有了一定的提高,代码量也减少很多,相比于MVC框架和MVP框架,MVVM框架有了长足的进步。
从MVVM第一张图可以看出,MVVM框架有大致两个方向:
1、模型-->视图 ——实现方式:数据绑定
2、视图-->模型 ——实现方式:DOM事件监听
存在两个方向都实现的情况,叫做数据的双向绑定。双向数据绑定可以说是一个模板引擎,它会根据数据的变化实时渲染。如图View层和Model层之间的修改都会同步到对方。
MVVM模型中数据绑定方法一般有四种:
数据劫持vue2 - Object.defineProperty
原生Proxy vue3
发布-订阅模式
脏值检查
Vue2.js使用的就是数据劫持和发布-订阅模式两种方法。了解Vue.js数据绑定流程前,我们需要了解这三个概念:
Observer:数据监听器,用于监听数据变化,如果数据发生改变,不论是在View层还是在Model层,Observer都会知道,然后告诉Watcher。
Compiler:指定解析器,用于对数据进行解析,之后绑定指定的事件,在这里主要用于更新视图。
Watcher:订阅者。
首先将需要绑定的数据劫持方法找出来,之后用Observer监听这堆数据,如果数据发生变化,Observer就会告诉Watcher,然后Watcher会决定让那个Compiler去做出相应的操作,这样就完成了数据的双向绑定。
vue3.js使用更快的原生 Proxy,消除了之前 Vue2.x 中基于 Object.defineProperty 的实现所存在的很多限制:无法监听 属性的添加和删除、数组索引和长度的变更,并可以支持 Map、Set、WeakMap 和 WeakSet!
带来的特性:
vue3.0实现响应式
Proxy支持监听原生数组
Proxy的获取数据,只会递归到需要获取的层级,不会继续递归
Proxy可以监听数据的手动新增和删除
Proxy对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。 其实就是在对目标对象的操作之前提供了拦截,可以对外界的操作进行过滤和改写,修改某些操作的默认行为,这样我们可以不直接操作对象本身,而是通过操作对象的代理对象来间接来操作对象,达到预期的目的~
1.2 vue特性
目标:理解声明式,对比传统DOM开发
Vue从设计角度来讲,虽然能够涵盖这张图上所有的东西,但是你并不需要一上手就把所有东西全用上,都是可选的。
声明式渲染和组件系统是Vue的核心库所包含内容,而路由、状态管理、构建工具都有专门解决方案。这些解决方案相互独立,我们可以在核心的基础上任意选用其他的部件(以插件形势使用),不一定要全部整合在一起。
Vue.js的核心是一个允许采用简洁的模板语法来声明式的将数据渲染进DOM的系统。
假设需要输出 “hello ty2206”
准备工作:cnpm
$ npm install -g cnpm --registry=https://registry.npmmirror.com以后就可以使用cnpm 代替 npm
如果遇到类似于以下**psl这种错误
只需要找到这个文件删除即可(这个错误只会出现在windows电脑下)
补充:如果使用cnpm出现
randomUUID is not a function
,解决方法$ npm uninstall -g cnpm $ npm install cnpm@7.1.0 -g
传统开发模式的原生js,jQuery代码如下:
<div id="test"></div>
<!--原生js-->
<script>
const msg = "hello ty2206"
const test = document.getElementById('test')
test.innerHTML = msg
// test.innerText = msg
// test.textContent=""
</script>
<!--jQuery-->
<script>
var msg = 'hello ty2206'
$('#test').html(msg)
// $('#test').text(msg)
</script>
$ cnpm i vue jquery # 临时安装,不会出现package.json文件拷贝
node_modules/vue/dist/vue.global.js
以及vue.global.prod.js
,还有jquery/dist/jquery.js
以及jquery.min.js
到lib文件夹
完整代码:
<!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>传统的DOM操作</title>
</head>
<body>
<div id="jsDOM"></div>
<div id="jqDOM"></div>
</body>
<script src="../lib/jquery.min.js"></script>
<script>
const str = 'hello ty2206'
const jsDOM = document.getElementById('jsDOM')
// jsDOM.innerHTML = str
// jsDOM.innerText = str
jsDOM.textContent = str
// $('#jqDOM').html(str)
$('#jqDOM').text(str)
</script>
</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>vue3解决DOM操作</title>
</head>
<body>
{{ str }}
<div id="app">
<div>{{ str }}</div>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
str: 'hello ty2206',
list: ['a', 'b', 'c', 'd']
}
}
})
app.mount('#app')
</script>
</html>
$ cnpm i vue@2
拷贝 vue下的 vue.js 以及vue.min.js到lib文件夹
<!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>vue2解决DOM操作</title>
</head>
<body>
{{ str }}
<div id="app">
<div>{{ str }}</div>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</body>
<script src="../lib/vue.js"></script>
<script>
new Vue({
data: { // new Vue实例时使用对象,其余时刻使用函数
str: 'hello ty2206',
list: ['a', 'b', 'c', 'd', 'e']
}
}).$mount('#app')
</script>
</html>
1.3 vue3十大新特性
* setup ---- 组合式API * ref ---- 组合式API * reactive ---- 组合式API * 计算属性computed ---- 组合式API 以及 选项式API * 侦听属性watch ---- 组合式API 以及 选项式API * watchEffect函数 ---- 组合式API * 生命周期钩子 ---- 组合式API 以及 选项式API * 自定义hook函数 ---- 组合式API * toRef和toRefs ---- 组合式API 以及 选项式API * 其他新特性 * shallowReactive 与 shallowRef ---- 组合式API * readonly 与 shallowReadonly ---- 组合式API * toRaw 与 markRaw ---- 组合式API * customRef ---- 组合式API * provide 与 inject ---- 组合式API 以及 选项式API * 响应式数据的判断 ---- 组合式API 以及 选项式API * 新的组件 ----- 类似于新增的HTML标签 * Fragment * Teleport * Suspense * 其他变化 * data选项应始终被声明为一个函数 * 过渡类名的更改 * 移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes --- 事件处理 * 移除v-on.native修饰符 --- 事件处理 * 移除过滤器(filter) --- 单独讲解vue2和vue3差异化
1.4 创建第一个vue3应用
每个 Vue 应用都是通过 createApp 函数创建一个新的 应用实例:
<div id="app"></div>
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
app.mount('#app')
简单计数器案例:
<!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>vue3示例</title>
</head>
<body>
<div id="app">
{{ msg }}
<div>
{{ count }}
<div>count的double: {{ count * 2 }}</div>
<!-- 体验点击事件 -->
<button @click="count++">加1</button>
</div>
</div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
// 解构 创建应用实例的 函数
const { createApp } = Vue // 有的人喜欢写 window.Vue
// 创建应用实例
const app = createApp({ // 当前应用实例的选项
data () { // 书写形式为函数,表示vue实例中需要使用的 数据的变量,必须含有返回值,返回值为对象
return {
msg: 'hi ty2206',
count: 10
}
}
})
// 应用实例挂载
app.mount('#app')
</script>
</html>
1.我们传入
createApp
的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。应用实例必须在调用了
.mount()
方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串:
1.5 API风格
目标:选项式API以及组合式API如何选择
<!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>vue3_组合式API</title>
</head>
<body>
<div id="app">
{{ msg }}
<div>
{{ count }}
<div>count的double: {{ count * 2 }}</div>
<!-- 体验点击事件 -->
<button @click="count++">加1</button>
</div>
</div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
// 解构 创建应用实例的 函数
// ref 代表 组合式API 创建数据时的一个响应式的标识
const { createApp, ref } = Vue // 有的人喜欢写 window.Vue
// 创建应用实例
const app = createApp({ // 当前应用实例的选项
setup () { // 组合式API的标志
const msg = ref('hello ty2206!') // msg 的初始值
const count = ref(100) // count 的初始值
// 数据需要在views视图响应,需要将其返回去
return {
msg,
count
}
}
})
// 应用实例挂载
app.mount('#app')
</script>
</html>
使用组合式API可以
-
更好的逻辑复用
-
更灵活的代码组织
-
更好的类型推导
-
更小的生产包体积
选项式 API 确实允许你在编写组件代码时“少思考”,这是许多用户喜欢它的原因。然而,在减少费神思考的同时,它也将你锁定在规定的代码组织模式中,没有摆脱的余地,这会导致在更大规模的项目中难以进行重构或提高代码质量。在这方面,组合式 API 提供了更好的长期可维护性。
组合式 API 能够覆盖所有状态逻辑方面的需求
一个项目可以同时使用两种API
选项式API不会被抛弃
更多推荐
所有评论(0)