前端工程化

模块化、组件化、规范化、自动化

ECMA6Script

es6概述

ECMAScript 6,简称ES6,是JavaScript语言的一次重大更新。大量的新特性,包括箭头函数、模板字符串、let和const关键字、解构、默认参数值、模块系统等等。

  1. 更加简洁:ES6引入了一些新的语法,如箭头函数、类和模板字符串等,使代码更加简洁易懂。

  2. 更强大的功能:ES6引入了一些新的API、解构语法和迭代器等功能,从而使得JavaScript更加强大。

  3. 更好的适用性:ES6引入的模块化功能为JavaScript代码的组织和管理提供了更好的方式,不仅提高了程序的可维护性,还让JavaScript更方便地应用于大型的应用程序。

es6的变量和模板字符串

let 和 var 的差别

  1. let 不能重复声明
javascript 复制代码
    let name = '天真'
    let name = '无邪'
  1. let 有块级作用域,非函数的花括号遇见 let 会有块级作用域,也就是只能在花括号里面访问
javascript 复制代码
    {
      let a = 1
      var b = 2
    }   
    console.log(a);  // a is not defined   花括号外面无法访问
    console.log(b);  // 可以正常输出
  1. let 不会预解析进行变量提升先声明,后使用
javascript 复制代码
    console.log(test) //可以     但是值为undefined
    var test = 'test'
    console.log(test1) //不可以  let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
    let test1 = 'test1' 
  1. let 定义的全局变量不会作为 window 的属性
javascript 复制代码
    var a = 100
    console.log(window.a) //100
    let b = 200
    console.log(window.b) //undefined
  1. let 在 ES6 中推荐优先使用
javascript 复制代码
    for (let i = 0; i < 10; i++) {
      // ...
    }
    console.log(i);

const和var的差异

1、新增const和let类似,只是const定义的变量不能修改基本数据类型,引用数据类型

javascript 复制代码
    //声明场景语法,建议变量名大写区分
    const PI = 3.1415926;

    //1.常量声明必须有初始化值
    //const A ; //报错

    //2.常量值不可以改动
    //const A  = 'atguigu'
    //A  = 'xx' //不可改动

    //3.和let一样,块儿作用域
    {
        const A = 'atguigu'
        console.log(A);
    }
    //console.log(A);

2、并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

javascript 复制代码
    //4.对应数组和对象元素修改,不算常量修改,修改值,不修改地址
    const TEAM = ['刘德华','张学友','郭富城'];
    TEAM.push('黎明');
    TEAM=[] // 报错
    console.log(TEAM)

模板字符串(template string)

