vue + echarts 快速入门

news2024/12/25 13:36:25

vue + echarts 快速入门

本案例即有nodejs和vue的基础,又在vue的基础上整合了echarts

vue echarts

Nodejs基础

1、Node简介

1.1、为什么学习Nodejs(了解)
  • 轻量级、高性能、可伸缩web服务器
  • 前后端JavaScript同构开发
  • 简洁高效的前端工程化
1.2、Nodejs能做什么(了解)

Node 打破了过去 JavaScript 只能在浏览器中运行的局面。前后端编程环境统一,大大降低了前后端语言切换的代价。以下是Node可以实现的工作:(仅作了解)

  • Web 服务器
  • 命令行工具
  • 网络爬虫
  • 桌面应用程序开发
  • app
  • 嵌入式
  • 游戏
1.3、Node是什么

Node.js®是基于 Chrome的V8 JavaScript 引擎构建的JavaScript运行环境。

Node.js不是新语言,也不是一个框架或者一个库,而是一个软件。

Node.js是一个 JavaScript 运行环境 ,说白了,就是用来运行js的。

官网:https://nodejs.org/en/

中文官网:https://nodejs.org/zh-cn/

1.4、Nodejs架构和运行过程

nodejs 架构
nodejs架构

1.4.1、Natives modules
  • 当前内容用JavaScript实现
  • 提供应用程序可以直接调用的模块,例如 fs、path、http等
  • JavaScript无法直接操作底层硬件
1.4.2、Builtin modules 胶水层
1.4.3、底层
  • V8:执行JavaScript代码,提供桥梁接口
  • libuv:事件循环 事件队列 异步IO
  • 第三方模块:zib http c-ares
1.4.1、libuv

Libevent、libev、libuv三个网络库,都是c语言实现的异步事件库

libevent :名气最大,应用最广泛,历史悠久的跨平台事件库;

libev :较libevent而言,设计更简练,性能更好,但对Windows支持不够好;

libuv :node开发需要一个跨平台的事件库,首选了libev,但又要支持Windows,故重新封装了一套,linux下用libev实现,Windows下用IOCP实现;

1.4.3、c-ares

C语言的异步DNS解析库,可以很方便的和使用者的事件循环统一起来,实现DNS的非阻塞异步解析,libcurl, libevent, gevent, nodejs都在使用。

1.5、Nodejs异步IO和事件驱动
  • IO是应用程序的瓶颈,异步IO提升性能无需原地等待返回结果返回,操作系统有对应的实现,
  • Nodejs单线程配事件驱动架构及libuv实现异步IO
1.5.1、Nodejs异步IO

mvc架构

同步阻塞

所谓同步,指的是协同步调。既然叫协同,所以至少要有2个以上的事物存在。协同的结果就是:

多个事物不能同时进行,必须一个一个的来,上一个事物结束后,下一个事物才开始。

那当一个事物正在进行时,其它事物都在干嘛呢?

严格来讲这个并没有要求,但一般都是处于一种“等待”的状态,因为通常后面事物的正常进行都需要依赖前面事物的结果或前面事物正在使用的资源。

因此,可以认为,同步更希望关注的是从宏观整体来看,多个事物是一种逐个逐个的串行化关系,绝对不会出现交叉的情况。

所以,自然也不太会去关注某个瞬间某个具体事物是处于一个什么状态。

把这个理论应用的出神入化的非“排队”莫属。凡是在资源少需求多的场景下都会用到排队

所谓阻塞,指的是阻碍堵塞。它的本意可以理解为由于遇到了障碍而造成的动弹不得。

同步阻塞

重复调用IO,判断IO是否结束沦陷技术

  1. read

  2. select:

    一个计算机函数,位于头文件#include <sys/select.h> 。该函数用于监视文件描述符的变化情况——读写或是异常。

  3. poll:

    Linux中的字符设备驱动中的一个函数。Linux 2.5.44版本后,poll被epoll取代。

    和select实现的功能差不多,poll的作用是把当前的文件指针挂到等待队列

  4. epoll

    epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

  5. kqueue

    kueue是在UNIX上比较高效的IO复用技术。
    所谓的IO复用,就是同时等待多个文件描述符就绪,以系统调用的形式提供。如果所有文件描述符都没有就绪的话,该系统调用阻塞,否则调用返回,允许用户进行后续的操作。
    常见的IO复用技术有select, poll, epoll以及kqueue等等。其中epoll为Linux独占,而kqueue则在许多UNIX系统上存在,包括OS X(好吧,现在叫macOS了。。)

  6. event ports

    跨协作组件的显式调用导致这些组件之间的紧密耦合。

    这会降低组件的可维护性、灵活性和可重用性。隐式调用模型解耦了组件,从而减少了组件间的依赖关系。

    然而,隐式调用倾向于忽略组件消息兴趣的历史确定性质,导致实现被保护代码污染以强制执行组件的时间顺序协议。

    将状态图与隐式调用相结合,可以在没有代码污染的情况下直接实现按时间排序的组件协议,从而为更清洁、更具适应性的组件协作策略提供了潜力。
    异步非阻塞

异步非阻塞

所谓异步,就是步调各异。既然是各异,那就是都不相同。所以结果就是:

多个事物可以你进行你的、我进行我的,谁都不用管谁,所有的事物都在同时进行中。

一言以蔽之,同步就是多个事物不能同时开工,异步就是多个事物可以同时开工。

所谓非阻塞,自然是和阻塞相对,可以理解为由于没有遇到障碍而继续畅通无阻。

异步

同步阻塞
异步非阻塞

期望无需注定判断的非阻塞IO

libuv 是一个跨平台的的基于事件驱动的异步I库,原先为 NodeJS 而写。

但是他提供的功能不仅仅是IO,包括进程、线程、信号、定时器、进程间通信等。

它围绕着事件驱动的异步I/O模型而设计。

这个库提供不仅仅是对不同I/O轮询机制的简单抽象,

还包括: ‘句柄’和‘流’对套接字和其他实体提供了高级别的抽象; 也提供了跨平台的文件I/O和线程功能,以及其他一些东西。

这是一份图表解释了组成libuv的不同组件和它们相关联的子系统:

libuvframework

从上图中我们看到

  • Libuv使用各平台提供的事件驱动模块实现异步(epoll, kqueue, IOCP, event ports)。

    他用来支持上层非文件io的模块。libuv把上层的事件和回调封装成io观察者(uv__io_t)放到底层的事件驱动模块。

    当事件触发的时候,libuv会执行io观察者中的回调。

  • Libuv实现一个线程池用来支持上层文件io、dns以及用户层耗cpu的任务。Libuv的整体执行架构

1.5.2、Nodejs事件驱动架构

Node.js 事件循环

Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。

Node.js 几乎每一个 API 都是支持回调函数的。

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。

Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。

这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式IO或者事件驱动IO)

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

eventloop

整个事件驱动的流程有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件

1.6、Nodejs单线程

Nodejs单线程指的是主线程是单线程,单线程实现高并发异步非阻塞IO配合事件回调通知

libuvframework

Nodejs单线程不适合CPU密集型业务场景,但是适合IO密集型高并发请求

前后端分离

2、安装Nodejs

官网:https://nodejs.org/en/

中文官网:https://nodejs.org/zh-cn/

中文下载页面:https://nodejs.org/zh-cn/download/current/

2.1、Linux 安装Nodejs

Linux 安装node

官网

mkdir ~/opt
wget https://nodejs.org/dist/v20.17.0/node-v20.17.0-linux-x64.tar.xz
tar -xvf node-v20.17.0-linux-x64.tar.xz -C ~/opt
mv ~/opt/node-v20.17.0-linux-x64 ~/opt/node

终端中输入sudo vim ~/.bashrc

在文档最后追加以下内容

export NODE_HOME=/home/lhz/opt/node
export PATH=$PATH:$NODE_HOME/bin

打开新控制台 输入

node -v
# 搭建环境时通过如下代码将npm设置成淘宝镜像
npm config set registry http://registry.npmmirror.com --global
# 查看镜像的配置结果
npm config get registry 
# 使用nrm工具切换淘宝源
npx nrm use taobao
# 如果之后需要切换回官方源可使用
npx nrm use npm

升级npm

npm install -g npm
npm install cnpm -g
npm install yarn -g
npm install pnpm -g
2.2、Windows安装Nodejs

powershell

控制台输入node --version

node --version

控制台输入node

node

控制台输入console.log(‘桃李不言下自成蹊’);

console.log('桃李不言下自成蹊');
# 搭建环境时通过如下代码将npm设置成淘宝镜像
npm config set registry http://registry.npmmirror.com --global
# 查看镜像的配置结果
npm config get registry 
# 使用nrm工具切换淘宝源
npx nrm use taobao

升级 npm

npm install -g npm
npm install cnpm -g
npm install yarn -g
npm install pnpm -g
3.1、使用WebStorm创建Nodejs项目并运行Nodejs代码
3.2、VSCode运行Nodejs代码
3.2.1、初始化Nodejs项目
# Linux
mkdir code
cd code
sudo npm init --yes
# Linux
mkdir code
cd ./project01
npm init --yes

返回

Wrote to D:\dev\node\code\project01\package.json:

{
  "name": "project01",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}

编写index.js文件

console.log('我爱你中国,亲爱的母亲');

控制台执行:node index.js

node index.js
3.2.2、VScode安装扩展Code Runner
3.3、Node的注意事项(了解)

nodejs : ECMAScript + 核心的api(重点) . 没有 DOM、 BOM

4、Nodejs核心模块API使用

4.1、模块化
4.1.1、模块化历程

传统开发常见问题导致项目难以维护不便于复用

  • 命名冲突和污染
  • 代码冗余,过多的无效请求
  • 文件间依赖关系纷杂

模块化是指将一个大的程序文件,拆分为许多小的文件(模块),然后将小的文件组合起来。

  • 防止命名冲突
  • 代码复用
  • 高维护性

模块化开发是一种管理方式,是一种生产方式,一种解决问题的方案,一个模块就是实现特定功能的文件,有了模块,想要什么功能,就加载什么模块

模块开发需要遵循一定的规范,否则就都乱套了,因此,才有了后来大家熟悉的AMD规范,CMD规范

ES6之前本身没有模块化,社区衍生出模块化产品

  • CommonJS ===> NodeJS、Browserify
  • AMD ===> RequireJS
  • CMD ===> SeazJS

CommonJS规范

CommonJS 是以在浏览器环境之外构建 javaScript 生态系统为目标而产生的写一套规范,

主要是为了解决 javaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行,该规范的主要内容是,

模块必须通过 module.exports 导出对外的变量或者接口,

通过 require() 来导入其他模块的输出到当前模块的作用域中;

目前在服务器和桌面环境中,node.js 遵循的是 CommonJS 的规范;

CommonJS 对模块的加载时同步的;

  • 模块引用
  • 模块标识
  • 模块定义

module属性

  • 任意js文件就是一个模块,可以直接使用module属性
  • id:返回模块标识符,一般是一个据对路径
  • filename:返回文件模块决定路径
  • loaded:返回布尔值,表示模块是否加载完成
  • parent:返回对象存放调用当前模块的模块
  • children:返回数组,存放当前模块调用其他模块
  • exports:返回当前模块需要暴露的内容
  • paths:返回数组,存放不同目录下的node_module位置

require属性

  • 基本功能:读入并且执行一个模块文件
  • reslove:模块文件据对路径
  • extensions:依据不同后缀名执行解析操作
  • main:返回主模块对象

AMD

AMD 即Asynchronous Module Definition,中文名是“异步模块定义”的意思,它采用异步方式加载模块,模块的加载不影响它后面语句的运行,所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行

一般来说,AMD是 RequireJS 在推广过程中对模块定义的规范化的产出,因为平时在开发中比较常用的是require.js进行模块的定义和加载,

一般是使用define来定义模块,使用require来加载模块

AMD 主要是为前端 js 的表现指定的一套规范;而 CommonJS 是主要为了 js 在后端的表现制定的,它不适合前端;

AMD 也是采用 require() 语句加载模块的,但是不同于 CommonJS ,它有两个参数;require([‘模块的名字’],callBack);requireJs 遵循的就是 AMD 规范;

// 定义模块
define('math',['jquery'], function ($) {//引入jQuery模块
    return {
        add: function(x,y){
            return x + y;
        }
    };
});
// 调用模块
require(['jquery','math'], function ($,math) {
    console.log(math.add(10,100));//110
});

CMD

CMD 即Common Module Definition通用模块定义,CMD规范是国内发展出来的,同时,CMD是在SeaaJS推广的过程中形成的,

CMD和AMD要解决的都是同个问题,在使用上也都很像,只不过两者在模块定义方式和模块加载时机上有所不同

CMD 也是通过异步的方式进行模块的加载的,不同于 AMD 的是,CMD 的加载是按照就近规则进行的,

AMD 依赖的是前置;CMD 在加载的使用的时候会把模块变为字符串解析一遍才知道依赖了哪个模块;

// 定义模块  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
  exports.data = 1;
});

// 加载模块
seajs.use(['myModule.js'], function(my){
    var star= my.data;
    console.log(star);  //1
});

ES6中的模块化

在ES6没有出来之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器,

ES module 在语言标准的层面上,实现了模块功能,而且相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案

ES module 中的模块化有一个比较大的特点,就是实现尽量的静态化

4.1.2、前端模块化

m01.js

export const slogan = '桃李不言下自成蹊';
export const love = function () {
    console.log('我爱你中国,亲爱的母亲');
}

