​🌈个人主页:前端青山
🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来vue篇专栏内容:vue3-选项式API

目录

1.vue3初探

1.1 MVX

1.1.1 MVC(Model-View-Controller)

1.1.2 MVP(Model-View-Presenter)

1.1.3 MVVM(Model-View-ViewModel)

1.2 vue特性

1.3 vue3十大新特性

1.4 创建第一个vue3应用

1.5 API风格

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

npmmirror 镜像站

$ 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不会被抛弃

Logo

前往低代码交流专区

更多推荐