增强版的字符串,用反引号(`)标识

1、字符串中可以出现换行符

javascript 复制代码
    // 1 多行普通字符串
    let ulStr =
        '<ul>'+
        '<li>JAVA</li>'+
        '<li>html</li>'+
        '<li>VUE</li>'+
        '</ul>'
    console.log(ulStr)    
    // 2 多行模板字符串
    let ulStr2 = `
        <ul>
        	<li>JAVA</li>
        	<li>html</li>
        	<li>VUE</li>
        </ul>`
    console.log(ulStr2) 

2、可以使用 ${xxx} 形式输出变量和拼接变量

javascript 复制代码
    // 3  普通字符串拼接
    let name ='张小明'
    let infoStr =name+'被评为本年级优秀学员'  
    console.log(infoStr)
    // 4  模板字符串拼接
    let infoStr2 =`${name}被评为本年级优秀学员`
    console.log(infoStr2)

es6的解构表达式

快速将数组或对象中的值拆分并赋值给变量。解构赋值的语法使用花括号 `{}` 表示对象,方括号 `\[\]` 表示数组。通过解构赋值,函数更方便进行参数接受

数组解构赋值

可以通过数组解构将数组中的值赋值给变量

javascript 复制代码
let arr = [11, 22, 33, 44]
// 使用解构表达式取出数据中的元素
let [a, b, c, d] = arr
console.log(a, b, c, d)
javascript 复制代码
let [a, b, c] = [1, 2, 3]; //新增变量名任意合法即可,本质是按照顺序进行初始化变量的值
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3

使用默认值为变量提供备选值,在数组中缺失对应位置的值时使用该默认值

javascript 复制代码
let [a, b, c, d = 4] = [1, 2, 3];
console.log(d); // 4

对象解构赋值

可以通过对象解构将对象中的值赋值给变量

javascript 复制代码
let person = {
    name: "zhangsan",
    age: 10
}
// 使用解构表达式获取对象的属性值
let {age, name} = person
console.log(age, name)

let {a, b} = {a: 1, b: 2};
console.log(a); // 1
console.log(b); // 2
//新增变量名必须和属性名相同,本质是初始化变量的值为对象中同名属性的值
//等价于 let a = 对象.a  let b = 对象.b

可以为标识符分配不同的变量名称,使用 : 操作符指定新的变量名。

javascript 复制代码
let {a: x, b: y} = {a: 1, b: 2};
console.log(x); // 1
console.log(y); // 2

函数参数解构赋值

该函数接受一个数组作为参数,将其中的第一个值赋给 x,第二个值赋给 y,然后返回它们的和。

javascript 复制代码
let arr = [11, 22, 33, 44]
function add([x, y]) {
  return x + y;
}
add(arr); // 33

es6的箭头函数

类似Java中的Lambda表达式

声明和特点

函数声明

javascript 复制代码
    let fn1 = function(){}
    let fn2 = ()=>{} //箭头函数,此处不需要书写function关键字
    let fn3 = x =>{} //单参数可以省略(),多参数无参数不可以!
    let fn4 = x => console.log(x) //只有一行方法体可以省略{};
    let fun5 = x => x + 1 //当函数体只有一句返回值时,可以省略花括号和 return 语句

使用特点 箭头函数this关键字:箭头函数没有自己的this,this指向的是所属环境的外层的上下文环境的this

javascript 复制代码
    let person ={
        name:"张三",
        showName:function (){
            console.log(this) //  这里的this是person
            console.log(this.name)
        },
        viewName: () =>{
            console.log(this) //  这里的this是window
            console.log(this.name)
        }
    }
    person.showName()
    person.viewName()
javascript 复制代码
       let xdd = document.getElementById("xdd");
       // 方案1 
       xdd.onclick = function(){
            console.log(this)
            let _this= this;  //this 是xdd
            //开启定时器
            setTimeout(function(){
                console.log(this)
                //变粉色
                _this.style.backgroundColor = 'pink';
            },2000);
        }
        // 方案2
        xdd.onclick = function(){
            console.log(this)
            //开启定时器
            setTimeout(()=>{
                console.log(this)// 使用setTimeout() 方法所在环境时的this对象
                //变粉色
                this.style.backgroundColor = 'pink';
            },2000);
        }

rest和spread

rest参数,在形参上使用 和JAVA中的可变参数几乎一样用于解决剩余的参数接收问题,只能有一个rest参数

javascript 复制代码
<script>
    // 1 参数列表中多个普通参数  普通函数和箭头函数中都支持
    let fun1 = function (a,b,c,d=10){console.log(a,b,c,d)}
    let fun2 = (a,b,c,d=10) =>{console.log(a,b,c,d)}
    fun1(1,2,3)
    fun2(1,2,3,4)
    // 2 ...作为参数列表,称之为rest参数 普通函数和箭头函数中都支持 ,因为箭头函数中无法使用arguments,rest是一种解决方案
    let fun3 = function (...args){console.log(args)}
    let fun4 = (...args) =>{console.log(args)}
    fun3(1,2,3)
    fun4(1,2,3,4)
    // rest参数在一个参数列表中的最后一个只,这也就无形之中要求一个参数列表中只能有一个rest参数
    //let fun5 =  (...args,...args2) =>{} // 这里报错
</script>

spread参数,在实参上使用rest

javascript 复制代码
<script>
    let arr =[1,2,3]
    //let arrSpread = ...arr;// 这样不可以,...arr必须在调用方法时作为实参使用
    let fun1 =(a,b,c) =>{
        console.log(a,b,c)
    }
    // 调用方法时,对arr进行转换 转换为1,2,3 
    fun1(...arr)
    //应用场景1 合并数组
    let arr2=[4,5,6]
    let arr3=[...arr,...arr2]
    console.log(arr3)
    //应用场景2 合并对象属性
    let p1={name:"张三"}
    let p2={age:10}
    let p3={gender:"boy"}
    let person ={...p1,...p2,...p3}
    console.log(person)

</script>

es6的对象创建和拷贝

对象创建的语法糖

javascript 复制代码
class Person{
      // 属性
      #n;//私有化属性,只能通过get和set方法
      age;
      get name(){
          return this.n;
      }
      set name(n){
          this.n =n;
      }
      // 实例方法
      eat(food){
          console.log(this.age+"岁的"+this.n+"用筷子吃"+food)
      }
      // 静态方法
      static sum(a,b){
          return a+b;
      }
      // 构造器
      constructor(name,age){
          this.n=name;
          this.age = age;

      }
  }
  let person =new Person("张三",10);
  // 访问对象属性
  // 调用对象方法
  console.log(person.name)
  console.log(person.n)
  person.name="小明"
  console.log(person.age)
  person.eat("火锅")
  console.log(Person.sum(1,2))
javascript 复制代码
  class Student extends  Person{
      grade ;
      score ;
      study(){

      }
      constructor(name,age ) {
          super(name,age);
      }
  }

  let stu =new Student("学生小李",18);
  stu.eat("面条")

对象的深拷贝和浅拷贝

对象的拷贝,快速获得一个和已有对象相同的对象的方式

浅拷贝:指向相同的内存地址

javascript 复制代码
<script>
    let arr  =['java','c','python']
    let person ={
        name:'张三',
        language:arr
    }
    // 浅拷贝,person2和person指向相同的内存
    let person2 = person;
    person2.name="小黑"
    console.log(person.name)
</script>

深拷贝:通过JSON和字符串的转换解构表达式形成一个新的对象

javascript 复制代码
<script>
    let arr  =['java','c','python']
    let person ={
        name:'张三',
        language:arr
    }
    // 深拷贝,通过JSON和字符串的转换形成一个新的对象
    let person2 = JSON.parse(JSON.stringify(person))
    person2.name="小黑"
    console.log(person.name)
    console.log(person2.name) 
</script>

es6的模块化处理

模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。

ES6模块化的几种暴露和导入方式:分别导出;统一导出;默认导出

导出的都是一个对象,导出的内容都可以理解为是向导入的这个对象中添加的属性或者方法

分别导出

模块想对外导出,添加export关键字即可!

导出目标文件:

javascript 复制代码
//1.分别暴露
// 导出一个变量
export const PI = 3.14
// 导出一个函数
export function sum(a, b) {
  return a + b;
}
// 导出一个类
export class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  sayHello() {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
  }
}

导入目标文件:

javascript 复制代码
/* 
    *代表module.js中的所有成员
    m1代表所有成员所属的对象
*/
import * as m1 from './module.js'
// 使用暴露的属性
console.log(m1.PI)
// 调用暴露的方法
let result =m1.sum(10,20)
console.log(result)
// 使用暴露的Person类
let person =new m1.Person('张三',10)
person.sayHello()

导入目标文件引用:

html 复制代码
<!-- 导入JS文件 添加type='module' 属性,否则不支持ES6的模块化 -->
<script src="./app.js" type="module" /> 

统一导出

导出目标文件:

javascript 复制代码
//2.统一暴露
// 模块想对外导出,export统一暴露想暴露的内容!
// 定义一个常量
const PI = 3.14
// 定义一个函数
function sum(a, b) {
  return a + b;
}
// 定义一个类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  sayHello() {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
  }
}
// 统一对外导出(暴露)
export {
	PI,
    sum,
    Person
}

导入目标文件:

javascript 复制代码
/* 
    {}中导入要使用的来自于module.js中的成员
    {}中导入的名称要和module.js中导出的一致,也可以在此处起别名
    {}中如果定义了别名,那么在当前模块中就只能使用别名
    {}中导入成员的顺序可以不是暴露的顺序
    一个模块中可以同时有多个import
    多个import可以导入多个不同的模块,也可以是同一个模块
*/
//import {PI ,Person ,sum }  from './module.js'
//import {PI as pi,Person as People,sum as add}  from './module.js'
import {PI ,Person ,sum,PI as pi,Person as People,sum as add}  from './module.js'
// 使用暴露的属性
console.log(PI)
console.log(pi)
// 调用暴露的方法
let result1 =sum(10,20)
console.log(result1)
let result2 =add(10,20)
console.log(result2)
// 使用暴露的Person类
let person1 =new Person('张三',10)
person1.sayHello()
let person2 =new People('李四',11)
person2.sayHello()

导入目标文件引用:

html 复制代码
<!-- 导入JS文件 添加type='module' 属性,否则不支持ES6的模块化 -->
<script src="./app.js" type="module" /> 

默认导出

默认导出一个文件中只能有一个

javascript 复制代码
// 3默认和混合暴露
/* 
    默认暴露语法  export default sum
    默认暴露相当于是在暴露的对象中增加了一个名字为default的属性
    三种暴露方式可以在一个module中混合使用

*/
export const PI = 3.14
// 导出一个函数
function sum(a, b) {
  return a + b;
}
// 导出一个类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  sayHello() {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
  }
}

// 导出默认
export default sum
// 统一导出
export {
   Person
}
javascript 复制代码
/*
    *代表module.js中的所有成员
    m1代表所有成员所属的对象
*/
import * as m1 from './module.js'
import {default as add} from './module.js' // 用的少
import add2 from './module.js' // 等效于 import {default as add2} from './module.js'

// 调用暴露的方法
let result =m1.default(10,20)
console.log(result)
let result2 =add(10,20)
console.log(result2)
let result3 =add2(10,20)
console.log(result3)

// 引入其他方式暴露的内容
import {PI,Person} from './module.js'
// 使用暴露的Person类
let person =new Person('张三',10)
person.sayHello()
// 使用暴露的属性
console.log(PI)

前端工程化环境搭建

Nodejs

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,可以使 JavaScript 运行在服务器端。可以使用 JavaScript 来编写服务器端程序,这也使得前端开发人员可以利用自己已经熟悉的技能来开发服务器端程序,同时也让 JavaScript 成为一种全栈语言。

javascript 复制代码
单线程,但是采用了事件驱动、异步 I/O 模型,可以处理高并发请求。
轻量级,使用 C++ 编写的 V8 引擎让 Node.js 的运行速度很快。
模块化,Node.js 内置了大量模块,同时也可以通过第三方模块扩展功能。
跨平台,可以在 Windows、Linux、Mac 等多种平台下运行。

Node.js 的核心是其管理事件和异步 I/O 的能力。Node.js 的异步 I/O 使其能够处理大量并发请求,并且能够避免在等待 I/O 资源时造成的阻塞。

npm

PM全称Node Package Manager,是Node.js包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的前端框架的下载工具;也是Node.js的包管理工具,相当于后端的Maven前端项目的管理工具

npm 常用命令

项目初始化
javascript 复制代码
npm init

npm 会引导您在命令行界面上回答一些问题,例如项目名称、版本号、作者、许可证等信息,并最终生成一个package.json 文件。package.json信息会包含项目基本信息!

javascript 复制代码
npm init -y

-y yes的意思,所有信息使用当前文件夹的默认值!不用挨个填写!

安装依赖
javascript 复制代码
npm install 包名 或者 npm install 包名@版本号

安装包或者指定版本的依赖包(安装到当前项目中)

javascript 复制代码
npm install -g 包名

安装全局依赖包(安装到d:/GlobalNodeModules)则可以在任何项目中使用它,而无需在每个项目中独立安装该包。

javascript 复制代码
npm install

安装package.json中的所有记录的依赖

升级依赖
javascript 复制代码
npm update 包名

将依赖升级到最新版本

卸载依赖
javascript 复制代码
npm uninstall 包名
查看依赖
javascript 复制代码
npm ls

查看项目依赖

javascript 复制代码
npm list -g

查看全局依赖

运行命令

npm run 命令是在执行 npm 脚本时使用的命令。npm 脚本是一组在 package.json 文件中定义的可执行命令。

在 package.json 文件中,scripts 字段是一个对象,其中包含一组键值对,键是要运行的脚本的名称,值是要执行的命令。

javascript 复制代码
{
	"name": "my-app",
  	"version": "1.0.0",
    "scripts": {
        "start": "node index.js",
        "test": "jest",
        "build": "webpack"
    },
    "dependencies": {
        "express": "^4.17.1",
        "jest": "^27.1.0",
        "webpack": "^5.39.0"
    }
}

Vue3

简介

渐进式JavaScript框架:根据需求逐步导入使用的文件

声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系(声明数据与页面元素的关系)。

响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM

非工程化应用

html 复制代码
<!-- 这里也可以用浏览器打开连接,然后将获得的文本单独保存进入一个vue.js的文件,导入vue.js文件即可 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app">
    <!-- 给style属性绑定colorStyle数据 -->
    <!-- 插值表达式:直接将数据放在该位置 -->
    <h1 v-bind:style="colorStyle">{{ headline }}</h1>

    <!-- v-text 设置双标签中的文本 -->
    <p v-text="article"></p>

    <!-- 给type属性绑定inputType数据 -->
    <input v-bind:type="inputType" value="helloVue3"><br>

    <!-- 给按钮单击事件绑定函数 -->
    <button @click="sayHello()">hello</button>
</div>

<script>
    // 组合式 API
    const app = Vue.createApp({
        // 在 setup 内部自由声明数据和方法即可,最终返回
        setup() {
            // 定义数据
            // 在 Vue 中实现 DOM 的思路是:通过修改数据而影响页面元素
            // Vue3 中,数据默认不是响应式的,需要加 ref 或者 reactive 处理,后面会详细讲解
            let inputType = 'text'
            let headline = 'hello vue3'
            let article = 'vue is awesome'
            let colorStyle = { color: 'red' }

            // 定义函数
            let sayHello = () => {
                alert("hello Vue")
            }

            // 在 setup 函数中,return 返回的数据和函数可以在 html 中使用
            return {
                inputType,
                headline,
                article,
                colorStyle,
                sayHello
            }
        }
    });

    // 挂载到视图
    app.mount("#app");
</script>

Vue3通过Vite实现工程化

概述

  1. 快速创建项目:使用脚手架可以快速搭建项目基本框架,避免从零开始搭建项目的重复劳动和繁琐操作,从而节省时间和精力。

  2. 统一的工程化规范:前端脚手架可以预设项目目录结构、代码规范、git提交规范等统一的工程化规范,让不同开发者在同一个项目上编写出风格一致的代码,提高协作效率和质量。

  3. 代码模板和组件库:前端脚手架可以包含一些常用的代码模板和组件库,使开发者在实现常见功能时不再重复造轮子,避免因为轮子质量不高带来的麻烦,能够更加专注于项目的业务逻辑。

  4. 自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等。

Vite创建Vue3工程化项目

Vite+Vue3项目的创建、启动、停止

1 使用命令行创建工程

html 复制代码
npm create vite@latest

选择项目名+vue+JavaScript选项即可

2 安装项目所需依赖

html 复制代码
cd [所选项目地址]
npm install

npm install命令安装基础依赖根据package.json

3 启动项目

html 复制代码
"scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
html 复制代码
npm run dev

根据package.json内的scripts不同模式启动

5 停止项目

命令行上 ctrl+c

Vite+Vue3项目的目录结构

Vite + Vue 项目目录结构说明

public 目录

存放公共资源,如 HTML 文件、图像、字体等。这些资源会被直接复制到构建输出的目录中。

src 目录:存放项目源代码,包括 JavaScript、CSS、Vue 组件、图像和字体等。开发过程中,Vite 会实时编译并处理这些文件,支持浏览器中的实时预览和调试。

assets:存放项目中用到的静态资源,如图片、字体、样式文件等。

components:存放组件相关文件。组件用于代码复用,抽象出可复用的 UI 部件,方便在不同场景中重复使用。

layouts:存放布局组件文件。布局组件通常负责应用程序的整体布局,如头部、底部、导航菜单等。

pages:存放页面级别的组件文件,通常对应路由中的页面。可以按功能创建子文件夹来组织不同的页面组件。

plugins:存放 Vite 插件相关文件,可以按需加载不同的插件来实现不同功能,如自动化测试、代码压缩等。

router:存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面跳转和数据传递。

store:存放 Vuex 状态管理相关文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。

utils:存放通用的工具函数,如日期处理函数、字符串操作函数等。

vite.config.js

Vite 的配置文件,可以通过该文件配置项目的参数、插件、打包优化等。支持 CommonJS 或 ES6 模块语法。

package.json

标准的 Node.js 项目配置文件,包含项目的基本信息和依赖关系。可以通过 scripts 字段定义命令,如 dev、build、serve 等,用于启动开发、构建和启动本地服务器等操作。

入口文件

Vite 项目的入口为 src/main.js 文件,这是 Vue.js 应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中,通常会引入 Vue.js 及相关插件和组件,同时创建 Vue 实例,挂载到 HTML 页面上指定的 DOM 元素中。

Vite+Vue3项目组件(SFC入门)

一个页面作为整体,是由多个部分组成的,每个部分在这里就可以理解为一个组件,每个.vue文件就可以理解为一个组件,多个.vue文件可以构成一个整体页面

传统的页面有.html文件.css文件和.js文件三个文件组成(多文件组件)

vue将这文件合并成一个.vue文件(Single-File Component,简称 SFC,单文件组件)

.vue文件对js/css/html统一封装,这是VUE中的概念 该文件由三个部分组成 <script> <template> <style>

template标签 代表组件的html部分代码 代替传统的.html文件只能有一个一级子标签

script标签 代表组件的js代码 代替传统的.js文件

style标签 代表组件的css样式代码 代替传统的.css文件

index.html是项目的入口,其中 `<div id ='app'></div>`是用于挂载所有组建的元素

index.html中的script标签引入了一个main.js文件,具体的挂载过程在main.js中执行

javascript 复制代码
// 从vue框架中导入一个createApp函数
import { createApp } from 'vue'

// 导入全局的css样式文件,该文件中的样式或作用到所有的.vue元素上
import './style.css'

// 导入了一个App.vue的组件 并起了一个别名 App
import App from './App.vue'

// 使用导入的App组件生成一个对象
let app = createApp(App)

// 将app对象挂载到id值为app的元素上
app.mount('#app')

main.js是vue工程中非常重要的文件,他决定这项目使用哪些依赖,导入的第一个组件

App.vue是vue中的核心组件,所有的其他组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换

Vite+Vue3响应式入门和setup函数

非响应式数据: 修改后VUE不会更新DOM,非实时更新

响应式数据: 修改后VUE会更新DOM,实时更新

VUE2中数据默认是响应式的

VUE3中数据要经过ref(通过.value操作{value:1},对象.属性)或者reactive处理后才是响应式的

ref是VUE3框架提供的一个函数,需要导入

javascript 复制代码
<script type="module">
    //存储vue页面逻辑js代码
    import {ref} from 'vue'
    export default{
        setup(){
            let counter = ref(1)// 对象{value:1}
            function increase(){
                // 通过.value修改响应式数据
                counter.value++
            }
            function decrease(){
                counter.value--
            }
            return {
                counter,
                increase,
                decrease
            }
        }
    }
</script>
javascript 复制代码
<template>
    <div>
      <button @click="decrease()">-</button>
      {{ counter }}
      <button @click="increase()">+</button>
    </div>
    
</template>

setup函数和语法糖简化开发

javascript 复制代码
<script type="module" setup>
    //存储vue页面逻辑js代码
    import {ref} from 'vue'
    let counter = ref(1)
    function increase(){
        // 通过.value修改响应式数据
        counter.value++
    }
    function decrease(){
        counter.value--
    }
</script>

Vite+Vue3关于样式的导入方式

1 在.vue文件中的style标签中

javascript 复制代码
<style scoped>
.s1 {
    color: aqua;
    font-size: 40px;
}
</style>

2 将css样式保存到独立的css文件中,哪个.vue文件需要,就在哪里导入即可

vue文件script代码引入

javascript 复制代码
import './style/reset.css'

Vue文件style代码引入

javascript 复制代码
@import './style/reset.css'

3 如果某个样式要在所有的.vue文件中生效,那么可以在main.js中导入

javascript 复制代码
// 导入全局的css样式文件
import './style/test.css'

Vue3视图渲染技术

模版语法

Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。

插值表达式和文本渲染

插值表达式:最基本的数据绑定形式是文本插值,它使用的是"Mustache"语法 ,即双大括号`{{数据名/函数/对象调用API}}`

javascript 复制代码
<script setup type="module">
  let msg ="hello vue3"
  let getMsg= ()=>{
    return 'hello vue3 message'
  }
  let age = 19
  let bee = '蜜 蜂'
  // 购物车
  const carts = [{name:'可乐',price:3,number:10},{name:'薯片',price:6,number:8}];
  //计算购物车总金额
  function compute(){
      let count = 0;
      for(let index in carts){
          count += carts[index].price*carts[index].number;
      }
      return count;
  }
</script>
html 复制代码
<template>
  <div>
    <!-- 将数据绑定到下面的元素上 -->
    <h1>{{ msg }}</h1>
    msg的值为 {{ msg }} <br>

    <!-- 插值表达式中可以调用函数,将函数的返回值渲染到指定的位置 -->
    msg的值为 {{ getMsg() }} <br>

    <!-- 插值表达式支持一些常见的运算符 -->
    年龄: {{ age }},是否成年: {{ age > 18 ? '是' : '否' }} <br>

    <!-- 插值表达式中支持对象调用一些API -->
    {{ bee.split('.').reverse().join('') }} <br>
    {{ carts[0].price * carts[0].number + carts[1].price * carts[1].number }} <br>
    {{ compute() }} <br>
  </div>
</template>

渲染双标签中的文本,我们也可以选择使用`v-text`不识别html元素结构的文本和`v-html`识别html元素结构的文本命令

v-*** 这种写法的方式使用的是vue的命令

v-***的命令必须依赖元素,并且要写在元素的开始标签中

v-***指令支持ES6中的字符串模板

插值表达式中支持javascript的运算表达式

插值表达式中也支持函数的调用

javascript 复制代码
<script setup type="module">
  let msg ='hello vue3'
  let getMsg= ()=>{
    return msg
  }
  let age = 19
  let bee = '蜜 蜂'
  let redMsg ='<font color=\'red\'>msg</font>'
  let greenMsg =`<font color=\'green\'>${msg}</font>`
</script>
html 复制代码
<template>
  <div>
    <span v-text='msg'></span> <br>
    <span v-text='redMsg'></span> <br>
    <span v-text='getMsg()'></span> <br>
    <span v-text='age>18?"成年":"未成年"'></span> <br>
    <span v-text='bee.split(" ").reverse().join("-")'></span> <br>

    <span v-html='msg'></span> <br>
    <span v-html='redMsg'></span> <br>
    <span v-html='greenMsg'></span> <br>
    <span v-html="`<font color='green'>${msg}</font>`"></span> <br>
  </div>
</template>

Attribute属性渲染

想要渲染一个元素的 attribute,应该使用 `v-bind`指令

v-bind可以用于渲染任何元素的属性,语法为 `v-bind:属性名='数据名'`, 可以简写为 `:属性名='数据名'`

javascript 复制代码
<script setup type="module">
  const data = {
    name:'尚硅谷',
    url:"http://www.atguigu.com",
    logo:"http://www.atguigu.com/images/index_new/logo.png"
  }
</script>
html 复制代码
<template>
  <div>
    <a 
      v-bind:href='data.url' 
      target="_self">
      <img 
        :src="data.logo" 
        :title="data.name">
      <br>
      <input type="button" 
             :value="`点击访问${data.name}`">
    </a>
  </div>
</template>

事件的绑定

可以使用 `v-on` 来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。

v-on:事件名称="函数名()"

v-on:事件名可以简写 @事件名

用法:`v-on:click="handler()"` 或简写为 `@click="handler()"`

vue中的事件名=原生事件名去掉`on` 前缀 如:`onClick --> click`

handler的值可以是方法事件处理器,也可以是内联事件处理器

绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下

.once:只触发一次事件。重点

.prevent:阻止组件默认事件。重点

.stop:阻止事件冒泡。

.capture:使用事件捕获模式而不是冒泡模式。

.self:只在事件发送者自身触发时才触发事件。

javascript 复制代码
<script setup type="module">
  import {ref} from 'vue'
  // 响应式数据 当发生变化时,会自动更新 dom树
  let count=ref(0)
  let addCount= ()=>{
    count.value++
  }
  let incrCount= (event)=>{
    count.value++
    // 通过事件对象阻止组件的默认行为
    event.preventDefault();
    
  }
</script>
html 复制代码
<template>
  <div>
    <h1>count的值是:{{ count }}</h1>
    <!-- 方法事件处理器 -->
    <button v-on:click="addCount()">addCount</button> <br>
    <!-- 内联事件处理器 -->
    <button @click="count++">incrCount</button> <br>
    <!-- 事件修饰符 once 只绑定事件一次 -->
    <button @click.once="count++">addOnce</button> <br>
    <!-- 事件修饰符 prevent 阻止组件的默认行为 -->
    <a href="http://www.atguigu.com" target="_blank" @click.prevent="count++">prevent</a> <br>
    <!-- 原生js方式阻止组件默认行为 (推荐) -->
    <a href="http://www.atguigu.com" target="_blank" @click="incrCount($event)">prevent</a> <br>
  </div>
</template>

响应式基础

数据模型发生变化时,自动更新DOM树内容,页面上显示的内容会进行同步变化,vue3的数据模型不是自动响应式的,需要我们做一些特殊的处理

让一个普通数据转换为响应式数据,两种方式:

  1. ref函数 更适合单个变量

在script标签中操作ref响应式数据要通过.value

在template中操作ref响应式数据则无需.value

javascript 复制代码
import {ref} from 'vue'
let counter = ref(0);

/* 函数中要操作ref处理过的数据,需要通过.value形式 */
let decr = () =>{
  counter.value--;
}
let incr = () =>{
  counter.value++;
}
  1. reactive函数 更适合对象

在script和template中操作reactive响应式数据,都直接使用对象名.属性名的方式即可

javascript 复制代码
import {reactive} from 'vue'
let data = reactive({
  counter:0
})

/* 函数中要操作reactive处理过的数据,需要通过 对象名.属性名的方式 */
let decr = () =>{
  data.counter--;
}
let incr = () =>{
  data.counter++;
}
  1. 扩展响应式关键字toRefs 和 toRef

toRef函数 将reactive响应式数据中的某个属性转换为ref响应式数据同一个

toRefs函数 同时将reactive响应式数据中的多个属性转换为ref响应式数据

javascript 复制代码
/* 从vue中引入reactive方法 */
import { toRef, toRefs } from 'vue'

let data = reactive({
    counter: 0,
    name: "test"
})

// 将一个 reactive 响应式对象中的某个属性转换成一个 ref 响应式对象
let ct = toRef(data, 'counter')

// 将一个 reactive 响应式对象中的多个属性转换成多个 ref 响应式对象
let { counter, name } = toRefs(data)

function show() {
    alert(data.counter)
    // 获取 ref 的响应式对象,需要通过 .value 属性
    alert(counter.value)
    alert(name.value)
}

/* 函数中要操作 ref 处理过的数据,需要通过 .value 形式 */
let decr = () => {
    data.counter--
}

let incr = () => {
    /* ref 响应式数据,要通过 .value 属性访问 */
    counter.value++
}

条件和列表渲染

条件渲染v-if='表达式',v-show

v-if='表达式' 只会在指令的表达式返回真值时才被渲染

也可以使用 v-else 为 v-if 添加一个"else 区块"。

一个 v-else 元素必须跟在一个 v-if 元素后面,否则它将不会被识别。

v-if = "表达式/数据" 数据为true,则当前元素会渲染进入dom树

v-else 自动和前一个v-if做取反操作

javascript 复制代码
<script type="module" setup>
    import {ref} from 'vue'
    let awesome = ref(true)
</script>

<template>
  <div>
    <h1 v-if="awesome">Vue is awesome!</h1>
    <h1 v-else>Oh no 😢</h1>
    <button @click="awesome = !awesome">Toggle</button>
  </div>
</template> 

v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。

javascript 复制代码
<script type="module" setup>
    import {ref} from 'vue'
    let awesome = ref(true)
</script>

<template>
  <div>
    <h1 id="ha"  v-show="awesome">Vue is awesome!</h1>
    <h1 id="hb"  v-if="awesome">Vue is awesome!</h1>
    <h1 id="hc"  v-else>Oh no 😢</h1>
    <button @click="awesome = !awesome">Toggle</button>
  </div>
</template> 

列表渲染v-for

基于一个数组来渲染一个列表

v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名

在 v-for 块中可以完整地访问父作用域内的属性和变量。v-for 也支持使用可选的第二个参数表示当前项的位置索引。

javascript 复制代码
<script type="module" setup>
    import {ref,reactive} from 'vue'
    let parentMessage= ref('产品')
    let items =reactive([
      {
        id:'item1',
        message:"薯片"
      },
      {
        id:'item2',
        message:"可乐"
      }
    ])
</script>

<template>
  <div>
    <ul>
      <!-- :key不写也可以 -->
      <li v-for='item in items' :key='item.id'>
        {{ item.message }}
      </li>
    </ul>

    <ul>
      <!-- index表示索引,当然不是非得使用index这个单词 -->
      <li v-for="(item, index) in items" :key="index">
        {{ parentMessage }} - {{ index }} - {{ item.message }}
      </li>
    </ul>
   
  </div>
</template> 

双向绑定

单项绑定 v-bind 响应式数据发生变化时,更新dom树,不会影响响应数据

双向绑定 v-model 页面上的数据由于用户的操作造成了改变,也会同步修改对应的响应式数据

双向绑定一般都用于表单标签

双向绑定也有人称呼为收集表单信息的命令

v-model:value="数据" 双向绑定

v-model:value 一般都省略 :value

javascript 复制代码
<script type="module" setup>
  //引入模块
  import { reactive,ref} from 'vue' 

  let hbs = ref([]); //装爱好的值
  let user = reactive({username:null,password:null,introduce:null,pro:null})  
 
  function login(){
    alert(hbs.value);
    alert(JSON.stringify(user));
  }

  function clearx(){
    //user = {};// 这中写法会将数据变成非响应的,应该是user.username=""
    user.username=''
    user.password=''
    user.introduce=''
    user.pro=''
    hbs.value.splice(0,hbs.value.length);;
  }
</script>
html 复制代码
<template>
  <div>
      账号: <input type="text" placeholder="请输入账号!" v-model="user.username"> <br>
      密码: <input type="text" placeholder="请输入账号!" v-model="user.password"> <br>
      爱好: 
        吃 <input type="checkbox" name="hbs" v-model="hbs" value="吃"> 
        喝 <input type="checkbox" name="hbs" v-model="hbs" value="喝">
        玩 <input type="checkbox" name="hbs" v-model="hbs" value="玩">
        乐 <input type="checkbox" name="hbs" v-model="hbs" value="乐">
      <br>
      简介:<textarea v-model="user.introduce"></textarea>
      <br>
      籍贯:
          <select v-model="user.pro">
            <option value="1">黑</option>
            <option value="2">吉</option>
            <option value="3">辽</option>
            <option value="4">京</option>
            <option value="5">津</option>
            <option value="6">冀</option>
          </select> 
      <br>
      <button @click="login()">登录</button> 
      <button @click="clearx()">重置</button>
      <hr>
      显示爱好:{{ hbs }}
      <hr>
      显示用户信息:{{ user }}
  </div> 
</template> 

属性计算

使用计算属性来描述依赖响应式状态的复杂逻辑。计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。

javascript 复制代码
<script type="module" setup>
    //引入模块
    import { reactive,computed} from 'vue'
    const author = reactive({
      name: "张三",
      books: ["java从入门到精通", "算法", "Mysql"]
    })

    // 通过方法返回数据,每使用一次,执行一次
    function hasBooks() {
      console.log("hasBooks")
      return author.books.length > 0 ? "是" : "否"
    }

    // 通过计算属性获得数据,每次使用时,如果和上次使用时数据没有变化,则直接使用上一次的结果
    let bookMessage = computed(() => {
      console.log("bookMessage")
      return author.books.length > 0 ? "是" : "否"
    })
</script>

数据监听器

需要在状态变化时执行一些"副作用":例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。

watch主要用于以下场景:

当数据发生变化时需要执行相应的操作

监听数据变化,当满足一定条件时触发相应操作

在异步操作前或操作后需要执行相应的操作

javascript 复制代码
<script setup>
    import { ref, reactive, watch } from 'vue'
    let fullname = ref("")
    let firstname = ref("李")
    let lastname = reactive({
        name: "小花"
    })

    // watch函数监听一个ref的响应式数据
    watch(firstname, (newValue, oldValue) => {
        console.log('${oldValue}变为${newValue}')
        fullname.value = newValue + lastname.name
    })

    // watch函数监听一个reactive响应式数据,专门监听reactive响应式数据中的一个属性
    watch(() => lastname.name, (newValue, oldValue) => {
        console.log('${oldValue}变为${newValue}')
        fullname.value = firstname.value + newValue
    })
    
    // watch函数监听整个reactive响应式数据,需要开启深度监听
    watch(() => lastname, (newValue, oldValue) => {
        // newValue和oldValue都一样,都是lastname
        fullname.value = firstname.value + lastname.name
    }, { deep: true, immediate: true })
</script>

watchEffect默认监听所有ref,reactive的响应式

javascript 复制代码
<script setup>
    import { ref, reactive, watchEffect } from 'vue'
    let fullname = ref("")
    let firstname = ref("李")
    let lastname = reactive({
      name: "小花"
    })

    // 任何的响应式数据,如果想监听,直接监听即可,无需将要监听的响应式数据作为参数
    watchEffect(() => {
      fullname.value = firstname.value + lastname.name
    })
</script>

Vue生命周期

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,会运行生命周期钩子函数,让开发者有机会在特定阶段运行自己的代码

常见钩子函数

onMounted() 注册一个回调函数,在组件挂载完成后将VUE加载到DOM树执行。

onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

onUnmounted() 注册一个回调函数,在组件实例被卸载之后将VUE卸载DOM树调用。

onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。

onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。

javascript 复制代码
<script setup>
    import {ref,onUpdated,onMounted,onBeforeUpdate} from 'vue'

    // 挂载之前
    onBeforeMount(() => {
        console.log("---onBeforeMount---")
        console.log(document.getElementById("s1"))
    })
    // 挂载完成
    onMounted(() => {
        console.log("---onMounted---")
        console.log(document.getElementById("s1"))
    })

    // 更新之前
    onBeforeUpdate(() => {
        console.log("---onBeforeUpdate---")
        let ele = document.getElementById("s1")
        console.log(message.value, ele.innerHTML)
    })
    // 更新之后
    onUpdated(() => {
        console.log("---onUpdated---")
        let ele = document.getElementById("s1")
        console.log(message.value, ele.innerHTML)
    })
    let message = ref(1)
</script>
html 复制代码
<template>
  <div>
    <span id="s1">{{ message }}</span>
    <button @click="message++">changeMessage</button>
  </div>
</template>

Vue组件

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!

案例

javascript 复制代码
<script setup>
    import Header  from './components/Header.vue'
    import Navigator  from './components/Navigator.vue'
    import Content  from './components/Content.vue'
</script>
html 复制代码
<template>
  <div>
     <Header class="header"></Header>
     <Navigator class="navigator"></Navigator>
     <Content class="content"></Content>
  </div>
</template>

组件之间传递数据

子组件定义好提交数据的事件defineEmits,通过方法向父组件传递数据"sendMenu", data

javascript 复制代码
<script setup>
    // 向父组件发送参数
    
    // defineEmits 用于定义向父组件提交数据的事件以及正式的提交数据
    import { defineEmits } from 'vue'
    // 定义一个向父组件提交数据的事件,事件名称自定义
    const emits = defineEmits(['sendMenu'])

    // 提交数据的方法
    function send(data) {
        emits("sendMenu", data)
    }
</script>
html 复制代码
<template>
  <div>
    <ul>
      <li @click="send('学员管理')">学员管理</li>
      <li @click="send('图书管理')">图书管理</li>
      <li @click="send('请假管理')">请假管理</li>
    </ul>
  </div>
</template>

父组件在引用的子组件上写提交事件标志和接收函数@sendMenu="receiver"

javascript 复制代码
<script setup>
    let menu = ref("")

    function receiver(data) {
        menu.value = data
    }
</script>
html 复制代码
<template>
  <div>
    <Navigator @sendMenu="receiver" class="navigator"></Navigator>
    <Content class="content" :message="menu"></Content>
  </div>
</template>

子组件根据父组件的标准:message,定义接受变量defineProps({message: String})

javascript 复制代码
<script setup>
    // 接收父组件的参数
    import { defineProps } from "vue"

    defineProps({
      message: String
    })
</script>
html 复制代码
<template>
  <div>
    {{ message }}
  </div>
</template>

Vue3路由机制router

简介

路由就是根据不同的 URL 地址页面切换展示不同的内容或页面。

单页应用程序(SPA)中,路由可以实现不同视图之间的无刷新切换,提升用户体验;

路由还可以实现页面的认证和权限控制,保护用户的隐私和安全;

路由还可以利用浏览器的前进与后退,帮助用户更好地回到之前访问过的页面。

案例

导入路由依赖

复制代码
npm install vue-router

准备路由配置src/routers/router.js

javascript 复制代码
/* 导入创建路由对象需要使用的函数 */
import { createRouter, createWebHashHistory } from 'vue-router'

/* 导入.vue组件 */
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Update from '../components/Update.vue'
import Add from '../components/Add.vue'

// 创建一个路由对象
const router = createRouter({
    // history属性用于记录路由的历史
    history: createWebHashHistory(),

    // 用于定义多个不同的路径和组件之间的对应关系
    routes: [
        {
            path: "/home",
            component: Home
        },
        {
            path: "/list",
            component: List
        },
        {
            path: "/add",
            component: Add
        },
        {
            path: "/update",
            component: Update
        },
        {
            path: "/",
            component: Home
        }
    ],
});

// 向外暴露router
export default router

main.js引入router配置

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'

// 在整个App.vue中可以使用路由
import router from './router/router.js'

const app = createApp(App)

app.use(router)
app.mount('#app')

准备页面

router-link:更改URL路径

router-view:替换VUE组件

html 复制代码
<template>
  <div>
    App 开头的内容
    <hr>

    <router-link to="/home">home页</router-link> <br>
    <router-link to="/list">List页</router-link> <br>
    <router-link to="/update">update页</router-link> <br>
    <router-link to="/add">add页</router-link> <br>

    <hr>

    <!-- 该标签会被替换成具体的.vue -->
    <router-view></router-view>

    <hr>
    App 开头的内容
  </div>
</template>

路由重定向redirect

javascript 复制代码
path: "/showAll",
redirect: "/list"
javascript 复制代码
/* 导入创建路由对象需要使用的函数 */
import { createRouter, createWebHashHistory } from 'vue-router'

/* 导入.vue组件 */
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Update from '../components/Update.vue'
import Add from '../components/Add.vue'

// 创建一个路由对象
const router = createRouter({
    // history属性用于记录路由的历史
    history: createWebHashHistory(),

    // 用于定义多个不同的路径和组件之间的对应关系
    routes: [
        {
            path: "/home",
            component: Home
        },
        {
            path: "/list",
            component: List
        },
        {
            path: "/add",
            component: Add
        },
        {
            path: "/update",
            component: Update
        },
        {
            path: "/",
            component: Home
        },
        {
            path: "/showAll",
            redirect: "/list"
        }
    ],
});

// 向外暴露router
export default router
html 复制代码
<template>
  <div>
    App 开头的内容
    <hr>

    <router-link to="/home">home页</router-link> <br>
    <router-link to="/list">List页</router-link> <br>
    <router-link to="/update">update页</router-link> <br>
    <router-link to="/add">add页</router-link> <br>
    <router-link to="/showAll">showAll页</router-link> <br>

    <hr>

    <!-- 该标签会被替换成具体的.vue -->
    <router-view></router-view>

    <hr>
    App 开头的内容
  </div>
</template>

使用细节

通过在router.js文件进行组件绑定,决定router-view哪些生效,同时router-view内name属性未赋值则不展示,通过default进行判断

javascript 复制代码
/* 导入创建路由对象需要使用的函数 */
import { createRouter, createWebHashHistory } from 'vue-router'

/* 导入.vue组件 */
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Update from '../components/Update.vue'
import Add from '../components/Add.vue'

// 创建一个路由对象
const router = createRouter({
    // history属性用于记录路由的历史
    history: createWebHashHistory(),

    // 用于定义多个不同的路径和组件之间的对应关系
    routes: [
        {
            path: "/home",
            components:{
                default:Home,
                homeView:Home
            }
        },
        {
            path: "/list",
            components:{
                listView:List
            }
        },
        {
            path: "/add",
            components:{
                addView:Add
            }
        },
        {
            path: "/update",
            components:{
                updateView:Update
            }
        },
        {
            path: "/",
            components:{
                listView:List
            }
        },
        {
            path: "/showAll",
            redirect: "/list"
        }
    ],
});

// 向外暴露router
export default router
html 复制代码
<template>
  <div>
    App 开头的内容
    <hr>

    <router-view></router-view>
    <hr>
    <home页<router-view name="homeView"></router-view>
    <hr>
    <list页<router-view name="listView"></router-view>
    <hr>
    <add页<router-view name="addView"></router-view>
    <hr>
    <update页<router-view name="updateView"></router-view>

    <hr>

    <!-- 该标签会被替换成具体的.vue -->
    <router-view></router-view>

    <hr>
    App 开头的内容
  </div>
</template>

编程式路由(useRouter)

通过useRouter,动态决定向那个组件切换的路由

html 复制代码
<template>
  <div>
    <!-- 声明式路由 -->
    <router-link to="/home">home</router-link> <br>
    <router-link to="/list">list</router-link> <br>
    <router-link to="/add">add</router-link> <br>
    <router-link to="/update">update</router-link> <br>

    <!-- 编程式路由 -->
    <button @click="goMyPage()">Go</button><input type="text" v-model="myPath">
    <hr>
    <router-view></router-view>
  </div>
</template>
javascript 复制代码
<script setup>
    import { useRouter } from 'vue-router'
    import { ref } from 'vue'

    const router = useRouter()
    let myPath = ref('')

    function goMyPage() {
        router.push(myPath.value)
    }
</script>

路由传参(useRoute)

路径参数

在路径中使用一个动态字段来实现

javascript 复制代码
const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {
            path: "/showDetail/:id/:language",
            component: ShowDetail
        },
        {
            path: "/",
            component: Home
        },
        {
            path: "/home",
            component: Home
        }
    ]
})
javascript 复制代码
<script setup>
    /* 接收传递过来的路径参数
    useRoute函数是用来接收参数
    route.params 表示路径参数
    */

    import { useRoute } from 'vue-router'
    import { ref } from 'vue'

    let languageId = ref(0)
    let languageName = ref("")

    let route = useRoute()

    onUpdated(() => {
        languageId.value = route.params.id
        languageName.value = route.params.language
    })