m01.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模块化</title>
</head>

<body>

</body>

</html>
<script type="module">
    import * as m01 from './src/js/m01.js'
    console.log(m01.slogan);
    m01.love()
</script>

m02.js

const slogan = '桃李不言下自成蹊';
const love = function () {
    console.log('我爱你中国,亲爱的母亲');
}
export{slogan,love}

m02.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模块化</title>
</head>

<body>

</body>

</html>
<script type="module">
    import * as m02 from './src/js/m02.js'
    console.log(m02.slogan);
    m02.love()
    // 解构赋值
	import {slogan,love} from './src/js/m02.js';
    console.log(slogan);
    love()
</script>

m03.js

export default{
    slogan : '桃李不言下自成蹊',
    love :function () {
        console.log('我爱你中国,亲爱的母亲');
    }
}

m03.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模块化</title>
</head>

<body>

</body>

</html>
<script type="module">
    import * as m03 from './src/js/m03.js'
    console.log(m03.default.slogan);
    m03.default.love()
    // 解构赋值
	import {default as m003} from './src/js/m03.js';
    console.log(m003.slogan);
    m003.love()
    // 针对默认暴露 简便形式
    import m30 from './src/js/m03.js';
    console.log(m30.slogan);
    m30.love()
</script>

app.js

import * as m001 from "./m01.js"
import * as m002 from "./m02.js"
import {default as m300} from "./m03.js"
window.console.log(m001.slogan);
m001.love();
window.console.log(m002.slogan);
m002.love();
window.console.log(m300.slogan);
m300.love();

app.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模块化</title>
</head>

<body>

</body>

</html>
<script src="./src/js/app.js" type="module" charset="utf-8"></script>

由于兼容性问题,需要将高版本js代码转化为兼容性较好的低版本代码
使用babel可以轻松的进行转换
安装三个工具:babel-cli babel-preset-env browserify(webpack)

首先使用 init 初始化 npm 包文件

npm init --yes 	

安装babel工具

npm i babel-cli babel-preset-env browserify -D	

编译(js为源文件夹 dist/js 为目标文件夹,会自动生成)

–presets=babel-preset-env /可以单独配置babel配置文件实现

npx babel src/js -d dist/js --presets=babel-preset-env	

browserify

app.js中 require 浏览器还是不识别,需要再次转换

npx browserify dist/js/app.js -o dist/bundle.js	
npm init --yes 			
npm i babel-cli babel-preset-env browserify -D				
npx babel src/js -d dist/js --presets=babel-preset-env				
npx browserify dist/js/app.js -o dist/bundle.js						

bundle.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>bundle</title>
</head>
<body>
    
</body>
</html>
<script src="dist/bundle.js" type="text/javascript" charset="utf-8"></script>

引入npm模块

npm i jquery

jquery.js

// const $ = require('jquery')
import $ from 'jquery'
$('body').css('background', '#FA6060');
npx babel src/js -d dist/js --presets=babel-preset-env				
npx browserify dist/js/jquery.js -o dist/jquery.js		
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jquery</title>
</head>
<body>
    
</body>
</html>
<script src="./dist/jquery.js"></script>
4.1.3、Nodejs模块化

m01.js

const slogan = '桃李不言下自成蹊';
const love = () => {
    return '我爱你中国,亲爱的母亲';
}
module.exports = {
    slogan: slogan,
    love: love
}

r01.js

const m01 = require('./src/js/m01');
console.log(m01.slogan);
console.log(m01.love());

m02.js

console.log(module);

r02.js

const m02 = require('./src/js/m02');

m03.js

exports.name = 'lhz'

r03.js

const m03 = require('./src/js/m03');
console.log(m03);

m04.js

console.log('我嫁人了');

r04.js

const m04 = require('./src/js/m04');
console.log('我娶你');

m05

console.log('我嫁人了');
let date = new Date();
while (new Date() - date < 6000) {}

r05.js

const m05 = require('./src/js/m05');
console.log('我娶你');


4.2、Nodejs全局对象
  • 与浏览器中的window不完全相同
  • Nodejs全局对象global
4.3、Nodejs全局变量
  • __filename:返回正在执行脚本文件的绝对路径
  • __dirame:返回正在执行脚本文件所在目录的(相对路径)
  • time类函数:执行顺序与事件循环间的关系
  • process:提供与当前进程互动的接口
  • require:加载模块
  • exports:模块导出
  • module:模块导出
  • this:
module.exports 初始值为一个空对象 {}

exports 是指向的 module.exports 的引用

require() 返回的是 module.exports 而不是 exports
4.3.1、process
// const fs = require('fs');

console.log(process.memoryUsage());
console.log(process.cpuUsage());
console.log(process.cwd());
console.log(process.version);
console.log(process.versions);
console.log(process.arch);
console.log(process.env.Path);
console.log(process.env.USERPROFILE);
console.log(process.platform);


console.log(process.argv);
console.log(process.pid);
// 当前程序运行时间
console.log(process.uptime());


// 事件
process.on('exit',function (code) {
    console.log('exit : ' + code)
});

process.on('beforeExit',function (code) {
    console.log('beforeExit : ' + code)
});

//{
//   rss: 18563072,
//   heapTotal: 4321280,
//   heapUsed: 3440000,
//   external: 212402,
//   arrayBuffers: 11146
// }
// rss: (Resident Set Size)操作系统分配给进程的总的内存大小。
// heapTotal:堆的总大小,包括3个部分,
// 已分配的内存,用于对象的创建和存储,对应于heapUsed
// 未分配的但可用于分配的内存
// 未分配的但不能分配的内存,例如在垃圾收集(GC)之前对象之间的内存碎片
// heapUsed: 已分配的内存,即堆中所有对象的总大小,是heapTotal的子集。
// external: 进程使用到的系统链接库所占用的内存
// arrayBuffers:
4.3.2、path
  • basename()获取路径中的基础名称
  • dirname()获取路径中的目录名
  • extname()获取后缀名
  • isAbsolute()获取路径是否为绝对路径
  • join()路径拼接
  • reslove()返回绝对路径
  • parse()解析路径
  • formart()序列化路径
  • normalize()规范化路径

5、Nodejs创建HTTP服务

5.1、Nodejs创建HTTP服务
mkdir httpServer
cd httpServer
npm init --yes

新建文件node.js,编写代码如下:

// 引入http模块
var http = require('http');

// 使用http模块创建httpServer
var server = http.createServer();

// 处理HTTP请求
server.on('request', function (request, response) {
    // 设置响应头
    response.writeHead(200, { 'Content-type': 'text/html;charset=utf-8' });
    // 给客户端发送字符串
    response.write('<h3>我爱你中国<h3>');
    // 结束响应 (关闭输出流)
    response.end();
});

// 端口监听 (启动的服务)
server.listen(6633, function () {
    console.log('服务已启动,请访问http://127.0.0.1:6633');
});
node app.js

5.2、nodemon

全局安装nodemon

npm i nodemon -g

在项目目录以开发模式安装nodemon

npm i nodemon -D

编写package.json文件,在scripts中增加以下内容:

"start": "nodemon app.js"

修改后的package.json文件内容如下:

{
  "name": "httpserver",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^2.0.12"
  }
}

vue

使用vite创建vue工程

npm create vite@latest -- --template vue-app

cd vue-app
npm install
npm run dev

1、chap01

  1. 文本插值

    <script setup lang="ts">
    import {ref} from "vue";
    // ref 使用该函数装饰响应式的值
    const slogan = ref('以吾辈之青春,捍卫盛世之华夏');
    const msg = ref('欢迎台湾同胞回家');
    const html = ref('<a href="https://space.bilibili.com/480308139/">李昊哲小课</a>');
    const count = ref(0);
    </script>
    
    <template>
      <!--
      组件
      index.html -> main.ts -> chap01/HelloLhz.vue -> chap01/LhzA.vue
      -->
      <h1>文本插值</h1>
      <div>{{slogan}}</div>
      <div v-text="msg"></div>
      <!-- v-text = innerText -->
      <div v-text="html"></div>
      <!-- v-html = innerHTML -->
      <div v-html="html"></div>
      <div v-text="count"></div>
    </template>
    
    <style scoped>
    
    </style>
    
  2. 事件处理

    <script setup lang="ts">
    import {ref} from "vue";
    // ref 使用该函数装饰响应式的值
    const slogan = ref('以吾辈之青春,捍卫盛世之华夏');
    const msg = ref('欢迎台湾同胞回家');
    const html = ref('<a href="https://space.bilibili.com/480308139/">李昊哲小课</a>');
    const count = ref(0);
    const getMsg = () => {
      window.alert(msg.value);
    }
    const fun01 = () => {
      window.alert(1);
    }
    const fun02 = () => {
      window.alert(2);
    }
    const fun03 = (e) => {
      console.log(e);
    }
    const accumulation = () => {
      count.value++;
    }
    </script>
    
    <template>
      <h1>事件处理</h1>
      <div v-text="msg"></div>
      <!-- 默认情况下调用无参函数不加括号 -->
      <button type="button" v-on:click="getMsg">你过来呀</button>
      <!-- 默认情况下调用无参函数也可以加括号 -->
      <button type="button" v-on:click="getMsg()">你过来呀()</button>
      <!-- @动作指令 语法糖 -->
      <button type="button" @click="getMsg">语法糖</button>
      <!-- 同一个动作指令调同时调用多个函数的时候 函数需要加括号 -->
      <button type="button" @click="fun01(),fun02()">多事件绑定</button>
      <button type="button" @click="fun03($event)">事件对象参数</button>
      <button type="button" @click="count++" v-text="count"></button>
      <button type="button" @click="accumulation" v-text="count"></button>
      <!-- v-once 表示其内容只会被渲染一次 -->
      <div><span v-text="count" v-once></span></div>
    </template>
    
    <style scoped>
    
    </style>
    
  3. 事件修饰符

    <script setup lang="ts">
    const out = () => {
      window.alert("out_box");
    }
    const inner = () => {
      window.alert("inner_box");
    }
    const go = () => {
      window.alert("once");
    }
    
    </script>
    
    <template>
    <div>
      <h1>事件修饰符</h1>
      <!--
       prevent 取消事件默认行为
       stop 阻止事件冒泡
       once 只执行一次 仅一次生效
      -->
      <div>
        <form action="https://www.baidu.com/s?" method="get">
          <input type="search" name="wd" id="">
          <input type="submit" value="千度以下,你就知道" @click.prevent>
        </form>
      </div>
      <div id="out_box" @click="out">
        <div id="inner_box" @click.stop="inner">
          <!-- 链式调用 方法进执行一次 但 prevent 仅一次生效 -->
          <a href="https://space.bilibili.com/480308139/" @click.prevent.once="go">李昊哲-小课</a>
          <br>
          <!-- 分开写 一切正常 -->
          <a href="https://space.bilibili.com/480308139/" @click.prevent @click.once="go">李昊哲</a>
        </div>
      </div>
    </div>
    </template>
    
    <style scoped>
    #out_box{
      margin-top: 30px;
      width: 450px;
      height: 450px;
      background-color: #4365c3;
      text-align: center;
      position: relative;
      //color: #ffffff;
    }
    #inner_box{
      width: 300px;
      height: 300px;
      background-color: #929854;
      position: absolute;
      left: 75px;
      top: 75px;
    }
    </style>
    
  4. 属性绑定

    <script setup lang="ts">
    // 引入图片静态资源
    import logo from '../../assets/logo.png';
    import {ref} from "vue";
    
    const alt = ref('vue logo')
    const title = ref('忘记历史等于背叛');
    const width = ref(600);
    const id = ref('img-attr');
    </script>
    
    <template>
    <h1>属性绑定</h1>
      <img :src="logo" :alt="alt" :title="title" :width="width" :id="id"/>
    </template>
    
    <style scoped>
    
    </style>
    
  5. 动态属性

    <script setup lang="ts">
    // 引入图片静态资源
    import logo from '../../assets/logo.png';
    import coding from '../../assets/coding.jpg';
    import {ref} from "vue";
    
    const alt = ref('vue logo')
    const title = ref('忘记历史等于背叛');
    const figure = ref(600);
    const id = ref('img-attr');
    const woh = ref('width');
    const changeAttr = () => {
      woh.value = woh.value === 'width' ? 'height' : 'width';
    }
    </script>
    
    <template>
      <h1>动态属性</h1>
      <img :alt="alt" :src="coding" :title="title" :[woh]="figure" :id="id" @click="changeAttr">>
    </template>
    
    <style scoped>
    
    </style>
    

