Vue3学习笔记(一)——MVC与vue3概要、模板、数据绑定与综合示例

2022-10-28 10:25:15 浏览数 (1)

一、前端MVC概要

1.1、库与框架的区别

框架是一个软件的半成品,在全局范围内给了大的约束。库是工具,在单点上给我们提供功能。框架是依赖库的。Vue是框架而jQuery则是库。

1.2、MVC(Model View Controller)

MVC的核心理念是:你应该把管理数据的代码(Model)、业务逻辑的代码(Controller)、以及向用户展示数据的代码(View)清晰的分离开

  • 模型:代表应用当前的状态
  • 视图:用于展示数据,用于接口
  • 控制器:用来管理模型和视图之间的关系

典型思路是 View 层通过事件通知到 Controller 层,Controller 层经过对事件的处理完成相关业务逻辑,要求 Model 层改变数据状态,Model 层再将新数据更新到 View层。

View层和 Model 层相互持有、相互操作,导致紧密耦合,在可维护性上有待提升。由此,MVP 模式应运而生

通过MVC框架又衍生出了许多其它的架构,统称MV*,最常见的是MVP与MVVM

1.3、MVP (Model View Presenter)

MVP 模式将程序分为三个部分:模型(Model)、视图(View)、管理层(Presenter)。

Model 模型层: 只负责存储数据,与 View 呈现无关,也与 UI 处理逻辑无关,发生更新也不用主动通知 View;

View 视图层: 人机交互接口,一般为展示给用户的界面;

Presenter 管理层 : 负责连接 Model 层和 View 层,处理 View 层的事件,负责获取数据并将获取的数据经过处理后更新 View;

MVC 模式的 View 层和 Model 层存在耦合,为了解决这个问题,MVP 模式将 View 层和 Model 层解耦,之间的交互只能通过 Presenter 层,实际上,MVP 模式的目的就是将 View 层和 Model 层完全解耦,使得对 View 层的修改不会影响到 Model 层,而对 Model 层的数据改动也不会影响到View 层。

典型流程是 View 层触发的事件传递到 Presenter 层中处理,Presenter 层去操作 Model 层,并且将数据返回给 View层,这个过程中,View 层和 Model 层没有直接联系。而 View 层不部署业务逻辑,除了展示数据和触发事件之外,其它时间都在等着 Presenter 层来更新自己,被称为「被动视图」。

由于 Presenter 层负责了数据获取、数据处理、交互逻辑、UI 效果等等功能,所以 Presenter 层就变得强大起来,相应的,Model 层只负责数据存储,而 View 层只负责视图,Model 和 View 层的责任纯粹而单一,如果我们需要添加或修改功能模块,只需要修改 Presenter 层就够了。由于 Presenter 层需要调用 View 层的方法更新视图,Presenter 层直接持有 View 层导致了 Presenter 对 View 的依赖。

正如上所说,更新视图需要 Presenter 层直接持有 View 层,并通过调用 View 层中的方法来实现,还是需要一系列复杂操作,有没有什么机制自动去更新视图而不用我们手动去更新呢,所以,MVVM 模式应运而生。

1.3、MVVM (Model View ViewModel)

MVVM 模式将程序分为三个部分:模型(Model)、视图(View)、视图模型(View-Model)。

和 MVP 模式类似,Model 层和 View 层也被隔离开,彻底解耦,ViewModel 层相当于 Presenter 层,负责绑定 Model 层和 View 层,相比于 MVP 增加了双向绑定机制。

MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。

Vue与Angular就是一个MVVM框架,MVVM与MVC最大的区别是模型与视图实现了双向绑定。

在Vue中用户自定义的实例就是vm,功能与Controller类似

MVVM 模式的特征是 ViewModel 层和 View 层采用双向绑定的形式(Binding),View 层的变动,将自动反映在 ViewModel 层,反之亦然。

但是双向绑定给调试和错误定位带来困难,View 层的异常可能是 View 的代码有问题,也有可能是 Model 层的问题。数据绑定使得一个位置的 Bug 被传递到别的位置,要定位原始出问题的地方就变得不那么容易了。

对简单UI 来说,实现 MVVM 模式的开销是不必要的,而对于大型应用来说,引入 MVVM 模式则会节约大量手动更新视图的复杂过程,是否使用,还是看使用场景。

1.3、React

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。支持虚拟DOM(Virtual DOM)和组件化的开发。

ReactJS官网地址: http://facebook.github.io/react/

Github地址: https://github.com/facebook/react

1.4、AngularJS简介

AngularJS是一个前端MVVM框架。

angular的英文字面意思是:有角的; 用角测量的

AngularJS是协助搭建单页面工程(SPA)的开源前端框架。它通过MVC模式使得开发与测试变得更容易。

AngularJS试图成为WEB应用中的一种端对端的解决方案。它将指导开发整个应用。

AngularJS于2009年发布第一个版本,由Google进行维护,压缩版94k。

1.3版后不再支持IE8

1.3版后不支持全局控制器

2.0版 alpha

git仓库: https://github.com/angular/

官网: https://www.angularjs.org/

http://www.angularjs.cn/中文社区

http://www.apjs.net/ 中文网

a web framework for modern web apps

1.5、Vue.js

1.5.1、Vue.js介绍

Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API,作者是尤雨溪是中国人。

官网: http://cn.vuejs.org/

仓库: https://github.com/vuejs

文档与资源大全:https://vue3js.cn/

易学易用

基于标准 HTML、CSS 和 JavaScript 构建,提供容易上手的 API 和一流的文档。