</script>

注:和router.js内的参数名一一对应

javascript 复制代码
<script setup>
    import { useRouter } from 'vue-router'
    import { ref } from 'vue'

    const router = useRouter()

    function showDetail(id, language) {
        //router.push(`/showDetail/${id}/${language}`)
        router.push({psth:`/showDetail/${id}/${language}`})
    }
</script>
<template>
  <div>
    <router-link to="/showDetail/1/java">声明式路由路径传参</router-link>
    <button @click="showDetail(2,'PHP')">编程式路由路径传参</button>
    <hr>
    <router-view></router-view>
  </div>
</template>

键值对参数

类似与get请求通过url传参,数据是键值对形式

javascript 复制代码
const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {
            path: "/showDetai2",
            component: ShowDetai2
        },
        {
            path: "/",
            component: Home
        },
        {
            path: "/home",
            component: Home
        }
    ]
})
javascript 复制代码
<script setup>
    /* 接收传递过来的路径参数
    useRoute函数是用来接收参数
    route.query 表示键值对参数
    */

    import { useRoute } from 'vue-router'
    import { ref } from 'vue'

    let languageId = ref(0)
    let languageName = ref("")

    let route = useRoute()

    onUpdated(() => {
        languageId.value = route.query.id
        languageName.value = route.query.language
    })