2、chap02

  1. v-show

    <script setup lang="ts">
    import computer_room from '../../assets/computer_room.png';
    import {ref} from "vue";
    
    const alt = ref('computer_room');
    const figure = ref(600);
    const title = ref('忘记历史等于背叛');
    const condition = ref(true)
    </script>
    
    <template>
      <h1 @click="condition = !condition">v-show</h1>
      <!-- v-show 控制元素是否显示 相当于 在css样式中使用 display; -->
      <!-- v-show 值为 true 相当于 在css样式中使用 display: block; -->
      <!-- v-show 值为 false 相当于 在css样式中使用 display: none; -->
      <img :src="computer_room" :alt="alt" :width="figure" :title="title" v-show="condition"/>
    </template>
    
    <style scoped>
    
    </style>
    
  2. v-if

    <script setup lang="ts">
    import computer_room from '../../assets/computer_room.png';
    import {ref} from "vue";
    
    const alt = ref('computer_room');
    const figure = ref(600);
    const title = ref('忘记历史等于背叛');
    const condition = ref(true)
    const gender = ref('男')
    const changeGender = () => {
      condition.value = !condition.value
      gender.value = condition.value ? '男' : '女'
    }
    const weekDay = ref(3);
    </script>
    
    <template>
      <h1 @click="condition = !condition">v-if</h1>
      <!-- v-if 控制元素是否显示 相当于 dom是否存在 -->
      <img :src="computer_room" :alt="alt" :width="figure" :title="title" v-if="condition"/>
      <h1 @click="changeGender" v-text="gender"></h1>
      <h1 v-if="condition">男性</h1>
      <h1 v-else>女性</h1>
      <span v-if="weekDay % 7 === 1">星期一</span>
      <span v-else-if="weekDay % 7 === 2">星期二</span>
      <span v-else-if="weekDay % 7 === 3">星期三</span>
      <span v-else-if="weekDay % 7 === 4">星期四</span>
      <span v-else-if="weekDay % 7 === 5">星期五</span>
      <span v-else-if="weekDay % 7 === 6">星期六</span>
      <span v-else>星期日</span>
    </template>
    
    <style scoped>
    
    </style>
    
  3. v-for

    <script setup lang="ts">
    import {reactive} from "vue";
    
    const names = reactive(['李昊哲', '大美丽', '李胜龙', '小可爱']);
    const person = reactive({
      name: '李昊哲',
      age: 43,
      gender: true
    })
    
    const persons = reactive([
      {
        id: 1,
        name: '李昊哲',
        age: 35,
        gender: true,
      }, {
        id: 2,
        name: '大美丽',
        age: 39,
        gender: false,
      }, {
        id: 3,
        name: '李胜龙',
        age: 39,
        gender: true,
      }, {
        id: 4,
        name: '小可爱',
        age: 27,
        gender: false,
      }
    ]);
    </script>
    
    <template>
      <h1>v-for</h1>
      <ul>
        <!--  第一个参数是列表元素 第二个参数是列表索引 -->
        <li v-for="(name,index) in names" v-text="name" :key="name"></li>
      </ul>
      <ul>
        <!--  第一个参数是列表元素 第二个参数是列表索引 -->
        <li v-for="(name,index) in names" v-text="names[index]" :key="name + index"></li>
      </ul>
      <!--  第一个参数是对象属性值 第二个参数是对象属性名 -->
      <div v-for="(value,field) in person" v-text="field + ':' + value" :key="field + ':' + value"></div>
      <table>
        <thead>
        <tr>
          <th>id</th>
          <th>姓名</th>
          <th>年龄</th>
          <th>性别</th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="(person,index) in persons" :key="field">
          <td v-for="(value,filed) in person" v-text="value"></td>
        </tr>
        </tbody>
      </table>
      <table>
        <thead>
        <tr>
          <th>id</th>
          <th>姓名</th>
          <th>年龄</th>
          <th>性别</th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="(person,index) in persons" :key="field">
          <td v-text="person.id"></td>
          <td v-text="person.name"></td>
          <td v-text="person.age"></td>
          <td v-text="person.gender === true? '男': '女'"></td>
        </tr>
        </tbody>
      </table>
    </template>
    
    <style scoped>
    ul > li {
      list-style: none;
    }
    
    table td {
      text-align: center;
      padding: 10px 30px;
    }
    </style>
    
  4. 计算属性

    <script setup lang="ts">
    import {computed, reactive, ref} from "vue";
    
    const slogan = ref('友谊第一,比赛第二');
    const time = ref(0);
    const countdown = setInterval(() => {
      time.value++;
      if (time.value === 10) {
        disabled.value = true;
        clearInterval(countdown);
      }
    }, 1000);
    
    const team = reactive({
      china: 0,
      korea: 0
    });
    const result = ref(computed(() => {
      if (team.china > team.korea) {
        return '中国队领先'
      } else if (team.china < team.korea) {
        return '韩国队领先'
      } else {
        return '势均力敌';
      }
    }))
    const disabled = ref(false);
    </script>
    
    <template>
      <h1>计算属性</h1>
      <h1 v-text="slogan"></h1>
      <h2>比赛时间:<span v-text="time"></span></h2>
      <h2>直播播报:<span v-text="result"></span></h2>
      <h2>
        中国队进球数:<span v-text="team.china"></span>
        <button type="button" @click="team.china++" :disabled="disabled">点击中国队进一球</button>
      </h2>
      <h2>
        韩国队进球数:<span v-text="team.korea"></span>
        <button type="button" @click="team.korea++" :disabled="disabled">点击韩国队进一球</button>
      </h2>
    </template>
    
    <style scoped>
    
    </style>
    
  5. 侦听器

    <script setup lang="ts">
    import {ref, watch, watchEffect} from "vue";
    
    const msg = ref('铭记历史勿忘国耻');
    watchEffect(() => {
      console.log('watchEffect msg >>> ' + msg.value)
    });
    
    // 第一个参数为侦听的对象
    watch(msg, (value, oldValue, onCleanup) => {
      // 第一个参数是变化后的值
      // 第二个参数是变化前的值
      console.log(`value >>> ${value} \t oldValue >>> ${oldValue}`);
    }, {
      immediate: true,
      // deep:当需要对对象等引用类型数据进行深度监听时,设置deep: true,默认值是false。
      // immediate:默认情况下watch是惰性的,设置immediate: true时,watch会在初始化时立即执行回调函数一次。
      // flush:控制回调函数的执行时机,。它可设置为 pre、post 或 sync。
      //   pre:默认值,当监听的值发生变更时,优先执行回调函数(在dom更新之前执行)。
      //   post:dom更新渲染完毕后,执行回调函数。
      //   sync:一旦监听的值发生了变化,同步执行回调函数(建议少用)
    });
    </script>
    
    <template>
      <input type="text" name="" id="" v-model="msg">
      <h1 v-text="msg"></h1>
    </template>
    
    <style scoped>
    
    </style>
    
  6. 侦听器案例

    <script setup lang="ts">
    import {computed, reactive, ref, watch} from "vue";
    
    const slogan = ref('友谊第一,比赛第二');
    const time = ref(0);
    const countdown = setInterval(() => {
      time.value++;
      if (time.value === 10) {
        disabled.value = true;
        clearInterval(countdown);
      }
    }, 1000);
    
    const team = reactive({
      china: 0,
      korea: 0
    });
    const result = ref('势均力敌')
    watch(team, (value, oldValue, onCleanup) => {
      if (team.china > team.korea) {
        result.value = '中国队领先'
      } else if (team.china < team.korea) {
        result.value = '韩国队领先'
      } else {
        result.value = '势均力敌';
      }
    }, {deep: true})
    const disabled = ref(false);
    </script>
    
    <template>
      <h1>计算属性</h1>
      <h1 v-text="slogan"></h1>
      <h2>比赛时间:<span v-text="time"></span></h2>
      <h2>直播播报:<span v-text="result"></span></h2>
      <h2>
        中国队进球数:<span v-text="team.china"></span>
        <button type="button" @click="team.china++" :disabled="disabled">点击中国队进一球</button>
      </h2>
      <h2>
        韩国队进球数:<span v-text="team.korea"></span>
        <button type="button" @click="team.korea++" :disabled="disabled">点击韩国队进一球</button>
      </h2>
    </template>
    
    <style scoped>
    
    </style>
    

3、chap03

表单操作

  1. v-model

    <script setup lang="ts">
    import {ref} from "vue";
    
    const msg = ref('山河统一,解放台湾');
    const male = ref(1);
    const female = ref(0);
    const checked = ref(true);
    const hobbies = ref(["swimming", "skiing"]);
    const hometown = ref('jilin');
    const fruits = ref(['pear']);
    </script>
    
    <template>
      <h1>v-model</h1>
      <input type="text" name="" id="" v-model="msg" />
      <h1 v-text="msg"></h1>
      性别:
      <label for="male">
        男:<input type="radio" name="gender" id="male" v-model="male" />
      </label>
      &nbsp;&nbsp;&nbsp;&nbsp;
      <label for="female">
        女:<input type="radio" name="gender" id="female" v-model="female" :checked="checked" />
      </label>
      <br>
      <label for="remember">
        记住我:<input type="checkbox" name="remember" id="remember" :checked="checked" />
      </label>
      <h3>多选按钮</h3>
      爱好:&nbsp;&nbsp;&nbsp;&nbsp;
      <input type="checkbox" id="swimming" name="hobby" value="swimming" v-model="hobbies">
      &nbsp;&nbsp;&nbsp;&nbsp;
      <label for="running">跑步</label>
      <input type="checkbox" id="running" name="hobby" value="running" v-model="hobbies">
      &nbsp;&nbsp;&nbsp;&nbsp;
      <label for="skiing">滑雪</label>
      <input type="checkbox" id="skiing" name="hobby" value="skiing" v-model="hobbies">
      <br>
      <h3>单选下拉列表</h3>
      籍贯:&nbsp;&nbsp;&nbsp;&nbsp;
      <select name="hometown" id="hometown" v-model="hometown">
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
        <option value="jilin">吉林</option>
      </select>
      <h3>多选下拉列表</h3>
      水果:&nbsp;&nbsp;&nbsp;&nbsp;
      <select name="fruits" id="fruits" multiple v-model="fruits">
        <option value="banana">香蕉</option>
        <option value="apple">苹果</option>
        <option value="pear">梨</option>
      </select>
    </template>
    
    <style scoped>
    
    </style>
    

4、chap04

父子组件

<script setup lang="ts">
const props = defineProps(['person','msg'])
const {msg} = props
const {person} = props
</script>

<template>
  <!-- 父子组件 defineProps 传值 子组件 -->
  <h1 v-text="props.msg"></h1>
  <h1 v-text="props.person.realName"></h1>
  <h1 v-text="props.person.slogan"></h1>
  <h1 v-text="msg"></h1>
  <h1 v-text="person.realName"></h1>
  <h1 v-text="person.slogan"></h1>
</template>

<style scoped>