性能出色

经过编译器优化、完全响应式的渲染系统,几乎不需要手动优化。

灵活多变

丰富的、可渐进式集成的生态系统,可以根据应用规模在库和框架间切换自如。

当前三大前端MVC框架的对比:

Vue3.JS简介

Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。

1.性能的提升

  • 打包大小减少41%
  • 初次渲染快55%, 更新渲染快133%
  • 内存减少54%

2.源码的升级

  • 使用Proxy代替defineProperty实现响应式
  • 重写虚拟DOM的实现和Tree-Shaking

3.拥抱TypeScript

  • Vue3可以更好的支持TypeScript

4.新的特性

  • Composition API(组合API)
  • setup配置
  • ref与reactive
  • watch与watchEffect
  • provide与 inject
  • 新的内置组件
    • Fragment
    • Teleport
    • Suspense
  • 其他改变
    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符

如果你已经是有经验的前端开发者,想知道 Vue 与其它库/框架有哪些区别,请查看 对比其它框架。

不适合SEO、交互频繁的,如游戏之类交互体验网站

浏览器支持:

Vue.js 不支持 IE8 及其以下版本,因为 Vue.js 使用了 IE8 不能模拟的 ECMAScript 5 特性。Vue.js 支持所有 兼容 ECMAScript 5 的浏览器。

1.5.2、Vue3带来的新变化

  1. 性能提升
  2. 首次渲染更快
  3. diff算法更快
  4. 内存占用更少
  5. 打包体积变小
  6. 更好的Typescript支持
  7. Composition API (重点)

在使用vue2.x版本开发较复杂的组件时,逻辑难以复用,组合式api的出现可以解决此类问题

相关阅读:

  1. Vue3 中文文档  https://vue3js.cn/docs/zh/
  2. Vue3 设计理念  https://vue3js.cn/vue-composition/ 破坏性语法更新
  3. 官网:https://vuejs.org/  https://cn.vuejs.org/

vue3.0对于2.0版本的大部分语法都是可以兼容的,但是也有一些破坏性的语法更新,需要格外注意

  1. 实例方法$on移除   (eventBus现有实现模式不再支持 可以使用三方插件替代)
  2. 过滤器filter移除 (插值表达式里不能再使用过滤器 可以使用methods替代)
  3. .sync语法移除  (和v-model语法合并)

1.6、使用vue-cli构建第一个Vue程序

1.6.1、安装Node.js

详细请查看:https://cloud.tencent.com/developer/article/2126719

 查看版本显示版本号则表示正常安装了node.js

1.6.2、安装Vue-cli

如果之前安装过Vue2,需要把Vue2卸载。

代码语言:javascript复制
npm uninstall vue-cli -g

安装最新版本的vue-cli

代码语言:javascript复制
npm i -g @vue/cli

 查看版本号

1.6.3、创建Vue3.0项目

(1)、进入命令行,进入要创建项目的目录,如:F:NFvue3demos

 (2)、输入创建项目的命令

代码语言:javascript复制
vue create 项目名称

项目名中不能包含大写字母

代码语言:javascript复制
vue create vue3demo01

选择模板,如果选择Vue3则项目使用默认项直接创建完成,这里选择最后一项,手动选择并创建项目。

使用空格键可以选择,使用上下键可以移动,各项的意义如下:

Babel:将源代码转换成指定版本的JS,如ES6=>ES5

TypeScript:使用强类型的JavaScript预处理语言

PWA:使用渐进式网页应用

Router:使用Vue路由

Vuex:使用Vuex状态管理

CSS Pre-processors:CSS预处理器,如Less、Sass

Linter/Formatter:使用代码风格检查和格式化器

Unit Testing:使用单元测试

E2E Testing:使用一种End to End (端到端)的黑盒测试

 选择Vue版本,这里直接选择3

是否使用Class风格的组件定义语法?

即原本是:home = new Vue()创建vue实例

使用装饰器后:class home extends Vue{}

这里选择 y

 使用Babel与TypeScript一起用于自动检测的填充? y

 是否使用history路由模式,如果启用,则项目生成之后有可能会出现打开浏览器页面是空白,这里选择n

ESLint with error prevention only (仅具有错误预防功能)

ESLint Airbnb config (Airbnb配置)

ESLint Standard config (标准配置)

ESLint Prettier (更漂亮)

Lint on save(保存时检查)

Lint and fix on commit(提交时检查)

 Babel,ESLint等配置文件的存放方式

In dedicated config files 存放到独立文件中

In package.json 存放到 package.json 中

这里选择默认项In dedicated config files

 是否需要保存当前配置,为以后生成新项目时进行快速构建,这里选择n,不保存。

1.6.4、运行项目

使用cd命令进入项目,然后运行

在浏览器中输入http://localhost:8080查看

1.6.5、使用图形化界面创建项目

你也可以通过 vue ui 命令以图形化界面创建和管理项目:

代码语言:javascript复制
vue ui

上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。

1.6.6、项目结构

(1)、目录结构

Vue/cli文档:https://cli.vuejs.org/zh/guide/

代码语言:javascript复制
|-dist                          -- 生成打包后文件
|-node_modules                  -- 项目依赖包的目录
|-public                        -- 项目公用文件
	|--favicon.ico              	-- 网站地址栏前面的小图标
	|--index.html                  -- 项目的默认首页,Vue的组件需要挂载到这个文件上
