菜鸟教程 --- Vue3 教程

runoob vue3 教程:https://www.runoob.com/vue3/vue3-install.html
VUE 超级详细教程https://blog.csdn.net/weixin_42371679/article/details/112408800
vue 爱好者:https://vue3js.cn/
vue 官网、文档、指南、教程:https://staging-cn.vuejs.org/
IDEA版 (vue前后端分离项目):https://code100.blog.csdn.net/article/details/118926442
在线工具 ( RUNOOB.com 在线工具 ):https://www.runoob.com/

4个小时带你快速入门vue:https://www.bilibili.com/video/BV12J411m7MG

  • Vue 基础
  • 本地应用 --- 记事本
  • 网络应用 --- 查询天气(网络请求库axios + vue) ( axios介绍和使用:https://blog.csdn.net/qq_40837310/article/details/123028044 )
  • 综合应用 --- 播放器

黑马程序员Vue全套视频教程,从vue2.0到vue3.0一套全覆盖,前端必会的框架教程

https://search.bilibili.com/all?keyword=Vue+3+%E5%85%A8%E5%AE%B6%E6%A1%B6

https://www.bilibili.com/video/BV1zq4y1p7ga

vue 源码阅读解析:https://zhuanlan.zhihu.com/p/419896443
深入理解vue底层原理:https://blog.csdn.net/dream2222222222/article/details/103256281
Vue 源码解读 ( 系列):https://juejin.cn/user/1028798616461326/posts
视频:https://search.bilibili.com/all?keyword=Vue.js源码全方位深入解析

1、Vue 简介、安装、创建项目

Vue 简介

Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的渐进式框架。
Vue 只关注视图层, 采用自底向上增量开发的设计。
Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
Vue 学习起来非常简单。

提示:学习 vue 时,默认你已经具有 HTML、CSS 和 JavaScript 的中级知识。

Vue 特点 (响应的 数据绑定)

Vue 是一个前端框架,具有如下特点:

  • 响应的 数据绑定 :数据发生变化时,可以立即感知到。就是 实时(双向)更新数据。比如你改变一个输入框 Input 标签的值,会自动同步更新到页面上其他绑定该输入框的组件的值。
  • 组件化:页面上小到一个按钮都可以是一个单独的文件.vue,这些小组件直接可以像乐高积木一样通过互相引用而组装起来

基于 vue 的 web 开发

  • 早期 web 开发是基于DOM的,通过 JavaScript 获取网页 DOM 元素,然后操作 DOM 元素
  • 基于 vue 的 web 开发:vue 本身是使用 JavaScript 编写,只是封装了一些以 v- 开头的一系列指令,可以通过这些指令直接操作 DOM 元素,不用再通过 JavaScript 直接操作
    vue 作用就是把 Javascript 对象数据绑定到 HTML 的 dom 节点上。
    vue 开发特点:页面由数据生成 ( 根据数据生成页面),数据改变时,页面会同步改变。

响应的 数据绑定

响应式就是直接在控制台上对数组中的元素进行增删改查时,页面中的展示会跟着变化。

Vue.js 的目标:通过尽可能简单的 API 实现 响应的数据绑定 和 组合的视图组件

MVVM 模式

  • M:即Model,模型,包括数据和一些基本操作
  • V:即View,视图,页面渲染结果
  • VM:即 View-Model,模型与视图间的双向操作(无需开发人员干涉)

不使用 MVVM 和 使用 MVVM 的区别

  • 不使用 MVVM 时,开发人员从后端获取需要的数据模型,然后要通过 DOM 操作 Model 并渲染到View中。而后当用户操作视图,还需要通过 DOM 获取 View 中的数据,然后同步到 Model 中。
  • 使用 MVVM 后,MVVM 中的 VM 要做的事情就是把 DOM 操作完全封装起来,开发人员不用再关心 Model 和 View 之间是如何互相影响的。只要 Model 发生了改变,View上自然就会表现出来。当用户修改了 View,Model 中的数据也会跟着改变。这样就把开发人员从繁琐的DOM 操作中解放出来,把关注点放在如何操作 Model 上。

示例:单文件

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 </title>
<script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
  {{ message }}
</div>

<script>
const HelloVueApp = {
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
}

Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>

JavaScript 高阶函数

filter:对数组中的数据进行过滤,满足条件的则放到新数组中

const nums=[20,40,80,100,111,212,212]
//filter
const newNum1=nums.filter(function (n) {
    return n<100
})

map:对数组中的数据进行处理后放到新数组里面

const nums=[20,40,80,100,111,212,212]
//2、map
const newNum2=newNum1.map(function (n) {
    return n*2
})

reduce:对数组中的数据进行累加

const nums=[20,40,80,100,111,212,212]
//3、reduce
const newNum3=nums.reduce(function (previousValue,n) {
   return previousValue+n
},0)  //初始值是0

箭头函数

  • 两个参数 写法:
    const sum=(num1,num2)=>{ return num1+num2; }
    console.log(sum(2,3));
  • 一个参数 写法:const power2 = num => { num*num;}
  • 没有参数 写法:const demo = ()=> {console.log("没有参数");}

this 指向的问题。在箭头函数中,this是向外层作用域中,一层一层向外找,直到有this

const obj={
    aaa(){
        setTimeout(function () {
            console.log(this);
        },1000);

        setTimeout(()=>console.log(this),1000);
    }
};
obj.aaa();
 const obj={
   aaa(){
       setTimeout(function () {
           setTimeout(function () {
               console.log(this);
           });
           setTimeout(()=>{
               console.log(this);
           });
       });
       setTimeout(()=>{
            setTimeout(function () {
                console.log(this);
            });
            setTimeout(()=>{
                console.log(this);
            });
       });
   }
};
obj.aaa();

导入 vue 框架 

快速开始:https://cn.vuejs.org/guide/quick-start.html

方法 1:CDN 包的形式导入。示例:<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>  。通过 CDN 使用 Vue 时,不涉及“构建步骤”。这使得设置更加简单,并且可以用于增强静态的 HTML 或与后端框架集成。但是,你将无法使用单文件组件 (SFC) 语法。

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app">{{ message }}</div>

<script>
  const { createApp } = Vue
  
  createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

方法 2:在Vue.js官网下载最新版本,然后使用 <script> 标签引入。与 CDN 方法类似。

方法 3:使用 ES 模块构建版本。现代浏览器大多都已原生支持 ES 模块。因此可以像下面一样通过 CDN 以及原生 ES 模块使用 Vue:

<div id="app">{{ message }}</div>

<script type="module">
  import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
  
  createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

方法 4:使用导入映射表 (Import Maps) 来告诉浏览器如何定位到导入的 vue

<script type="importmap">
  {
    "imports": {
      "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
    }
  }
</script>

<div id="app">{{ message }}</div>

<script type="module">
  import { createApp } from 'vue'

  createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

方法 5:拆分模块。可能需要将代码分割成单独的 JavaScript 文件,以便更容易管理。例如:

<!-- index.html -->
<div id="app"></div>

<script type="module">
  import { createApp } from 'vue'
  import MyComponent from './my-component.js'

  createApp(MyComponent).mount('#app')
</script>
// my-component.js
export default {
  data() {
    return { count: 0 }
  },
  template: `<div>Count is: {{ count }}</div>`
}

如果直接在浏览器中打开了上面的 index.html,你会发现它抛出了一个错误,因为 ES 模块不能通过 file:// 协议工作,也即是当你打开一个本地文件时浏览器使用的协议。

由于安全原因,ES 模块只能通过 http:// 协议工作,也即是浏览器在打开网页时使用的协议。为了使 ES 模块在我们的本地机器上工作,我们需要使用本地的 HTTP 服务器,通过 http:// 协议来提供 index.html

要启动一个本地的 HTTP 服务器,请先安装 Node.js,然后通过命令行在 HTML 文件所在文件夹下运行 npx serve。你也可以使用其他任何可以基于正确的 MIME 类型服务静态文件的 HTTP 服务器。

可能你也注意到了,这里导入的组件模板是内联的 JavaScript 字符串。如果你正在使用 VS Code,你可以安装 es6-string-html 扩展,然后在字符串前加上一个前缀注释 /*html*/ 以高亮语法。

Vue3 创建项目

项目脚手架:https://cn.vuejs.org/guide/scaling-up/tooling.html

安装 cnpm

由于国内 npm 安装速度慢,可以使用淘宝的镜像及其命令 cnpm,安装使用介绍参照:使用淘宝 NPM 镜像
        npm -v                 # 查看版本
        cnpm install npm -g    # 升级 npm
        npm install cnpm -g    # 升级或安装 cnpm
npm、cnpm 能很好地和诸如 webpack 或 Rollup 模块打包器配合使用。

方法 1: npm init vue@latest

执行命令:npm init vue@latest    # 安装最新稳定版。同时将会安装并执行 create-vue,它是 Vue 官方的脚手架工具。如果不确定是否要开启某个功能,你可以直接按下回车键选择 No。在项目被创建后,通过以下步骤安装依赖并启动开发服务器:
$ cd runoob-vue3-test
$ npm install
$ npm run dev

直接访问:http://localhost:5173/ 显示如下。在使用 Vue 时,推荐安装浏览器插件 Vue Devtools,它允许你在一个更友好的界面中审查和调试 Vue 应用。

方法 2 (推荐):npm create vue@latest

命令执行成功后,会安装和执行 create-vue,它是 Vue 提供的官方脚手架工具。跟随命令行的提示继续操作即可。

  • 要学习更多关于 Vite 的知识,请查看 Vite 官方文档
  • 若要了解如何为一个 Vite 项目配置 Vue 相关的特殊行为,比如向 Vue 编译器传递相关选项,请查看 @vitejs/plugin-vue 的文档。

目录结构

目录说明

方法 3 (推荐):npm init vite

Vite 是一个轻量级的、速度极快的构建工具,可以快速构建 Vue 项目。对 Vue SFC 提供第一优先级支持。老方法:npm init @vitejs/app <project-name>    ( 注意:@vitejs/create-app 已经废弃, 使用 npm init vite 代替 )

创建 vue 项目,依次执行下面命令

npm init vite <project-name>
cd vite-project
npm install
npm run dev

打开 http://localhost:3000/,显示如下:

方法 4:Vue CLI

Vue CLI 是官方提供的基于 Webpack 的 Vue 工具链,是一个命令行工具,可用于快速搭建大型单页应用。它现在处于维护模式。建议使用 Vite 开始新的项目,除非你依赖特定的 Webpack 的特性。在大多数情况下,Vite 将提供更优秀的开发体验。

关于从 Vue CLI 迁移到 Vite 的资源:

全局安装:npm install -g @vue/cli 或 cnpm install -g @vue/cli 或 yarn global add @vue/cli

安装完后查看版本:vue --version
然后在 Vue 项目中运行:vue upgrade --next

创建一个项目:vue create my-project 或者 vue ui

执行 vue ui 命令成功后,在浏览器中会打开一个图形化界面,可以用来创建和管理项目:

方法 5 (推荐):使用 IDE 创建

推荐的 IDE 配置是 Visual Studio Code + Vue - Official 扩展。如果使用其他编辑器,参考 IDE 支持章节

Vue3 项目打包

当准备将应用发布到生产环境时,就需要打包 Vue 项目。

打包 Vue 项目使用以下命令:npm run build

执行完成后,会在 Vue 项目下会生成一个 dist 目录,该目录一般包含 index.html 文件及 static 目录,static 目录包含了静态文件 js、css 以及图片目录 images(如果有图片的话)。

如果直接双击打开 index.html,在浏览器中页面可能是空白了,要正常显示则需要修改下 index.html 文件中 js、css 文件路径。

例如我们打开 dist/index.html 文件看到 css 和 js 文件路径是绝对路径:

<link href=/static/css/app.33da80d69744798940b135da93bc7b98.css rel=stylesheet>
<script type=text/javascript src=/static/js/app.717bb358ddc19e181140.js></script>
...

我们把 js、css 文件路径修改为相对路径:

<link href=static/css/app.33da80d69744798940b135da93bc7b98.css rel=stylesheet>
<script type=text/javascript src=static/js/app.717bb358ddc19e181140.js></script>
...

这样直接双击 dist/index.html 文件就可以在浏览器中看到效果了。


 

2、vue2 教程、vue3 教程

vue2 教程

Vue.js 2.0 教程:https://www.w3cschool.cn/vuejs2/

Vue.js 基础教程

  • Vue.js 2.0 安装
  • Vue.js 2.0 介绍
  • Vue.js 2.0 实例
  • Vue.js 2.0 模板语法
  • Vue.js 2.0 计算属性
  • Vue.js 2.0 Class 与 Style 绑定
  • Vue.js 2.0 条件渲染
  • Vue.js 2.0 列表渲染
  • Vue.js 2.0 事件处理器
  • Vue.js 2.0 表单控件绑定
  • Vue.js 2.0 组件

Vue.js 2.0 进阶教程

  • Vue.js 2.0 深入响应式原理
  • Vue.js 2.0 过渡效果
  • Vue.js 2.0 过渡状态
  • Vue.js 2.0 Render 函数
  • Vue.js 2.0 自定义指令
  • Vue.js 2.0 混合
  • Vue.js 2.0 插件
  • Vue.js 2.0 单文件组件
  • Vue.js 2.0 生产环境部署
  • Vue.js 2.0 路由
  • Vue.js 2.0 状态管理
  • Vue.js 2.0 单元测试
  • Vue.js 2.0 服务端渲染
  • Vue.js 2.0 对比其他框架

vue3 教程

VUE3 教程:https://www.w3cschool.cn/vuejs3/

Vue.js 3.0 教程基础

  • Vue 3.0 安装
  • Vue 3.0 介绍
  • Vue 3.0 应用&组件实例
  • Vue 3.0 模板语法
  • Vue 3.0 Data Property和方法
  • Vue 3.0 计算属性和侦听器
  • Vue 3.0 Class与Style绑定
  • Vue 3.0 条件渲染
  • Vue 3.0 列表渲染
  • Vue 3.0 事件处理
  • Vue 3.0 表单输入绑定
  • Vue 3.0 组件基础

Vue.js 3.0 深入组件

  • Vue 3.0 组件注册
  • Vue 3.0 Props
  • Vue 3.0 非Prop的Attribute
  • Vue 3.0 自定义事件
  • Vue 3.0 插槽
  • Vue 3.0 提供/注入
  • Vue 3.0 动态组件&异步组件
  • Vue 3.0 模板引用
  • Vue 3.0 处理边界情况

Vue.js 3.0 过渡&动画

  • Vue 3.0 过渡&动画概述
  • Vue 3.0 进入过渡&离开过渡
  • Vue 3.0 列表过渡
  • Vue 3.0 状态过渡

Vue.js 3.0 可复用&组合

  • Vue 3.0 混入
  • Vue 3.0 自定义指令
  • Vue 3.0 Teleport
  • Vue 3.0 渲染函数
  • Vue 3.0 插件
  • Vue3 组合式函数

Vue.js 3.0 高阶指南

  • Vue 3.0 响应性 深入响应性原理
  • Vue 3.0 响应性 基础
  • Vue 3.0 响应式 计算和侦听
  • Vue 3.0 组合式API 介绍
  • Vue 3.0 组合式API Setup
  • Vue 3.0 组合式API 生命周期钩子
  • Vue 3.0 组合式API 提供/注入
  • Vue 3.0 组合式API 模板引用
  • Vue 3.0 渲染机制和优化
  • Vue2中的更改检测警告

Vue.js 3.0 工具

  • Vue 3.0 单文件组件
  • Vue 3.0 测试
  • Vue 3.0 TypeScript支持
  • Vue 3.0 Mobile

Vue.js 3.0 规模化

  • Vue 3.0 路由
  • Vue 3.0 状态管理
  • Vue 3.0 服务端渲染

Vue.js 3.0 无障碍

  • Vue 3.0 基础
  • Vue 3.0 语义学
  • Vue 3.0 标准
  • Vue 3.0 资源

Vue.js 3.0 从Vue2迁移

  • Vue 3.0 介绍
  • Vue 3.0 v-for中的Ref数组
  • Vue 3.0 异步组件
  • Vue 3.0 attribute强制行为
  • Vue 3.0 自定义指令
  • Vue 3.0 自定义元素交互
  • Vue 3.0 Data选项
  • Vue 3.0 事件API
  • Vue 3.0 过滤器
  • Vue 3.0 片段
  • Vue 3.0 函数式组件
  • Vue 3.0 全局API
  • Vue 3.0 全局API Treeshaking
  • Vue 3.0 内联模板 Attribute
  • Vue 3.0 key attribute
  • Vue 3.0 按键修饰符
  • Vue 3.0 在prop的默认函数中访问this
  • Vue 3.0 渲染函数API
  • Vue 3.0 Slot统一
  • Vue 3.0 过渡的class名更改
  • Vue 3.0 v-model
  • Vue 3.0 v-if与v-for的优先级对比
  • Vue 3.0 v-bind合并行为

Vue.js 3.0 贡献文档

  • Vue 3.0文档编写指南
  • Vue 3.0文档风格指南
  • Vue 3.0 翻译

Vue.js 3.0 API参考

  • Vue 3.0 应用配置
  • Vue 3.0 应用API
  • Vue 3.0 全局API
  • Vue 3.0 选项 Data
  • Vue 3.0 选项 DOM
  • Vue 3.0 选项 生命周期钩子
  • Vue 3.0 选项/资源
  • Vue 3.0 选项 组合
  • Vue 3.0 选项 杂项
  • Vue 3.0 实例property
  • Vue 3.0 实例方法
  • Vue 3.0 指令
  • Vue 3.0 特殊指令
  • Vue 3.0 内置组件
  • Vue 3.0 响应性基础 API
  • Vue 3.0 响应性API Refs
  • Vue 3.0 响应性API Computed与watch
  • Vue 3.0 组合式API

Vue.js 3.0 风格指南

  • Vue 3.0 风格指南

vue2、vue3 区别

盘点 Vue3 与 Vue2 的区别:https://juejin.cn/post/7067413380922867725

Vue 2.0 和 Vue 3.0 的区别:https://developer.aliyun.com/article/891519

创建方式 不同

  • vue2 通过构造函数 Vue 创建一个 Vue 的根实例 启动
  • vue3 通过 Vue.createApp 创建一个 Vue 的根实例 启动

声明周期 不同

生命周期方面(大多生命周期前加on)

3、vue3 使用教程

学 vue,主要从以下几个方面学习:

  • 模板语法(怎么写)
  • 指令
  • 选项、生命周期(写在哪儿)
  • vuejs-devtools(怎么调试)

相关 JavaScript 知识

在 JavaScript 中 " 对象、数组" 是引用类型,指向同一个内存空间。

vue 本身是使用 JavaScript 编写,只是封装了一些以 v- 开头的一系列指令,可以通过这些指令直接操作 DOM 元素,不用再通过 JavaScript 直接操作。Vue.js 的核心是使用简洁的模板语法来声明式的将数据渲染进 DOM 的系统

ES5 中,对象的书写形式

<script>
    const name = 'hello'
    const age = 20
    const height = 1.80
    const person = {
        name: name,
        age: age,
        heigth: height
    }
    console.log(person)
</script>

ES6 中,对象的书写形式

<script>
    const name = 'hello'
    const age = 20
    const height = 1.80
    const person = {
        name,
        age,
        height
    }
    console.log(person)
</script>

ES5 中,对象中方法的写法:

const demo={
    run:function () {
        // 
    }
}

ES6 中,对象中方法的写法:

const demo={
   run(){
        //
   }
}

Vue3 基础 语法

创建 Vue 实例是开始使用 Vue.js 的第一步,通常你是在一个 HTML 文件中创建一个 Vue 实例并将其挂载到一个 DOM 元素上。

创建 html 文件并执行vue

Vue3 中应用是通过 createApp 函数来创建。语法格式:const app = Vue.createApp({ /* 选项 */ })

注意:被挂载的 div 要放在 vue 的上面,否则 vue 管不到

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
    {{ message }}
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                message: 'Hello Vue!!'
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>

HTML 部分说明:

  • <div id="hello-vue" class="demo">:这是一个 <div> 元素,它具有 id 为 hello-vue 和 class 为 demo。在 Vue 应用中,这个 <div> 将会被 Vue 实例管理,并且会在数据发生变化时更新内容。
  • {{ message }}:这是 Vue.js 的模板语法,用于将 Vue 实例中的 message 数据绑定到页面上。当 Vue 实例中的 message 数据变化时,页面上的内容也会随之更新。

Vue 实例定义:

const HelloVueApp = {
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
}
  • HelloVueApp 是一个普通的 JavaScript 对象,包含了 Vue 组件选项。
  • data() 方法返回一个包含 message 属性的对象,这个属性的初始值是 'Hello Vue!!'。

创建并挂载 Vue 应用:

  • Vue.createApp() 方法用于创建一个 Vue 应用实例,参数是一个包含组件选项的对象(这里是 HelloVueApp)。
  • .mount('#hello-vue') 方法将 Vue 应用实例挂载到页面中具有 id="hello-vue" 的 DOM 元素上。

执行过程

  • 页面加载时,浏览器解析 HTML 和 JavaScript。
  • Vue.js 脚本执行时,创建了一个 Vue 应用实例,并将其绑定到 <div id="hello-vue"> 元素上。
  • Vue 应用实例根据 data() 中的初始数据,将 message 的值渲染到页面上的 {{ message }} 处。
  • 当 message 数据发生变化时(例如通过用户交互或异步操作),页面会自动更新以反映这些变化。

示例:Hello Vue

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 </title>
<script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
  {{ message }}
</div>

<script>
const HelloVueApp = {
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
}

Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>

说明:上面代码使用 mount('#hello-vue') 将 Vue 的 HelloVueApp(根组件) 挂载到 dom 节点 <div id="hello-vue"></div> 中。

  • createApp 的参数是 根组件(这里是 HelloVueApp)。在挂载应用时,该组件是渲染的起点。
  • createApp 创建一个应用。
  • 使用 mount() 挂载应用到 dom 中 (应用必须挂载到一个 DOM 元素中)。
  • {{ }} 用于输出对象属性和函数返回值。{{ message }} 对应 应用 中message的值。

简单示例:一个计数器(v-on:click)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 </title>
    <script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
    <div>{{ message }}</div>

    <div>
        <h2>当前计数: {{ count }}</h2>
        <button v-on:click="count++">+</button>
        <button v-on:click="count--">-</button>
    </div>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                count: 0,
                message: 'Hello Vue!!'
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>

采用函数(如果代码太多的话)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 </title>
    <script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
    <div>{{ message }}</div>

    <div>
        <h2>当前计数: {{ count }}</h2>
        <button v-on:click="add">+</button>
        <button v-on:click="mid">-</button>
    </div>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                count: 0,
                message: 'Hello Vue!!'
            }
        },
        methods: {
            add: function () {
                this.count++;
            },
            mid: function () {
                this.count--;
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>

声明式 渲染

  • 1. 首先 声明 dom 节点
  • 2. 然后 再把数据渲染到 dom 节点。就是vue内部用JavaScript 把数据渲染同步到dom节点

"单向 、双向" 数据同步

  • 单向 数据同步:dom 节点中可以使用 vue 的 指令来实时同步 JavaScript 的数据。这个是单向数据同步 。如果需要双向数据同步,即从 dom 同步数据到 JavaScript 中,可以使用 Vue 的 v-model 指令。v-model是做双向数据绑定的。
  • 双向 数据同步:又叫 双向绑定,就是对属性值进行改变时,页面中展示的值跟着改变,对页面展示的值进行改变时,属性值也跟着改变。也就是:数据变化更新视图,视图变化更新数据。v-model 双向数据绑定原理:v-model 其实就是一个语法糖,它的背后本质上是包含两个操作:v-bind 绑定一个value属性; v-on 指令给当前元素绑定input事件

刚开始学习 Vue 时,推荐直接在页面引入 vue.global.js 文件来测试学习。Vue 鼓励开发者沿着数据驱动的思路,尽量避免直接接触并操作 DOM

选项式、组合式

在 Vue 中,选项式 API 和组合式 API 是两种不同的编写组件逻辑的方式。

  • 选项式:这是 Vue 2 中主要使用的方式,在 Vue 3 中仍然支持。它将组件的不同方面(数据、方法、计算属性、监听等)分别定义在不同的选项中。
export default {
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  watch: {
    count(newValue, oldValue) {
      // 监听 count 的变化
    }
  }
};
  • 组合式 API:这是 Vue 3 引入的新特性,它使用 setup 函数来集中组织组件的逻辑。
import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);

    const increment = () => {
      count.value++;
    };

    const doubleCount = computed(() => count.value * 2);

    return {
      count,
      increment,
      doubleCount
    };
  }
};