</style>
  1. defineProps

    <script setup lang="ts">
    const props = defineProps(['person','msg'])
    const {msg} = props
    const {person} = props
    </script>
    
    <template>
      <!-- 父子组件 defineProps 传值 子组件 -->
      <h1 v-text="props.msg"></h1>
      <h1 v-text="props.person.realName"></h1>
      <h1 v-text="props.person.slogan"></h1>
      <h1 v-text="msg"></h1>
      <h1 v-text="person.realName"></h1>
      <h1 v-text="person.slogan"></h1>
    </template>
    
    <style scoped>
    
    </style>
    
  2. useAttrs

    <script setup lang="ts">
    import {useAttrs} from "vue";
    
    defineOptions({
      inheritAttrs: false,
    })
    
    const attrs = useAttrs()
    const {msg} = attrs
    const {person} = attrs
    </script>
    
    <template>
      <!-- 父子组件 useAttrs 传值 子组件 -->
      <h1 v-text="attrs.msg"></h1>
      <h1 v-text="attrs.person.realName"></h1>
      <h1 v-text="attrs.person.slogan"></h1>
      <h1 v-text="msg"></h1>
      <h1 v-text="person.realName"></h1>
      <h1 v-text="person.slogan"></h1>
    </template>
    
    <style scoped>
    
    </style>
    
  3. defineEmits

    <script setup lang="ts">
    import LhzE from "./LhzE.vue";
    import {reactive, ref} from "vue";
    
    const msg = ref('忘记历史等于背叛');
    const person = reactive({
      realName: '李昊哲',
      slogan: '河山统一,解放台湾'
    });
    const updateName = (value) => {
      person.realName = value;
    }
    </script>
    
    <template>
      <h1>父组件:<span v-text="msg"></span></h1>
      <h1>父组件:<span v-text="person.realName"></span></h1>
      <h1>父组件:<span v-text="person.slogan"></span></h1>
      <hr>
      <!-- 父子组件 defineProps 传值 父组件 -->
      <lhz-e :person="person" :msg="msg" @update="updateName"/>
    </template>
    
    <style scoped>
    
    </style>
    
    <script setup lang="ts">
    import {ref} from "vue";
    
    const props = defineProps(['person', 'msg'])
    const {msg} = props
    const {person} = props
    const newName = ref('');
    const emit = defineEmits()
    const updateName = () => {
      // 第一个参数为 父组件接收子组件的动作指令
      // 第二个参数为 子组件向父组件发送的数据
      emit('update', newName)
    }
    </script>
    
    <template>S
      <!-- 父子组件 defineProps 传值 子组件 -->
      <input type="text" name="" id="" autocomplete="off" v-model="newName" @input="updateName">
      <!-- 父子组件 defineProps 传值 子组件 -->
      <h1>子组件:<span v-text="msg"></span></h1>
      <h1>子组件:<span v-text="person.realName"></span></h1>
      <h1>子组件:<span v-text="person.slogan"></span></h1>
    </template>
    
    <style scoped>
    
    </style>
    
  4. provide 与 inject

    const slogan = Symbol();
    const obj = Symbol();
    const msg = Symbol();
    const location = Symbol();
    export {
        slogan,obj,msg,location
    }
    
    <script setup lang="ts">
    import LhzM from "./LhzM.vue";
    import {provide, reactive, ref} from "vue";
    import {slogan, obj, msg, location} from "./keys"
    
    const my_slogan = ref('河山一统');
    const my_obj = reactive({
      slogan: '解放台湾'
    });
    
    const my_msg = () => {
      return my_slogan;
    }
    // provide('slogan', my_slogan);
    // provide('obj', my_obj);
    // provide('msg', my_msg);
    
    provide(slogan, my_slogan);
    provide(obj, my_obj);
    provide(msg, my_msg);
    provide(location, {my_slogan, my_obj, my_msg});
    </script>
    
    <template>
      <h1>LhzL</h1>
      <lhz-m/>
    </template>
    
    <style scoped>
    
    </style>
    
    <script setup lang="ts">
    import LhzN from "./LhzN.vue";
    </script>
    
    <template>
      <h1>LhzM</h1>
      <lhz-n/>
    </template>
    
    <style scoped>
    
    </style>
    
    <script setup lang="ts">
    import {inject, ref} from "vue";
    import {slogan, obj, msg,location} from "./keys"
    // const new_slogan = inject('slogan')
    // const new_obj = inject('obj')
    // const new_msg01 = inject('msg')()
    // const new_msg02 = inject('msg')
    // const content = ref(new_msg02())
    
    const new_slogan = inject(slogan)
    const new_obj = inject(obj)
    const new_msg01 = inject(msg)()
    const new_msg02 = inject(msg)
    const content = ref(new_msg02())
    const {my_slogan,my_obj,my_msg} = inject(location)
    const result = ref(my_msg())
    </script>
    
    <template>
      <h1>LhzN</h1>
      <div v-text="new_slogan"></div>
      <div v-text="new_obj"></div>
      <div v-text="new_obj.slogan"></div>
      <div v-text="new_msg01"></div>
      <div v-text="content"></div>
      <hr>
      <div v-text="my_slogan"></div>
      <div v-text="my_obj"></div>
      <div v-text="my_obj.slogan"></div>
      <div v-text="result"></div>
    </template>
    
    <style scoped>
    
    </style>
    
  5. 选项卡案例

    <script setup lang="ts">
    import LhzTabBtn from "./LhzTabBtn.vue";
    import LhzTabContent from "./LhzTabContent.vue";
    import {ref} from "vue";
    
    const index = ref(0);
    const changeTab = (param) => {
      index.value = param;
    }
    </script>
    
    <template>
      <lhz-tab-btn @changeTab="changeTab"/>
      <lhz-tab-content :tabIndex="index"/>
    </template>
    
    <style scoped>
    </style>
    
    <script setup lang="ts">
    import {reactive} from "vue";
    
    const lis = reactive([
      {
        'title': '商品介绍',
        'classList': ['current'],
      }, {
        'title': '规格包装',
        'classList': [],
      }, {
        'title': '售后保障',
        'classList': [],
      }, {
        'title': '商品评价(66)',
        'classList': [],
      }, {
        'title': '手机社区',
        'classList': [],
      }
    ]);
    const emit = defineEmits(['changeTab']);
    const change = (index) => {
      // 遍历列表中中的所有元素
      outer:for (const item of lis) {
        // 获取列表中当前元素的的 classList 属性
        let class_list = item.classList;
        // 遍历 classList 即当前元素的css样式列表
        for (let i = 0; i < class_list.length; i++) {
          // 获取当前元素的css样式列表
          if (class_list[i] === 'current') {
            // 如果当前的 css 样式 列表中 存在 current 样式 则将 current 从当前列表移除
            class_list.splice(i, 1);
            // 结束外出循环
            break outer;
          }
        }
      }
      // 被点击的元素添加 current 样式
      lis[index].classList.push('current');
      // 将被点击的元素的 index 值 向上发射
      emit('changeTab', index);
    }
    </script>
    
    <template>
      <!-- 选项卡按钮 -->
      <div class="tab-btn">
        <ul>
          <li v-for="(item,index) in lis" :key="item.title + index" v-text="item.title" :class="item.classList" @click="change(index)"></li>
        </ul>
      </div>
    </template>
    
    <style scoped>
    .tab-btn {
      width: 990px;
      border-bottom: 1px solid #e4393c;
      color: #666;
      cursor: pointer;
      position: absolute;
      left: 0;
      top: 0;
    }
    
    ul > li {
      list-style: none;
      float: left;
      font-size: 14px;
      width: 160px;
      height: 38px;
      line-height: 38px;
      text-align: center;
    }
    
    .current {
      background-color: #e4393c;
      color: #fff;
    }
    </style>
    
    <script setup lang="ts">
    import {reactive, useAttrs, watchEffect} from "vue";
    
    const contents = reactive([
      {
        'title': '商品介绍',
        isShow: true
      }, {
        'title': '规格包装',
        isShow: false
      }, {
        'title': '售后保障',
        isShow: false
      }, {
        'title': '商品评价(66)',
        isShow: false
      }, {
        'title': '手机社区',
        isShow: false
      }
    ]);
    
    const attrs = useAttrs();
    watchEffect(() => {
      for (const index in contents) {
        contents[index].isShow = attrs.tabIndex === parseInt(index)
      }
    })
    </script>
    
    <template>
      <div class="tab-content">
        <div v-for="(content,index) in contents" :key="content.title + ' ' +  index" v-text="content.title" v-show="content.isShow"></div>
      </div>
    </template>
    
    <style scoped>
    .tab-content {
      width: 100%;
      color: #666;
      background-color: #ffffff;
      position: absolute;
      top: 80px;
      left: 0;
      padding: 30px;
      font-size: 66px;
    }
    </style>
    

5、chap05

插槽

  1. slot

    <script setup lang="ts">
    import LhzA from "./LhzA.vue";
    import {ref} from "vue";
    const username = ref('李胜龙');
    const love = () => {
      username.value = '李昊哲';
    }
    </script>
    
    <template>
      <lhz-a>
        <template v-slot:nav>
          <!-- 具名插槽 -->
          <div>
            头部导航
          </div>
        </template>
        <template v-slot:default>
          <!-- 默认插槽 -->
          <input type="text" autocomplete="off" v-model="username">
          <button type="button" @click="love">love</button>
        </template>
        <template #footer>
          <!-- 具名插槽缩写 -->
          <div>
            友情链接
          </div>
        </template>
        <template #lhz-aa/>
        <template #lhz-bb="{names}">
          <ul>
            <li v-for="(name,index) in names" v-text="name"></li>
          </ul>
        </template>
        <template #lhz-cc="{item}">
          <span v-text="item"></span>
        </template>
      </lhz-a>
    </template>
    
    <style scoped>
    
    </style>
    
    <script setup lang="ts">
    import {reactive} from "vue";
    
    const names = reactive(['李昊哲', '李大宝', '李胜龙']);
    </script>
    
    <template>
      <slot name="nav"/>
      <slot/>
      <h1>我是LhzA</h1>
      <slot/>
      <slot name="footer"/>
      <hr>
      <ul>
        <li v-for="(name,index) in names" v-text="name"></li>
      </ul>
      <hr>
      <slot name="lhz-aa">
        <ul>
          <li v-for="(name,index) in names" v-text="name"></li>
        </ul>
      </slot>
      <slot name="lhz-bb" :names="names"/>
      <ul>
        <li v-for="(item,index) in names">
          <slot name="lhz-cc" :item="item"/>
        </li>
      </ul>
    </template>
    
    <style scoped>
    
    </style>
    

6、chap06

步骤:
  1. 安装路由

    npm install vue-router@4
    
  2. 准备视图组件

    • HomeView.vue
    • AboutView.vue
  3. 编写路由规则

    import {createMemoryHistory, createRouter} from 'vue-router'
    
    import HomeView from "../../views/chap06/HomeView.vue";
    import AboutView from "../../views/chap06/AboutView.vue";
    
    const routes = [
        {path: '/home', component: HomeView},
        {path: '/about', component: AboutView},
    ]
    
    const router = createRouter({
        // 4. 内部提供了 history 模式的实现。
        // memory 模式。createMemoryHistory
        // hash 模式。createWebHashHistory
        // html5 模式。createWebHistory
        history: createMemoryHistory(),
        routes,
    })
    export default router
    
  4. main.ts 或者 main.js 文件中引入了并使用路由规则

    import { createApp } from 'vue'
    import './style.css'
    import router from "./router/chap06/router.ts";
    import App from './App.vue'
    const app = createApp(App)
    app.use(router)
    app.mount('#app')
    
    
  5. 在组件中编写路由跳转按钮

    <script setup lang="ts">
    import {useRoute} from "vue-router";
    
    const route = useRoute()
    </script>
    
    <template>
      <h1>忘记历史等于背叛</h1>
      <p>
        <strong>
          当前路由路径:<span v-text="route.fullPath"/>
        </strong>
      </p>
      <!--使用 router-link 组件进行导航 -->
      <!--通过传递 `to` 来指定链接 -->
      <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
      <router-link to="/home">Go to Home</router-link>
      &nbsp;&nbsp;&nbsp;&nbsp;
      <router-link to="/about">Go to About</router-link>
      <router-view/>
    </template>
    
    <style scoped>
    
    </style>
    
路由模式

hash模式

memory模式

html5模式

router.ts 路由配置文件