|-src                           -- 源文件目录,程序员主要工作的地方
	|-api                           -- 与后端交互使用相关方法和配置
	|-assets                   		-- 静态文件目录,图片图标、样式,比如网站logo
	|-components                	-- Vue3.x的自定义组件目录
	|-router                    	-- vue-router相关配置
	|--utils                    	-- 通用工具包
	|--views                    	-- 页面
	|--App.vue                  	-- 项目的根组件,单页应用都需要的
	|--main.css                	-- 一般项目的通用CSS样式写在这里,main.js引入
	|--main.js                  	-- 项目入口文件,SPA单页应用都需要入口文件
|--.gitignore                   -- git的管理配置文件,设置那些目录或文件不管理
|--package-lock.json           -- 项目包的锁定文件,用于防止包版本不一样导致的错误
|--package.json                -- 项目配置文件,包管理、项目名称、版本和命令

(2)、.browserslistrc

用于指定浏览器范围。你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。

1% 代表着全球超过1%人使用的浏览器 last 2 versions  表示所有浏览器兼容到最后两个版本

not dead  不是24个月没有官方支持或更新的浏览器。目前它是IE 11,IE_Mob 11,黑莓10,黑莓7,三星4,OperaMobile 12.1和百度的所有版本。

not ie 11 排除ie11

如果你的项目要兼容IE,但你的browserslist规则是这样的,那么打包的就会有个提示:All browser targets in the browserslist configuration have supported ES module.Therefore we don't build two separate bundles for differential loading.那么这里就不会打包兼容IE的代码。

范围

说明

last 2 versions

最新的两个大版本

1%

全球使用人数超过1%的浏览器,如 > 5% in US表示在美国5%以上的用户

cover 99.5%

覆盖99.5%主流浏览器

chrome > 50 ie 9-11

指定某个版本的浏览器

not ie < 11

排除ie11 以下版本

since 2013 last 2 years

某个时间范围发布的所有浏览器版本

maintained node versions

所有被node基金会维护的node版本

current node

当前环境node版本

dead

通过last 2 versions筛选的浏览器中,全球使用率低于0.5%,且官方声明不再维护或者事实上已经两年没有再更新的版本

default

默认配置,> 0.5% last 2 versions Firefox ESR not dead

(3)、BASE_URL

<link rel="icon" href="<%= BASE_URL %>favicon.ico">

 (4)、<%= htmlWebpackPlugin.options.title %>

代码语言:javascript复制
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  pages: {
    index: {
      // page 的入口
      entry: 'src/main.ts',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Hello Vue3!!!',
      // 在这个页面中包含的块,默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    }
  }
})

1.7、使用Vite构建第一个Vue程序

官方文档:v3.cn.vuejs.org/guide/insta…

vite官网:vitejs.cn

  • 什么是vite?—— 是Vue团队打造的新一代前端构建工具。
  • 优势如下:
代码语言:txt复制
- 开发环境中,无需打包操作,可快速的冷启动。
- 轻量快速的热重载(HMR)。
- 真正的按需编译,不再等待整个应用编译完成。传统构建 与 vite构建对比图

传统构建模式,是将所有资源都打包好,再上线

而Vite有点按需加载的意思在那里了~

接下来我们就用Vite来创建一个Vue3的项目

代码语言:javascript复制
## 创建工程
npm init vite-app yk_vue3
## 进入工程目录
cd yk_vue3
## 安装依赖
npm install
## 运行
npm run dev

构建速度明显vite快 

运行

1.8、单文件组件

在大多数启用了构建工具的 Vue 项目中,我们可以使用一种类似 HTML 格式的文件来书写 Vue 组件,它被称为单文件组件 (也被称为 *.vue 文件,英文 Single-File Components,缩写为 SFC)。顾名思义,Vue 的单文件组件会将一个组件的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。下面我们将用单文件组件的格式重写上面的计数器示例:

代码语言:javascript复制
<script>
export default {
  data() {
    return {
      count: 0
    }
  }
}
</script>

<template>
  <button @click="count  ">Count is: {{ count }}</button>
</template>

<style scoped>
button {
  font-weight: bold;
}
</style>

单文件组件是 Vue 的标志性功能。如果你的用例需要进行构建,我们推荐用它来编写 Vue 组件。你可以在后续相关章节里了解更多关于单文件组件的用法及用途。但你暂时只需要知道 Vue 会帮忙处理所有这些构建工具的配置就好。

1.9、API 风格

Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API。

1.9.1、选项式 API (Options API)

使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 datamethods 和 mounted。选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例。

代码语言:javascript复制
<script>
export default {
  // data() 返回的属性将会成为响应式的状态
  // 并且暴露在 `this` 上
  data() {
    return {
      count: 0
    }
  },

  // methods 是一些用来更改状态与触发更新的函数
  // 它们可以在模板中作为事件监听器绑定
  methods: {
    increment() {
      this.count  
    }
  },

  // 生命周期钩子会在组件生命周期的各个不同阶段被调用
  // 例如这个函数就会在组件挂载完成后被调用
  mounted() {
    console.log(`The initial count is ${this.count}.`)
  }
}
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

1.9.2、组合式 API (Composition API)

通过组合式 API,我们可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与 <script setup> 搭配使用。这个 setup attribute 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如,<script setup> 中的导入和顶层变量/函数都能够在模板中直接使用。

下面是使用了组合式 API 与 <script setup> 改造后和上面的模板完全一样的组件:

代码语言:javascript复制
<template>
    <div>
        <button type="button" @click="increment()">count is : {{count}}</button>
    </div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
  name: 'Hello',
  setup () {
    const count = ref(0)

    function increment () {
      count.value  
    }

    onMounted(() => {
      console.log(count)
    })

    return { count, increment }
  }
}

</script>
<style scoped>
    button{
        font-weight: bold;
    }
</style>

1.9.3、该选哪一个?

两种 API 风格都能够覆盖大部分的应用场景。它们只是同一个底层系统所提供的两套不同的接口。实际上,选项式 API 是在组合式 API 的基础上实现的!关于 Vue 的基础概念和知识在它们之间都是通用的。

选项式 API 以“组件实例”的概念为中心 (即上述例子中的 this),对于有面向对象语言背景的用户来说,这通常与基于类的心智模型更为一致。同时,它将响应性相关的细节抽象出来,并强制按照选项来组织代码,从而对初学者而言更为友好。

组合式 API 的核心思想是直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题。这种形式更加自由,也需要你对 Vue 的响应式系统有更深的理解才能高效使用。相应的,它的灵活性也使得组织和重用逻辑的模式变得更加强大。

在组合式 API FAQ 章节中,你可以了解更多关于这两种 API 风格的对比以及组合式 API 所带来的潜在收益。

如果你是使用 Vue 的新手,这里是我们的大致建议:

  • 在学习的过程中,推荐采用更易于自己理解的风格。再强调一下,大部分的核心概念在这两种风格之间都是通用的。熟悉了一种风格以后,你也能够很快地理解另一种风格。
  • 在生产项目中:
代码语言:txt复制
-  当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐采用选项式 API。
代码语言:txt复制
-  当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API   单文件组件。

在学习阶段,你不必只固守一种风格。在接下来的文档中我们会为你提供一系列两种风格的代码供你参考,你可以随时通过左上角的 API 风格偏好来做切换。

1.10、使用VS Code 集成开发工具(IDE)

1.10.1、下载并安装

VS code,全称Visual Studio Code,是Microsoft(微软)在2015年4月30日发布的,编写现代web和跨平台源代码编辑器。

官网:https://code.visualstudio.com/Download

根据自己电脑的系统和配置选择对应版本即可

1.10.2、安装插件

(1)、常用插件介绍

1、插件Vue 3 Snippets

作用:用于vue3的智能代码提示,语法高亮、智能感知、Emmet等。替代Vetur插件,Vetur在vue2时期比较流行。

常用命令:vueinit、v3等

2、插件Volar

 作用:语法高亮、智能感知、Emmet等

3、插件ESLint

 作用:检查代码是否符合规范

4、插件Prettier

  作用:代码格式化

5、其他一些常用插件

Auto Rename Tag 修改 html 标签,自动帮你完成尾部闭合标签的同步修改,和 webstorm 一样

Auto Close Tag 自动闭合HTML标签

Vscode-icons 让 vscode 资源目录加上图标

Path Intellisense 自动路径补全、默认不带这个功能

Vue-color vue 语法高亮主题

(2)、常用插件配置

1、每次保存的时候自动格式化

搜索 format On Save,勾选前面的复选框

2、每次保存的时候将代码按 eslint 格式进行修复

搜索 Code Actions On Save,点击下方圈中的地方

代码语言:javascript复制
"source.fixAll.eslint": true

 3、在函数名和后面的括号之间加个空格

搜索 Insert Space Before Function Parenthesis,并将前面的复选框勾上

 4、ESLint添加 vue 支持

搜索 validate,选择左边的 ESLint,点击后侧的圈中的地方

代码语言:javascript复制
"eslint.validate": [
      "html",
      "javascript",
      "javascriptreact",
      "vue"
    ]

 5、用单引号替代双引号

 搜索 Single Quote,勾选前面的复选框

6、代码结尾以分号结束

搜索 Semi,去掉前面的复选框

7、ESLint插件修改

 解决方法就是:给.eslintrc.js中的rule添加:    'prettier/prettier': 'off',(注意:该项会让前面2个设置失效)

1.11、在浏览器中直接使用Vue3.0

1.11.1、通过 CDN 使用 Vue

你可以借助 script 标签直接通过 CDN 来使用 Vue:

代码语言:javascript复制
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

这里我们使用了 unpkg,但你也可以使用任何提供 npm 包服务的 CDN,例如 jsdelivr 或 cdnjs。当然,你也可以下载此文件并自行提供服务。

通过 CDN 使用 Vue 时,不涉及“构建步骤”。这使得设置更加简单,并且可以用于增强静态的 HTML 或与后端框架集成。但是,你将无法使用单文件组件 (SFC) 语法。

上面的例子使用了_全局构建版本_的 Vue,该版本的所有顶层 API 都以属性的形式暴露在了全局的 Vue 对象上。这里有一个使用全局构建版本的例子:

代码语言:javascript复制
<!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>
    <div id="app">{{message}}</div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script>
      const { createApp } = Vue;
      createApp({
        data() {
          return {
            message: "Hello, Vue!",
          };
        },
      }).mount("#app");
    </script>
  </body>
</html>

运行结果:

1.11.2、通过 js文件使用 Vue

在浏览器端直接访问:https://unpkg.com/vue@3/dist/vue.global.js

新建一个vue3.js文件,将内容复制到文件中

 在代码中引用vue3.js文件

代码语言:javascript复制
<!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>
    <div id="app">{{message}}</div>
    <script src="js/vue3.js"></script>
    <script>
      const { createApp } = Vue;
      createApp({
        data() {
          return {
            message: "Hello, Vue!",
          };
        },
      }).mount("#app");
    </script>
  </body>