插值:文本、html、属性、表达式

文本:数据绑定最常见的形式就是使用 {{...}}(双大括号)的文本插值:<div>{{ message }}</div>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
    {{ message }}
</div>
 
<script>
    const HelloVueApp = {
        data() {
            return {
                message: 'Hello Vue!!'
            }
        }
    }
 
    Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>

{{...}} 标签的内容将会被替代为对应组件实例中 message 属性的值,如果 message 属性的值发生了改变,{{...}} 标签内容也会更新。如果不想改变标签的内容,可以使用 v-once 指令执行一次性插值,当数据改变时插值处的内容不会更新:<span v-once>这个将不会改变: {{ message }}</span>

html:使用 v-html 指令用于输出 html 代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="example1" class="demo">
    <p>使用双大括号的文本插值: {{ rawHtml }}</p>
    <p>使用 v-html 指令: <span v-html="rawHtml"></span></p>
</div>
 
<script>
    const RenderHtmlApp = {
        data() {
            return {
                rawHtml: '<span style="color: red">这里会显示红色!</span>'
            }
        }
    }
 
    Vue.createApp(RenderHtmlApp).mount('#example1')
</script>
</body>
</html>

分析: 

属性:HTML 标签的属性值,应使用 v-bind 指令。示例:<div v-bind:id="dynamicId"></div>

布尔属性是 true 或 false,如果属性值为 null 或 undefined,则该属性不会显示出来。

<button v-bind:disabled="isButtonDisabled">按钮</button>

以上代码中如果 isButtonDisabled 的值是 null 或 undefined,则 disabled 属性甚至不会被包含在渲染出来的 <button> 元素中。

以下实例判断 use 的值,如果为 true 使用 class1 类的样式,否则不使用该类:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
    <style>
        .class1 {
            background: #444;
            color: #eee;
        }
    </style>
</head>
<body>
<div id="app">
    <label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
    <br><br>
    <div v-bind:class="{'class1': use}">
        v-bind:class 指令
    </div>
</div>
 
<script>
    const app = {
        data() {
            return {
                use: false
            }
        }
    }
 
    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

表达式:Vue.js 都提供了完全的 JavaScript 表达式支持。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    {{ 5 + 5 }}<br>
    {{ ok ? 'YES' : 'NO' }}<br>
    {{ message.split('').reverse().join('') }}
    <div v-bind:id="'list-' + id">菜鸟教程</div>
</div>
 
<script>
    const app = {
        data() {
            return {
                ok: true,
                message: 'RUNOOB!!',
                id: 1
            }
        }
    }
 
    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效:

<!--  这是语句,不是表达式:-->
{{ var a = 1 }}
 
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

调用 单值 数据

<template>
  <div class="home">
    <!-- 调用变量 -->
    {{msg}}  <br/>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data() {
    return {
      msg: 'hello world'
    }
  }
}
</script>

调用 数组 数据

<template>
  <div class="home">
    <!-- 调用数组 -->
    {{fav[0]}}--{{fav[1]}}<br/>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data() {
    return {
      fav: ['java','vue']
    }
  }
}
</script>

调用 对象 数据

<template>
  <div class="home">
    <!-- 调用对象 -->
    {{student.name}} -- {{student.age}}
  </div>
</template>

<script>
export default {
  name: 'Home',
  data() {
    return {
      student:{'name':'xm','age':234}
    }
  }
}
</script>

组件 实例 属性:data对象、$符、$data、$props、$refs、$el、$attrs、$listeners

<script>
    const root_zu_jian = {
        data() {
            return {
                ok: true,
                message: 'RUNOOB!!',
                id: 1
            }
        }
    }
 
    Vue.createApp(root_zu_jian).mount('#app')
</script>

这段代码是使用 vue 的常用格式,

  • root_zu_jian 就是一个组件。因为 root_zu_jian 是用在 Vue.createApp(root_zu_jian) 创建应用的,所以 root_zu_jian  称为 "根组件"。
  • 组件(根组件、非根组件) 就是一个 JavaScript 对象,都会有自己的属性和方法。

data 只能是函数,并且返回一个对象。因为一个组件一般都是被使用多次的,而使用函数的话它是会返回一个对象的,然后不同函数返回的对象就是不一样的,就不会导致每个组件都是在操作同一个数据,避免了数据的错误。如果它是用对象的话,那么多个组件就是在操作同一个数据,造成数据的错误。

每个 Vue 实例都会代理其 data函数返回的对象 里所有的属性。Vue 在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以 $data 的形式存储在组件实例中。注意:只有这些被代理的属性是 响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。

除了 data 属性, Vue 实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $,以便与代理的 data 属性区分。( 带 $ 的是 VUE 框架(或插件)定义的属性方法 )。