import {createMemoryHistory, createWebHistory,createWebHashHistory,createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";

const routes = [
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
处理404
  1. 准备404页面

    <script setup lang="ts">
    
    </script>
    
    <template>
      <h1>NotFound</h1>
    </template>
    
    <style scoped>
    
    </style>
    
  2. router.ts 路由配置文件

    import {createMemoryHistory, createWebHistory,createWebHashHistory,createRouter} from 'vue-router'
    
    import HomeView from "../../views/chap06/HomeView.vue";
    import AboutView from "../../views/chap06/AboutView.vue";
    import NotFoundView from "../../views/chap06/NotFoundView.vue";
    
    const routes = [
        // 处理404
        {path:'/:pathMatch(.*)*',component:NotFoundView},
        {path: '/', component: HomeView},
        {path: '/home', component: HomeView},
        {path: '/about', component: AboutView},
    ]
    
    const router = createRouter({
        // 4. 内部提供了 history 模式的实现。
        // memory 模式。createMemoryHistory
        // hash 模式。createWebHashHistory
        // html5 模式。createWebHistory
        history: createWebHistory(),
        routes,
    })
    export default router
    
动态路由传参
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
</template>

<style scoped>

</style>
import {createMemoryHistory, createWebHistory,createWebHashHistory,createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";

const routes = [
    // 处理404
    {path:'/:pathMatch(.*)*',component:NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {computed, reactive, ref} from "vue";
import {useRoute} from "vue-router";

const orders = reactive([
  {
    id: '1',
    productName: '商品1'
  },
  {
    id: '2',
    productName: '商品2'
  },
  {
    id: '3',
    productName: '商品3'
  },
  {
    id: '4',
    productName: '商品4'
  },
  {
    id: '5',
    productName: '商品5'
  },
]);
const productName = ref(computed(() => {
  const order = orders.find(item => item.id === useRoute().params.id);
  if (order) {
    return order.productName;
  } else {
    return null;
  }
}));
</script>

<template>
  <h1>Order</h1>
  <h1 v-text="productName"></h1>
</template>

<style scoped>

</style>
嵌套路由
<script setup lang="ts">

</script>

<template>
  <h1>UserLoginView</h1>
</template>

<style scoped>

</style>
<script setup lang="ts">

</script>

<template>
  <h1>UserDetailView</h1>
</template>

<style scoped>

</style>
import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";
import UserLoginView from "../../views/chap06/UserLoginView.vue";
import UserDetailView from "../../views/chap06/UserDetailView.vue";

const routes = [
    // 处理404
    {path: '/:pathMatch(.*)*', component: NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
    {
        //  嵌套路由
        path: '/user',
        children: [
            {path: 'login', component: UserLoginView},
            {path: 'detail', component: UserDetailView},
        ]
    },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
</template>

<style scoped>

</style>
命名路由与别名
<script setup lang="ts">

</script>

<template>
  <h1>TestA</h1>
</template>

<style scoped>

</style>
import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";
import UserLoginView from "../../views/chap06/UserLoginView.vue";
import UserDetailView from "../../views/chap06/UserDetailView.vue";
import TestA from "../../views/chap06/TestA.vue";

const routes = [
    // 处理404
    {path: '/:pathMatch(.*)*', component: NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
    {
        //  嵌套路由
        path: '/user',
        children: [
            {path: 'login', component: UserLoginView},
            {path: 'detail', component: UserDetailView},
        ]
    },
    {
        path: '/testA',
        // 命名理由
        name: 'nameA',
        // 路由别名 指的是路径的别名
        alias: ['/ta1', '/ta2'],
        component: TestA
    },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
  <br/>
  <RouterLink to="/testA">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/testA'}">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{name:'nameA'}">Go to nameA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta1'}">Go to TestA alias ta1</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta2'}">Go to TestA alias ta2</RouterLink>
</template>

<style scoped>

</style>
路由传参
params
<script setup>
import {computed, ref} from "vue";
import {useRoute} from "vue-router";

const id = ref(computed(() => {
  return useRoute().params.id;
}));
</script>

<template>
  <h1>TestB</h1>
  <h1 v-text="id"></h1>
</template>

<style scoped>

</style>
import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";
import UserLoginView from "../../views/chap06/UserLoginView.vue";
import UserDetailView from "../../views/chap06/UserDetailView.vue";
import TestA from "../../views/chap06/TestA.vue";
import TestB from "../../views/chap06/TestB.vue";

const routes = [
    // 处理404
    {path: '/:pathMatch(.*)*', component: NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
    {
        //  嵌套路由
        path: '/user',
        children: [
            {path: 'login', component: UserLoginView},
            {path: 'detail', component: UserDetailView},
        ]
    },
    {
        path: '/testA',
        // 命名理由
        name: 'nameA',
        // 路由别名 指的是路径的别名
        alias: ['/ta1', '/ta2'],
        component: TestA
    },
    {
        // 路由传参
        name: 'nameB',
        path: '/testB/:id',
        component: TestB
    },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
  <br/>
  <RouterLink to="/testA">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/testA'}">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta1'}">Go to TestA alias ta1</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta2'}">Go to TestA alias ta2</RouterLink>
  <br/>
  <!-- 路由传参 -->
  <router-link :to="{ name: 'nameB', params: { id: '3' }}">Go to nameB</router-link>
</template>

<style scoped>

</style>
query
<script setup>
import {computed, ref} from "vue";
import {useRoute} from "vue-router";

const id = ref(computed(() => {
  return useRoute().params.id;
}));
</script>

<template>
  <h1>TestB</h1>
  <h1 v-text="id"></h1>
</template>

<style scoped>

</style>
import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";
import UserLoginView from "../../views/chap06/UserLoginView.vue";
import UserDetailView from "../../views/chap06/UserDetailView.vue";
import TestA from "../../views/chap06/TestA.vue";
import TestB from "../../views/chap06/TestB.vue";
import TestC from "../../views/chap06/TestC.vue";

const routes = [
    // 处理404
    {path: '/:pathMatch(.*)*', component: NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
    {
        //  嵌套路由
        path: '/user',
        children: [
            {path: 'login', component: UserLoginView},
            {path: 'detail', component: UserDetailView},
        ]
    },
    {
        path: '/testA',
        // 命名理由
        name: 'nameA',
        // 路由别名 指的是路径的别名
        alias: ['/ta1', '/ta2'],
        component: TestA
    },
    {
        // 路由传参
        name: 'nameB',
        path: '/testB/:id',
        component: TestB
    },
    {
        // 路由传参
        name: 'nameC',
        path: '/testC',
        component: TestC
    },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
  <br/>
  <RouterLink to="/testA">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/testA'}">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta1'}">Go to TestA alias ta1</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta2'}">Go to TestA alias ta2</RouterLink>
  <br/>
  <!-- 路由传参 -->
  <router-link :to="{ name: 'nameB', params: { id: '3' }}">Go to nameB</router-link>
  <router-link :to="{ name: 'nameC', query: { id: '3' }}">Go to nameC</router-link>
</template>

<style scoped>

</style>
重定向
import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";
import UserLoginView from "../../views/chap06/UserLoginView.vue";
import UserDetailView from "../../views/chap06/UserDetailView.vue";
import TestA from "../../views/chap06/TestA.vue";
import TestB from "../../views/chap06/TestB.vue";
import TestC from "../../views/chap06/TestC.vue";

const routes = [
    // 处理404
    {path: '/:pathMatch(.*)*', component: NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
    {
        //  嵌套路由
        path: '/user',
        children: [
            {path: 'login', component: UserLoginView},
            {path: 'detail', component: UserDetailView},
        ]
    },
    {
        path: '/testA',
        // 命名理由
        name: 'nameA',
        // 路由别名 指的是路径的别名
        alias: ['/ta1', '/ta2'],
        component: TestA
    },
    {
        // 路由传参
        name: 'nameB',
        path: '/testB/:id',
        component: TestB
    },
    {
        // 路由传参
        name: 'nameC',
        path: '/testC',
        component: TestC
    },
    {
        path: '/test_redirect',
        // redirect: '/testA',
        // redirect: '/ta1',
        redirect: {name: 'nameA'},
    },
    {
        name: 'redirect_with_query',
        path: '/redirect_with_query',
        redirect: {name: 'nameC'},
    },
    {
        name: 'redirect_with_params',
        path: '/redirect_with_params/:id',
        redirect: to => {
            console.log(to)
            // 方法接收目标路由作为参数
            // return 重定向的字符串路径/路径对象
            // return {path: '/testC', query: {id: to.params.id}}
            return {name: 'nameB', params: {id: to.params.id}}
        },
    },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
  <br/>
  <RouterLink to="/testA">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/testA'}">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta1'}">Go to TestA alias ta1</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta2'}">Go to TestA alias ta2</RouterLink>
  <br/>
  <!-- 路由传参 -->
  <router-link :to="{ name: 'nameB', params: { id: '3' }}">Go to nameB</router-link>
  <router-link :to="{ name: 'nameC', query: { id: '3' }}">Go to nameC</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <!-- redirect 重定向 -->
  <RouterLink :to="{path: '/test_redirect'}">Go to test_redirect</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'redirect_with_query', query: { id: '3' }}">Go to redirect_with_query</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'redirect_with_params', params: { id: '3' }}">Go to redirect_with_params</router-link>
</template>

<style scoped>

</style>
props传递路由组件
<script setup lang="ts">
import {ref} from "vue";

const props = defineProps(['id'])
const id = ref(props.id)
</script>

<template>
  <h1>TestD</h1>
  <h1>将 props 传递给路由组件 布尔模式</h1>
  <h1 v-text="id"></h1>
</template>

<style scoped>

</style>
<script setup lang="ts">
import {ref} from "vue";

const props = defineProps(['did'])
const id = ref(props.did)
</script>

<template>
  <h1>TestE</h1>
  <h1>将 props 传递给路由组件 对象模式</h1>
  <h1 v-text="did"></h1>
</template>

<style scoped>

</style>
<script setup lang="ts">
import {ref} from "vue";

const props = defineProps(['did'])
const id = ref(props.did)
</script>

<template>
  <h1>TestF</h1>
  <h1>将 props 传递给路由组件 对象模式</h1>
  <h1 v-text="did"></h1>
</template>

<style scoped>

</style>
import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'

import HomeView from "../../views/chap06/HomeView.vue";
import AboutView from "../../views/chap06/AboutView.vue";
import NotFoundView from "../../views/chap06/NotFoundView.vue";
import OrderView from "../../views/chap06/OrderView.vue";
import UserLoginView from "../../views/chap06/UserLoginView.vue";
import UserDetailView from "../../views/chap06/UserDetailView.vue";
import TestA from "../../views/chap06/TestA.vue";
import TestB from "../../views/chap06/TestB.vue";
import TestC from "../../views/chap06/TestC.vue";
import TestD from "../../views/chap06/TestD.vue";
import TestE from "../../views/chap06/TestE.vue";
import TestF from "../../views/chap06/TestF.vue";

const routes = [
    // 处理404
    {path: '/:pathMatch(.*)*', component: NotFoundView},
    {path: '/', component: HomeView},
    {path: '/home', component: HomeView},
    {path: '/about', component: AboutView},
    // {path: '/order/:id', component: OrderView},
    {path: '/order/:id(\\d+)', component: OrderView},
    // {path: '/order/:id(.*)', component: OrderView},
    // {path: '/order/:id(.?)', component: OrderView},
    {
        //  嵌套路由
        path: '/user',
        children: [
            {path: 'login', component: UserLoginView},
            {path: 'detail', component: UserDetailView},
        ]
    },
    {
        path: '/testA',
        // 命名理由
        name: 'nameA',
        // 路由别名 指的是路径的别名
        alias: ['/ta1', '/ta2'],
        component: TestA
    },
    {
        // 路由传参
        name: 'nameB',
        path: '/testB/:id',
        component: TestB
    },
    {
        // 路由传参
        name: 'nameC',
        path: '/testC',
        component: TestC
    },
    {
        path: '/test_redirect',
        // redirect: '/testA',
        // redirect: '/ta1',
        redirect: {name: 'nameA'},
    },
    {
        name: 'redirect_with_query',
        path: '/redirect_with_query',
        redirect: {name: 'nameC'},
    },
    {
        name: 'redirect_with_params',
        path: '/redirect_with_params/:id',
        redirect: to => {
            console.log(to)
            // 方法接收目标路由作为参数
            // return 重定向的字符串路径/路径对象
            // return {path: '/testC', query: {id: to.params.id}}
            return {name: 'nameB', params: {id: to.params.id}}
        },
    },
    // 将 props 传递给路由组件
    {
        name: 'nameD',
        path: '/testD/:id',
        component: TestD,
        // 布尔模式
        props: true,
    },
    {
        name: 'nameE',
        path: '/testE/:id',
        component: TestE,
        // 对象模式
        props: to => ({did: to.params.id}),
    },
    {
        name: 'nameF',
        path: '/testF',
        component: TestF,
        // 对象模式
        props: to => ({did: to.query.id}),
    },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。
    // memory 模式。createMemoryHistory
    // hash 模式。createWebHashHistory
    // html5 模式。createWebHistory
    history: createWebHistory(),
    routes,
})
export default router
<script setup lang="ts">
import {useRoute} from "vue-router";

const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <router-view/>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
  <br/>
  <RouterLink to="/testA">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/testA'}">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta1'}">Go to TestA alias ta1</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta2'}">Go to TestA alias ta2</RouterLink>
  <br/>
  <!-- 路由传参 -->
  <router-link :to="{ name: 'nameB', params: { id: '3' }}">Go to nameB</router-link>
  <router-link :to="{ name: 'nameC', query: { id: '3' }}">Go to nameC</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <!-- redirect 重定向 -->
  <RouterLink :to="{path: '/test_redirect'}">Go to test_redirect</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'redirect_with_query', query: { id: '3' }}">Go to redirect_with_query</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'redirect_with_params', params: { id: '3' }}">Go to redirect_with_params</router-link>
  <br>
  <!-- 将 props 传递给路由组件 -->
  <router-link :to="{ name: 'nameD', params: { id: '3' }}">Go to nameD</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'nameE', params: { id: '3' }}">Go to nameE</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'nameF', query: { id: '3' }}">Go to nameF</router-link>
</template>

<style scoped>

</style>
命名视图
<script setup lang="ts">

</script>

<template>
<h1>LeftSidebar</h1>
</template>

<style scoped>

</style>
<script setup lang="ts">

</script>

<template>
  <h1>MainContent</h1>
</template>

<style scoped>

</style>
<script setup lang="ts">

</script>

<template>
  <h1>RightSidebar</h1>
</template>

<style scoped>

</style>
编程式路由
<script setup lang="ts">
import {useRoute, useRouter} from "vue-router";
// 路由对象
const router = useRouter()

// 路由对象中地方路由表
const toTestA = () => {
  router.push('/testA')
}
const toTestAbyName = () => {
  router.push({
    name: "nameA"
  })
}
const toTestA1 = () => {
  router.push('/ta1')
}
const toTestA2 = () => {
  router.push('/ta2')
}

const toTestB = () => {
  router.push({
    name: "nameB",
    params: {id: 3}
  })
}
const toTestC = () => {
  router.push({
    name: "nameC",
    query: {id: 3}
  })
}
const toBack = () => {
  router.back()
}
const route = useRoute()
</script>

<template>
  <h1>忘记历史等于背叛</h1>
  <p>
    <strong>
      当前路由路径:<span v-text="route.fullPath"/>
    </strong>
  </p>
  <!--使用 router-link 组件进行导航 -->
  <!--通过传递 `to` 来指定链接 -->
  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
  <router-link to="/home">Go to Home</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/about">Go to About</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link to="/order/3">Go to Order</router-link>
  <main style="display: flex; justify-content: space-between; width: 1280px;">
    <!--命名视图 -->
    <router-view name="LeftSidebar" class="content"/>
    <router-view class="content"/>
    <router-view name="RightSidebar" class="content"/>
  </main>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/login">Go to UserLogin</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink to="/user/detail">Go to UserDetail</RouterLink>
  <br/>
  <RouterLink to="/testA">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/testA'}">Go to TestA</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta1'}">Go to TestA alias ta1</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <RouterLink :to="{path: '/ta2'}">Go to TestA alias ta2</RouterLink>
  <br/>
  <!-- 路由传参 -->
  <router-link :to="{ name: 'nameB', params: { id: '3' }}">Go to nameB</router-link>
  <router-link :to="{ name: 'nameC', query: { id: '3' }}">Go to nameC</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <!-- redirect 重定向 -->
  <RouterLink :to="{path: '/test_redirect'}">Go to test_redirect</RouterLink>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'redirect_with_query', query: { id: '3' }}">Go to redirect_with_query</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'redirect_with_params', params: { id: '3' }}">Go to redirect_with_params</router-link>
  <br>
  <!-- 将 props 传递给路由组件 -->
  <router-link :to="{ name: 'nameD', params: { id: '3' }}">Go to nameD</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'nameE', params: { id: '3' }}">Go to nameE</router-link>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <router-link :to="{ name: 'nameF', query: { id: '3' }}">Go to nameF</router-link>
  <hr>
  <button type="button" @click="toTestA">TestA</button>
  <button type="button" @click="toTestAbyName">TestAbyName</button>
  <button type="button" @click="toTestA1">TestA1</button>
  <button type="button" @click="toTestA2">TestA2</button>
  <button type="button" @click="toTestB">nameB</button>
  <button type="button" @click="toTestC">nameC</button>
  <button type="button" @click="toBack">back</button>
</template>

<style scoped>
.content {
  display: flex;
  justify-content: center;
  width: 1280px;
}
</style>

7、chap07

ajax

fetch

axios

axios
npm install axios --save
  1. 获取静态数据

    准备 json 格式的的数据文件,保存在项目的 public 目录

    <script setup lang="ts">
    import axios from "axios";
    
    const axios_get_books = () => {
      axios.get("/books.json")
          .then((response) => {
            console.log(response.data);
          })
          .catch((error) => {
            console.log(error);
          });
    }
    </script>
    
    <template>
      <h1 @click="axios_get_books">AxiosLocalView</h1>
    </template>
    
    <style scoped>
    
    </style>
    
    import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'
    import NotFoundView from "../../views/chap06/NotFoundView.vue";
    import AxiosLocalView from "../../views/chap07/AxiosLocalView.vue";
    import LoginView from "../../views/chap07/LoginView.vue";
    import IndexView from "../../views/chap07/IndexView.vue";
    import UserView from "../../views/chap07/UserView.vue";
    
    const routes = [
        // 处理404
        {path: '/:pathMatch(.*)*', component: NotFoundView},
        {
            name: 'AxiosLocalView',
            path: '/AxiosLocalView',
            component: AxiosLocalView,
        },
        {
            name: 'login',
            path: '/',
            component: LoginView,
        },
        {
            name: 'index',
            path: '/index',
            component: IndexView,
        },
        {
            name: 'user',
            path: '/user/:id(\\d+)',
            component: UserView,
        },
    
    ]
    
    const router = createRouter({
        // 4. 内部提供了 history 模式的实现。
        // memory 模式。createMemoryHistory
        // hash 模式。createWebHashHistory
        // html5 模式。createWebHistory
        history: createWebHistory(),
        routes,
    })
    export default router
    
  2. axios跨域

    创建express项目

    mkdir express
    cd express
    
    npx --yes --package express-generator express --force --no-view
    
    npm install
    npm start
    

    网页访问

    npm i nodemon -D
    

    修改 package.json 文件

    {
      "name": "express",
      "version": "0.0.0",
      "private": true,
      "scripts": {
        "start": "nodemon ./bin/www"
      },
      "dependencies": {
        "cookie-parser": "~1.4.4",
        "debug": "~2.6.9",
        "express": "~4.16.1",
        "morgan": "~1.9.1"
      },
      "devDependencies": {
        "nodemon": "^3.1.7"
      }
    }
    
    

    修改 vite.config.ts 文件实现跨域代理

    import {defineConfig} from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    const base_url = 'http://localhost:3000'
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
      ],
      server: {
        host: '0.0.0.0',
        strictPort: true,
        open: true,
        proxy: {
          // 使用 proxy 实例
          '/api': {
            target: 'http://localhost:3000',
            changeOrigin: true,
            rewrite: (path) => path.replace(/^\/api/, 'api'),
          },
        }
      },
    })
    
    

    编写组件

    <script setup lang="ts">
    import axios from "axios";
    
    const axios_get_books = () => {
      // axios.get("http://localhost:3000/api/books")
      axios.get("/api/books")
          .then((response) => {
            console.log(response.data);
          }).catch((error) => {
        console.log(error)
      })
    }
    </script>
    
    <template>
      <h1 @click="axios_get_books">AxiosRemoteView</h1>
    </template>
    
    <style scoped>
    
    </style>
    

    编写路由

    import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'
    import NotFoundView from "../../views/chap06/NotFoundView.vue";
    import AxiosLocalView from "../../views/chap07/AxiosLocalView.vue";
    import AxiosRemoteView from "../../views/chap07/AxiosRemoteView.vue";
    import LoginView from "../../views/chap07/LoginView.vue";
    import IndexView from "../../views/chap07/IndexView.vue";
    import UserView from "../../views/chap07/UserView.vue";
    
    
    const routes = [
      // 处理404
      {path: '/:pathMatch(.*)*', component: NotFoundView},
      {
        name: 'AxiosLocalView',
        path: '/AxiosLocalView',
        component: AxiosLocalView,
      },
      {
        name: 'AxiosRemoteView',
        path: '/AxiosRemoteView',
        component: AxiosRemoteView,
      },
      {
        name: 'login',
        path: '/',
        component: LoginView,
      },
      {
        name: 'index',
        path: '/index',
        component: IndexView,
      },
      {
        name: 'user',
        path: '/user/:id(\\d+)',
        component: UserView,
      },
    
    ]
    
    const router = createRouter({
      // 4. 内部提供了 history 模式的实现。
      // memory 模式。createMemoryHistory
      // hash 模式。createWebHashHistory
      // html5 模式。createWebHistory
      history: createMemoryHistory(),
      routes,
    })
    export default router
    