</html>

运行结果:

1.11.3、声明式渲染

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>vue3介绍</title>
  </head>

  <body>
    <div id="app1">{{message}}</div>
    <div id="app2">
      <span v-bind:title="message"> 把鼠标放到这里试试 </span>
    </div>
    <script src="js/vue3.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
      const { createApp } = Vue;

      //vue应用对象
      var app2 = createApp({
        data() {
          return {
            message: "Hello Vue3!",
          };
        },
      }).mount("#app1");

      //绑定属性
      var app2 = createApp({
        data() {
          return {
            message: "页面加载时间是:"   new Date().toLocaleString(),
          };
        },
      }).mount("#app2");
    </script>
  </body>
</html>

结果:

这里我们遇到了一点新东西。你看到的 v-bind 特性被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊特性。可能你已经猜到了,它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的 title 特性和 Vue 实例的 message 属性保持一致”。

如果你再次打开浏览器的 JavaScript 控制台,输入 app2.message = '新消息',就会再一次看到这个绑定了 title 特性的 HTML 已经进行了更新。

我们已经成功创建了第一个 Vue 应用!看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.message 的值,你将看到上例相应地更新。

 1.11.4、条件与循环

代码语言:javascript复制
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>vue3介绍</title>
  </head>

  <body>
    <div id="app3">
      <span v-if="isShow"> isShow为true时你可以看到我 </span>
    </div>
    <div id="app4">
      <span v-if="isShow">
        <table border="1" cellspacing="1" cellpadding="1" width="50%">
          <tr>
            <th>序号</th>
            <th>名称</th>
            <th>价格</th>
          </tr>
          <tr v-for="(obj,index) in fruits">
            <td>{{index 1}}</td>
            <td>{{obj.name}}</td>
            <td>{{obj.price}}</td>
          </tr>
        </table>
      </span>
    </div>
    <script src="./js/vue3.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
      //if指令
      const { createApp } = Vue;

      //vue应用对象
      var app3 = createApp({
        data() {
          return {
            isShow: true,
          };
        },
      }).mount("#app3");

      //循环指令
      var app4 = createApp({
        data() {
          return {
            isShow: true,
            fruits: [
              {
                name: "苹果",
                price: "6.8",
              },
              {
                name: "橙子",
                price: "3.5",
              },
              {
                name: "香蕉",
                price: "2.3",
              },
            ],
          };
        },
      }).mount("#app4");
    </script>
  </body>
</html>

结果:

这个例子演示了我们不仅可以把数据绑定到 DOM 文本或特性,还可以绑定到 DOM 结构。此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用 过渡效果。

1.11.5、事件、处理用户输入与计算

为了让用户和你的应用进行交互,我们可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法:

代码语言:javascript复制
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>vue3介绍</title>
  </head>

  <body>
    <div id="app5">
      <button v-on:click="showMe">{{message}}</button>
      <input v-model="message" />
    </div>

    <p id="app6">
      <button v-on:click="n1 =1"> </button><input v-model="n1" /> 
      <input v-model="n2" />= <input v-model="sum" />
    </p>

    <script src="./js/vue3.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
      //事件
      const { createApp } = Vue;

      var app5 = createApp({
        data() {
          return {
            message: "vue 事件",
          };
        },
        methods: {
          showMe: function () {
            this.message = this.message.split("").reverse().join("");
          },
        },
      }).mount("#app5");

      //计算
      var app6 = createApp({
        data() {
          return {
            n1: 0,
            n2: 0,
          };
        },
        computed: {
          sum: function () {
            return parseInt(this.n1)   parseInt(this.n2);
          },
        },
      }).mount("#app6");
    </script>
  </body>
</html>

结果:

注意在 showMe方法中,我们更新了应用的状态,但没有触碰 DOM——所有的 DOM 操作都由 Vue 来处理,你编写的代码只需要关注逻辑层面即可。

Vue 还提供了 v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定。

二、综合示例

通过一个综合示例来快速了解Vue3,会使用到模板、计算,表达式、组件等等,要求实现一个简单的购物车,运行时的效果如下:

 参考代码:

代码语言:javascript复制
<template>
    <h2>购物车</h2>
    <table class="mainTable">
        <tr>
            <th>序号</th>
            <th>名称</th>
            <th>价格</th>
            <th>数量</th>
            <th>小计</th>
            <th>操作</th>
        </tr>
        <tr v-for="(product,index) in products" :class="{bg:index%2==0}">
            <td>{{index 1}}</td>
            <td>{{product.title}}</td>
            <td>{{formatPrice(product.price)}}</td>
            <td>
              <button @click="add(index)"> </button>
              <input type="text" v-model="product.quantity" class="quantity" @keyup="product.quantity=(product.quantity>=0?product.quantity:1)"/>
              <button @click="minus(index)">-</button>
            </td>
            <td>{{formatPrice(product.price*product.quantity)}}</td>
            <td>
                <button type="button" @click="remove(index)">移除</button>
            </td>
        </tr>
        <tfoot>
          <td colspan="6" class="total">
            共计:{{formatPrice(totalPrice)}}
          </td>
        </tfoot>
    </table>