注意:不要在实例属性或者回调函数中(如 vm.$watch('a', newVal => this.myMethod()))使用箭头函数。因为箭头函数绑定父上下文,所以 this 不会像预想的一样是 Vue 实例,而是 this.myMethod 未被定义。 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title> Vue 测试实例 </title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app" class="demo"></div>

<script>
    const app = Vue.createApp({
        data() {
            return {count: 4}
        }
    })

    const vm = app.mount('#app')

    document.write(vm.$data.count) // => 4
    document.write("<br>")
    document.write(vm.count)       // => 4
    document.write("<br>")
    // 修改 vm.count 的值也会更新 $data.count
    vm.count = 5
    document.write(vm.$data.count) // => 5
    document.write("<br>")
    // 反之亦然
    vm.$data.count = 6
    document.write(vm.count) // => 6
</script>
</body>
</html>

以上实例属性仅在实例首次创建时被添加,所以你需要确保它们都在 data 函数返回的对象中。

  • $data 属性包含组件实例的所有数据属性。它是响应式的,即当数据发生变化时,相关的视图会自动更新。
const app = Vue.createApp({
  data() {
    return {
      message: 'Hello, Vue!'
    };
  }
});

const vm = app.mount('#app');
console.log(vm.$data); // { message: 'Hello, Vue!' }
  • $props 属性包含当前组件接收的所有父组件传递的属性(props)。它是只读的。
<!-- ParentComponent.vue -->
<template>
  <child-component :message="parentMessage"></child-component>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: 'Message from parent'
    };
  }
};
</script>
// ChildComponent.vue
export default {
  props: ['message'],
  mounted() {
    console.log(this.$props.message); // 'Message from parent'
  }
};
  • $refs 包含了组件内所有拥有 ref 特性的 DOM 元素或子组件实例。可以通过 $refs 来访问这些元素或组件实例。
<template>
  <div>
    <input type="text" ref="inputField">
  </div>
</template>

<script>
export default {
  mounted() {
    console.log(this.$refs.inputField); // 访问 DOM 元素
    this.$refs.inputField.focus(); // 调用 DOM 元素方法
  }
}
</script>
  • $el 属性 表示当前组件实例的根 DOM 元素。在组件被挂载后才能访问到该属性。
const app = Vue.createApp({
  template: '<div>Hello, Vue!</div>'
});

const vm = app.mount('#app');
console.log(vm.$el); // <div id="app">Hello, Vue!</div>
  • $attrs 包含了父组件传递给当前组件但未在子组件声明的所有属性。这些属性可以用于向子组件传递额外的非 prop 特性。
<!-- ParentComponent.vue -->
<template>
  <child-component custom-attr="value"></child-component>
</template>

<!-- ChildComponent.vue -->
<script>
export default {
  mounted() {
    console.log(this.$attrs); // { custom-attr: 'value' }
  }
};
</script>
  • $listeners 包含了父组件传递给当前组件的所有监听器(事件处理函数),可以直接绑定到组件的根元素或其他元素上。
<!-- ChildComponent.vue -->
<template>
  <button v-on="$listeners">Click me</button>
</template>

<script>
export default {
  mounted() {
    console.log(this.$listeners); // { click: handleClick }
  },
  methods: {
    handleClick() {
      console.log('Button clicked');
    }
  }
};
</script>

Vue3 内置 属性

Vue.js 中,is、key 和 ref 是三个常用的内置属性

is 属性

通常用于动态组件的实现,特别是当你希望在运行时动态地切换不同的组件时非常有用。

  • 类型: String 或 Object
  • 作用: is 属性用于动态地指定当前 <component> 组件应该渲染哪一个子组件。通常结合 <component> 元素和动态组件的特性一起使用,允许你在运行时基于数据来切换不同的组件。
<template>
  <div>
    <component :is="currentComponent"></component>
    <button @click="toggleComponent">Toggle Component</button>
  </div>
</template>

<script>
import FirstComponent from './FirstComponent.vue';
import SecondComponent from './SecondComponent.vue';

export default {
  data() {
    return {
      currentComponent: 'FirstComponent'
    };
  },
  components: {
    FirstComponent,
    SecondComponent
  },
  methods: {
    toggleComponent() {
      this.currentComponent = this.currentComponent === 'FirstComponent' ? 'SecondComponent' : 'FirstComponent';
    }
  }
};
</script>

以上代码中,currentComponent 是一个数据属性,它决定了 <component> 元素要渲染的具体组件。当点击按钮时,调用 toggleComponent 方法会切换 currentComponent 的值,从而动态改变渲染的组件。

注意事项:

  • is 属性的值可以是组件的名称字符串,也可以是组件对象。
  • 当使用字符串时,组件名称应该与注册的组件名一致。
  • 如果使用对象,可以直接引用组件对象或者使用动态计算属性返回组件对象。

key 属性

  • 类型: String 或 Number
  • 作用: 在 Vue.js 的列表渲染中,每个 v-for 循环中的元素需要有一个唯一的 key 属性。key 的作用是帮助 Vue 识别每个节点的唯一性,从而高效地更新虚拟 DOM。当 Vue 重新渲染列表时,会根据 key 来决定是否重新使用现有 DOM 元素或者重新创建。
<ul>
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>

以上代码中,item.id 作为 key 属性,确保了每个列表项在数据更新时可以正确地被 Vue 辨识和处理。

注意事项:

  • key 应该是唯一且稳定的。稳定的意思是,key 不应该随着数据的变化而变化,否则会影响 Vue 的性能和虚拟 DOM 的复用策略。
  • 避免使用索引作为 key,除非你明确知道列表项的顺序不会改变。

ref 属性

  • 类型: String 或 Object
  • 作用: ref 是用来给元素或子组件注册引用信息的。在实际开发中,可以使用 ref 来访问具体的 DOM 元素或子组件实例,从而进行一些操作,如调用方法、访问属性等。

在 Vue 中,ref 主要有以下作用:

  • 引用 DOM 元素。通过给 DOM 元素添加 ref 属性,可以在组件的方法中直接访问和操作该 DOM 元素。
    <template>
      <div>
        <input type="text" ref="inputField">
      </div>
    </template>
    
    <script>
    export default {
      mounted() {
        console.log(this.$refs.inputField); // 访问 DOM 元素
        this.$refs.inputField.focus(); // 调用 DOM 元素方法
      }
    }
    </script>
    <template>
      <div>
        <input ref="myInput" type="text" />
        <button @click="focusInput">聚焦输入框</button>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const myInput = ref(null);
    
    function focusInput() {
      if (myInput.value) {
        myInput.value.focus();
      }
    }
    </script>
  • 引用子组件实例。当给子组件添加 ref 属性时,可以获取到子组件的实例,从而调用子组件暴露的方法或访问子组件的属性。$refs 包含了组件内所有拥有 ref 特性的 DOM 元素或子组件实例。可以通过 $refs 来访问这些元素或组件实例。
    <template>
      <div>
        <child-component ref="childRef"></child-component>
      </div>
    </template>
    
    <script>
    import ChildComponent from './ChildComponent.vue';
    
    export default {
      components: {
        ChildComponent
      },
      mounted() {
        console.log(this.$refs.childRef); // 访问子组件实例
        this.$refs.childRef.doSomething(); // 调用子组件方法
      }
    }
    </script>
    <template>
      <div>
        <ChildComponent ref="child" />
        <button @click="invokeChildMethod">调用子组件方法</button>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    import ChildComponent from './ChildComponent.vue';
    
    const child = ref(null);
    
    function invokeChildMethod() {
      if (child.value) {
        child.value.someMethodInChild(); 
      }
    }
    </script>
  • 配合 template 中的 v-for 使用。在 v-for 循环中使用 ref 可以获取到一组 DOM 元素或组件实例的数组。
    <template>
      <div>
        <div v-for="item in list" :key="item.id" ref="divRefs">
          {{ item.name }}
        </div>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const list = [
      { id: 1, name: 'Item 1' },
      { id: 2, name: 'Item 2' },
      { id: 3, name: 'Item 3' }
    ];
    
    const divRefs = ref([]);
    </script>
  • 响应式数据。当 ref 用于普通数据时,它会被 Vue 转换为一个具有响应式特性的对象,通过 ref.value 来访问和修改其值。
    <template>
      <div>
        <button @click="increment">增加</button>
        {{ counter }}
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const counter = ref(0);
    
    function increment() {
      counter.value++;
    }
    </script>

总的来说,ref 提供了一种在 Vue 中方便地获取和操作 DOM 元素、子组件实例以及实现响应式数据的方式。

注意事项:

  • 在模板中使用 ref 时,Vue 将会提供一个对应的 $refs 对象,通过该对象可以访问到注册的 DOM 元素或子组件实例。
  • 避免滥用 ref,尽可能通过数据驱动和事件处理来操作 DOM 和子组件,保持代码的可维护性和清晰性。

在 Vue 中,ref 主要用于创建一个响应式的数据引用。通过 ref 创建的响应式引用,可以在组件的逻辑中方便地操作和跟踪这个值的变化。视图可以实时更新。ref 通常用于基本数据类型(如数字、字符串、布尔值等),使其具有响应式特性。示例:

import { ref } from 'vue';

// 创建一个初始值为 0 的 ref
const count = ref(0);

// 可以通过 count.value 来修改其值
count.value = 1;
<script setup>
import { ref } from 'vue';

const count = ref(0);

function increment() {
  count.value++;
}
</script>

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

组件 实例 方法

  • 使用 methods 选项,可以在组件中添加方法。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app" class="demo"></div>

<script>
    const app = Vue.createApp({
        data() {
            return {count: 4}
        },
        methods: {
            increment() {
                // `this` 指向该组件实例
                this.count++
            }
        }
    })

    const vm = app.mount('#app')

    document.write(vm.count) // => 4
    document.write("<br>")
    vm.increment()

    document.write(vm.count) // => 5
</script>
</body>
</html>
  • $mount() 方法手动将 Vue 组件挂载到 DOM 元素上。在 Vue 3 中,通常通过 createApp().mount() 自动挂载,少数情况下需要手动挂载。
const app = Vue.createApp({
  template: '<div>Hello, Vue!</div>'
});

const vm = app.mount('#app');
  • $destroy() 方法销毁当前组件实例,解绑其所有的事件监听器并释放其占用的资源。在一些特定的场景,如动态创建和销毁组件时很有用。vm.$destroy();
  • $emit() 方法用于触发当前组件实例上的自定义事件,并传递数据给父组件。通常与 v-on 指令结合使用。<button @click="$emit('custom-event', eventData)">Click me</button>
  • $nextTick() 方法在下次 DOM 更新循环结束之后执行指定的回调函数。用于在 DOM 更新之后执行一些操作。
vm.$nextTick(() => {
  // DOM 更新完成后执行的操作
});
  • $watch() 方法用于监听组件实例上的数据变化,并在数据变化时执行指定的回调函数。
vm.$watch('message', (newValue, oldValue) => {
  console.log(`Message changed from ${oldValue} to ${newValue}`);
});

这些属性和方法使得 Vue 组件实例具有丰富的功能和灵活性,能够满足开发者在构建复杂应用时的各种需求,从数据管理到事件处理,再到动态组件的操作,都能够通过组件实例来进行管理和控制。

Vue 实例 选项

Vue 中的选项是使用 Options API 时在 Vue 实例上可用的不同选项。Vue 实例的一些常见选项:https://www.runoob.com/vue3/vue-ref-options.html

  • el - 挂载点,用于指定 Vue 实例挂载的 DOM 元素。如果不提供则 Vue 实例不会自动挂载。

const app = Vue.createApp({
  // options
});
app.mount('#app');
  • data - 类型是 "函数或对象"。作用: data 是 Vue 实例中用来存储组件数据的地方。你可以在 data 中定义各种数据属性,并通过模板语法在页面上使用它们。data 中的数据会响应式地更新,即当数据发生变化时,相关的页面内容会自动更新。

const app = Vue.createApp({
  data() {
    return {
      message: 'Hello Vue!'
    };
  }
});
  • computed - 类型是 对象。作用: computed 属性允许你声明基于响应式依赖的计算属性。计算属性只有在相关依赖发生改变时才会重新求值,并且会缓存计算结果,避免不必要的重复计算。

const app = Vue.createApp({
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    };
  },
  computed: {
    fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  }
});
  • methods - 定义了 Vue 实例的方法,可以在模板中通过事件监听器调用。类型是 对象。作用: methods 属性用于存放 Vue 实例中的方法。这些方法可以在模板中调用,也可以在实例的其他方法中使用。与计算属性不同,每次调用方法时都会重新执行,适合用于响应事件或执行异步操作。

const app = Vue.createApp({
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
});
  • watch - 包含一个或多个观察者,用于观察和响应 Vue 实例中数据的变化。类型是 对象或函数。作用: watch 属性用于观察和响应 Vue 实例中数据的变化。当监视的数据发生变化时,执行指定的回调函数。适合处理异步或复杂的数据变化逻辑。

const app = Vue.createApp({
  data() {
    return {
      message: 'Hello Vue!'
    };
  },
  watch: {
    message(newValue, oldValue) {
      console.log(`Message changed from ${oldValue} to ${newValue}`);
    }
  }
});
  • props - 定义了组件可以接收的外部数据。类型是 数组或对象。作用: props 属性用于接收父组件传递给子组件的数据。子组件通过 props 接收父组件传递的数据,并可以在组件内部使用。

// MyComponent 接收一个 title 属性,并在模板中使用它:
const MyComponent = {
  props: ['title'],
  template: '<h1>{{ title }}</h1>'
};

const app = Vue.createApp({
  components: {
    'my-component': MyComponent
  },
  template: '<my-component title="Hello Vue 3!"></my-component>'
});

app.mount('#app');
  • template - 定义 Vue 实例的 HTML 模板。类型是 字符串。作用: template 属性定义了 Vue 组件的模板结构。模板中可以使用 Vue 的模板语法,包括插值、指令、计算属性等。模板最终会被编译为渲染函数,用于生成组件的虚拟 DOM。

const app = Vue.createApp({
  data() {
    return {
      message: 'Hello Vue!'
    };
  },
  template: '<div>{{ message }}</div>'
});
  • components - 允许在 Vue 实例中注册自定义组件。components 用于注册局部组件。

const ChildComponent = {
  template: '<div>A child component</div>'
};

const app = Vue.createApp({
  components: {
    'child-component': ChildComponent
  },
  template: '<child-component></child-component>'
}).mount('#app');
  • directives - 允许注册全局自定义指令。

  • filters - 定义了过滤器,可以在模板中使用,用于文本的格式化。

  • model - 指定组件使用的自定义 v-model 属性和事件。

  • provide / inject - 提供依赖注入的能力,允许跨组件传递数据。父组件提供数据,子组件注入使用。