</script>
javascript 复制代码
<script setup>
    import { useRouter } from 'vue-router'
    import { ref } from 'vue'

    const router = useRouter()

    function showDetail2(param_id, param_language) {
        // router.push('/showDetail2?id=${id}&language=${language}')
        router.push({ path: '/showDetail2', query: { id: param_id, language: param_language } })
    }
</script>
<template>
  <div>
    <router-link to="/showDetail2?id=1&language=java">声明式路由键值对传参</router-link>
    <router-link v-bind:to="{path: '/showDetail2', query: {id: 2, language: 'php'}}">声明式路由键值对传参</router-link>
    <button @click="showDetail2(3, 'python')">编程式路由键值对传参</button>
    <hr>
    <router-view></router-view>
  </div>
</template>

路由守卫

用于在路由切换期间进行一些特定任务的回调函数。在router.js中

全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。

全局后置守卫:在路由切换后被调用,可以用于处理数据、操作 DOM 、记录日志等。

javascript 复制代码
// 设置全局前置守卫
// 每次路由切换页面前,都会执行beforeEach中的回调函数
router.beforeEach(
    (to, from, next) => {
        /* 
            from 上一个页面 从哪里来
            to 下一个页面 到哪里去
            next 放行的方法 只有执行了该方法 才会放行路由
                next() 放行
                next("/路径") 路由的重定向
        */
        console.log("beforeEach")
        console.log(from.path)
        console.log(to.path)
        next()
    }
)
javascript 复制代码
// 设置全局后置守卫
// 每次路由切换页面后,都会执行afterEach中的回调函数
router.afterEach(
    (to, from) => {
        console.log("afterEach")
        console.log(from.path)
        console.log(to.path)
    }
)