</template>
<script lang="ts">
// 从vue中导入对象
import { reactive, computed } from 'vue'
// 产品类型
type Goods={
    title:string,
    price:number,
    quantity:number
}
export default {
  name: 'ShoppingCart',
  setup () {
    // 产品
    const products = reactive<Goods[]>([{
      title: 'paint pot',
      quantity: 9,
      price: 3.95
    }, {
      title: 'polka dots',
      quantity: 17,
      price: 12.3
    }, {
      title: 'pebbles',
      quantity: 5,
      price: 6.71
    }, {
      title: 'Mi Note5',
      quantity: 8,
      price: 2985.6
    }, {
      title: 'iPhone XS',
      quantity: 10,
      price: 8906.72
    }])
    
    // 格式化数据
    const formatPrice = computed(() => {
      return function (value:number) {
        return '¥'   value.toFixed(2)
      }
    })

    // 减少购物车数量
    function minus (index:number) {
      const product = products[index]
      if (product.quantity > 0) {
        product.quantity -= 1
      } else {
        remove(index)
      }
    }

    // 增加购物车数量
    function add (index:number) {
      products[index].quantity  
    }

    // 移除购物车中的产品
    function remove (index :number) {
      if (window.confirm('你确定要移除吗?')) {
        products.splice(index, 1)
      }
    }

    const totalPrice = computed<number>(() => {
      let sum = 0
      products.forEach(item => { sum  = (item.price * item.quantity) })
      return sum
    })

    return { products, formatPrice, minus, add, remove, totalPrice }
  }
}
</script>
<style scoped>
    .mainTable{
        width: 100%;
        border: 2px solid #666;
        border-collapse: collapse;
    }
    .mainTable th,.mainTable td{
        border: 2px solid #666;
        border-collapse: collapse;
    }
    .quantity{
      width: 30px;
    }
    .total{
      color: red;
      text-align: right;
      font-size: 18px;
      font-weight: bold;
      padding: 5px;
    }
    .bg{
      background: blue;
      color:white;
    }
</style>

 思考:如果这里还要实现排序应该怎样做?

三、javascript数组

因为上面的示例中需要用到对象数组与排序,这里单独讲讲:

3.1、创建

var arrayObj = new Array();

var arrayObj = new Array(size);

var arrayObj = new Array([element0[, element1[, ..., elementN]]]);

示例:

代码语言:javascript复制
            var array11 = new Array();  //空数组
            var array12 = new Array(5);  //指定长度,可越界
            var array13 = new Array("a","b","c",1,2,3,true,false);  //定义并赋值
            var array14=[];  //空数组,语法糖
            var array15=[1,2,3,"x","y"];  //定义并赋值

3.2、访问与修改

var testGetArrValue=arrayObj1; 

arrayObj1= "值";

代码语言:javascript复制
            //4.2、访问与修改
            array12[8]="hello array12";  //赋值或修改
            console.log(array12[8]);   //取值
            //遍历
            for (var i = 0; i < array13.length; i  ) {
                console.log("arrayl3[" i "]=" array13[i]);
            }
            //枚举
            for(var i in array15){ 
                console.log(i "=" array15[i]);  //此处的i是下标
            }

结果:

3.3、添加元素

将一个或多个新元素添加到数组未尾,并返回数组新长度

arrayObj. push([item1 [item2 [. . . itemN ]]]);

将一个或多个新元素添加到数组开始,数组中的元素自动后移,返回数组新长度

arrayObj.unshift([item1 [item2 [. . . itemN ]]]);

将一个或多个新元素插入到数组的指定位置,插入位置的元素自动后移,返回被删除元素数组,deleteCount要删除的元素个数

arrayObj.splice(insertPos,deleteCount,[item1[, item2[, . . . ,itemN]]])

示例代码:

代码语言:javascript复制
            //4.3、添加元素
            var array31=[5,8];
            //添加到末尾
            array31.push(9);
            var len=array31.push(10,11);
            console.log("长度为:" len "——" array31);
            //添加到开始
            array31.unshift(4);
            var len=array31.unshift(1,2,3);
            console.log("长度为:" len "——" array31);
            //添加到中间
            var len=array31.splice(5,1,6,7);  //从第5位开始插入,删除第5位后的1个元素,返回被删除元素
            console.log("被删除:" len "——" array31);

运行结果:

3.4、删除

移除最后一个元素并返回该元素值

arrayObj.pop();

移除最前一个元素并返回该元素值,数组中元素自动前移

arrayObj.shift(); 

删除从指定位置deletePos开始的指定数量deleteCount的元素,数组形式返回所移除的元素

arrayObj.splice(deletePos,deleteCount); 

示例:

代码语言:javascript复制
            //4.4、删除
            var array41=[1,2,3,4,5,6,7,8];
            console.log("array41:" array41);
            //删除最后一个元素,并返回
            var e=array41.pop();
            console.log("被删除:" e "——" array41);
            //删除首部元素,并返回
            var e=array41.shift();
            console.log("被删除:" e "——" array41);
            //删除指定位置与个数
            var e=array41.splice(1,4);  //从索引1开始删除4个
            console.log("被删除:" e "——" array41);

结果:

3.5、截取和合并

以数组的形式返回数组的一部分,注意不包括 end 对应的元素,如果省略 end 将复制 start 之后的所有元素

arrayObj.slice(start, end); 

将多个数组(也可以是字符串,或者是数组和字符串的混合)连接为一个数组,返回连接好的新的数组

arrayObj.concat([item1[, item2[, . . . ,itemN]]]); 

示例: 