const ChildComponent = {
  inject: ['message'],
  template: '<div>{{ message }}</div>'
};

const app = Vue.createApp({
  provide() {
    return {
      message: 'Provided Message'
    };
  },
  components: {
    'child-component': ChildComponent
  },
  template: '<child-component></child-component>'
}).mount('#app');
  • lifecycle hooks - 包括 onMountedonUpdatedonUnmounted 等生命周期钩子,用于在 Vue 实例的不同阶段执行代码。

  • render function - 允许使用 JavaScript 函数来渲染组件,提供了更高级的自定义能力。

  • errorCaptured - 用于全局捕获 Vue 实例的错误。

  • emits - 定义了组件可以触发的事件。

  • setup - Vue 3 的 Composition API 中的入口函数,用于组织组件的逻辑。

计算(computed) 属性

计算属性用于根据其他数据的变化动态计算衍生出来的属性值,而且具有缓存机制,只有相关依赖发生变化时才会重新计算。计算属性关键词: computed。计算属性在处理一些复杂逻辑时是很有用的。

计算属性的优点:计算属性是有缓存的
使用时不需要加 (),因为它是当做一个属性来使用的

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
    <p>原始字符串: {{ message }}</p>
    <p>计算后反转字符串: {{ reversedMessage }}</p>
</div>