路由过度
  1. 安装 animate.css 动画

    npm install animate.css --save
    
  2. main.ts 文件中引入

    import 'animate.css'
    
  3. 编写路由出口组件 HelloLhz

    <script setup lang="ts">
    
    </script>
    
    <template>
      <!-- 想要在路由组件上使用转场 并对导航进行动画处理 -->
      <router-view v-slot="{Component ,route}">
        <!--<transition name="fade" mode="out-in">-->
        <transition mode="out-in"
                    :enter-active-class="`animate__animated ${route.meta.transitionIn}`"
                    :leave-active-class="`animate__animated ${route.meta.transitionOut}`">
          <component :is="Component"/>
        </transition>
      </router-view>
    </template>
    
    <style scoped>
    
    </style>
    
  4. 编写登录组件

    <script setup lang="ts">
    import {reactive} from "vue";
    import {useRouter} from "vue-router";
    
    const user = reactive({
      account: '',
      password: '',
    });
    const router = useRouter();
    const onSubmit = () => {
      router.push({
        name: "index",
        query: user
      });
    };
    </script>
    <template>
      <div class="form_container">
        <div class="form_item">
          账号:<input type="text" name="account" class="form_input" v-model="user.account">
        </div>
        <div class="form_item">
          密码:<input type="password" name="password" class="form_input" v-model="user.password">
        </div>
        <button type="button" @click="onSubmit">登录</button>
      </div>
    </template>
    
    <style scoped>
    .form_container {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-content: space-between;
      width: 300px;
    }
    
    .form_item {
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      align-items: center;
      padding-bottom: 10px;
    }
    
    .form_input {
      width: 220px;
      height: 30px;
      padding-left: 15px;
    }
    </style>
    
  5. 编写登录后的组件

    <script setup lang="ts">
    </script>
    
    <template>
      <h1>忘记历史等于背叛</h1>
      <div>
        <!-- 想要在路由组件上使用转场 并对导航进行动画处理 -->
        <router-view v-slot="{Component ,route}">
          <!--<transition name="fade" mode="out-in">-->
          <transition mode="out-in"
                      :enter-active-class="`animate__animated ${route.meta.transitionIn}`"
                      :leave-active-class="`animate__animated ${route.meta.transitionOut}`">
            <component :is="Component"/>
          </transition>
        </router-view>
      </div>
    </template>
    
    <style scoped>
    
    </style>
    
  6. 编写路由

    import {createMemoryHistory, createWebHistory, createWebHashHistory, createRouter} from 'vue-router'
    import NotFoundView from "../../views/chap06/NotFoundView.vue";
    import AxiosLocalView from "../../views/chap07/AxiosLocalView.vue";
    import AxiosRemoteView from "../../views/chap07/AxiosRemoteView.vue";
    import LoginView from "../../views/chap07/LoginView.vue";
    import IndexView from "../../views/chap07/IndexView.vue";
    import UserView from "../../views/chap07/UserView.vue";
    
    
    const routes = [
      // 处理404
      {path: '/:pathMatch(.*)*', component: NotFoundView},
      {
        name: 'AxiosLocalView',
        path: '/AxiosLocalView',
        component: AxiosLocalView,
      },
      {
        name: 'AxiosRemoteView',
        path: '/AxiosRemoteView',
        component: AxiosRemoteView,
      },
      {
        name: 'login',
        path: '/',
        component: LoginView,
        meta: {
          requiresAuth: true,
          transitionIn: 'animate__zoomIn',
          transitionOut: 'animate__zoomOut',
        },
      },
      {
        name: 'index',
        path: '/index',
        component: IndexView,
        meta: {
          requiresAuth: true,
          transitionIn: 'animate__zoomIn',
          transitionOut: 'animate__zoomOut',
        },
      },
      {
        name: 'user',
        path: '/user/:id(\\d+)',
        component: UserView,
      },
    
    ]
    
    const router = createRouter({
      // 4. 内部提供了 history 模式的实现。
      // memory 模式。createMemoryHistory
      // hash 模式。createWebHashHistory
      // html5 模式。createWebHistory
      history: createMemoryHistory(),
      routes,
    })
    export default router
    

8、chap08

piaia

  1. 安装pinia

    npm install pinia
    
  2. main.ts 文件中 引入 pinia

    import { createApp } from 'vue'
    import './style.css'
    
    // import router from "./router/chap06/router.ts";
    // import router from "./router/chap07/router.ts";
    import router from "./router/chap08/router.ts";
    import App from './App.vue'
    
    import { createPinia } from 'pinia';
    
    const app = createApp(App)
    app.use(router)
    
    app.mount('#app')
    
    
  3. 编写状态文件 store.ts

    import {defineStore} from 'pinia'
    import {computed, reactive, ref} from "vue";
    
    // 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。
    // (比如 `useUserStore`,`useCartStore`,`useProductStore`)
    // 第一个参数是你的应用中 Store 的唯一 ID。
    // export const useStore = defineStore('main', {
    //   // 为了完整类型推理,推荐使用箭头函数
    //   state: () => {
    //     return {
    //       count: 0,
    //       users: [
    //         {
    //           name: 'user0',
    //           gender: 0
    //         },
    //         {
    //           name: 'user1',
    //           gender: 1
    //         },
    //         {
    //           name: 'user2',
    //           gender: 0
    //         },
    //         {
    //           name: 'user3',
    //           gender: 1
    //         },
    //         {
    //           name: 'user4',
    //           gender: 0
    //         },
    //       ]
    //     }
    //   },
    //   getters: {
    //     getByGender: (state, gender) => {
    //       return state.users.fiter(user => user.gender === gender.value);
    //     }
    //   },
    //   actions: {
    //     findByGender: (state, gender) => {
    //       return state.users.fiter(user => user.gender === gender.value);
    //     }
    //   },
    // })
    
    
    export const useStore = defineStore('main', () => {
      // ref() 和 reactive() 就是 state 属性
      // computed() 就是 getters
      // function() 就是 actions
    
      const count = ref(0);
      const users = reactive([
        {
          name: 'user0',
          gender: 0
        },
        {
          name: 'user1',
          gender: 1
        },
        {
          name: 'user2',
          gender: 0
        },
        {
          name: 'user3',
          gender: 1
        },
        {
          name: 'user4',
          gender: 0
        },
      ]);
    
      function increment() {
        count.value++
      }
    
      function updateCount(num) {
        count.value += num;
      }
    
      const gender = ref(-1)
      const persons = computed(() => {
        return gender.value === -1 ? users : users.filter(user => user.gender === gender.value)
      });
    
      function updateByGender(sex) {
        gender.value = sex
      }
    
      return {count, increment, updateCount, persons, updateByGender}
    });
    
  4. 编写视图

    <script setup lang="ts">
    import {useStore} from "../../store/chap08/store.ts";
    import {storeToRefs} from "pinia";
    
    const store = useStore();
    const {count, persons} = storeToRefs(store)
    const {increment, updateCount,updateByGender} = store
    </script>
    
    <template>
      <div>
        <h1 v-text="count" @click="count++"></h1>
        <h1 v-text="count" @click="increment"></h1>
        <h1 v-text="count" @click="updateCount(5)"></h1>
        <button type="button" @click="updateByGender(-1)">红男绿女</button>
        <button type="button" @click="updateByGender(1)">精神小伙儿</button>
        <button type="button" @click="updateByGender(0)">扒蒜老妹儿</button>
        <table>
          <thead>
          <tr>
            <th>姓名</th>
            <th>性别</th>
          </tr>
          </thead>
          <tbody>
          <tr v-for="(person,index) in persons" :key="person.name">
            <td v-text="person.name"></td>
            <td v-text="person.gender === 1 ? '精神小伙儿':'扒蒜老妹儿'"></td>
          </tr>
          </tbody>
        </table>
      </div>
    </template>
    
    <style scoped>
    table {
      width: 400px;
    }
    </style>
    
  5. 编写路由

    import {createRouter, createMemoryHistory} from "vue-router";
    import LhzA from "../../views/chap08/LhzA.vue";
    
    const routes = [
      {
        path: "/",
        component: LhzA
      },
    ]
    const router = createRouter({
      // 4. 内部提供了 history 模式的实现。
      // memory 模式。createMemoryHistory
      // hash 模式。createWebHashHistory
      // html5 模式。createWebHistory
      history: createMemoryHistory(),
      routes,
    })
    export default router
    

9、chap09

less、sass、scss

element plus

echarts

scss
  1. 安装sass、scss

    npm install scss --save-dev
    npm install scss-loader --save-dev
    npm install sass --save-dev
    npm install sass-loader --save-dev
    
  2. 编写scss文件

    var.scss

  3. vite.config.ts文件中配置scss

    import {defineConfig} from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    const base_url = 'http://localhost:3000'
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
      ],
      server: {
        host: '0.0.0.0',
        strictPort: true,
        open: true,
        proxy: {
          // 使用 proxy 实例
          '/api': {
            target: 'http://localhost:3000',
            changeOrigin: true,
            rewrite: (path) => path.replace(/^\/api/, 'api'),
          },
        }
      },
      css: {
        preprocessorOptions: {
          scss: {
            // additionalData: '@import "./src/style/var.scss";'
            additionalData: `@use "./src/style/var.scss" as *;`,
          }
        }
      }
    })
    
    