代码语言:javascript复制
            //4.5、截取和合并
            var array51=[1,2,3,4,5,6];
            var array52=[7,8,9,0,"a","b","c"];
            //截取,切片
            var array53=array51.slice(2);  //从第3个元素开始截取到最后
            console.log("被截取:" array53 "——" array51);
            var array54=array51.slice(1,4);  //从第3个元素开始截取到索引号为3的元素
            console.log("被截取:" array54 "——" array51);
            //合并
            var array55=array51.concat(array52,["d","e"],"f","g");
            console.log("合并后:" array55);

结果:

3.6、拷贝

返回数组的拷贝数组,注意是一个新的数组,不是指向

arrayObj.slice(0); 

返回数组的拷贝数组,注意是一个新的数组,不是指向

arrayObj.concat(); 

因为数组是引用数据类型,直接赋值并没有达到真正实现拷贝,地址引用,我们需要的是深拷贝。

3.7、排序

反转元素(最前的排到最后、最后的排到最前),返回数组地址

arrayObj.reverse(); 

对数组元素排序,返回数组地址

arrayObj.sort(); 

arrayObj.sort(function(obj1,obj2){}); 

示例:

代码语言:javascript复制
            var array71=[4,5,6,1,2,3];
            array71.sort();
            console.log("排序后:" array71);
            var array72=[{name:"tom",age:19},{name:"jack",age:20},{name:"lucy",age:18}];
            array72.sort(function(user1,user2){
                return user1.age<user2.age;
            });
            console.log("排序后:");
            for(var i in array72) console.log(array72[i].name "," array72[i].age);

结果:

3.8、合并成字符

返回字符串,这个字符串将数组的每一个元素值连接在一起,中间用 separator 隔开。

arrayObj.join(separator); 

示例代码:

代码语言:javascript复制
            //4.8、合并成字符与将字符拆分成数组
            var array81=[1,3,5,7,9];
            var ids=array81.join(",");
            console.log(ids);
            
            //拆分成数组
            var text="hello nodejs and angular";
            var array82=text.split(" ");
            console.log(array82);

运行结果:

所有代码:

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>数组操作</title>
    </head>

    <body>
        <script type="text/javascript">
            //4.1、创建
            var array11 = new Array();  //空数组
            var array12 = new Array(5);  //指定长度,可越界
            var array13 = new Array("a","b","c",1,2,3,true,false);  //定义并赋值
            var array14=[];  //空数组,语法糖
            var array15=[1,2,3,"x","y"];  //定义并赋值
            
            //4.2、访问与修改
            array12[8]="hello array12";  //赋值或修改
            console.log(array12[8]);   //取值
            //遍历
            for (var i = 0; i < array13.length; i  ) {
                //console.log("arrayl3[" i "]=" array13[i]);
            }
            //枚举
            for(var i in array15){ 
                //console.log(i "=" array15[i]);  //此处的i是下标
            }
            
            //4.3、添加元素
            var array31=[5,8];
            //添加到末尾
            array31.push(9);
            var len=array31.push(10,11);
            console.log("长度为:" len "——" array31);
            //添加到开始
            array31.unshift(4);
            var len=array31.unshift(1,2,3);
            console.log("长度为:" len "——" array31);
            //添加到中间
            var len=array31.splice(5,1,6,7);  //从第5位开始插入,删除第5位后的1个元素,返回被删除元素
            console.log("被删除:" len "——" array31);
            
            //4.4、删除
            var array41=[1,2,3,4,5,6,7,8];
            console.log("array41:" array41);
            //删除最后一个元素,并返回
            var e=array41.pop();
            console.log("被删除:" e "——" array41);
            //删除首部元素,并返回
            var e=array41.shift();
            console.log("被删除:" e "——" array41);
            //删除指定位置与个数
            var e=array41.splice(1,4);  //从索引1开始删除4个
            console.log("被删除:" e "——" array41);
            
            //4.5、截取和合并
            var array51=[1,2,3,4,5,6];
            var array52=[7,8,9,0,"a","b","c"];
            //截取,切片
            var array53=array51.slice(2);  //从第3个元素开始截取到最后
            console.log("被截取:" array53 "——" array51);
            var array54=array51.slice(1,4);  //从第3个元素开始截取到索引号为3的元素
            console.log("被截取:" array54 "——" array51);
            //合并
            var array55=array51.concat(array52,["d","e"],"f","g");
            console.log("合并后:" array55);
            
            //4.7、排序
            var array71=[4,5,6,1,2,3];
            array71.sort();
            console.log("排序后:" array71);
            var array72=[{name:"tom",age:19},{name:"jack",age:20},{name:"lucy",age:18}];
            array72.sort(function(user1,user2){
                return user1.age<user2.age;
            });
            console.log("排序后:");
            for(var i in array72) console.log(array72[i].name "," array72[i].age);
            
            //4.8、合并成字符与将字符拆分成数组
            var array81=[1,3,5,7,9];
            var ids=array81.join(",");
            console.log(ids);
            
            //拆分成数组
            var text="hello nodejs and angular";
            var array82=text.split(" ");
            console.log(array82);
            
        </script>
    </body>

</html>

四、JavaScript排序

4.1、概要

javascript内置的sort函数是多种排序算法的集合,数组在原数组上进行排序,不生成副本。

JavaScript实现多维数组、对象数组排序,其实用的就是原生的sort()方法,用于对数组的元素进行排序。

sort() 方法用于对数组的元素进行排序。语法如下:

代码语言:javascript复制
ArrayObject.sort(order); 

返回值为对数组的引用

4.2、简单排序