<script>
    const app = {
        data() {
            return {
                message: 'RUNOOB!!'
            }
        },
        computed: {
            // 计算属性的 getter
            reversedMessage: function () {
                // `this` 指向 vm 实例
                return this.message.split('').reverse().join('')
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

上面代码声明一个计算属性 reversedMessage 。提供的函数将用作属性 vm.reversedMessage 的 getter 。vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="n in evenNumbers">{{ n }}</li>
    </ul>
</div>

<script>
    const app = {
        data() {
            return {
                numbers: [1, 2, 3, 4, 5]
            }
        },
        computed: {
            evenNumbers() {
                return this.numbers.filter(number => number % 2 === 0)
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

computed 和 methods 区别

可以使用 methods 来替代 computed,效果上两个都是一样的,

  • 但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。
  • 而使用 methods ,在重新渲染的时候,函数总会重新调用执行。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
    <p>原始字符串: {{ message }}</p>
    <p>使用 computed 计算后反转字符串: {{ reversedMessage }}</p>
    <p>使用 methods 计算后反转字符串: {{ reversedMessage2() }}</p>
</div>

<script>
    const app = {
        data() {
            return {
                message: 'RUNOOB!!'
            }
        },
        computed: {
            // 计算属性的 getter
            reversedMessage: function () {
                // `this` 指向 vm 实例
                return this.message.split('').reverse().join('')
            }
        },
        methods: {
            reversedMessage2: function () {
                return this.message.split('').reverse().join('')
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

computed 属性默认只有 getter ,不过在需要时你也可以提供一个 setter :

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">

</div>

<script>
    const app = {
        data() {
            return {
                name: 'Google',
                url: 'http://www.google.com'
            }
        },
        computed: {
            site: {
                // getter
                get: function () {
                    return this.name + ' ' + this.url
                },
                // setter
                set: function (newValue) {
                    var names = newValue.split(' ')
                    this.name = names[0]
                    this.url = names[names.length - 1]
                }
            }
        }
    }
    vm = Vue.createApp(app).mount('#app')
    document.write('name: ' + vm.name);
    document.write('<br>');
    document.write('url: ' + vm.url);
    document.write('<br>------ 更新数据 ------<br>');
    // 调用 setter, vm.name 和 vm.url 也会被对应更新
    vm.site = '菜鸟教程 https://www.runoob.com';
    document.write('name: ' + vm.name);
    document.write('<br>');
    document.write('url: ' + vm.url);
</script>
</body>
</html>

从实例运行结果看在运行 vm.site = '菜鸟教程 http://www.runoob.com'; 时,setter 会被调用, vm.name 和 vm.url 也会被对应更新。

监听(watch) 属性

watch 的作用是用于监测响应式属性的变化,并在属性发生改变时执行特定的操作,它是 Vue 中的一种响应式机制,允许你在数据发生变化时做出相应的响应,执行自定义的逻辑。

watch 使得在响应式属性变化时能够有更多的控制权和灵活性,让你的组件能够更好地响应数据的变化并执行相应的逻辑。

以下实例通过使用 watch 实现计数器:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <p style="font-size:25px;">计数器: {{ counter }}</p>
    <button @click="counter++" style="font-size:25px;">点我</button>
</div>

<script>
    const app = {
        data() {
            return {
                counter: 1
            }
        }
    }
    vm = Vue.createApp(app).mount('#app')
    vm.$watch('counter', function (nval, oval) {
        alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
    });
</script>
</body>
</html>

示例:千米之间的换算:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    千米 : <input type="text" v-model="kilometers" @focus="currentlyActiveField = 'kilometers'">
    米 : <input type="text" v-model="meters" @focus="currentlyActiveField = 'meters'">
</div>
<p id="info"></p>
<script>
    const app = {
        data() {
            return {
                kilometers: 0,
                meters: 0
            }
        },
        watch: {
            kilometers: function (newValue, oldValue) {
                // 判断是否是当前输入框
                if (this.currentlyActiveField === 'kilometers') {
                    this.kilometers = newValue;
                    this.meters = newValue * 1000
                }
            },
            meters: function (newValue, oldValue) {
                // 判断是否是当前输入框
                if (this.currentlyActiveField === 'meters') {
                    this.kilometers = newValue / 1000;
                    this.meters = newValue;
                }
            }
        }
    }
    vm = Vue.createApp(app).mount('#app')
    vm.$watch('kilometers', function (newValue, oldValue) {
        // 这个回调将在 vm.kilometers 改变后调用
        document.getElementById("info").innerHTML = "修改前值为: " + oldValue + ",修改后值为: " + newValue;
    })
</script>
</body>
</html>

异步数据的加载 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。

以下实例我们使用 axios 库,后面会具体介绍。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
    <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
    <script src="https://cdn.staticfile.net/axios/0.27.2/axios.min.js"></script>
    <script src="https://cdn.staticfile.net/vue/3.2.37/vue.global.min.js"></script>
</head>
<body>
<div id="watch-example">
    <p>
        输入一个问题,以 ? 号结尾输出答案:
        <input v-model="question"/>
    </p>
    <p>{{ answer }}</p>
</div>
<script>
    const watchExampleVM = Vue.createApp({
        data() {
            return {
                question: '',
                answer: '每个问题结尾需要输入 ? 号。'
            }
        },
        watch: {
            // 每当问题改变时,此功能将运行,以 ? 号结尾,兼容中英文 ?
            question(newQuestion, oldQuestion) {
                if (newQuestion.indexOf('?') > -1 || newQuestion.indexOf('?') > -1) {
                    this.getAnswer()
                }
            }
        },
        methods: {
            getAnswer() {
                this.answer = '加载中...'
                axios
                    .get('/try/ajax/json_vuetest.php')
                    .then(response => {
                        this.answer = response.data.answer
                    })
                    .catch(error => {
                        this.answer = '错误! 无法访问 API。 ' + error
                    })
            }
        }
    }).mount('#watch-example')
</script>
</body>
</html>

样式(style) 绑定

https://www.runoob.com/vue3/vue3-class-bind.html

class 与 style 是 HTML 元素的属性,用于设置元素的样式,可以用 v-bind 来设置样式属性。

v-bind 在处理 class 和 style 时, 表达式除了可以使用字符串之外,还可以是对象或数组。

v-bind:class 可以简写为 :class。

示例:将 isActive 设置为 true 显示了一个绿色的 div 块,如果设置为 false 则不显示:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
    <style>
        .active {
            width: 100px;
            height: 100px;
            background: green;
        }
    </style>
</head>
<body>
<div id="app">
    <div :class="{ 'active': isActive }"></div>
</div>

<script>
    const app = {
        data() {
            return {
                isActive: true
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

上面示例 div class 渲染结果为:<div class="active"></div>

也可以在对象中传入更多属性用来动态切换多个 class 。:class 可以与普通的 class 属性共存。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
    <style>
        .static {
            width: 100px;
            height: 100px;
        }

        .active {
            background: green;
        }

        .text-danger {
            background: red;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="static" :class="{ 'active': isActive, 'text-danger': hasError }">
    </div>
</div>

<script>
    const app = {
        data() {
            return {
                isActive: false,
                hasError: true
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

也可以直接绑定数据里的一个对象:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
    <style>
        .static {
            width: 100px;
            height: 100px;
        }

        .active {
            background: green;
        }

        .text-danger {
            background: red;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="static" :class="classObject"></div>
</div>

<script>
    const app = {
        data() {
            return {
                classObject: {
                    'active': false,
                    'text-danger': true
                }
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

也可以绑定一个返回对象的计算属性。这是一个常用且强大的模式:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
    <style>
        .static {
            width: 100px;
            height: 100px;
        }

        .active {
            background: green;
        }

        .text-danger {
            background: red;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="static" :class="classObject"></div>
</div>

<script>
    const app = {
        data() {
            return {
                isActive: true,
                error: null
            }
        },
        computed: {
            classObject() {
                return {
                    active: this.isActive && !this.error,
                    'text-danger': this.error && this.error.type === 'fatal'
                }
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

可以把一个数组传给 v-bind:class ,实例如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
    <style>
        .static {
            width: 100px;
            height: 100px;
        }

        .active {
            background: green;
        }

        .text-danger {
            background: red;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="static" :class="[activeClass, errorClass]"></div>
</div>

<script>
    const app = {
        data() {
            return {
                activeClass: 'active',
                errorClass: 'text-danger'
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

以上实例 div class 渲染结果为:<div class="static active text-danger"></div>

还可以使用三元表达式来切换列表中的 class :

<div id="app">
    <div class="static" :class="[isActive ? activeClass : '', errorClass]"></div>
</div>

<script>
const app = {
    data() {
        return {
            isActive: false,
			activeClass: 'active',
			errorClass: 'text-danger'
        }
    }
}

Vue.createApp(app).mount('#app')
</script>

以上实例 div class 渲染结果为:<div class="static text-danger"></div>

Vue.js style(内联样式)。可以在 v-bind:style 直接设置样式,可以简写为 :style

<div id="app">
    <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">菜鸟教程</div>
</div>

<script>
const app = {
    data() {
        return {
            activeColor: 'red',
			fontSize: 30
        }
    }
}

Vue.createApp(app).mount('#app')
</script>

以上实例 div style 渲染结果为:<div style="color: red; font-size: 30px;">菜鸟教程</div>

也可以直接绑定到一个样式对象,让模板更清晰:

<div id="app">
    <div :style="styleObject">菜鸟教程</div>
</div>

<script>
const app = {
    data() {
        return {
			styleObject: {
                color: "red",
			    fontSize: "30px"
			}
        }
    }
}

Vue.createApp(app).mount('#app')
</script>

v-bind:style 可以使用数组将多个样式对象应用到一个元素上:

<div id="app">
    <div :style="[baseStyles, overridingStyles]">菜鸟教程</div>
</div>

<script>
const app = {
    data() {
        return {
			baseStyles: {
                color: 'green',
                fontSize: '30px'
            },
	        overridingStyles: {
                'font-weight': 'bold'
            }
        }
    }
}

Vue.createApp(app).mount('#app')
</script>

多重值:可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。

组件上使用 class 属性。当你在带有单个根元素的自定义组件上使用 class 属性时,这些 class 将被添加到该元素中。此元素上的现有 class 将不会被覆盖。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
    <runoob class="classC classD"></runoob>
</div>

<script>
    // 创建一个Vue 应用
    const app = Vue.createApp({})

    // 定义一个名为 runoob的新全局组件
    app.component('runoob', {
        template: '<h1 class="classA classB">I like runoob!</h1>'
    })

    app.mount('#app')
</script>
</body>
</html>

以上实例 div class 渲染结果为:<h1 class="classA classB classC classD">I like runoob!</h1>

如果组件有多个根元素,你需要定义哪些部分将接收这个类。可以使用 $attrs 组件属性执行此操作:

<div id="app">
    <runoob class="classA"></runoob>
</div>
 
<script>
const app = Vue.createApp({})
 
app.component('runoob', {
  template: `
    <p :class="$attrs.class">I like runoob!</p>
    <span>这是一个子组件</span>
  `
})
 
app.mount('#app')
</script>

注意:template 中 ` 是反引号,不是单引号 '。

以上实例 div class 渲染结果为:<div id="app" data-v-app=""><p class="classA">I like runoob!</p><span>这是一个子组件</span></div>

模板 语法

Vue 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。
Vue 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统。
结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。

<template>
  <div>
    <!-- 插值 -->
    <p>{{ message }}</p>
    
    <!-- v-bind -->
    <a :href="url">Link</a>
    
    <!-- v-on -->
    <button @click="handleClick">Click me</button>
    
    <!-- v-for -->
    <ul>
      <li v-for="item in items" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

vue3 的 指令

Vue 指令是带有前缀 v- 的特殊 HTML 属性,它赋予 HTML 标签额外的功能。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM 。与传统的 JavaScript 方法相比,使用 Vue 创建响应式页面要容易得多,并且需要的代码更少。

Vue 指令(Directives)是 Vue.js 的一项核心功能,它们可以在 HTML 模板中以 v- 开头的特殊属性形式使用,用于将响应式数据绑定到 DOM 元素上或在 DOM 元素上进行一些操作。

v-text == {{message}}
v-html <div v-html="message2"></div>
v-show:<h1 v-show="ok">Hello!</h1>
v-if:<div v-if="type === 'A'">
v-else:<div v-else>
v-else-if:<div v-else-if="type === 'B'">
v-for:<div v-for="(item, index) in items"></div>
v-on==@:<button v-on:click="doThat('hello', $event)"></button>
v-bind==:<img v-bind:src="imageSrc"> 缩写<img :src="imageSrc">
v-model:<input v-model="message" placeholder="edit me"><p>Message is: {{ message }}</p>

v-pre

不让 {{}} 被 vue 解析

v-if、v-else-if、v-else 和 v-show

v-if 条件渲染。使用 v-if 和 v-else 指令根据表达式的值(true或false)来动态的渲染元素或组件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app" class="demo">
    <p v-if="showMessage">Hello Vue!</p>
    <p v-else>Goodbye Vue!</p>
    <button @click="change_value">改变值</button>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                showMessage: true
            }
        },
        methods: {
            change_value () {
                if (this.showMessage){
                    this.showMessage = false;
                }else{
                    this.showMessage = true;
                }
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#app')
</script>
</body>
</html>

因为 v-if 是一个指令,所以必须将它添加到一个元素上。如果是多个元素,可以包裹在 <template> 元素上,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

示例:在 <template> 元素上使用 v-if 指令:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <template v-if="seen">
        <h1>网站</h1>
        <p>Google</p>
        <p>Runoob</p>
        <p>Taobao</p>
    </template>
</div>
    
<script>
const app = {
  data() {
    return {
      seen: true /* 改为false,信息就无法显示 */
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
</body>
</html>

示例:v-else-if

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <div v-if="type === 'A'">
        A
    </div>
    <div v-else-if="type === 'B'">
        B
    </div>
    <div v-else-if="type === 'C'">
        C
    </div>
    <div v-else>
        Not A/B/C
    </div>
</div>

<script>
    const app = {
        data() {
            return {
                type: "C"
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

 示例:随机生成一个数字,判断是否大于 0.5,然后输出对应信息:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <div v-if="Math.random() > 0.5">
      随机数大于 0.5
    </div>
    <div v-else>
      随机数小于等于 0.5
    </div>
</div>
    
<script>
Vue.createApp(app).mount('#app')
</script>
</body>
</html>

注意:v-if 和 v-show 区别

  • v-if:对dom节点进行删除和增加
  • v-show:通过css控制节点的隐藏和显示
  • 如果隐藏/显示位置切换不是很频繁,建议使用v-if,如果切换频繁则使用v-show
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
    <button v-on:click="showMessage = !showMessage">显示/隐藏</button>
    <p v-show="showMessage">Hello Vue!</p>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                showMessage: true
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <h1 v-show="ok">Hello!</h1>
</div>
    
<script>
const app = {
  data() {
    return {
      ok: true 
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
</body>
</html>

v-bind

v-bind 动态绑定一个或多个特性,或一个组件 prop。v-bind 指令可以用于响应式地更新

<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app" class="demo">
    <a v-bind:href="bing_url">{{website}}</a>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                website: '绑定测试 (美女写真)',
                bing_url: 'https://iitang.com/favorites/meinvxiezhen'
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#app')
</script>
</body>
</html>

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <p><a v-bind:href="url">v-bing 完整形式</a></p>
    <p><a :href="url">v-bing 缩写形式</a></p>
</div>

<script>
    const app = {
        data() {
            return {
                url: 'https://www.runoob.com'
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

v-on ( 事件处理,简写 @ )

可以使用 v-on 指令来监听 DOM 事件,从而执行 JavaScript 代码。

  • v-on:监听 DOM 事件,并在触发时执行一些 JavaScript 代码。
  • 自定义事件
    组件内抛出:this.$emit('myEvent')
    外部监听:<my-component v-on:myEvent="doSomething"></my-component>
  • 将原生事件绑定到组件:<base-input v-on:focus.native="onFocus"></base-input>
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
    <button @click="counter += 1">增加 1</button>
    <p>这个按钮被点击了 {{ counter }} 次。</p>
</div>

<script>
    const app = {
        data() {
            return {
                counter: 0
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

示例:v-on 可以接收一个定义的方法来调用。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <button @click="greet">Greet</button>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                message: 'Hello Vue!!'
            }
        },
        methods: {
            greet() {
                alert('Hello!');
            }
        }
    }
    Vue.createApp(HelloVueApp).mount('#app')
</script>
</body>
</html>

除了直接绑定到一个方法,也可以用内联 JavaScript 语句:

<div id="app">
  <button @click="say('hi')">Say hi</button>
  <button @click="say('what')">Say what</button>
</div>

<script>
const app = {
  data() {
   
  },
  methods: {
    say(message) {
      alert(message)
    }
  }
}

Vue.createApp(app).mount('#app')
</script>

事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:

<div id="app">
  <!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
  <button @click="one($event), two($event)">
  点我
  </button>
</div>
 
<script>
const app = {
  data() {
  },
  methods: {
    one(event) {
      alert("第一个事件处理器逻辑...")
    },
    two(event) {
      alert("第二个事件处理器逻辑...")
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 如果函数不是写在{{}}里面或者函数没有参数,则()可以省略:<button @click="print">按钮</button>
  • 如果函数中有参数,但是没有传入参数的话,那么就将undefined传进去:<button @click="print()">按钮</button>
  • 如果函数中有参数,但是没有写()那么将点击事件传进去:<button @click="print">按钮</button>

如果既要传递参数,又要获得event

<body>
    <div id="app">
       <button @click="print('hello',$event)">按钮</button>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        const bpp=new Vue({
            el: "#app",
            data : {
                count: 0,
                movices: ['one','two','three']
            },
            methods: {
                print(name,event){
                    console.log(name+'-----'+event)
                }
            }
        })
    </script>
</body>

事件 修饰符

Vue.js 为 v-on 提供了 "事件修饰符" 来处理 DOM 事件细节,即只有触发事件设置的条件,事件才会发生。 修饰符是由点开头的指令后缀来表示的。( https://v3.cn.vuejs.org/guide/events.html )

  • .stop - 阻止冒泡,停止该按钮的作用。停止某个事件的作用,如果不加 stop 修饰的话,点击按钮那某 divClick 也会起作用,这不是想要的,所以可以加上 stop 就阻止了

  • .prevent - 阻止默认事件,该例子阻止了表单的提交

  • .capture - 阻止捕获
  • .self - 只监听触发该元素的事件
  • .once - 只触发一次。让点击事件只起一次作用

  • .left - 左键事件
  • .right - 右键事件
  • .middle - 中间滚轮事件
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联  -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>

<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>

示例:事件修饰符。下面示例是只有按下 enter ,才会触发事件 sayHi

按键 修饰符

Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">

全部的按键别名:

  • .enter
  • .tab
  • .delete (捕获 "删除" 和 "退格" 键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统修饰键:

  • .ctrl
  • .alt
  • .shift
  • .meta

鼠标按钮修饰符:

  • .left
  • .right
  • .middle
<p><!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

v-for

使用数组来渲染一个列表 ,因此被称为"列表渲染"。

  • key 的作用: 使用 v-for 渲染列表时,必须为每个项提供一个唯一的 key 属性,以便 Vue 能够识别每个项的唯一性,从而进行高效的 DOM 更新。

  • 嵌套循环: 可以嵌套使用多个 v-for 来渲染多维数组或对象结构。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <ol>
        <li v-for="site in sites">
            {{ site.text }}
        </li>
    </ol>
</div>
<script>
    const app = {
        data() {
            return {
                sites: [
                    {text: 'Google'},
                    {text: 'Runoob'},
                    {text: 'Taobao'}
                ]
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

遍历数组:v-for="(item, index) in items"

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app" class="demo">
    <ul>
        <li v-for="item in items" :key="item.id">
            {{ item.text }}
        </li>
    </ul>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                items: [
                    {id: 1, text: 'Item 1'},
                    {id: 2, text: 'Item 2'},
                    {id: 3, text: 'Item 3'}
                ]
            }
        }
    }

    Vue.createApp(HelloVueApp).mount('#app')
</script>
</body>
</html>

遍历对象:v-for="(value, key, index) in object"

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(value, key, index) in object">
            {{ index }}. {{ key }} : {{ value }}
        </li>
    </ul>
</div>

<script>
    const app = {
        data() {
            return {
                object: {
                    name: '菜鸟教程',
                    url: 'https://www.runoob.com',
                    slogan: '学的不仅是技术,更是梦想!'
                }
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

模板中使用 v-for

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
	<ul>
	  <template v-for="site in sites">
		<li>{{ site.text }}</li>
		<li>--------------</li>
	  </template>
	</ul>
</div>
<script>
const app = {
  data() {
    return {
      sites: [
        { text: 'Google' },
        { text: 'Runoob' },
        { text: 'Taobao' }
      ]
    }
  }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>

迭代整数

<div id="app">
    <ul>
        <li v-for="n in 10">
            {{ n }}
        </li>
    </ul>
</div>

<script>
    Vue.createApp({}).mount('#app')
</script>

 v-for/v-if 联合使用

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <select @change="changeVal($event)" v-model="selOption">
        <template v-for="(site,index) in sites" :site="site" :index="index" :key="site.id">
            <!-- 索引为 1 的设为默认值,索引值从0 开始-->
            <option v-if="index == 1" :value="site.name" selected>{{ site.name }}</option>
            <option v-else :value="site.name">{{ site.name }}</option>
        </template>
    </select>
    <div>您选中了:{{ selOption }}</div>
</div>

<script>
    const app = {
        data() {
            return {
                selOption: "Runoob",
                sites: [
                    {id: 1, name: "Google"},
                    {id: 2, name: "Runoob"},
                    {id: 3, name: "Taobao"},
                ]
            }

        },
        methods: {
            changeVal: function (event) {
                this.selOption = event.target.value;
                alert("你选中了" + this.selOption);
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

在组件上使用 v-for。

如果你还没了解组件的内容,可以先跳过这部分。在自定义组件上,你可以像在任何普通元素上一样使用 v-for:<my-component v-for="item in items" :key="item.id"></my-component>

然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 props:

<my-component
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
></my-component>

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="todo-list-example">
    <form v-on:submit.prevent="addNewTodo">
        <label for="new-todo">添加 todo</label>
        <input
                v-model="newTodoText"
                id="new-todo"
                placeholder="例如:明天早上跑步"
        />
        <button>添加</button>
    </form>
    <ul>
        <todo-item
                v-for="(todo, index) in todos"
                :key="todo.id"
                :title="todo.title"
                @remove="todos.splice(index, 1)"
        ></todo-item>
    </ul>
</div>

<script>
    const app = Vue.createApp({
        data() {
            return {
                newTodoText: '',
                todos: [
                    {
                        id: 1,
                        title: '看电影'
                    },
                    {
                        id: 2,
                        title: '吃饭'
                    },
                    {
                        id: 3,
                        title: '上 RUNOOB 学习'
                    }
                ],
                nextTodoId: 4
            }
        },
        methods: {
            addNewTodo() {
                this.todos.push({
                    id: this.nextTodoId++,
                    title: this.newTodoText
                })
                this.newTodoText = ''
            }
        }
    })

    app.component('todo-item', {
        template: `
          <li>
            {{ title }}
            <button @click="$emit('remove')">删除</button>
          </li>
        `,
        props: ['title'],
        emits: ['remove']
    })

    app.mount('#todo-list-example')
</script>
</body>
</html>

建议:v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。 所以,我们应尽可能在使用 v-for 时提供 key attribute , 以便它能跟踪每个节点的身份,从而重用和重新排序现有元素

不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。

v-model ( 表单 双向数据绑定 )

双向绑定:就是对属性值进行改变时,页面中展示的值跟着改变,对页面展示的值进行改变时,属性值也跟着改变。也就是:数据变化更新视图,视图变化更新数据

  • v-model 指令用来在 input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。
  • v-model 其实就是一个语法糖,它的背后本质上是包含两个操作:
            1、v-bind 绑定一个value属性
            2、v-on 指令给当前元素绑定input事件
  • v-model 会忽略所有表单元素的 value、checked、selected 属性的初始值,使用的是 data 选项中声明初始值。v-model 在内部为不同的输入元素使用不同的属性,并抛出不同的事件:
            text 和 textarea 元素使用 value 属性和 input 事件;
            checkbox 和 radio 使用 checked 属性和 change 事件;
            select 字段将 value 作为属性并将 change 作为事件。
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

示例:输入框

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app" class="demo">
    <input type="text" v-model="message">
    <p>{{ message }}</p>
</div>

<script>
    const HelloVueApp = {
        data() {
            return {
                message: 'Hello Vue!!'
            }
        }
    }
    Vue.createApp(HelloVueApp).mount('#app')
</script>
</body>
</html>

示例:textarea 文本框

在文本区域 textarea 插值是不起作用,需要使用 v-model 来代替:

<!-- 错误 -->
<textarea>{{ text }}</textarea>

<!-- 正确 -->
<textarea v-model="text"></textarea>
<div id="app">
  <p>input 元素:</p>
  <input v-model="message" placeholder="编辑我……">
  <p>input 表单消息是: {{ message }}</p>
    
  <p>textarea 元素:</p>
  <textarea v-model="message2" placeholder="多行文本输入……"></textarea>
  <p>textarea 表单消息是:</p>
  <p style="white-space: pre">{{ message2 }}</p>
  
</div>
 
<script>
const app = {
  data() {
    return {
      message: '',
      message2: '菜鸟教程\r\nhttps://www.runoob.com'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

示例:复选框。复选框如果是一个为逻辑值,如果是多个则绑定到同一个数组:

<div id="app">
  <p>单个复选框:</p>
  <input type="checkbox" id="checkbox" v-model="checked">
  <label for="checkbox">{{ checked }}</label>
    
  <p>多个复选框:</p>
  <input type="checkbox" id="runoob" value="Runoob" v-model="checkedNames">
  <label for="runoob">Runoob</label>
  <input type="checkbox" id="google" value="Google" v-model="checkedNames">
  <label for="google">Google</label>
  <input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
  <label for="taobao">taobao</label>
  <br>
  <span>选择的值为: {{ checkedNames }}</span>
</div>
 
<script>
const app = {
  data() {
    return {
      checked : false,
      checkedNames: []
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

示例:单选按钮

<div id="app">
  <input type="radio" id="runoob" value="Runoob" v-model="picked">
  <label for="runoob">Runoob</label>
  <br>
  <input type="radio" id="google" value="Google" v-model="picked">
  <label for="google">Google</label>
  <br>
  <span>选中值为: {{ picked }}</span>
</div>
 
<script>
const app = {
  data() {
    return {
      picked : 'Runoob'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

示例:select 列表

<div id="app">
  <select v-model="selected" name="site">
    <option value="">选择一个网站</option>
    <option value="www.runoob.com">Runoob</option>
    <option value="www.google.com">Google</option>
  </select>
 
  <div id="output">
      选择的网站是: {{selected}}
  </div>
</div>
 
<script>
const app = {
  data() {
    return {
      selected: '' 
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

多选时会绑定到一个数组:

<div id="app">
  <select v-model="selected" name="fruit" multiple>
    <option value="www.runoob.com">Runoob</option>
    <option value="www.google.com">Google</option>
    <option value="www.taobao.com">Taobao</option>
  </select>
 
  <div id="output">
      选择的网站是: {{selected}}
  </div>
</div>
 
<script>
const app = {
  data() {
    return {
      selected: '' 
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

使用 v-for 循环输出选项:

<div id="app" class="demo">
  <select v-model="selected">
    <option v-for="option in options" :value="option.value">
      {{ option.text }}
    </option>
  </select>
  <span>选择的是: {{ selected }}</span>
</div>
 
<script>
const app = {
  data() {
    return {
      selected: 'www.runoob.com',
      options: [
        { text: 'Runoob', value: 'www.runoob.com' },
        { text: 'Google', value: 'www.google.com' },
        { text: 'Taobao', value: 'www.taobao.com' }
      ]
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

值绑定:对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值):

<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a" />

<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle" />

<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

但是有时我们可能想把值绑定到当前活动实例的一个动态属性上,这时可以用 v-bind 实现,此外,使用 v-bind 可以将输入值绑定到非字符串。

复选框 (Checkbox):

<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
...
// 选中时
vm.toggle === 'yes'
// 取消选中 
vm.toggle === 'no'

单选框 (Radio):

<input type="radio" v-model="pick" v-bind:value="a" />
// 当选中时
vm.pick === vm.a

选择框选项 (Select):

<select v-model="selected">
  <!-- 内联对象字面量 -->
  <option :value="{ number: 123 }">123</option>
</select>
// 当被选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123

修饰符

  • 在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:
    <!-- 在 "change" 而不是 "input" 事件中更新 -->
    <input v-model.lazy="msg" >
    
  • 如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以使用修饰符 number 给 v-model 来处理输入值:<input v-model.number="age" type="number">
  • 如果要自动过滤用户输入的首尾空格,可以使用 trim 修饰符:<input v-model.trim="msg">

自定义 指令

除了默认设置的核心指令( 例如:v-model 和 v-show ),Vue 也允许注册自定义指令。

示例:注册一个全局指令 v-focus, 该指令的功能是在页面加载时,元素获得焦点:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
    <p>页面载入时,input 元素自动获取焦点:</p>
    <input v-focus>
</div>

<script>
    const app = Vue.createApp({})
    // 注册一个全局自定义指令 `v-focus`
    app.directive('focus', {
        // 当被绑定的元素挂载到 DOM 中时……
        mounted(el) {
            // 聚焦元素
            el.focus()
        }
    })
    app.mount('#app')
</script>
</body>
</html>

也可以在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
    <p>页面载入时,input 元素自动获取焦点:</p>
    <input v-focus>
</div>

<script>
    const app = {
        data() {
            return {}
        },
        directives: {
            focus: {
                // 指令的定义
                mounted(el) {
                    el.focus()
                }
            }
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

内置指令(Directives)

Vue.js 内置了多个指令(Directives),用于在模板中添加特定的响应式行为或操作 DOM。以下是 Vue.js 中常用的内置指令:https://www.runoob.com/vue3/vue3-ref-directives.html

指令用法示例说明
v-text<p v-text="message"></p>更新元素的 textContent,类似于插值 {{ }},但是是单向绑定。
v-html<div v-html="htmlContent"></div>更新元素的 innerHTML,用于输出 HTML。
v-model<input v-model="value">在表单控件元素上创建双向数据绑定,将输入字段与数据属性同步。
v-show<div v-show="isVisible"></div>根据表达式的真假切换元素的显示与隐藏,使用 CSS 的 display 属性。
v-if<p v-if="isVisible">Visible</p>根据表达式的真假条件性地渲染元素,当条件为假时元素将从 DOM 中移除。
v-else<p v-if="isVisible">Visible</p><p v-else>Not Visible</p>v-if 的补充指令,用于显示条件为假时的备用内容。
v-else-if<template v-if="type === 'A'"><p>Type A</p></template><template v-else-if="type === 'B'"><p>Type B</p></template>连续使用于 v-if 和 v-else 之后,用于多条件判断。
v-for<li v-for="(item, index) in items" :key="index">{{ item }}</li>遍历数组或对象的每个元素,生成相应数量的元素。
v-on<button v-on:click="handleClick">Click me</button>绑定事件监听器,用于监听 DOM 事件,可以简写为 @
v-bind<img v-bind:src="imageSrc">动态绑定 HTML 属性,可以简写为 :
v-slot<template v-slot:header><h2>Header</h2></template>用于具名插槽的语法,提供插入子组件内容的位置。
v-pre<div v-pre>{{ message }}</div>跳过这个元素和它的所有子元素的编译过程,用于显示原始 Mustache 标签。
v-cloak[v-cloak] { display: none; }在 Vue 实例编译完成前隐藏模板内容,避免显示未编译的 Mustache 标签。
v-once<p v-once>{{ message }}</p>只渲染元素和组件一次,不会随后续数据变化而更新。

vue3 的 组件

vue 组件 ( 注册 (全局、局部)、导入、使用、组件之间传递数据 ) :https://www.w3cschool.cn/vuejs3/vuejs3-pgsc3f26.html

组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素封装可重用的代码。可以将用户界面拆分成独立和可复用的部分。

每个 Vue 组件都是一个独立的 Vue 实例,具有自己的 模板、数据、方法和生命周期钩子,使得组件可以自包含地定义和管理自己的功能和样式。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。

vue 视图层是 基于 HTML 的,所以 "组件" 可以看作是 HTML 的 标签。可以类比 C# 中 WinForm 开发中的 "控件"。 vue 中的 "组件" 就相当于 WinForm 中的 "控件"。

Vue 组件的特性和优势

  • 复用性: 组件可以在不同的地方多次使用,提高代码的复用性和可维护性。
  • 封装性: 每个组件都是独立的作用域,可以封装自己的状态和逻辑,避免全局污染。
  • 组合性: 可以通过组合多个小组件构建复杂的界面。
  • 响应式: 组件的数据响应式地绑定到视图,数据更新时自动更新视图。
  • 模块化: 支持模块化开发,可以使用现代前端工具链进行构建和管理。

每个 Vue 应用都是通过用 createApp 函数创建的,传递给 createApp 的选项用于配置根组件。当挂载应用时,该组件被用作渲染的起点。一个应用需要被挂载到一个 DOM 元素中。

const RootComponent = { /* 选项 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')

全局 组件

全局组件 和 局部组件区别在于注册的方式

  • 全局 注册:是注册到 const app = Vue.createApp(根组件) 创建的 app 上。可以被不同的 Vue实例 中的dom 中使用
  • 局部 注册:是注册到 创建应用时的 "根组件上"。只能在注册这个组件的 Vue 实例中的dom 中使用

注册一个全局组件语法格式如下

const app = Vue.createApp({...})

app.component('my-component-name', {
  /* ... */
})

my-component-name 为组件名,/* ... */ 部分为配置选项。注册后,就可以使用以下方式来调用组件:<my-component-name></my-component-name>

示例:template 中 ` 是反引号,不是单单引号 '。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>

<script>
    // 创建一个Vue 应用
    const app = Vue.createApp({})

    // 定义一个名为 button-counter 的新全局组件
    app.component('button-counter', {
        data() {
            return {
                count: 0
            }
        },
        template: `
          <button @click="count++">
            点了 {{ count }} 次!
          </button>`
    })
    app.mount('#app')
</script>
</body>
</html>

局部 组件

  • 全局注册:是注册到 const app = Vue.createApp(根组件) 创建的 app 上
  • 局部注册:是注册到 创建应用时的 "根组件上"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <runoob-a></runoob-a>
</div>
<script>

    var runoobA = {
        template: '<h1>自定义组件!</h1>'
    }

    const app = Vue.createApp({
        components: {
            'runoob-a': runoobA
        }
    })

    app.mount('#app')
</script>
</body>
</html>

编译 作用域

各种标签,属性的使用都是在本模板内起作用的

插槽 作用域

一般在组件中有默认的显示数据的方式。如果父组件调用了子组件,但是父组件并不想要子组件显示数据的方式,而是想要按照自己的方式显示数据,但是在父组件中是不能直接访问子组件中的数据的。所以可以采用插槽的方式来处理。

单文件 组件 (.vue 文件)

Vue3 创建单文件组件(SFC)。单文件组件能够更好地组织和管理 Vue 组件,一个组件通常由三部分组成:模板、脚本和样式。

<!-- MyComponent.vue -->
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello from MyComponent',
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
</script>

<style scoped>
/* 组件私有样式 */
p {
  color: blue;
}
</style>

Vue 的单文件组件 (即 *.vue 文件,英文 Single-File Component,简称 SFC) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。

在 Vue3 中,你可以使用 .vue 文件来创建单文件组件(Single File Components, SFCs),这个文件包含了组件的模板、JavaScript 代码以及 CSS 样式。

现在我们将删除通过 npm init vue@latest 命令创建的实例项目中的所有内容(参考 Vue3 安装),以便在 Vue 中创建自己的简单网页。

在我们开始编写代码之前,删除 <template>、<script> 和 <style> 标签内的所有内容,以及任何像 setup 或 scoped 这样的属性。

您的 App.vue 文件现在应该如下所示:

<script></script>

<template></template>

<style></style>

同时我们可以先清空 src 目录中的文件夹 assets 和 components,里面的文件我们可以后期自己添加补充 。

main.js 文件代码修改为如下代码:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

现在我们就创建了一个简单的项目,在 App.vue 文件写入以下代码:

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, RUNOOB!'
    }
  }
}
</script>

<style>
h1 {
  color: blue;
}
</style>

以上实例中我们定义了一个包含一个标题的简单组件。组件的模板包含一个 <h1> 元素,使用了 Vue3 的模板语法将 message 属性绑定到了这个元素的文本内容中。

JavaScript 部分包含了一个 data 函数,返回了一个包含了 message 属性的对象,我们将这个属性绑定到了模板中。

最后,CSS 样式定义了标题的颜色为蓝色。

在组件的 JavaScript 部分,我们使用了新的 export default 语法,这个语法可以让我们将组件定义导出为一个默认的对象。在 Vue3 中,我们可以使用这个语法来定义组件,而不必像 Vue2 那样使用 Vue.component 函数。

访问 http://localhost:5173/

使用组件

当我们定义好了一个组件之后,我们可以在其他组件中使用这个组件。

使用组件,我们需要先创建组件,比如以下实例在 ./src/components/ 目录下创建 HelloRunoob.vue 组件文件,代码如下:

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Runoob!'
    }
  }
}
</script>

<style>
h1 {
  color: red;
}
</style>

然后我们在 ./src/main.js 文件中引入并定义该组件:

import { createApp } from 'vue'

import App from './App.vue'
import HelloRunoob from './components/HelloRunoob.vue'

const app = createApp(App)
app.component('hello-runoob', HelloRunoob) // 自定义标签
app.mount('#app')

在父组件的模板中,我们可以使用自定义标签的方式来引入子组件,就像以下 App.vue 文件代码:

<template>
  <div>
    <hello-runoob></hello-runoob>
  </div>
</template>

访问 http://localhost:5173/,以上代码执行结果为

使用 props 传递数据

组件实例 的 作用域 是 孤立的。但是组件之间经常协同工作

  • 组件 A 在它的模版中使用了组件 B 。它们之间必然需要相互通信:父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件。

父子 组件之间 相互传递数据

  • 父组件通过 props 向下传递数据给子组件,子组件需要显式地用 props 选项声明 "prop"
  • 子组件通过 events 给父组件发送消息。

prop 是单向绑定的 (单向 数据流):当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop 。如果你这么做了,Vue 会在控制台给出警告。

注意:在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <site-name title="Google"></site-name>
    <site-name title="Runoob"></site-name>
    <site-name title="Taobao"></site-name>
</div>

<script>
    const app = Vue.createApp({})

    app.component('site-name', {
        props: ['title'],
        template: `<h4>{{ title }}</h4>`
    })

    app.mount('#app')
</script>
</body>
</html>

类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <site-info
            v-for="site in sites"
            :id="site.id"
            :title="site.title"
    ></site-info>
</div>

<script>
    const Site = {
        data() {
            return {
                sites: [
                    {id: 1, title: 'Google'},
                    {id: 2, title: 'Runoob'},
                    {id: 3, title: 'Taobao'}
                ]
            }
        }
    }

    const app = Vue.createApp(Site)

    app.component('site-info', {
        props: ['id', 'title'],
        template: `<h4>{{ id }} - {{ title }}</h4>`
    })

    app.mount('#app')
</script>
</body>
</html>

组件可以为 props 指定验证要求。为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。type 可以是下面原生构造器,type 也可以是一个自定义构造器,使用 instanceof 检测。

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

父、子 组件

事件 ( 子组件向父组件 传递数据 )

  • 父组件是使用 props 传递数据给子组件。
  • 子组件通过 $emit 触发事件,父组件监听这些事件,并获取子组件传递的数据。父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

使用 v-on 绑定自定义事件。每个 Vue 实例都实现了事件接口(Events interface),即:可以使用 v-on 指令 (通常缩写为 @ 符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为 v-on:click="methodName" 或使用快捷方式 @click="methodName"

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="app">
    <button-counter @increment="incrementTotal"></button-counter>
    <p>Total clicks: {{ total }}</p>
</div>

<script>
    const app = Vue.createApp({
        data() {
            return {
                total: 0
            };
        },
        methods: {
            incrementTotal() {
                this.total++;
            }
        }
    });

    app.component('button-counter', {
        template: '<button @click="increment">You clicked me {{ count }} times.</button>',
        data() {
            return {
                count: 0
            };
        },
        methods: {
            increment() {
                this.count++;
                this.$emit('increment');
            }
        }
    });

    app.mount('#app');
</script>
</body>
</html>

父传子 ( 通过 props )

  • 在 Vue 3 中,defineProps 用于定义组件接收的父组件传递过来的 props 。使用 props :在子组件中使用 defineProps 来定义接收父组件传递过来的数据。父组件在使用子组件时,通过属性的形式传递数据。

子组件:

<script setup>
import { defineProps } from 'vue'

const props = defineProps({
  title: String,
  count: Number
})
</script>

<template>
  <div>
    <h2>{{ title }}</h2>
    <p>Count: {{ count }}</p>
  </div>
</template>

父组件:

    <ChildComponent message="Hello from parent" title="title_test" />

通过 defineProps 定义了两个 props :title 为字符串类型,count 为数字类型。在模板中可以直接使用这些 props 来展示数据。defineProps 是在 <script setup> 语法中使用的方式来定义组件的 props 。这种方式使得组件的逻辑更加简洁和直观。

子传父

  • 自定义事件:子组件通过 emit 方法触发自定义事件,并携带相关数据。父组件在使用子组件时,监听该自定义事件来获取数据。

子组件:

    <script setup>
    import { defineEmits } from 'vue'
    const emits = defineEmits(['updateMessage'])
    const handleClick = () => {
      emits('updateMessage', 'New message from child')
    }
    </script>

父组件:

    <ChildComponent @updateMessage="handleUpdateMessage" />

非父子组件通信

有时候非父子关系的组件也需要通信。在简单的场景下,使用一个空的 Vue 实例作为中央事件总线:

var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
})

在更多复杂的情况下,你应该考虑使用专门的状态管理模式.

插槽

插槽,其实就相当于 打桩,占个位置。vue 中的插槽就是在组件的<template></template>标签中加入<slot></slot>标签,然后如果需要在这里面加入新的内容就可以直接添加

具名插槽

如果没有对插槽指定具体的名称,那么所有的插槽都会替换

运行结果:

加了名称,就会替换了那些加名称的

运行结果:

替换指定名称的插槽

使用 Slots 分发内容

单个 Slot

具名Slots

动态组件

多个组件可以使用同一个挂载点,然后动态地在它们之间切换。使用保留的 <component>元素,动态地绑定到它的 is 特性:

子组件索引 ( ref 属性 )

尽管有 props 和 events ,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个索引 ID 。例如:

<div id="parent">
  <user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 访问子组件
var child = parent.$refs.profile

当 ref 和 v-for 一起使用时, ref 是一个数组或对象,包含相应的子组件。

$refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案 —— 应当避免在模版或计算属性中使用 $refs 。

异步组件

在大型应用中,我们可能需要将应用拆分为多个小模块,按需从服务器下载。为了让事情更简单, Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。例如:

Vue3 内置 组件

在 Vue.js 中,有一些内置的全局组件和内置组件,它们提供了一些常用的功能和布局支持,可以帮助开发者快速构建应用界面。

以下是一些常见的 Vue 内置组件和它们的作用:

  • <component> 是一个抽象的组件,用于动态地渲染不同的组件或元素。通过绑定 is 属性可以实现动态组件的切换和渲染。<component :is="currentComponent"></component>
  • <transition> 和 <transition-group> 提供了在 Vue.js 中实现过渡和动画效果的功能。通过定义过渡的 CSS 类名,可以控制元素在进入或离开 DOM 时的动画效果。
    <transition name="fade">
      <div v-if="show">Hello!</div>
    </transition>
    <transition-group name="list" tag="ul">
      <li v-for="item in items" :key="item.id">{{ item.text }}</li>
    </transition-group>

  • <keep-alive> 是一个抽象组件,用于保持组件状态或避免多次渲染。当组件被 <keep-alive> 包裹时,其状态将会被缓存,而不是每次切换时重新渲染。
    <keep-alive>
      <component :is="currentComponent"></component>
    </keep-alive>

  • <slot> 是 Vue.js 中用于插入内容的插槽组件。它允许父组件将子组件的内容传递到特定的插槽位置,使得组件更加灵活和可复用。
    <child-component>
      <template #header>
        <h2>Header Content</h2>
      </template>
      <template #default>
        <p>Default Content</p>
      </template>
    </child-component>

  • <teleport> 允许你将 DOM 元素渲染到应用的任何地方,而不受当前 DOM 结构的限制。这在需要在应用中动态移动元素时非常有用,例如在模态框中渲染弹出内容。
    <teleport to="body">
      <modal-dialog v-if="showModal">
        <!-- modal content -->
      </modal-dialog>
    </teleport>

  • <Suspense> 是 Vue.js 3.x 中新增的组件,用于处理异步组件的加载和状态。它可以在异步组件加载完成之前显示占位内容,并处理加载状态和错误。
    <Suspense>
      <template #default>
        <AsyncComponent />
      </template>
      <template #fallback>
        <div>Loading...</div>
      </template>
    </Suspense>

Vue 组件 实例

在 Vue.js 中,组件实例是 Vue 应用中最基本的构建块之一。每个组件实例都是一个 Vue 组件,它具有自己的模板、数据、方法和生命周期钩子,可以独立地管理自己的状态和行为。

Vue 组件实例的创建。创建一个组件实例通常通过 createApp 方法和组件选项对象来实现。

组件选项对象可以包含如下属性:

  • data: 数据对象,用于存储组件的响应式数据。
  • props: 属性数组或对象,用于接收父组件传递的数据。
  • computed: 计算属性对象,根据响应式依赖进行动态计算。
  • methods: 方法对象,包含组件内部的函数和操作。
  • watch: 监听器对象,用于监听数据变化并做出响应。
  • template: 字符串,定义组件的模板结构。
  • render: 函数,用于渲染组件的虚拟 DOM。
  • setup: 函数,用于设置组件的初始状态、数据、计算属性和监听器等(Vue 3 中)。

以下是一个简单的 Vue 组件实例的示例:

import { createApp } from 'vue';

const app = createApp({
  data() {
    return {
      message: 'Hello, Vue!'
    };
  },
  methods: {
    greet() {
      alert(this.message);
    }
  },
  template: `
    <div>
      <p>{{ message }}</p>
      <button @click="greet">Greet</button>
    </div>
  `
});

app.mount('#app');

说明:

  • data 函数返回一个包含 message 属性的对象,用于存储组件的状态。
  • methods 对象包含了一个 greet 方法,当点击按钮时会弹出一个提示框显示 message 的内容。
  • template 字符串定义了组件的模板结构,使用了插值和事件绑定来实现数据展示和交互。

生命周期钩子

Vue 组件实例具有一些生命周期钩子函数,允许开发者在组件生命周期的不同阶段执行自定义逻辑。常见的生命周期钩子包括 createdmountedupdated 和 destroyed 等。

const app = createApp({
  data() {
    return {
      message: 'Hello, Vue!'
    };
  },
  created() {
    console.log('Component created');
  },
  mounted() {
    console.log('Component mounted');
  },
  updated() {
    console.log('Component updated');
  },
  destroyed() {
    console.log('Component destroyed');
  },
  template: `
    <div>
      <p>{{ message }}</p>
    </div>
  `
});

组件通信

Vue 组件实例之间通过 props、events、provide/inject、$parent/$children、$attrs/$listeners 等方式进行通信,具体取决于组件的关系和需求。这些机制允许组件在不同层级之间传递数据和响应用户交互。

vue3 的 插件

插件通常会为Vue添加全局功能。插件的范围没有限制,一般有下面几种:

  1. 添加全局方法或者属性,如:vue-element
  2. 添加全局资源:指令/过滤器/过渡等,如:vue-touch
  3. 通过全局 mixin方法添加一些组件选项,如:vuex
  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如:vue-router

vue 中 使用 插件:https://www.w3cschool.cn/vuejs2/plugins.html

Vue.js 的插件应当有一个公开方法 install 。这个方法的第一个参数是 Vue 构造器 , 第二个参数是一个可选的选项对象:

模块化的 导出、导入

模块化有两个核心:导出和导入

模块 的 导出

module.exports={
    flag:true,
    test(a,b){
        return a+b
    },
    demo(a,b){
        return a*b
    }
}

模块 的 导入

//Commonjs的导入
let{test,demo,flag}=require('moduleA');
//等同于
let _mA=required('moduleA');
let test=_mA.test;
let demo=_mA.demo;
let flag=_mA.flag;

ES6 的导出和导入

可以通过在<script>标签中加入type="module"属性来标志这个一个模块,就不会出现模块间数据冲突的问题

变量的导出和导入

函数/类的导出和导入

export default

一个模块只能有一个 export default,使用这个东西之后在导入时就可以自定义名称

webpack

webpack是一个前端模块化打包工具,对于有些技术,浏览器是识别不了的,而通过webpack打包之后就可以转化为浏览器可以识别的东西

全局安装 webpack:npm install webpack@3.6.0 -g

局部安装 webpack,–save-dev 是开发时依赖,项目打包后不需要继续使用
cd 对应目录,执行命令:npm install webpack@3.6.0 --save-dev

Vue3 路由

Vue 路由允许我们通过不同的 URL 访问不同的内容。

通过 Vue 可以实现多视图的单页 Web 应用(single page web application,SPA)。

Vue.js 路由需要载入 vue-router 库

中文文档地址:vue-router 文档

直接下载 / CDN:https://unpkg.com/vue-router@4
NPM 推荐使用淘宝镜像:
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install vue-router@4

简单实例

Vue.js + vue-router 可以很简单的实现单页应用。

<router-link> 是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。

以下实例中我们将 vue-router 加进来,然后配置组件和路由映射,再告诉 vue-router 在哪里渲染它们。代码如下所示:

<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-router@4"></script>
 
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!--使用 router-link 组件进行导航 -->
    <!--通过传递 `to` 来指定链接 -->
    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

router-link

请注意,我们没有使用常规的 a 标签,而是使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。我们将在后面看到如何从这些功能中获益。

router-view

router-view 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
    <script src="https://cdn.staticfile.net/vue-router/4.2.4/vue-router.global.min.js"></script>
</head>
<body>
<div id="app">
    <h1>Hello App!</h1>
    <p>
        <!--使用 router-link 组件进行导航 -->
        <!--通过传递 `to` 来指定链接 -->
        <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
        <router-link to="/">Go to Home</router-link>
        <router-link to="/about">Go to About</router-link>
    </p>
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>

<script>
    // 1. 定义路由组件.
    // 也可以从其他文件导入
    const Home = {template: '<div>Home</div>'}
    const About = {template: '<div>About</div>'}

    // 2. 定义一些路由
    // 每个路由都需要映射到一个组件。
    // 我们后面再讨论嵌套路由。
    const routes = [
        {path: '/', component: Home},
        {path: '/about', component: About},
    ]

    // 3. 创建路由实例并传递 `routes` 配置
    // 你可以在这里输入更多的配置,但我们在这里
    // 暂时保持简单
    const router = VueRouter.createRouter({
        // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
        history: VueRouter.createWebHashHistory(),
        routes, // `routes: routes` 的缩写
    })

    // 5. 创建并挂载根实例
    const app = Vue.createApp({})
    //确保 _use_ 路由实例使
    //整个应用支持路由。
    app.use(router)

    app.mount('#app')

    // 现在,应用已经启动了!
</script>
</body>
</html>

点击过的导航链接都会加上样式 class ="router-link-exact-active router-link-active"。

<router-link> 相关属性

接下来我们可以了解下更多关于 <router-link> 的属性。

  • to:表示目标路由的链接。 当被点击后,内部会立刻把 to 的值传到 router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。
<!-- 字符串 -->
<router-link to="home">Home</router-link>
<!-- 渲染结果 -->
<a href="home">Home</a>

<!-- 使用 v-bind 的 JS 表达式 -->
<router-link v-bind:to="'home'">Home</router-link>

<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
<router-link :to="'home'">Home</router-link>

<!-- 同上 -->
<router-link :to="{ path: 'home' }">Home</router-link>

<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
  • replace:设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。<router-link :to="{ path: '/abc'}" replace></router-link>
  • append:设置 append 属性后,则在当前 (相对) 路径前添加其路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b:<router-link :to="{ path: 'relative/path'}" append></router-link>
  • tag:有时候想要 <router-link> 渲染成某种标签,例如 <li>。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>
  • active-class:设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。注意这里 class 使用 active-class="_active"。
<style>
   ._active{
      background-color : red;
   }
</style>
<p>
   <router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
   <router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>
  • exact-active-class:配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。
<p>
   <router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>
   <router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>
  • event:声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>  以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。

Vue3 混入

混入 (mixins)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

来看一个简单的实例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
    // 定义混入对象
    const myMixin = {
        created() {
            this.hello()
        },
        methods: {
            hello() {
                document.write('欢迎来到混入实例-RUNOOB!')
            }
        }
    }

    // 定义一个应用,使用混入
    const app = Vue.createApp({
        mixins: [myMixin]
    })

    app.mount('#app') // => "欢迎来到混入实例-RUNOOB!"
</script>
</body>
</html>

选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。

比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先。以下实例中,Vue 实例与混入对象包含了相同的方法。从输出结果可以看出两个选项合并了。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
    const myMixin = {
        data() {
            return {
                message: 'hello',
                foo: 'runoob'
            }
        }
    }

    const app = Vue.createApp({
        mixins: [myMixin],
        data() {
            return {
                message: 'goodbye',
                bar: 'def'
            }
        },
        created() {
            document.write(JSON.stringify(this.$data))
        }
    })

    app.mount('#app')
</script>
</body>
</html>

输出结果为:{"message":"goodbye","foo":"runoob","bar":"def"}

同名钩子函数将合并为一个数组,因此都将被调用。另外,mixin 对象的钩子将在组件自身钩子之前调用。

const myMixin = {
  created() {
    console.log('mixin 对象的钩子被调用')
  }
}

const app = Vue.createApp({
  mixins: [myMixin],
  created() {
    console.log('组件钩子被调用')
  }
})

// => "mixin 对象的钩子被调用"
// => "组件钩子被调用"

值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。

const myMixin = {
  methods: {
    foo() {
      console.log('foo')
    },
    conflicting() {
      console.log('from mixin')
    }
  }
}
 
const app = Vue.createApp({
  mixins: [myMixin],
  methods: {
    bar() {
      console.log('bar')
    },
    conflicting() {
      console.log('from self')
    }
  }
})
 
const vm = app.mount('#app')
 
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

以上实例,我们调用了以下三个方法:
vm.foo();
vm.bar();
vm.conflicting();
从输出结果 methods 选项中如果碰到相同的函数名则 Vue 实例有更高的优先级会执行输出。

全局混入

也可以全局注册混入对象。注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
    const app = Vue.createApp({
        myOption: 'hello!'
    })

    // 为自定义的选项 'myOption' 注入一个处理器。
    app.mixin({
        created() {
            const myOption = this.$options.myOption
            if (myOption) {
                document.write(myOption)
            }
        }
    })

    app.mount('#app') // => "hello!"
</script>
</body>
</html>

谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。

Vue3 Ajax(axios)

Vue 版本推荐使用 axios 来完成 ajax 请求。

Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。

Github开源地址: https://github.com/axios/axios

使用 cdn:<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
或 <script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
使用 npm: $ npm install axios
使用 bower: $ bower install axios
使用 yarn:$ yarn add axios

使用方法:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})

this.axios.get(api).then((response) => {
  console.log(response.data)
})

this.$http.get(api).then((response) => {
  console.log(response.data)
})

GET 方法

我们可以简单的读取 JSON 数据:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
    <script src="https://cdn.staticfile.net/axios/1.5.0/axios.min.js"></script>
</head>
<body>
<div id="app">
    {{ info }}
</div>

<script>
    const app = {
        data() {
            return {
                info: 'Ajax 测试!!'
            }
        },
        mounted() {
            axios
                .get('https://www.runoob.com/try/ajax/json_demo.json')
                .then(response => (this.info = response))
                .catch(function (error) { // 请求失败处理
                    console.log(error);
                });
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

使用 response.data 读取 JSON 数据:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
    <script src="https://cdn.staticfile.net/axios/1.5.0/axios.min.js"></script>
</head>
<body>
<div id="app">
    <h1>网站列表</h1>
    <div v-for="site in info">
        {{ site.name }}
    </div>
</div>

<script>
    const app = {
        data() {
            return {
                info: 'Ajax 测试!!'
            }
        },
        mounted() {
            axios
                .get('https://www.runoob.com/try/ajax/json_demo.json')
                .then(response => (this.info = response.data.sites))
                .catch(function (error) { // 请求失败处理
                    console.log(error);
                });
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

GET 方法传递参数格式如下:

// 直接在 URL 上添加参数 ID=12345
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
 
// 也可以通过 params 设置参数:
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

POST 方法

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
    <script src="https://cdn.staticfile.net/axios/1.5.0/axios.min.js"></script>
</head>
<body>
<div id="app">
    {{ info }}
</div>


<script>
    const app = {
        data() {
            return {
                info: null
            }
        },
        mounted() {
            axios
                .post('https://www.runoob.com/try/ajax/demo_axios_post.php')
                .then(response => (this.info = response))
                .catch(function (error) { // 请求失败处理
                    console.log(error);
                });
        }
    }

    Vue.createApp(app).mount('#app')
</script>
</body>
</html>

POST 方法传递参数格式如下:

axios.post('/user', {
    firstName: 'Fred',        // 参数 firstName
    lastName: 'Flintstone'    // 参数 lastName
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

执行多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}
 
function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));

axios API

可以通过向 axios 传递相关配置来创建请求。

axios(config)
// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
//  GET 请求远程图片
axios({
  method:'get',
  url:'http://bit.ly/2mTM3nY',
  responseType:'stream'
})
  .then(function(response) {
  response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
axios(url[, config])
// 发送 GET 请求(默认的方法)
axios('/user/12345');

请求方法的别名

为方便使用,官方为所有支持的请求方法提供了别名,可以直接使用别名来发起请求:

axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])

注意:在使用别名方法时, url、method、data 这些属性都不必在配置中指定。

并发

处理并发请求的助手函数:

axios.all(iterable)
axios.spread(callback)

创建实例

可以使用自定义配置新建一个 axios 实例:

axios.create([config])
const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

实例方法

以下是可用的实例方法。指定的配置将与实例的配置合并:

axios#request(config)
axios#get(url[, config])
axios#delete(url[, config])
axios#head(url[, config])
axios#post(url[, data[, config]])
axios#put(url[, data[, config]])
axios#patch(url[, data[, config]])

请求配置项

下面是创建请求时可用的配置选项,注意只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。

{
  // `url` 是用于请求的服务器 URL
  url: "/user",

  // `method` 是创建请求时使用的方法
  method: "get", // 默认是 get

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: "https://some-domain.com/api/",

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 只能用在 "PUT", "POST" 和 "PATCH" 这几个请求方法
  // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
  transformRequest: [function (data) {
    // 对 data 进行任意转换处理

    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对 data 进行任意转换处理

    return data;
  }],

  // `headers` 是即将被发送的自定义请求头
  headers: {"X-Requested-With": "XMLHttpRequest"},

  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params: {
    ID: 12345
  },

  // `paramsSerializer` 是一个负责 `params` 序列化的函数
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function(params) {
    return Qs.stringify(params, {arrayFormat: "brackets"})
  },

  // `data` 是作为请求主体被发送的数据
  // 只适用于这些请求方法 "PUT", "POST", 和 "PATCH"
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data: {
    firstName: "Fred"
  },

  // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求花费了超过 `timeout` 的时间,请求将被中断
  timeout: 1000,

  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // 默认的

  // `adapter` 允许自定义处理请求,以使测试更轻松
  // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
  adapter: function (config) {
    /* ... */
  },

  // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
  // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
  auth: {
    username: "janedoe",
    password: "s00pers3cret"
  },

  // `responseType` 表示服务器响应的数据类型,可以是 "arraybuffer", "blob", "document", "json", "text", "stream"
  responseType: "json", // 默认的

  // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
  xsrfCookieName: "XSRF-TOKEN", // default

  // `xsrfHeaderName` 是承载 xsrf token 的值的 HTTP 头的名称
  xsrfHeaderName: "X-XSRF-TOKEN", // 默认的

  // `onUploadProgress` 允许为上传处理进度事件
  onUploadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  onDownloadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

  // `maxContentLength` 定义允许的响应内容的最大尺寸
  maxContentLength: 2000,

  // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
  validateStatus: function (status) {
    return status &gt;= 200 &amp;&amp; status &lt; 300; // 默认的
  },

  // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
  // 如果设置为0,将不会 follow 任何重定向
  maxRedirects: 5, // 默认的

  // `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
  // `keepAlive` 默认没有启用
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // "proxy" 定义代理服务器的主机名称和端口
  // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
  // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
  proxy: {
    host: "127.0.0.1",
    port: 9000,
    auth: : {
      username: "mikeymike",
      password: "rapunz3l"
    }
  },

  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken: new CancelToken(function (cancel) {
  })
}

响应结构

axios请求的响应包含以下信息:

{
  // `data` 由服务器提供的响应
  data: {},

  // `status`  HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: "OK",

  // `headers` 服务器响应的头
  headers: {},

  // `config` 是为请求提供的配置信息
  config: {}
}

使用 then 时,会接收下面这样的响应:

axios.get("/user/12345")
  .then(function(response) {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
    console.log(response.config);
  });

在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用。

配置的默认值

你可以指定将被用在各个请求的配置默认值。

全局的 axios 默认值:

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

自定义实例默认值:

// 创建实例时设置配置的默认值
var instance = axios.create({
  baseURL: 'https://api.example.com'
});

// 在实例已创建后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

配置的优先顺序

配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。这里是一个例子:

// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
var instance = axios.create();

// 覆写库的超时默认值
// 现在,在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;

// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
  timeout: 5000
});

拦截器

在请求或响应被 then 或 catch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

如果你想在稍后移除拦截器,可以这样:

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

可以为自定义 axios 实例添加拦截器。

var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

错误处理:

axios.get('/user/12345')
  .catch(function (error) {
    if (error.response) {
      // 请求已发出,但服务器响应的状态码不在 2xx 范围内
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else {
      // Something happened in setting up the request that triggered an Error
      console.log('Error', error.message);
    }
    console.log(error.config);
  });

可以使用 validateStatus 配置选项定义一个自定义 HTTP 状态码的错误范围。

axios.get('/user/12345', {
  validateStatus: function (status) {
    return status < 500; // 状态码在大于或等于500时才会 reject
  }
})

取消

使用 cancel token 取消请求。

Axios 的 cancel token API 基于cancelable promises proposal

可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:

var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:

var CancelToken = axios.CancelToken;
var cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// 取消请求
cancel();

注意:可以使用同一个 cancel token 取消多个请求。

请求时使用 application/x-www-form-urlencoded

axios 会默认序列化 JavaScript 对象为 JSON。 如果想使用 application/x-www-form-urlencoded 格式,你可以使用下面的配置。

浏览器

在浏览器环境,你可以使用 URLSearchParams API:

const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);

URLSearchParams 不是所有的浏览器均支持。

除此之外,你可以使用 qs 库来编码数据:

const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));

// Or in another way (ES6),

import qs from 'qs';
const data = { 'bar': 123 };
const options = {
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  data: qs.stringify(data),
  url,
};
axios(options);

Node.js 环境

在 node.js里, 可以使用 querystring 模块:

const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));

当然,同浏览器一样,你还可以使用 qs 库。

Promises

axios 依赖原生的 ES6 Promise 实现而被支持

如果你的环境不支持 ES6 Promise,你可以使用 polyfill

TypeScript支持

axios 包含 TypeScript 的定义。

import axios from "axios";
axios.get("/user?ID=12345");

Vue3 组合式 API

Vue3 组合式 API(Composition API) 主要用于在大型组件中提高代码逻辑的可复用性。

传统的组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。

Vue3 使用组合式 API 的地方为 setup。

在 setup 中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式 API(Composition API) 允许我们编写更有条理的代码。

setup 组件

setup() 函数在组件创建 created() 之前执行。

setup() 函数接收两个参数 props 和 context。

第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。

第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。

注意:在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。

以下实例使用组合 API 定义一个计数器:

<template>
    <div>
        <p>计数器实例: {{ count }}</p>
        <input @click="myFn" type="button" value="点我加 1">
    </div>
</template>

<script> 
import {ref, onMounted} from 'vue';

export default {
    setup(){
        //定义初始值为0的变量,要使用ref方法赋值,直接赋值的话变量改变不会更新 UI
        let count = ref(0);

        // 定义点击事件 myFn
        function myFn(){
            console.log(count);
            count.value += 1;
        }
       
       // 组件被挂载时,我们用 onMounted 钩子记录一些消息
        onMounted(() => console.log('component mounted!'));

        // 外部使用组合API中定义的变量或方法,在模板中可用。
        return {count,myFn} // 返回的函数与方法的行为相同
    }
}
</script>

在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:

import { ref } from 'vue'

let count = ref(0);

ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。

在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问。

import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1

Vue 组合式 API 生命周期钩子

在 Vue2 中,我们通过以下方式实现生命周期钩子函数:

export default {
  beforeMount() {
    console.log('V2 beforeMount!')
  },
  mounted() {
    console.log('V2 mounted!')
  }
};

在 Vue3 组合 API 中实现生命周期钩子函数可以在 setup() 函数中使用带有 on 前缀的函数

import { onBeforeMount, onMounted } from 'vue';
export default {
  setup() {
    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })
    onMounted(() => {
      console.log('V3 mounted!');
    })
  }
};

下表为 Options API 和 Composition API 之间的映射,包含如何在 setup () 内部调用生命周期钩子:

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

这些函数接受一个回调函数,当钩子被组件调用时将会被执行:

setup() {
...
    // 组件被挂载时,我们用 onMounted 钩子记录一些消息
    onMounted(() => console.log('component mounted!'));
...
}

模板引用

在使用组合式 API 时,响应式引用和模板引用的概念是统一的。

为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回:

<template> 
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // DOM 元素将在初始渲染后分配给 ref
        console.log(root.value) // <div>This is a root element</div>
      })

      return {
        root
      }
    }
  }
</script>

以上实例中我们在渲染上下文中暴露 root,并通过 ref="root",将其绑定到 div 作为其 ref。

作为模板使用的 ref 的行为与任何其他 ref 一样:它们是响应式的,可以传递到 (或从中返回) 复合函数中。

v-for 中的用法

组合式 API 模板引用在 v-for 内部使用时没有特殊处理。相反,请使用函数引用执行自定义处理:

<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // 确保在每次更新之前重置ref
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>

侦听模板引用

侦听模板引用的变更可以替代前面例子中演示使用的生命周期钩子。

但与生命周期钩子的一个关键区别是,watch() 和 watchEffect() 在 DOM 挂载或更新之前运行会有副作用,所以当侦听器运行时,模板引用还未被更新。

<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        // 这个副作用在 DOM 更新之前运行,因此,模板引用还没有持有对元素的引用。
        console.log(root.value) // => null
      })

      return {
        root
      }
    }
  }
</script>

因此,使用模板引用的侦听器应该用 flush: 'post' 选项来定义,这将在 DOM 更新后运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素。

<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        console.log(root.value) // => <div>This is a root element</div>
      }, 
      {
        flush: 'post'
      })

      return {
        root
      }
    }
  }
</script>

vue3 的 生命周期

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。

在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

  • onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码:
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  console.log(`the component is now mounted.`)
})
</script>