案例

javascript 复制代码
// 设置路由的全局前置守卫
router.beforeEach((to,from,next)=>{
    /* 
    to 要去那
    from 从哪里来
    next 放行路由时需要调用的方法,不调用则不放行
    */
    console.log(`从哪里来:${from.path},到哪里去:${to.path}`)

    if(to.path == '/login'){
        //放行路由  注意放行不要形成循环  
        next()
    }else{
        //let username =window.sessionStorage.getItem('username'); 
        let username =window.localStorage.getItem('username'); 
        if(null != username){
            next()
        }else{
            next('/login')
        }

    }
})

Vue3数据交互axios

promise

普通函数和回调函数

普通函数: 正常调用的函数,一般函数执行完毕后才会继续执行下一行代码逐行执行

javascript 复制代码
<script>
    function fun1() {
        console.log("fun1 invoked")
    }
    console.log("code1 invoked")
    // 函数的调用
    fun1()
    console.log("code2 invoked")
</script>

回调函数:回调函数是一种基于事件的,自动调用函数,未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了;回调函数其他的代码不会等待回调函数执行完毕定时执行

javascript 复制代码
<script>
    function fun1() {
        setTimeout(function() {
            console.log("fun1 invoked")
        }, 2000)
    }

    console.log("code1 invoked")
    fun1()
    console.log("code2 invoked")