如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>排序</title>
    </head>
    <body>
        <script>
            var numbers=[2,4,6,8,0,1,2,3,7,9];
            numbers.sort();
            console.log(numbers.join(','));  //默认按升序排列
            numbers.reverse();  //反转
            console.log(numbers.join(','));   //将元素用逗号连接成一个字符串
        </script>
    </body>
</html>

结果:

4.3、简单数组自定义排序

如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:

代码语言:javascript复制
/*
默认升序: 
a>b 返回1
a=b 返回0
a<b 返回-1
*/

解释:a>b 升序,a<b降序。

代码语言:javascript复制
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>排序</title>
    </head>
    <body>
        <script>
            var numbers=[2,4,6,8,0,1,2,3,7,9];
            //当a>b的结果为正数时则为升序
            numbers.sort(function(a,b){
                if(a>b){return 1;}
                if(a<b){return -1;}
                return 0;
            });
            console.log(numbers.join(','));
            //简化,注意类型
            numbers.sort(function(a,b){
                return a-b;
            });
            console.log(numbers.join(','));
            
            //降序
            numbers.sort(function(a,b){
                return b-a;
            });
            console.log(numbers.join(','));
        </script>
    </body>
</html>

结果:

4.4、简单对象List自定义属性排序

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>排序</title>
    </head>

    <body>
        <script>
            //对象数组
            var pdts = [{
                title: "z-paint pot",
                quantity: 9,
                price: 3.95
            },{
                title: "iPhone XS",
                quantity: 10,
                price: 8906.72
            },{
                title: "polka dots",
                quantity: 17,
                price: 12.3
            }, {
                title: "pebbles",
                quantity: 5,
                price: 6.71
            }, {
                title: "Mi Note5",
                quantity: 8,
                price: 2985.6
            }];
            
            //按价格升序
            pdts.sort(function(x,y){
                return x.price-y.price;
            });
            document.write(JSON.stringify(pdts));
            
            document.write("<br/>");
            //按名称排序
            pdts.sort(function(x,y){
                if(x.title>y.title) return 1;
                if(x.title<y.title) return -1;
                return 0;
            });
            document.write(JSON.stringify(pdts));
        </script>
    </body>

</html>

结果:

4.5、封装通用的排序函数

如果排序的条件要不断变化,将反复写简单的排序函数,封装可以带来方便:

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>排序</title>
    </head>

    <body>
        <script>
            //对象数组
            var pdts = [{
                title: "z-paint pot",
                quantity: 9,
                price: 3.95
            }, {
                title: "iPhone XS",
                quantity: 10,
                price: 8906.72
            }, {
                title: "polka dots",
                quantity: 17,
                price: 12.3
            }, {
                title: "pebbles",
                quantity: 5,
                price: 6.71
            }, {
                title: "Mi Note5",
                quantity: 8,
                price: 2985.6
            }];
            
            //根据排序关键字与是否为升序产生排序方法
            var sortExp = function(key, isAsc) {
                return function(x, y) {
                    if(isNaN(x[key])) { //如果当前排序的不是数字
                        if(x[key] > y[key]) return 1*(isAsc?1:-1);
                        if(x[key] < y[key]) return -1*(isAsc?1:-1);
                        return 0;
                    }else{
                        return (x[key]-y[key])*(isAsc?1:-1);
                    }
                }
            };

            //按价格升序
            pdts.sort(sortExp("price",true));
            document.write(JSON.stringify(pdts));
            document.write("<br/>------------------------------<br/>");
            pdts.sort(sortExp("price",false));
            document.write(JSON.stringify(pdts));
            document.write("<br/>------------------------------<br/>");
            pdts.sort(sortExp("title",true));
            document.write(JSON.stringify(pdts));
            document.write("<br/>------------------------------<br/>");
            pdts.sort(sortExp("title",false));
            document.write(JSON.stringify(pdts));
        </script>
    </body>

</html>

结果:

五、Vue Devtools

当使用 Vue 时,我们推荐同时在你的浏览器上安装 Vue Devtools,它允许你在一个更加友善的界面中审查和调试你的 Vue 应用。

Vue Devtools是一个内嵌在Google Chrome浏览器中的插件,用于更好的调试Vue应用程序

源码:https://github.com/vuejs/vue-devtools#vue-devtools

https://github.com/vuejs/devtools/releases/download/v6.1.3/devtools-chrome.zip

安装方法:

下载后解压,在chrome地址栏中输入:chrome://extensions/

直接将crx文件插入浏览器,确认就安装成功了。

插件会自动检测当前是否使用了vue,如果使用了图标会亮起,在开发者工具中有一个调试界面:

六、作业

a)、请实现一个购物车

  • 1、双向绑定集合中的数据
  • 2、执行移除事件
  • 3、隔行换色、使用vue2完成
  • 4、点击数量与小计时进行排序(选作)
  • 5、实现实时统计功能
  • 6、实现添加与编辑功能
  • 7、实现排序功能

b)、购物车升级

  • 1、将数据持久化到数据库中,数据库类型不限
  • 2、新增加(增加、删除、查询、更新、排序)功能
  • 3、使用组件
  • 4、使用AJAX,可以使用jQuery,也可以使用axios

c)、将作业a使用angular实现

d)、将作业a使用react实现

e)、将作业b使用angular实现

f)、将作业b使用react实现

七、示例下载

https://git.coding.net/zhangguo5/vue2.git

八、视频

【Vue3 Vuex Pinia TypeScript Router】 https://www.bilibili.com/video/BV1at4y1F75D?share_source=copy_web&vd_source=475a31f3c5d6353a782007cd4c638a8a

0 人点赞