还有其他一些钩子,会在实例生命周期的不同阶段被调用,最常用的是 onMountedonUpdated 和 onUnmounted。所有生命周期钩子的完整参考及其用法请参考 API 索引

当调用 onMounted 时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册。例如,请不要这样做:

setTimeout(() => {
  onMounted(() => {
    // 异步注册时当前组件实例已丢失
    // 这将不会正常工作
  })
}, 100)

注意这并不意味着对 onMounted 的调用必须放在 setup() 或 <script setup> 内的词法上下文中。onMounted() 也可以在一个外部函数中调用,只要调用栈是同步的,且最终起源自 setup() 就可以。

生命周期钩子的注意事项

  • this 上下文:所有生命周期钩子函数中的 this 自动指向调用它的组件实例。
  • 避免使用箭头函数:定义生命周期钩子时,不要使用箭头函数,因为这会导致无法通过 this 获取组件实例。

组合式 API:生命周期钩子

onMounted() 注册一个回调函数,在组件挂载完成后执行。

<script setup>
import { ref, onMounted } from 'vue'

const el = ref()

onMounted(() => {
  el.value // <div>
})
</script>

<template>
  <div ref="el"></div>
</template>

onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

<script setup>
import { ref, onUpdated } from 'vue'

const count = ref(0)