</script>

Promise 简介

Promise 是异步编程的一种解决方案,所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象代表一个异步操作,有三种状态:`Pending`(进行中)、`Resolved`(已完成,又称 Fulfilled)和`Rejected`(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从`Pending`变为`Resolved`和从`Pending`变为`Rejected`。

Promise 基本语法

javascript 复制代码
<script>
    /*
    resolve 函数 在回调函数中如果调用resolve方法,promise会由pending转换为resolved
    reject 函数 在回调函数中如果调用reject方法,promise会由pending转换为reject
    */

    let promise = new Promise(function(resolve, reject) {
        console.log("function invoked")
            //resolve("promise success")
            reject("promise fail")
    })

    console.log("other code1")  
    promise.then(
        function(value) {
            // promise 转换为 resolved 状态时会执行的函数
            console.log("promise sucess:" + value)
        }
        /*function(value) {
            // promise 转换为 reject 状态时会执行的函数
            console.log("promise fail:" + value)
        }*/
    ).catch(
        function(value) {
        // 当 promise 状态为 reject 或者 promise 出现异常时会执行的函数
        console.log("promise catch:" + value)
        })
    console.log("other code2")
</script>

async和await的使用

async 帮助我们使用简洁的语法获得一个promise对象

javascript 复制代码
let promise = new Promise(function () {})

async function aaa() {}
let aaa = async () => {}

async标识函数后,async函数的返回值会变成一个promise对象

方法如果正常return结果,promise状态就是resolved,return后的结果就是成功状态的返回值

方法中出现了异常,则返回的promise就是一个失败状态

如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由内部的promise对象决定

javascript 复制代码
<script>
async function fun1() {
    // return 10
    // throw new Error("something wrong")
    let promise = Promise.reject("heihei")
    return promise
}

let promise = fun1()

promise.then(
    function(value) {
        console.log("success:" + value)
    }
).catch(
    function(value) {
        console.log("fail:" + value)
    }
)
</script>

await 是帮助我们获取promise成功状态的返回值的关键字

await右边如果是一个普通值,则直接返回该值;如果右边是promise,返回promise成功状态的结果

javascript 复制代码
   let res = await "张三"
   let res = await Promise.resolve("张三")
   res = "张三"

await右边如果是一个失败状态的promise,那么await会直接抛异常

await关键字必须在async修饰的函数中使用,async函数中可以没有await

await后边的代码会等待await执行完毕继续运行同一个代码块内部

javascript 复制代码
<script>
async function fun1() {
    return 10
}

async function fun2() {
    // let res = await fun1()
    try {
        let res = await Promise.reject("something wrong")
    } catch (e) {
        console.log("catch got:" + e)
    }

    console.log("await got:" + res)
}

fun2()
</script>

Axios介绍

axios 是一个基于 Promise 的简单 HTTP 客户端,适用于浏览器和 Node.js。对原生ajaxAsynchronous JavaScript and XML(异步的 JavaScript 和 XML)的封装

Axios 案例

安装axios

bash 复制代码
npm install axios
javascript 复制代码
<script setup>
import { ref,reactive } from 'vue'
import axios from 'axios'

//let message=ref('')
let message=reactive({
    "code":1,
    "content":""
})

function getLoveMessage() {
    // 使用axios发送请求获取土味情话
    // axios({设置请求的参数}) 请求三要素 1 请求的url 2 请求的方式 3 请求的参数 keyvalue json ....
    let promise = axios({
        method: "get",
        url: "https://api.uomg.com/api/rand.qinghua?format=json",
        data: {
            // 这里的数据会放入请求体,前提是请求方式是 post
        },
        params: {
            // 都是以键值对方式将数据放入 url 后
            format: 'json',
            username: "zhangsan"
        }
    })

    promise.then(
        function(response) {
            /* response 响应结果对象
            data 服务端响应回来的数据
            status 响应状态码    200
            statusText 响应状态描述  OK
            headers 本次响应的所有响应头
            config 本次请求的配置信息
            request 本次请求发送时所使用的XMLHttpRequest对象
            */
            console.log(response.data)
            console.log(response.data.code)
            console.log(response.data.content)
            //message.value=response.data.content
            Object.assign(message,response.data)
        }
    ).catch(
        function() {
        }
    )
}
</script>

自动将服务端发送过来的JSON串转换为对象

Axios get和post方法

javascript 复制代码
axios.get(url[, config])

axios.get(url,{
   上面指定配置key:配置值,
   上面指定配置key:配置值
})
javascript 复制代码
axios.post(url[, data[, config]])

axios.post(url,{key:value //此位置数据,没有空对象即可{}},{
   上面指定配置key:配置值,
   上面指定配置key:配置值
})
javascript 复制代码
function getLoveWords() {
    // 发送get请求的方法
    // axios.get(url)
    // axios.get(url, {请求的其他信息})
    // axios.get(url, {params: {键值对参数}, header: {设置一些特殊的请求头}})
    // return axios.get("https://api.uomg.com/api/rand.qinghua?format=json")
    return axios.get(
        "https://api.uomg.com/api/rand.qinghua",
        {
            params: {
                format: "json",
                username: "xxxxx",
                userpwd: "lisi"
            },
            header:{
            Accept:"application/json,text/plain,text/html.*/*"
            }
        }
    )
}
//function getLoveWords() {
//    return axios.post("https://api.uomg.com/api/rand.qinghua?format=json")
//}

async function getLoveMessage() {
    let { data } = await getLoveWords()
    Object.assign(message, data)
}
</script>

Axios 拦截器

在axios发送请求之前,或者是数据响应回来在执行then方法之前做一些额外的工作,可以通过拦截器完成src/axios.js

javascript 复制代码
import axios from 'axios'


//  创建instance实例
const instance = axios.create({
    baseURL:'https://api.uomg.com',
    timeout:10000
})

//  添加请求拦截
instance.interceptors.request.use(
    // 设置请求头配置信息
    config=>{
        //处理指定的请求头
        console.log("before request")
        config.headers.Accept = 'application/json, text/plain, text/html,*/*'
        return config
    },
    // 设置请求错误处理函数
    error=>{
        console.log("request error")
        return Promise.reject(error)
    }
)

// 添加响应拦截器
instance.interceptors.response.use(
    function(response) {
        // 响应状态码为200要执行的方法
        // 处理相应数据
        // 最后要返回response
        console.log("response success:")
        console.log(response)
        return response
    },
    function(error) {
        console.log("response fail:")
        console.log(error)

        // 最后一定要返回一个promise
        return Promise.reject("something wrong")
    }
)

// 默认导出
export default instance

Vue3状态管理Pinia

通过pinia状态管理定义共享数据传参,定义一个域,存放数据默认响应式,响应时导入,并且结合sessionStorage与localStorage,实现持久化

Pinia基本用法

安装pinia

javascript 复制代码
npm install pinia

定义pinia store对象 src/store/store.js

javascript 复制代码
import {defineStore } from 'pinia'

//定义数据并且对外暴露
// store就是定义共享状态的包装对象
// 内部包含四个属性: id 唯一标识 state 完整类型推理,推荐使用箭头函数 存放的数据 getters 类似属性计算,存储放对数据
// 操作的方法  actions 存储数据的复杂业务逻辑方法
// 理解: store类似Java中的实体类, id就是类名, state 就是装数据值的属性  getters就是get方法,actions就是对数据操作的其他方法
export const definedPerson = defineStore(
    {
        id: 'personPinia', //必须唯一
        state:()=>{ // state中用于定义数据
            return {
                username:'张三',
                age:0,
                hobbies:['唱歌','跳舞']
            }
        },
        getters:{// 用于定义一些通过数据计算而得到结果的一些方法 一般在此处不做对数据的修改操作
                 // getters中的方法可以当做属性值方式使用
            getHobbiesCount(){
                return this.hobbies.length
            },
            getAge(){
                return this.age
            }
        },
        actions:{ // 用于定义一些对数据修改的方法
            doubleAge(){
                this.age=this.age*2
            }
        }
    }
)

在main.js配置pinia组件到vue中

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import router from './routers/router.js'
// 导pinia
import { createPinia } from 'pinia'
// 创建pinia对象
let pinia= createPinia()

let app =createApp(App)
app.use(router)
// app中使用pinia功能
app.use(pinia) 
app.mount('#app')

vue 中操作Pinia数据

html 复制代码
<script setup type="module">
    import { ref} from 'vue';
    import { definedPerson} from '../store/store';
    // 读取存储的数据
    let person= definedPerson()
    let hobby = ref('')
</script>
html 复制代码
<template>
    <div>
        <h1>operate视图,用户操作Pinia中的数据</h1>
        请输入姓名:<input type="text" v-model="person.username"> <br>
        请输入年龄:<input type="text" v-model="person.age"> <br>
        请增加爱好:
        <input type="checkbox" value="吃饭"  v-model="person.hobbies"> 吃饭
        <input type="checkbox" value="睡觉"  v-model="person.hobbies"> 睡觉
        <input type="checkbox" value="打豆豆"  v-model="person.hobbies"> 打豆豆 <br>
        
        <!-- 事件中调用person的doubleAge()方法 -->
        <button @click="person.doubleAge()">年龄加倍</button> <br>
        <!-- 事件中调用pinia提供的$reset()方法恢复数据的默认值 -->
        <button @click="person.$reset()">恢复默认值</button> <br>
        <!-- 事件中调用$patch方法一次性修改多个属性值 -->
        <button @click="person.$patch({username:'奥特曼',age:100,hobbies:['晒太阳','打怪兽']})">变身奥特曼</button> <br>
		显示pinia中的person数据:{{person}}
    </div>
</template>

Pinia使用细节

关于id

javascript 复制代码
import {defineStore} from 'pinia'

export const definedPerson = defineStore(
    'personPinia',
    {
        state:()=>{},
        getters:{},
        actions:{}
    }
)

Getter 完全等同于 store 的 state 的计算值

Action 相当于组件中的 method

Element-plus组件库

简介

Element Plus 是一套基于 Vue 3 的开源 UI 组件库,是由饿了么前端团队开发的升级版本 Element UI。Element Plus 提供了丰富的 UI 组件、易于使用的 API 接口和灵活的主题定制功能,可以帮助开发者快速构建高质量的 Web 应用程序。

Element-plus入门

准备vite项目

javascript 复制代码
npm create vite  // 注意选择 vue+typeScript
npm install 
npm install vue-router@4 --save
npm install pinia
npm install axios
npm install element-plus

完整引入element-plus

TypeScript 复制代码
import { createApp } from 'vue'
//导入element-plus相关内容
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')

案例

TypeScript 复制代码
<script setup>
  import { ref } from 'vue'

  const value = ref(true)
</script>

<template>
  <div>
    <!-- 直接使用element-plus组件即可 -->
    <el-button>按钮</el-button>
    <br>
    <el-switch
      v-model="value"
      size="large"
      active-text="Open"
      inactive-text="Close"
    />
    <br />
    <el-switch v-model="value" active-text="Open" inactive-text="Close" />
    <br />
    <el-switch
      v-model="value"
      size="small"
      active-text="Open"
      inactive-text="Close"
    />
  </div>
</template>

<style scoped>

</style>
相关推荐
前端 贾公子2 小时前
uni-app工程化实战:基于vue-i18n和i18n-ally的国际化方案 (上)
前端·javascript·vue.js
无聊的老谢4 小时前
Web GIS 最佳实践:Vue 集成 Leaflet/OpenLayers 实现基站海量点位渲染
前端·javascript·vue.js
老毛肚7 小时前
jeecgboot TS + Vue 模板化 03
前端·javascript·vue.js
FlyWIHTSKY7 小时前
React 19 + Next.js 16(App Router)项目中集成 MSW
开发语言·javascript·vue.js
2401_868534788 小时前
常见的 vue面试题目
前端·javascript·vue.js
向日的葵0069 小时前
vue路由(二)
前端·javascript·vue.js·vue
xkxnq10 小时前
第八阶段:工程化、质量管控与高级拓展(130天),Vue端到端测试:Cypress自动化测试(登录流程+表单提交+页面跳转)
前端·vue.js·flutter
老毛肚10 小时前
jeecgboot vue API 拆分02
前端·javascript·vue.js
爱因斯坦乐21 小时前
Vue项目整合
前端·javascript·vue.js