element plus
  1. 安装 element plus

    npm install element-plus --save
    
  2. 引入 element plus

    完整引入

    按需导入

    自动导入(本配置使用自动加载)

    vite.config.ts

    import {defineConfig} from 'vite'
    import vue from '@vitejs/plugin-vue'
    import AutoImport from 'unplugin-auto-import/vite'
    import Components from 'unplugin-vue-components/vite'
    import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
    
    const base_url = 'http://localhost:3000'
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
        AutoImport({
          resolvers: [ElementPlusResolver()],
        }),
        Components({
          // resolvers: [ElementPlusResolver({importStyle: "sass"})],
          resolvers: [ElementPlusResolver()],
        }),
      ],
      server: {
        host: '0.0.0.0',
        strictPort: true,
        open: true,
        proxy: {
          // 使用 proxy 实例
          '/api': {
            target: 'http://localhost:3000',
            changeOrigin: true,
            rewrite: (path) => path.replace(/^\/api/, 'api'),
          },
        }
      },
      css: {
        preprocessorOptions: {
          scss: {
            // additionalData: '@import "./src/style/var.scss";'
            additionalData: `@use "./src/style/var.scss" as *;`,
          }
        }
      }
    })
    
    
echarts

vue echarts

  1. 快速入门

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>ECharts</title>
        <!-- 引入刚刚下载的 ECharts 文件 -->
        <script src="echarts.js"></script>
      </head>
      <body>
        <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
        <div id="main" style="width: 600px;height:400px;"></div>
        <script type="text/javascript">
          // 基于准备好的dom,初始化echarts实例
          var myChart = echarts.init(document.getElementById('main'));
    
          // 指定图表的配置项和数据
          var option = {
            title: {
              text: 'ECharts 入门示例'
            },
            tooltip: {},
            legend: {
              data: ['销量']
            },
            xAxis: {
              data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
            },
            yAxis: {},
            series: [
              {
                name: '销量',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
              }
            ]
          };
    
          // 使用刚指定的配置项和数据显示图表。
          myChart.setOption(option);
        </script>
      </body>
    </html>
    
  2. 安装 echarts

    npm install echarts --save
    
  3. vue 整合 echarts

    var.scss文件中配置单个组件高度变量

    $chartHeight: 490px
    

    CharBar.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    
    const chartBar = ref()
    const attrs = useAttrs()
    const {charColor} = attrs
    
    // const chartBar = useTemplateRef('chartBar')
    function chartBarInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartBar.value, 'dark');
      // 指定图表的配置项和数据
      const option = reactive({
        color: charColor,
        title: {
          text: '柱状图'
        },
        tooltip: {},
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartBarInit()
    })
    </script>
    
    <template>
      <div id="chartBar" ref="chartBar"></div>
    </template>
    
    <style scoped lang="scss">
    #chartBar {
      width: 100%;
      height: $chartHeight;
    }
    </style>
    
  4. 首页布局

    IndexView.vue

    <script setup lang="ts">
    import ChartBar from "./chartBar/ChartBar.vue";
    import ChartLine from "./chartLine/ChartLine.vue";
    import ChartMap from "./chartMap/ChartMap.vue";
    import ChartPie from "./chartPie/ChartPie.vue";
    import ChartRadar from "./chartRadar/ChartRadar.vue";
    </script>
    
    <template>
      <div>
        <div class="index-container">
           <div class="side-item">
             <div class="item">
               <chart-bar :charColor="'#1e9fff'"/>
             </div>
             <div class="item">
               <chart-pie/>
             </div>
           </div>
          <div class="side-item" style="flex: 0 1 150%">
            <div class="item">
              <chart-map/>
            </div>
          </div>
           <div class="side-item">
             <div class="item">
               <chart-radar/>
             </div>
             <div class="item">
               <chart-line :charColor="'#1e9fff'"/>
             </div>
           </div>
        </div>
      </div>
    </template>
    
    <style scoped>
    .index-container {
      width: 100%;
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      align-items: stretch;
    
      .side-item {
        width: 100%;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        align-content: stretch;
      }
    
      .item {
        padding: 10px;
      }
    }
    </style>
    
  5. 柱状图

    ChartBar.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    
    const chartBar = ref()
    const attrs = useAttrs()
    const {charColor} = attrs
    
    // const chartBar = useTemplateRef('chartBar')
    function chartBarInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartBar.value, 'dark');
      // 指定图表的配置项和数据
      const option = reactive({
        color: charColor,
        title: {
          text: '柱状图'
        },
        tooltip: {},
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartBarInit()
    })
    </script>
    
    <template>
      <div id="chartBar" ref="chartBar"></div>
    </template>
    
    <style scoped lang="scss">
    #chartBar {
      width: 100%;
      height: $chartHeight;
    }
    </style>
    

    ChartBarView.vue

    <script setup lang="ts">
    import ChartBar from "./ChartBar.vue";
    </script>
    
    <template>
      <div>
        <div class="index-container">
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-bar :charColor="'#dd6b66'"/>
            </div>
          </div>
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-bar :charColor="'#1e9fff'"/>
            </div>
          </div>
        </div>
        <div class="index-container">
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-bar :charColor="'#16b777'"/>
            </div>
          </div>
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-bar :charColor="'#a233c6'"/>
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <style scoped>
    .index-container {
      width: 100%;
      display: flex;
      flex-direction: row;
      .item{
        padding: 10px;
      }
    }
    </style>
    
  6. 折线图

    ChartLine.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    
    const chartLine = ref()
    const attrs = useAttrs()
    const {charColor} = attrs
    
    // const chartBar = useTemplateRef('chartBar')
    function chartBarInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartLine.value, 'dark');
      // 指定图表的配置项和数据
      const option = reactive({
        color: charColor,
        title: {
          text: '折线图'
        },
        tooltip: {},
        legend: {
          data: ['pay']
        },
        xAxis: {
          type: 'category',
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            name: 'pay',
            data: [820, 932, 901, 934, 1290, 1330, 1320],
            type: 'line',
            smooth: true
          }
        ]
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartBarInit()
    })
    </script>
    
    <template>
      <div id="chartLine" ref="chartLine"></div>
    </template>
    
    <style scoped lang="scss">
    #chartLine {
      width: 100%;
      height: $chartHeight;
    }
    </style>
    

    ChartLineView.vue

    <script setup lang="ts">
    import ChartLine from "./ChartLine.vue";
    </script>
    
    <template>
      <div>
        <div class="index-container">
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-line :charColor="'#dd6b66'"/>
            </div>
          </div>
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-line :charColor="'#1e9fff'"/>
            </div>
          </div>
        </div>
        <div class="index-container">
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-line :charColor="'#16b777'"/>
            </div>
          </div>
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-line :charColor="'#a233c6'"/>
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <style scoped>
    .index-container {
      width: 100%;
      display: flex;
      flex-direction: row;
      .item{
        padding: 10px;
      }
    }
    </style>
    
  7. 饼图

    ChartPie.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    
    const chartPie = ref()
    const attrs = useAttrs()
    
    // const chartPie = useTemplateRef('chartPie')
    function chartPieInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartPie.value, 'dark');
      // 指定图表的配置项和数据
      const option = reactive({
        title: {
          text: 'Referer of a Website',
          subtext: 'Fake Data',
          left: 'center'
        },
        tooltip: {
          trigger: 'item'
        },
        legend: {
          orient: 'vertical',
          left: 'left'
        },
        series: [
          {
            name: 'Access From',
            type: 'pie',
            radius: '50%',
            data: [
              {value: 1048, name: 'Search Engine'},
              {value: 735, name: 'Direct'},
              {value: 580, name: 'Email'},
              {value: 484, name: 'Union Ads'},
              {value: 300, name: 'Video Ads'}
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
              }
            }
          }
        ]
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartPieInit()
    })
    </script>
    
    <template>
      <div id="chartPie" ref="chartPie"></div>
    </template>
    
    <style scoped lang="scss">
    #chartPie {
      width: 100%;
      height: $chartHeight;
    }
    </style>
    

    ChartPieView.vue

    <script setup lang="ts">
    import ChartPie from "./ChartPie.vue";
    </script>
    
    <template>
      <div>
        <div class="index-container">
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-pie/>
            </div>
          </div>
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-pie/>
            </div>
          </div>
        </div>
        <div class="index-container">
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-pie/>
            </div>
          </div>
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-pie/>
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <style scoped>
    .index-container {
      width: 100%;
      display: flex;
      flex-direction: row;
      .item{
        padding: 10px;
      }
    }
    </style>
    
  8. 雷达图

    ChartRadar.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    
    const chartRadar = ref()
    const attrs = useAttrs()
    
    // const chartRadar = useTemplateRef('chartRadar')
    function chartRadarInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartRadar.value, 'dark');
      // 指定图表的配置项和数据
      const option = reactive({
        title: {
          text: 'Basic Radar Chart'
        },
        legend: {
          data: ['Allocated Budget', 'Actual Spending']
        },
        radar: {
          // shape: 'circle',
          indicator: [
            {name: 'Sales', max: 6500},
            {name: 'Administration', max: 16000},
            {name: 'Information Technology', max: 30000},
            {name: 'Customer Support', max: 38000},
            {name: 'Development', max: 52000},
            {name: 'Marketing', max: 25000}
          ]
        },
        series: [
          {
            name: 'Budget vs spending',
            type: 'radar',
            data: [
              {
                value: [4200, 3000, 20000, 35000, 50000, 18000],
                name: 'Allocated Budget'
              },
              {
                value: [5000, 14000, 28000, 26000, 42000, 21000],
                name: 'Actual Spending'
              }
            ]
          }
        ]
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartRadarInit()
    })
    </script>
    
    <template>
      <div id="chartRadar" ref="chartRadar"></div>
    </template>
    
    <style scoped lang="scss">
    #chartRadar {
      width: 100%;
      height: $chartHeight;
    }
    </style>
    

    ChartRadarView.vue

    <script setup lang="ts">
    import ChartRadar from "./ChartRadar.vue";
    </script>
    
    <template>
      <div>
        <div class="index-container">
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-radar/>
            </div>
          </div>
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-radar/>
            </div>
          </div>
        </div>
        <div class="index-container">
          <div style="flex: 0 1 70%">
            <div class="item">
              <chart-radar/>
            </div>
          </div>
          <div style="flex: 0 1 30%">
            <div class="item">
              <chart-radar/>
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <style scoped>
    .index-container {
      width: 100%;
      display: flex;
      flex-direction: row;
      .item{
        padding: 10px;
      }
    }
    </style>
    
  9. 地图

    提前准备省份json文件和经纬度坐标文件

    ChartMap.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    import china_province from "../../../store/chap09/china_province.json"
    import china_geo from "../../../store/chap09/china_full.json"
    
    const chartMap = ref()
    const attrs = useAttrs()
    const convertData = function (data) {
      let res = [];
      for (let i = 0; i < data.length; i++) {
        let geoCoord = china_geo.features[i].properties;
        if (geoCoord) {
          res.push({
            name: data[i].name,
            value: geoCoord.center.concat(data[i].value)
          });
        }
      }
      return res;
    };
    
    // const chartMap = useTemplateRef('chartMap')
    function chartMapInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartMap.value, 'dark');
      // 注入地图数据 GeoJson 注意第一个参数为 china 才会显示 海南岛缩略图 其它名字不会
      echarts.registerMap('china', china_geo);
      // 指定图表的配置项和数据
      const option = reactive({
        title: {
          text: '全国主要城市空气质量 - 百度地图',
          left: 'center'
        },
        tooltip: {
          trigger: 'item',
          formatter: function (params) {
            // console.log(params);
            return `${params.data.name}:${params.data.value[2]}`
          }
        },
        // 地图配置
        geo: {
          type: 'map',
          // chinaMap 这个参数 为 echarts.registerMap('chinaMap', response); 参数中第一个参数
          // 注意参数为 china 才会显示 海南岛缩略图 其它名字不会
          map: 'china',
          // 是否开启鼠标缩放和平移漫游。默认不开启。如果只想要开启缩放或者平移,可以设置成 'scale' 或者 'move'。设置成 true 为都开启
          roam: true,
          // 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等
          label: {
            // 是否显示标签
            show: true
          },
          // 默认缩放比例
          zoom: 1.1,
          // 地图中心点坐标
          // 当前视角的中心点,默认使用原始坐标(经纬度)。如果设置了projection则用投影后的坐标表示。
          // center: [125.3245, 43.886841]
    
        },
        series: [
          {
            geoIndex: 0,
            type: 'effectScatter',
            // 配置何时显示特效
            // 'render' 绘制完成后显示特效
            // 'emphasis' 高亮(hover)的时候显示特效。
            showEffectOn: 'render',
            // data: [{ name: '北京市', value: [116.405285, 39.904989, 9] }],
            data: convertData(china_province),
            // 使用地理坐标系,通过 geoIndex 指定相应的地理坐标系组件。
            coordinateSystem: 'geo',
            symbolSize: function (param) {
              // console.log(param[2]);
              return param[2] / 2
            },
          },
    
        ],
        // 是视觉映射组件,用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道
        visualMap: {
          min: 0,
          max: 50,
          // 筛选
          calculable: true
        }
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartMapInit()
    })
    </script>
    
    <template>
      <div id="chartMap" ref="chartMap"></div>
    </template>
    
    <style scoped lang="scss">
    #chartMap {
      width: 100%;
      height: 1000px;
    }
    </style>
    
  10. 编写路由

    import {createRouter, createMemoryHistory} from "vue-router";
    import IndexView from "../../views/chap09/IndexView.vue";
    import ChartBarView from "../../views/chap09/chartBar/ChartBarView.vue";
    import ChartLineView from "../../views/chap09/chartLine/ChartLineView.vue";
    import CharPieView from "../../views/chap09/chartPie/ChartPieView.vue";
    import ChartRadarView from "../../views/chap09/chartRadar/ChartRadarView.vue";
    import ChartMap from "../../views/chap09/chartMap/ChartMap.vue";
    
    const routes = [
      {
        path: "/",
        name: "index",
        component: IndexView
      },
      {
        path: "/ChartBarView",
        name: "ChartBarView",
        component: ChartBarView
      },
      {
        path: "/ChartLineView",
        name: "ChartLineView",
        component: ChartLineView
      },
      {
        path: "/CharPieView",
        name: "CharPieView",
        component: CharPieView
      },
      {
        path: "/ChartRadarView",
        name: "ChartRadarView",
        component: ChartRadarView
      },
      {
        path: "/ChartMap",
        name: "ChartMap",
        component: ChartMap
      },
    ]
    const router = createRouter({
      // 4. 内部提供了 history 模式的实现。
      // memory 模式。createMemoryHistory
      // hash 模式。createWebHashHistory
      // html5 模式。createWebHistory
      history: createMemoryHistory(),
      routes,
    })
    export default router
    