onUpdated(() => {
  // 文本内容应该与当前的 `count.value` 一致
  console.log(document.getElementById('count').textContent)
})
</script>

<template>
  <button id="count" @click="count++">{{ count }}</button>
</template>

onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。

<script setup>
import { onMounted, onUnmounted } from 'vue'

let intervalId
onMounted(() => {
  intervalId = setInterval(() => {
    // ...
  })
})

onUnmounted(() => clearInterval(intervalId))
</script>

onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。

onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。

onErrorCaptured() 注册一个钩子,在捕获了后代组件传递的错误时调用。

function onErrorCaptured(callback: ErrorCapturedHook): void

type ErrorCapturedHook = (
  err: unknown,
  instance: ComponentPublicInstance | null,
  info: string
) => boolean | void

示例:生命周期钩子函数的使用

一个完整的 Vue 组件示例,展示了所有生命周期钩子函数的使用:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/3.2.31/vue.global.min.js"></script>
</head>
<body>

<div id="app">
    <lifecycle-demo></lifecycle-demo>
</div>

<script>
    const LifecycleDemo = {
        template: `
          <div>{{ message }}</div>`,
        data() {
            return {
                message: 'Hello, Vue!'
            };
        },
        beforeCreate() {
            console.log('beforeCreate: 实例刚刚被创建');
        },
        created() {
            console.log('created: 实例创建完成');
        },
        beforeMount() {
            console.log('beforeMount: 挂载开始之前');
        },
        mounted() {
            console.log('mounted: 实例已挂载');
        },
        beforeUpdate() {
            console.log('beforeUpdate: 数据更新之前');
        },
        updated() {
            console.log('updated: 数据更新完成');
        },
        beforeUnmount() {
            console.log('beforeUnmount: 实例卸载之前');
        },
        unmounted() {
            console.log('unmounted: 实例已卸载');
        }
    };

    const app = Vue.createApp({
        components: {
            LifecycleDemo
        }
    });

    app.mount('#app');
</script>

</body>
</html>

会在控制台打印出每个生命周期钩子函数的日志,帮助理解各个生命周期阶段的顺序和用途。

Vue API

全局 API

应用实例

通用

组合式 API

setup()

响应式: 核心

响应式: 工具

响应式: 进阶

生命周期钩子

依赖注入

选项式 API

状态选项

渲染选项

生命周期选项

组合选项

其他杂项

组件实例

内置内容

指令

组件

特殊元素

特殊 Attributes

单文件组件

语法定义

<script setup>

CSS 功能

进阶 API

渲染函数

服务端渲染

TypeScript 工具类型

自定义渲染

编译时标志

  • 12
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值