width: 100%;
display: flex;
flex-direction: row;
.item{
padding: 10px;
}
}

```

  1. 地图

    提前准备省份json文件和经纬度坐标文件

    ChartMap.vue

    <script setup>
    import * as echarts from 'echarts';
    // import { BarChart } from 'echarts/charts';
    import {ref, reactive, onMounted, useAttrs, useTemplateRef} from "vue";
    import china_province from "../../../store/chap09/china_province.json"
    import china_geo from "../../../store/chap09/china_full.json"
    
    const chartMap = ref()
    const attrs = useAttrs()
    const convertData = function (data) {
      let res = [];
      for (let i = 0; i < data.length; i++) {
        let geoCoord = china_geo.features[i].properties;
        if (geoCoord) {
          res.push({
            name: data[i].name,
            value: geoCoord.center.concat(data[i].value)
          });
        }
      }
      return res;
    };
    
    // const chartMap = useTemplateRef('chartMap')
    function chartMapInit() {
      // 基于准备好的dom,初始化echarts实例
      const myChart = echarts.init(chartMap.value, 'dark');
      // 注入地图数据 GeoJson 注意第一个参数为 china 才会显示 海南岛缩略图 其它名字不会
      echarts.registerMap('china', china_geo);
      // 指定图表的配置项和数据
      const option = reactive({
        title: {
          text: '全国主要城市空气质量 - 百度地图',
          left: 'center'
        },
        tooltip: {
          trigger: 'item',
          formatter: function (params) {
            // console.log(params);
            return `${params.data.name}:${params.data.value[2]}`
          }
        },
        // 地图配置
        geo: {
          type: 'map',
          // chinaMap 这个参数 为 echarts.registerMap('chinaMap', response); 参数中第一个参数
          // 注意参数为 china 才会显示 海南岛缩略图 其它名字不会
          map: 'china',
          // 是否开启鼠标缩放和平移漫游。默认不开启。如果只想要开启缩放或者平移,可以设置成 'scale' 或者 'move'。设置成 true 为都开启
          roam: true,
          // 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等
          label: {
            // 是否显示标签
            show: true
          },
          // 默认缩放比例
          zoom: 1.1,
          // 地图中心点坐标
          // 当前视角的中心点,默认使用原始坐标(经纬度)。如果设置了projection则用投影后的坐标表示。
          // center: [125.3245, 43.886841]
    
        },
        series: [
          {
            geoIndex: 0,
            type: 'effectScatter',
            // 配置何时显示特效
            // 'render' 绘制完成后显示特效
            // 'emphasis' 高亮(hover)的时候显示特效。
            showEffectOn: 'render',
            // data: [{ name: '北京市', value: [116.405285, 39.904989, 9] }],
            data: convertData(china_province),
            // 使用地理坐标系,通过 geoIndex 指定相应的地理坐标系组件。
            coordinateSystem: 'geo',
            symbolSize: function (param) {
              // console.log(param[2]);
              return param[2] / 2
            },
          },
    
        ],
        // 是视觉映射组件,用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道
        visualMap: {
          min: 0,
          max: 50,
          // 筛选
          calculable: true
        }
      });
    
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
      window.addEventListener('resize', function () {
        myChart.resize();
      });
    }
    
    onMounted(() => {
      chartMapInit()
    })
    </script>
    
    <template>
      <div id="chartMap" ref="chartMap"></div>
    </template>
    
    <style scoped lang="scss">
    #chartMap {
      width: 100%;
      height: 1000px;
    }
    </style>
    
  2. 编写路由

    import {createRouter, createMemoryHistory} from "vue-router";
    import IndexView from "../../views/chap09/IndexView.vue";
    import ChartBarView from "../../views/chap09/chartBar/ChartBarView.vue";
    import ChartLineView from "../../views/chap09/chartLine/ChartLineView.vue";
    import CharPieView from "../../views/chap09/chartPie/ChartPieView.vue";
    import ChartRadarView from "../../views/chap09/chartRadar/ChartRadarView.vue";
    import ChartMap from "../../views/chap09/chartMap/ChartMap.vue";
    
    const routes = [
      {
        path: "/",
        name: "index",
        component: IndexView
      },
      {
        path: "/ChartBarView",
        name: "ChartBarView",
        component: ChartBarView
      },
      {
        path: "/ChartLineView",
        name: "ChartLineView",
        component: ChartLineView
      },
      {
        path: "/CharPieView",
        name: "CharPieView",
        component: CharPieView
      },
      {
        path: "/ChartRadarView",
        name: "ChartRadarView",
        component: ChartRadarView
      },
      {
        path: "/ChartMap",
        name: "ChartMap",
        component: ChartMap
      },
    ]
    const router = createRouter({
      // 4. 内部提供了 history 模式的实现。
      // memory 模式。createMemoryHistory
      // hash 模式。createWebHashHistory
      // html5 模式。createWebHistory
      history: createMemoryHistory(),
      routes,
    })
    export default router
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2181208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP-2;CSNSDWSSC;肿瘤血管及M1型巨噬细胞靶向肽

【TCP-2 简介】 TCP-2&#xff08;Tumor-conditioned medium-stimulated Clone-2&#xff09;是一种特定的靶向肽&#xff0c;主要针对肿瘤微环境中的肿瘤血管和M1型巨噬细胞。这种肽在肿瘤治疗领域显示出巨大的潜力&#xff0c;尤其是在提高药物递送的精确性、增强免疫介导的抗…

【网络安全】绕过 Etplorer 管理面板实现RCE

未经许可,不得转载。 文章目录 正文使用 ffuf 进行FUZZ查找漏洞漏洞复现目标网站:https://app.redacted.com 正文 使用 ffuf 进行FUZZ ffuf -u https://app.redacted.com/FUZZ -w wordlist.txt -c -r-c:表示彩色输出,方便用户在终端中查看结果。 -r:忽略响应中的重定向…

【重学 MySQL】四十三、多行子查询

【重学 MySQL】四十三、多行子查询 使用 IN 子查询示例&#xff1a;查找属于特定部门的员工 使用 ANY 和 ALL 子查询使用 ANY使用 ALL 注意事项 在 MySQL 中&#xff0c;多行子查询&#xff08;也称为 IN 子查询&#xff09;是指子查询返回多行数据&#xff0c;并且这些数据用于…

新质农业——水资源可持续管理

橙蜂智能公司致力于提供先进的人工智能和物联网解决方案&#xff0c;帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、埃域知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能&…

基于SpringBoot+Vue的服装销售管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

C++ | Leetcode C++题解之第437题路径总和III

题目&#xff1a; 题解&#xff1a; class Solution { public:unordered_map<long long, int> prefix;int dfs(TreeNode *root, long long curr, int targetSum) {if (!root) {return 0;}int ret 0;curr root->val;if (prefix.count(curr - targetSum)) {ret pref…

CaChe的基本原理

目录 一、Cache的定义与结构 二、Cache的工作原理 三、Cache的映射与替换策略 四、Cache的写操作处理 Cache&#xff0c;即高速缓冲存储器&#xff0c;是计算机系统中位于CPU与主存之间的一种高速存储设备。它的主要作用是提高CPU对存储器的访问速度&#xff0c;从而优化系…

YOLOv8改进 - 注意力篇 - 引入SCAM注意力机制

一、本文介绍 作为入门性篇章&#xff0c;这里介绍了SCAM注意力在YOLOv8中的使用。包含SCAM原理分析&#xff0c;SCAM的代码、SCAM的使用方法、以及添加以后的yaml文件及运行记录。 二、SCAM原理分析 SCAM官方论文地址&#xff1a;SCAM文章 SCAM官方代码地址&#xff1a;SC…

解决 Macos下 Orbstack docker网络问题

两种解决方法&#xff0c;第一种开代理 参考 —— 但是我这一种没成功&#xff0c;第二种方法是换镜像源 { "registry-mirrors": ["http://hub-mirror.c.163.com","https://docker.mirrors.ustc.edu.cn","https://mirrors.tencent.com&q…

安防监控/视频系统EasyCVR视频汇聚平台如何过滤134段的告警通道?

视频汇聚/集中存储EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。平台支持国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为…

大麦演唱会门票

切勿再令您所爱的人耗费高昂的价格去购置黄牛票 ⚠️核心内容参考: 据悉&#xff0c;于购票环节&#xff0c;大麦凭借恶意流量清洗技术&#xff0c;于网络层实时甄别并阻拦凭借自动化手段发起下单请求的流量&#xff0c;强化对刷票脚本、刷票软件以及虚拟设备的识别能力&#…

开源 AI 智能名片 2+1 链动模式 S2B2C 商城小程序的数据运营策略与价值创造

一、引言 1.1 研究背景 在当今数字化时代&#xff0c;数据运营已成为企业发展的核心驱动力。开源 AI 智能名片 21 链动模式 S2B2C 商城小程序作为一种创新的营销工具&#xff0c;与数据运营紧密相连。该小程序通过集成人工智能、大数据分析等先进技术&#xff0c;能够实时收集…

【问题解决】Xshell终端双击或者选中文字自动发送Ctrl+C

问题 在xshell终端&#xff0c;当鼠标双击或者选中一行文字时&#xff0c;xshell会自动发送一个 CtrlC 的命令。如下图&#xff1a; 原因 已知可能会导致这个问题的软件&#xff0c;关掉就没问题了&#xff1a; 有道词典金山词典词霸秒译bing翻译钉钉AI助理360极速搜索… …

Python保留数据删除Excel单元格的函数和公式

在分析处理Excel表格时&#xff0c;我们可能需要使用各种公式或函数对表格数据进行计算&#xff0c;从而分析出更多的信息。但在展示、分享或再利用分析结果时&#xff0c;我们可能需要将含有公式的单元格转换为静态数值&#xff0c;从而简化数据、保护计算结果不被更改&#x…

(c++)内存四区:1.代码区2.全局区(静态区)3.栈区4.堆区

//内存四区&#xff1a;1.代码区 2.全局区 3.栈区 4.堆区 1.放在代码区的有&#xff1a;1.写的代码&#xff1a;只读的、共享的、存放的二进制机器指令、由操作系统直接管理 2.放在全局区的有&#xff1a;1.全局的&#xff08;变量或常量&#xff09; 2.静态的&#xff0…

【毕业/转行】想从事GIS开发工程师?如何规划?

既然是GIS开发&#xff0c;那就离不开学习编程 那如何学习才能掌握呢?如何才能达到企业的用人标准? 给大家梳理了学习的路线&#xff0c;想从事gis开发的小伙伴可以直接按这个路线学习! 共分为6大阶段&#xff0c;让你从纯小白到成熟的三维GIS开发工程师! 大纲&#xff1a…

Python:import语句的使用(详细解析)(一)

相关阅读 Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 import语句是Python中一个很重要的机制&#xff0c;允许在一个文件中访问另一个文件的函数、类、变量等&#xff0c;本文就将进行详细介绍。 在具体谈论import语句前&a…

linux驱动编程——等待队列

一、等待队列 可实现调用read函数时阻塞等。 1、流程 &#xff08;1&#xff09;初始化等待队列头&#xff08;带参宏&#xff09; init_waitqueue_head(q) 等待队列头wq数据类型&#xff1a; wait_queue_head_t&#xff0c;等待条件condition&#xff1a;int型变量。 &…

Actor 并发控制模型

目录 一、模型概述 二、模型特点 三、模型组成 四、模型优势 五、应用实例 一般来说&#xff0c;我们有两种策略来在并发线程中实现通信&#xff1a;共享内存和消息传递。大多数传统语言&#xff0c;并发线程之间的通信使用的都是共享内存&#xff0c;共享内存最大的问题就…

分糖果C++

题目&#xff1a; 样例解释&#xff1a; 样例1解释 拿 k20 块糖放入篮子里。 篮子里现在糖果数 20≥n7&#xff0c;因此所有小朋友获得一块糖&#xff1b; 篮子里现在糖果数变成 13≥n7&#xff0c;因此所有小朋友获得一块糖&#xff1b; 篮子里现在糖果数变成 6<n7&#xf…