day69_Vue进阶

news2024/11/15 8:24:05

今日内容

零、 复习昨日

零、 复习昨日

nginx

  • 静态服务器(动静分离)
  • 反向代理服务器(代理后端服务器)
  • 负载均衡
  • 异步

前端工程化 —> java代码工程

一、使用Vue-Cli搭建Vue项目


1.1 什么是vue-cli

cli: Command Line 命令行工具,vue-cli就是vue的命令行工具,也称之为脚手架,使用vue-cli提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如webpack、Element UI、Element Admin等等。那么要想使用vue-cli命令,需要先安装node.js

1.2 node.js的介绍及安装

node.js的介绍

node.js提供了前端程序的运行环境,可以把node.js理解成是运行前端程序的服务器。

node.js的安装

从官网下载安装即可:
http://nodejs.cn/download/

image-20230630171746807 image-20230630171801300 image-20230630171838210 image-20230630171902451 image-20230630171935797

​ 不要勾选这个,否则会下载很多东西

image-20230630171950352
测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。(环境变量已经自动配置好)
image-20230630172915127

node -v 是 查看node的版本

npm -v 是 查看npm版本

npm是安装工具的工具包,类似于Linux中yum,Linux中安装软件 yum install ,node中安装软件使用npm install

设置npm源 , 配置镜像后,下载资源就会很快.

image-20230630173211116

npm config set registry https://registry.npm.taobao.org

1.3 使用node.js 安装 vue-cli

(管理员权限)使用如下命令安装vue-cli

npm install @vue/cli -g

# 如果出错,可以卸载,重新执行上一步安装
npm uninstall @vue/cli -g
  • npm: 使用node.js的命令
  • install: 安装
  • @vue/cli: 要安装的vue-cli
  • -g: 全局安装
当出现以下界面,表示正在安装:
image-20230630174255457image-20230630174544138

安装vue-cli检测

image-20230630174643935

1.4 使用vue-cli命令创建项目

进入工作空间,执行命令创建

  • vue create 项目名

image-20230630180103755

选择具体features

image-20230630180251951

选择vue版本

image-20230630180445128

一般选择第一项,为的是将文件分离打包

image-20230630180546418

是否将此操作存储为模板,本例选择n,选择为y就会在下次创建时使用

image-20230630180626238

创建完毕

image-20230630180841197

运行项目

image-20230630181044188

image-20230630181126743

1.5 使用图形界面方式创建

打开命令行,输入vue ui

会自动打开浏览器,弹出页面

image-20230701130829566

创建项目

image-20230701134703173

image-20230701134835493

image-20230701134927540

选择插件

image-20230701134954698

选择版本

image-20230701135119621

开始创建

image-20230701135215555

创建成功后,跳转控制面板页面

image-20230701135337501

启动项目& 关闭

image-20230701135438284

1.6 项目结构

image-20230701141932969

文件名解释
buildwebpack的一些配置
config不同环境的配置,比如开发环境,生产环境等
node_modulesnpm install下载的依赖包
public用于存放公共js文件夹、json文件和图片等
dist存放项目打包后的文件
src项目的源码
src/assets存放静态资源文件,如图片等
src/components存放公共组件
src/router静态路由表
src/storevuex
src/views开发的页面资源,路由页面相关的
src/main.js对应App.vue创建vue实例,也是入口文件
src/App.vue入口页面
gitignoregit管理时,忽略的文件或文件夹
package.json项目的核心配置记录,可以查看当前安装依赖的版本号
index.html首页的入口
vue.config.js可选的配置文件,存放在根目录中,@vue/cli-service 启动的时候会自动加载

可以在package.json中修改端口

image-20230701143121782

image-20230701143202078

二、idea开发vue

idea中安装Vue插件

image-20230602163052107

然后使用idea打开之前创建的vue项目即可

ps: 也可使用idea直接创建vue项目,但是创建的vue版本默认为Vue3,无法改成vue2,暂时先不使用

三、单文件组件[重点]

3.1 介绍

以前

在很多 Vue 项目中,我们使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#app'}) 在每个页面内指定一个容器元素。

这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:

  • 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复
  • 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \
  • 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

现在

文件扩展名为 .vuesingle-file components (单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。

单文件组件的组成结构:

  • template 组件的模块区域

  • script 业务逻辑区域

  • style 样式区域

3.2 使用

在工程的src/components下创建一个TestSingeVuePage.vue单文件

ps: 命名规则 驼峰版TestSingeVuePage 或者 test-single-vue

image-20230701193211146

填入内容

image-20230701200506779

<template>
  <div>
     <h1 class="red">这是测试单vue文件组件</h1>
    <h3>{{info}}</h3>
    <button @click='show()'>show</button>
  </div>
</template>

<script>
export default {
  name: "TestSingleVuePage",
  data:function (){
    return {
      info:"data-信息"
    }
  },
  methods:{
    show(){
      alert("单页面内弹出...")
    }
  }
}
</script>

<style scoped>
  .red {
    color: red;
  }
</style>

注册组件

  • 哪里注册? 哪个组件需要使用另一个组件,就在哪注册?

  • 例如: A.vue需要使用B.vue的组件内容

    • 就在V.vue中1)导入B组件,2)注册组件,3)使用组件
  • 在App.vue文件内注册组件

image-20230701201344985

使用组件

  • 在App.vue页面的template内使用组件

image-20230701200731630

启动测试

image-20230701200812767

3.3 练习

练习: 再创建一个单页面TestSingleVuePage2.vue页面,定义一些内容, 然后在TestSingleVuePage.vue页面内使用该页面

注意:

import Demo1 from "@/components/Demo1.vue";
import Demo2 from "./components/Demo2.vue";

其中./ 是相对路径,@是从根路径

四、Vue组件间的参数传递


4.1 父传子

通过子组件的props部分,来指明可以接收的参数,父组件通过在标签中写明参数的键值对来传递参数。

​ props是表示一个组件的参数部分,那么props的写法有两种:

​ 1)props:[参数列表]

​ 比如: props:[‘MyProp1’,‘MyProp2’,…]

​ 2)props:{参数名1:{type:String,required:true,default:‘XX’},参数名2:{…}}

演示

创建子组件,设置参数 等待父组件给其传值

<template>
<div>
  <h1>这是 子组件</h1>
  <span>{{name}}</span> --
  <span>{{age}}</span> --
  <span>{{sex}}</span>
</div>
</template>

<script>
export default {
  name: "MySon",
  props:{
    name: {
      // 类型
      type: String,
      // 默认值
      default: '无名'
    },
    age: Number,
    sex: Boolean
  }
}
</script>

<style scoped>
</style>

创建父组件,给子组件传值

<template>
  <div>
    <h1>这是 父组件 </h1>
    <!-- 父组件使用子组件 -->
    <!--
         可以通过 v-bind 动态赋值,比如 v-bind:name="",
         也可以简写:age=""
         也可以静态赋值,比如 messages="" , 注意没有冒号
    -->
    <MySon v-bind:name="username" :age="age" sex="true"></MySon>
  </div>
</template>

<script>
import MySon from "@/components/MySon";

export default {
  name: "MyFather",
  components: {
    MySon
  },
  data: function () {
    return {
      username: "小儿砸",
      age: 18
    }
  }
}
</script>

<style scoped>

</style>

在App.vue注册父组件并使用父组件演示效果即可

image-20230701212133867

注意:props支持的类型:String、Number、Boolean、Array、Object、Date、Function、Symbol

4.2 子传父

使用组件中的props属性是无法完成子组件向父组件传值的,在Vue的设计中,props是单向数据流,只能是父组件通过props向子组件传递数据.

image-20230701212537907

那么,如果需要子组件向父组件传递值该如何做?

  • Vue提供了方案: 在子组件中使用自定义事件向父组件传递数据.

具体步骤

  • 子组件定义一个函数,通过事件触发函数,函数内通过$emit 函数将数据发给父组件

    this.$emit('父组件中的函数名', 数据)
    
  • 父组件在使用子组件的时候,通过@emit中的函数名绑定数据,然后再将接收到数据传递给父组件自己的函数即可

子组件

<template>
  <div>
    <h1>这是 子组件2</h1>
    <button @click="sendNum">子组件将值num=10传递给父组件</button>
  </div>
</template>

<script>
export default {
  name: "MySon2",
  props: {},
  data: function () {
    return {
      num: 10
    }
  },
  methods: {
    sendNum() {
      // 触发父组件的事件,同时给事件传参
      this.$emit('addFun', this.num)
    }
  }
}
</script>

<style scoped>
</style>

父组件

<template>
  <div>
    <h1>这是 父组件2 </h1>
    <span>sum = {{sum}}</span>
    <MySon2 @addFun="addSum"></MySon2>
  </div>
</template>

<script>
import MySon2 from "@/components/MySon2";

export default {
  name: "MyFather2",
  components: {
    MySon2
  },
  data: function () {
    return {
      sum: 0
    }
  },
  methods:{
    addSum(v){
      this.sum += v;
    }
  }
}
</script>

<style scoped>

</style>

在App.vue注册父组件并使用父组件演示效果即可

image-20230701214725152

image-20230701214751783

五、Vue-router 路由[重点]

5.1 介绍

路由的本质就是一种对应关系,比如说我们在url地址中输入我们要访问的url地址之后,浏览器要去请求这个url地址对应的资源。

那么url地址和真实的资源之间就有一种对应的关系,就是路由。

路由分为前端路由后端路由

1)后端路由是由服务器端进行实现,并完成资源的分发(我们之前的项目)

image-20230701221228697

2)前端路由是依靠hash值(锚链接)的变化进行实现 (vue官网就是使用这种)

image-20230701221239070

前端路由的基本概念:
根据不同的事件来显示不同的页面内容,即事件与事件处理函数之间的对应关系
前端路由主要做的事情就是监听事件并分发执行事件处理函数

5.2 vue的路由

Vue默认属于单页面的程序,主要通过URL中的hash来实现不同页面之间的切换。这种切换方式称作前端路由。

Vue Router的核心作用

  • 跳转/切换页面

    • 声明式(写标签)

      <router-link to="要跳转的组件名称">
      
    • 编程式(写代码)

      this.$router.push("要跳转的组件名称")
      
  • 跳转携带数据

    • 编程式导航实现传递参数
      • 传递 this.$router.push({path:‘路径’,params:{参数名:值,…}})
      • 接收 this.$router.params.参数名
  • 路由模式

    • hash模式:监听浏览器地址hash值变化,执行相应的js切换网页;
    • history模式:利用history API实现url地址改变,网页内容改变;
    • 它们最明显的区别就是hash会在浏览器地址后面增加#号

5.3 安装路由模块

介绍 | Vue Router (vuejs.org)

npm install vue-router
# 如果报错,可能是npm版本过高,存在兼容性问题,那么使用如下命令
npm install --legacy-peer-deps vue-router@3.5.2
npm install --legacy-peer-deps vue-router@3.1.3

ps: 今天练习过一次以后,后续在创建项目时可以直接选择路由部分内容,创建出的项目直接就配置好了

5.5 路由初体验

在主页与个人中心直接切换显示

5.5.1 创建MyInfo.vue

路由页面建议创建在src/view/下

<template>
  <div>
    <h1>个人中心</h1>
  </div>
</template>

<script>
export default {
  name: "MyInfo"
}
</script>

<style scoped>

</style>

5.5.2 创建静态路由表

在/src下创建router文件夹,

在/src/router/创建index.js文件

填充以下内容

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '../components/HelloWorld'
import MyInfo from "../components/MyInfo";

Vue.use(Router)
// export是导出路由对象,只有导出了
// main.js文件头上面才能 import 导入
export default new Router({
    routes: [
        {
            path: '/', // 匹配<router-link to="">中的路径
            name: 'HelloWorld', // 这个name属性可写可不写
            component: HelloWorld // 要跳转的组件对象
        },
        {
            path: '/MyInfo',
            name: 'MyInfo',
            component: MyInfo
        }
    ],
    // mode: 'history'
    mode: 'hash'
})

5.5.3 引入路由模块并使用

在main.js中引入路由模块并使用

import Vue from 'vue'
import App from './App'
import router from './router' //引入上一步导出的路由模块,并设置名字为router
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router  // 使用路由
}).$mount('#app')

5.5.4 App.vue使用路由

<template>
  <div id="app">
    <ul>
      <li>
        <!-- 
           <router-link>用于导航,其实就是a标签
           to表示要跳转的资源的路径
           tag 将router-link渲染成想要的原生标签,默认是a标签,可以改成button
        -->
        <router-link to="/" tag="button">首页</router-link>
      </li>
      <li>
        <router-link to="/MyInfo">个人信息</router-link>
      </li>
    </ul>
    <!-- router-view 路由填充位,要跳转的组件在此展示,位置任意 -->
    <router-view/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

启动测试

5.6 编程式路由

刚才演示的是声明式路由,也可以使用编程式路由

演示: 主页与公共页面切面

创建公告页Note.vue

<template>
<div>
  <h1>网站公告</h1>
</div>
</template>

<script>
export default {
  name: "Note"
}
</script>
<style scoped>
</style>

src/router/index.js 注册路由

export default new Router({
    routes: [
        {
            path: '/',
            name: 'HelloWorld',
            component: HelloWorld
        },
        {
            path: '/MyInfo',
            name: 'MyInfo',
            component: MyInfo
        },
        {
            path: '/Note',
            name: 'Note',
            component: Note
        }
    ],
    // mode: 'history'
    mode: 'hash'
})

App.vue设置标签,绑定事件,事件触发函数,函数内编写代码实现路由

image-20230701235337484

this.$router.push("/Note")
// 注意!!!!别写错,有个叫做this.$route

bug: 第一次点击跳转没事,再点一次报错

原因: vueRouter版本问题

解决:

  • 降低版本
  • 设置异常捕获 (推荐)
// 在router中的index.js中加上以下代码,注意加在use之前写
const routerPush = Router.prototype.push;
Router.prototype.push = function (location) {
    return routerPush.call(this, location).catch((err) => {});
};

5.7 参数的传递

5.7.1 声明式路由传参

演示App.vue中将数据传递给MyInfo.vue

在路由表中设参

export default new Router({
  routes: [
	...
    {
      path:'/MyInfo/:id', //设参
      component:MyInfo
    }
  ]
})

在App.vue中传参

<template>
  <div id="app">
    <ul>
      <li>
        <router-link to="/" tag="button">首页</router-link>
      </li>
      <li>
        <!-- 跳转至MyInfo,并携带id 1001 -->  
        <router-link to="/MyInfo/1001">个人信息</router-link>
      </li>
    </ul>
    <!-- router-view 用于显示匹配的组件,类似显示路由到的组件的区域,位置任意 -->
    <router-view/>
  </div>
</template>

<script>
...
</script>

<style>
...
</style>

在MyInfo.vue中接参

<template>
  <div>
    <h1>个人信息</h1>
    <span>id-->{{id}}</span>
  </div>
</template>

<script>
export default {
  name: "EmployeeList",
  data:function(){
    return {
      id:this.$route.params.id // 注意是$route不是$router
    }
  }
}
</script>
<style scoped>
</style>

5.7.2 编程式路由传参

演示App.vue中将数据传递给Note.vue

App.vue的编程跳转js中设置要传递的数据

  methods:{
    toNote(){
      //this.$router.push("/Note")
      this.$router.push({path:"/Note",query:{id:1002}})
    }
  }

Note.vue中接收参数

<template>
<div>
  <h1>网站公告</h1>
  <span>id --> {{id}}</span>
</div>
</template>

<script>
export default {
  name: "Note",
  data:function(){
    return {
      id:this.$route.query.id  // 注意是query,因为是按照路径方式发送,注意查看浏览器url
    }
  }
}
</script>

ps: 后续也可以通过VueX来完成传递数据

5.8 嵌套路由

image-20230705221149496

例如:

image-20230706093624132

5.9 其他

路由重定向,导航守卫等等信息,查看官网自查

重定向| Vue Router (vuejs.org)

六、 Vuex[重点]


6.1 什么是Vuex

Vuex 是什么? | Vuex (vuejs.org)

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

说人话: 实现多个组件的数据共享,即一个数据需要在多个组件中使用,那么久需要把这个数据存储在VueX中

Vuex五大核心要素

  • state:存放状态,比如需要全局共享的数据
  • getters:可以对state中的数据进行额外处理,类似计算属性的作用
  • mutations:通过提交mutations来改变state的状态
  • actions:异步的mutations,可以通过dispatch调用mutaitons,从而改变state
  • modules:模块化状态管理,每个模块拥有自己的 state、mutation、action、getter

vuex

Vue组件通过dispatch调用Vuex中actions的方法,进行异步操作。

actions中的方法中,通过commit,调用mutations中方法,以保证state中数据的同步。

如果没有异步操作,可以直接在组件通过commit调用mutations中的方法对state中共享的数据进行操作。

6.2 安装

在项目根目录执行如下命令来安装 Vuex

若失败,可使用cnpm

# 本例使用的vue2,所以安装vuex装的是vuex3
npm install vuex@3

image-20230702172304145

6.3 配置 vuex

在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  }
})

修改 main.js 增加刚才配置的 store/index.js,关键代码如下:

import Vue from 'vue'
import App from './App.vue'
// 引入路由模块
import router from './router'
// 引入Vuex模块
import store from './store'

Vue.config.productionTip = false
// vue2 创建方式
new Vue({
  render: h => h(App),
  router,
  store  // 使用Vuex
}).$mount('#app')

6.2 6.3 安装配置也可以不做,那就是在创建项目时直接选这个依赖,创建好项目直接就配置好了

image-20230706100127943

  • 一个是路由Router,一个是状态管理Vuex

6.4 演示

6.4.1 存&取

  • 在Vuex中设置数据,在任何组件取出
// store/index.js文件的state中定义数据
export default new Vuex.Store({
    state: {
        username:"无名",
        count:443,
        tga:"tga"
    },
    mutations: {
    }
})

在任何组件中使用 以下命令取值

this.$store.state.username
// username是state中定义的key

6.4.2 计算属性使用state

<template>
<div>
  计算属性取值:{{num}}
</div>
</template>

<script>
export default {
  name: "TestVueX",
  computed:{  // 【注意: 计算属性的写法】
    num() { 
      return this.$store.state.count
    }
  }
}
</script>

重要用法: state中的数据要放在组件的computed中而不是data中!

为什么?

这是因为data 中的内容只会在 created 钩子触发前初始化一次,具体来说就是data中设置count: this. s t o r e . s t a t e . c o u n t 则 c o u n t 的值是 c r e a t e d 钩子执行前 t h i s . store.state.count则count的值是created钩子执行前this. store.state.countcount的值是created钩子执行前this.store.state.count的值,赋值之后属性的值就是纯粹的字面量,之后this.$store.state.count 如何变化均影响不到count的取值。而 computed 则是通过依赖追踪实现的,计算属性在它的相关依赖发生改变时会重新求值。

简而言之就是说,Vuex存储的数据如果有变化,computed中的数据就会变化,但是data中的不会变化

6.4.3 mapState

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性

<template>
<div>
  计算属性取值:{{count}}|
  计算属性取值:{{tga}}|
  计算属性取值:{{username}}|
</div>
</template>

<script>
// 【注意】 是 {} 引入
// 使用 import mapState from 'vuex' 的方式会将整个 vuex 模块导入,并将其中的 mapState 函数赋值给 mapState 变量。
// 而使用 import { mapState } from 'vuex' 的方式则是只导入 vuex 模块中的 mapState 函数
import {mapState} from 'vuex'
export default {
  name: "TestVueX",
  // computed:mapState({
  //   count: function (state) {
  //       return state.count
  //     }
  // }),

  // 如果使用和状态名一样的计算属性名,还可以这样写
  // 映射 this.count 为 store.state.count
  // computed: mapState(['count','tga','username']),

  // 如果有其他的计算属性,并且需要将vuex的state与其混合使用
  // 可以使用对象展开运算符'...'
  computed: {
    // 使用对象展开运算符将此对象混入到外部对象中
    // 映射为当前组件的计算属性
    ...mapState(['count','tga','username']),
  },

}
</script>

<style scoped>

</style>

6.4.4 修改数据

  • 在任何组件中修改Vuex中的数据
// store/index.js文件的mutations中定义方法修改数据
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    state: {
        username:"无名"
    },
    mutations: {
        // 参数1 是Vuex-state对象
        // 参数2 是其他组件使用时传过来的值,这个参数叫做payload
        updateName(state,v){
            state.username = v  // 修改Vuex中的值
        }
    }
})

在其他组件中通过事件触发函数,在其中使用Vuex的方法修改数据

<template>
  <div id="app">
    <!--  演示Vuex取值  -->
    <h1>Vuex --> {{ username }}</h1>
    <input v-model="username">
    <button @click="editName">修改vuex中的值</button>

  </div>
</template>

<script>

export default {
  name: 'App',
  methods: {
    editName(){
      // 修改Vuex数据
      // updateName是 src/store/index.js中mutations中定义的函数名
      this.$store.commit('updateName', this.username)
    }
  },
  data: function () {
    return {
      // 从Vuex中取出数据  
      username: this.$store.state.username
    }
  }
}
</script>

6.5.解决浏览器刷新后 Vuex 数据消失问题

  • 问题描述

Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。但是有一个问题就是:vuex 的存储的数据只是在页面的中,相当于我们定义的全局变量,刷新之后,里边的数据就会恢复到初始化状态。但是这个情况有时候并不是我们所希望的。

  • 解决方案

采用持久化,VueX整合插件实现持久化:vuex-persistedstate

  • 安装插件

    	npm install vuex-persistedstate
    

    image-20230702181913147

  • 配置

    在/src/store/index.js中实现配置

    import Vue from 'vue'
    import Vuex from 'vuex'
    // 引入持久化插件
    import vuexPersistedstate from "vuex-persistedstate";
    
    Vue.use(Vuex)
    export default new Vuex.Store({
        state: {
            username:"无名66"
        },
        mutations: {
            updateName(state,v){
                state.username = v
            }
        },
        plugins:[vuexPersistedstate()] // 加入插件
    })
    
  • 测试

    浏览器刷新测试效果

6.6 其他Vuex知识

七、Promise

主要解决异步深层嵌套的问题 ,promise 提供了简洁的API 使得异步操作更加容易,为后面学习axios提供基础

7.1 以前ajax中的问题

  • 异步请求外的代码,不能使用异步请求中的返回的数据
  • 多次异步调用结果顺序不确定
  • 异步调用的结果如果确实存在依赖关系,就需要嵌套处理

演示:

问题一: 异步请求外的代码,不能使用异步请求中的返回的数据

image-20230704232817410

像上面这样,下面的那个打印日志的结果是:

image-20230704232949139

并没有将ajax中请求的结果拼接,因为ajax是异步请求,即执行ajax时,会不等待回调执行就会去执行后续代码,也就是说,执行那个输出日志时,ajax的回调可能还没执行呢

问题二: 多次异步调用 结果顺序不确定

image-20230704233053371

分别多测试几次

imgimgimg

多次刷新,多次请求,会发现结果顺序跟代码的上下顺序并不一样.

问题三: 异步调用的结果如果确实存在依赖关系,就需要嵌套处理

假如,三个请求的结果确实需要等输出了hello1再输出hello2,最后输出hello3的话,就需要嵌套处理.

image-20230704233218191

image-20230704233228739

7.2 Promise

Promise 是一种用于处理异步操作的前端开发技术。它是 ECMAScript 6(ES6)规范中引入的一种对象,旨在解决 JavaScript 中的回调地狱问题,使异步操作更加优雅和可维护。

在传统的 JavaScript 开发中,异步操作通常使用回调函数来处理。然而,当嵌套多个异步操作时,回调函数的嵌套会导致代码结构复杂、难以理解和维护。Promise 提供了一种更好的解决方案。

image-20230704233613080

基本语法

- 创建Promise对象§,构造函数中传递函数,该函数中用于处理异步任务

- 该函数中resolve和reject两个参数用于处理成功和失败的两种情况,并通过实例对象p调用then方法获取处理的结果

image-20230704233655407

示例

<script type="text/javascript">
    var p = new Promise(function(resolve,reject){
        /**
          * resolve和reject都是方法
          * 成功是调用resolve([参数])函数
          * 失败时调用reject([参数])函数 
          */
        // 异步请求
        $.ajax({
            url:'http://localhost:8080/vue/hello',
            success:function(data) {
                resolve(data);
            },
            error:function(){
                reject("出错啦");
            }
        })
    });
    p.then(function(ret){
        console.log(ret);
    },function(ret){
        console.log(ret);
    })
</script>

将以上代码封装成一个函数,就可以将jQuery中ajax嵌套调用的那种情况变得优雅一点

<script type="text/javascript">
    function queryData(path){
        var p = new Promise(function(resolve,reject){
            /**
             * resolve和reject都是方法
             * 成功是调用resolve([参数])函数
             * 失败时调用reject([参数])函数 
             */
            // 异步请求
            $.ajax({
                url:path,
                success:function(data) {
                    resolve(data);
                },
                error:function(){
                    reject("出错啦");
                }
            })
        });
        return p;
    }
    queryData('http://localhost:8080/vue/hello1')
    .then(function(ret){
        console.log(ret);
    })
</script>

八、Axios[重点]


8.1 什么是 Axios

Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,她的主要作用就是实现 AJAX 异步通信,其功能特点如下:

  • 从浏览器中创建 XMLHttpRequests

  • 从 node.js 创建 http 请求

  • 支持 Promise API

  • 拦截请求和响应

  • 转换请求数据和响应数据

  • 取消请求

  • 自动转换 JSON 数据

  • 客户端支持防御 XSRF(跨站请求伪造)

  • 官网基本用例 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架

8.2 Axios的使用

8.2.1 安装vue axios

npm install --save axios vue-axios

image-20230702185555289

8.2.2 在需要使用的页面中引入

比如UserInfo.vue页面需要发请求,那就在页面的script中引入即可

import axios from 'axios'

8.2.3 发送ajax请求

<template>
<div>
  <button @click="getInfo">查询数据</button>
  <hr>
    {{deptList}}
      <hr>
  <table>
    <th>
        部门编号
    </th>
    <th>
      部门名称
    </th>
    <th>
      部门地址
    </th>
    <tr v-for="dept in deptList">
        <td>{{dept.deptno}}</td>
        <td>{{dept.dname}}</td>
        <td>{{dept.loc}}</td>
    </tr>
  </table>
</div>
</template>

<script>
// 引入axios    
import axios from 'axios'
export default {
  name: "TestAxios",
  data(){
    return {
      deptList:null
    }
  },
  methods:{
    getInfo(){
      // axios.get('/api/dept').then(function(ret){
      //   console.log(ret)
      //   this.deptList = ret.data // 【有bug,因为this问题】
      // })
      axios.get('/api/dept').then((ret)=>{
        console.log(ret) // 【注意:这个返回值不一般】  
          // 可以查看官网https://www.axios-http.cn/docs/res_schema
          this.deptList = ret.data.data   
      })
    }
  }
}
</script>

<style scoped>
</style>

8.2.4 服务端解决跨域问题

SpringBoot的controller层类上添加@CrossOrigin注解即可

8.2.5 BUG

在axios的回调中,无法使用this.数据 获得vuedata中的数据

造成axios取不到值得原因主要是this回调问题。当执行完函数function时,this指向放生变化。导致this指向函数体本身。这时this.data里的数据取不到。

简单直接的解决就是把function写成箭头函数形式,箭头函数不是构造函数,不会指定this,而是抓取上下文this作为自己的this

8.3 其他api演示

可以查看官网请求配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

也可以看示例

<!-- get请求(1) 路径拼接参数 -->
axios.get('http://localhost:8080/vue/user?id=456').then(function(ret){
    console.log(ret.data); 
})

<!-- get请求(2) Restful风格参数 -->
axios.get('http://localhost:8080/vue/user/123').then(function(ret){
    console.log(ret.data.); 
})

<!-- get请求(3) 使用params传递参数 -->
axios.get('http://localhost:8080/vue/user',{
    params:{
        id:20001
    }
}).then(function(ret){
    console.log(ret.data); 
})
<!-- post请求,发送的是json,后台需要使用@RequestBody -->
axios.post('http://localhost:8080/vue/user/json',{
    username:'张三',
    password:'987654'
}).then(function(ret){
    console.log(ret.data); 
})
<!-- put请求 -->
axios.put('http://localhost:8080/vue/user/form',{
    username:'张三丰',
    password:'11111'
}).then(function(ret){
    console.log(ret.data); 
}).catch(function (err){
   console.log(err)
})

8.4 Axios的响应

官网: https://www.axios-http.cn/docs/res_schema

响应结果的主要属性:

  • data: 服务器响应回的数据
  • headers: 响应头信息
  • status: 响应状态码
  • statusText: 响应状态信息

需要特别注意,我们后台返回的数据在data中,即data中是后台返回的ResultObject,我们ret.data获得到的是ResultData,如果还有继续获得其中的数据,还需要再ret.data.data

8.5 axios的全局配置

官网: 默认配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

我们讲一个特别实用的,我们在项目中调用数十次乃至百次接口是很常见的,后期一旦根域名发生改变,所有接口都需要修改,非常繁琐且容易出错。

axios提供了设置根域名的方法。
在main.js全局入口文件中,设置:

axios.defaults.baseURL = 'http://localhost:8888'

在其他vue页面中使用axios发请求的时候

axios.get('/api/dept').then((ret)=>{
    console.log(ret)
     this.deptList = ret.data.data
 })

8.6 响应拦截

还有请求拦截,暂时用不上先不讲

响应拦截可以拦截到axios发请求后的那个回调response对象,然后对其进行处理

  • 实战,将response数据简化后返回

    // 添加Axios响应拦截器
    axios.interceptors.response.use(function (response) {
      //console.log('响应拦截',response)
      return response.data;
    }, function (error) {
      console.log(error)
    });
    

    这样,在使用axios的页面,从回调中获得数据时,只需要ret.data,而不需要再ret.data.data

附录

对象展开运算符

对象展开运算符 ...(也称为扩展运算符)是一种用于展开对象或数组的语法,它允许将一个对象或数组拆分成单独的元素,并在其他对象或数组中使用。

在 JavaScript 中,... 运算符可以用在多种场景中,下面分别介绍它们的用法。

  1. 展开对象:
    可以使用对象展开运算符 ... 来创建一个新对象,并将另一个对象中的属性和值复制到新对象中。这可以用于对象浅拷贝或对象合并。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { ...obj1, city: 'New York' };
    
    console.log(obj2);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  2. 合并对象:
    将多个对象合并成一个新对象的简便方法。如果有相同的属性,后面的对象的属性值会覆盖前面的对象的属性值。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { city: 'New York' };
    const obj3 = { ...obj1, ...obj2 };
    
    console.log(obj3);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  3. 展开数组:
    在数组中使用 ... 可以将一个数组展开为独立的元素,可以用于创建一个新数组或者在函数调用时传递参数。

    示例:

    const arr1 = [1, 2, 3];
    const arr2 = [...arr1, 4, 5];
    
    console.log(arr2);
    // 输出:[1, 2, 3, 4, 5]
    
    const arr = [1, 2, 3];
    someFunction(...arr); // 将数组中的元素作为参数传递给函数
    

页面中使用axios发请求的时候

axios.get('/api/dept').then((ret)=>{
    console.log(ret)
     this.deptList = ret.data.data
 })

8.6 响应拦截

还有请求拦截,暂时用不上先不讲

响应拦截可以拦截到axios发请求后的那个回调response对象,然后对其进行处理

  • 实战,将response数据简化后返回

    // 添加Axios响应拦截器
    axios.interceptors.response.use(function (response) {
      //console.log('响应拦截',response)
      return response.data;
    }, function (error) {
      console.log(error)
    });
    

    这样,在使用axios的页面,从回调中获得数据时,只需要ret.data,而不需要再ret.data.data

附录

对象展开运算符

对象展开运算符 ...(也称为扩展运算符)是一种用于展开对象或数组的语法,它允许将一个对象或数组拆分成单独的元素,并在其他对象或数组中使用。

在 JavaScript 中,... 运算符可以用在多种场景中,下面分别介绍它们的用法。

  1. 展开对象:
    可以使用对象展开运算符 ... 来创建一个新对象,并将另一个对象中的属性和值复制到新对象中。这可以用于对象浅拷贝或对象合并。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { ...obj1, city: 'New York' };
    
    console.log(obj2);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  2. 合并对象:
    将多个对象合并成一个新对象的简便方法。如果有相同的属性,后面的对象的属性值会覆盖前面的对象的属性值。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { city: 'New York' };
    const obj3 = { ...obj1, ...obj2 };
    
    console.log(obj3);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  3. 展开数组:
    在数组中使用 ... 可以将一个数组展开为独立的元素,可以用于创建一个新数组或者在函数调用时传递参数。

    示例:

    const arr1 = [1, 2, 3];
    const arr2 = [...arr1, 4, 5];
    
    console.log(arr2);
    // 输出:[1, 2, 3, 4, 5]
    
    const arr = [1, 2, 3];
    someFunction(...arr); // 将数组中的元素作为参数传递给函数
    

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

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

相关文章

ThreadPoolExecutor 线程池源码学习

ThreadPoolExecutor 线程池源码学习 1.阅读源码 1.ThreadPoolExecutor.execute public void execute(Runnable command) {if (command null)throw new NullPointerException();// ctl 高三位记录线程状态。低29位记录线程池中线程数int c ctl.get();//位运算获取工作线程数 …

wireshark抓包实践

目录 ifconfig ( network interfaces configuring )tcpdump 命令tcpdump&wireshark例子 ifconfig ( network interfaces configuring ) eth0表示网卡UP代表网卡开启状态RUNNING代表网卡的网线被接上mtu1500: MTU&#xff08;最大传输单元&#xff09;是指在网络中传输数据时…

【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】

volatile的作用 JMM下volatile作用 volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候 改变线程工作内存中volatile变量副本的值将改变后的副本的值从工作内存刷新到主内存 代码在读取 volatile 修饰的变量的时…

B067-基础环境-抽取Basegit

目录 抽取base抽取domain和querymapper接口抽取service抽取 Git优点&#xff1a;Git安装及操作Git Bash命令行操作图形化客户端TortoiseGit操作Git集成Idea操作idea会把workspace作为本地仓库gitee操作idea解决代码冲突 抽取base 抽取domain和query domain&#xff1a;所有实体…

Nodejs 依赖包的存放路径设置(按其他博客修改路径后,安装路径仍在C盘的解决办法)

Nodejs 依赖包的存放路径设置 使用命令npm root -g 查看依赖包的安装位置 默认依赖包的安装位置是在C盘。为了防止C盘存太多东西&#xff0c;我这里已经将安装位置改到了D盘&#xff0c;下面就记录下修改的步骤。 1. 创建新的依赖包安装目录 在 nodejs 的安装目录下创建两个新…

8年资深测试总结,性能测试+性能优化(详细)进军高级测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能优化常见概念…

LabVIEW FPGA利用响应式数字电子板快速开发空间应用程序

LabVIEW FPGA利用响应式数字电子板快速开发空间应用程序 与传统的基于文本的语言相比&#xff0c;LabVIEW的编程和设计已被证明可以缩短开发时间。各种研究表明&#xff0c;生产率的提高在3到10倍之间。LabVIEW通过图形语言、集成开发环境和多个编译器的组合来实现这一点。 图…

qt对话框

完善文本编辑器 #include "second.h" #include "ui_second.h"second::second(QWidget *parent) :QWidget(parent),ui(new Ui::second) {ui->setupUi(this);this->setWindowTitle("聊天室界面");//设置标题this->setWindowIcon(QIcon(&…

边缘检测

目录 1、边缘检测原理 2、Sobel算子边缘检测 3、Scharr算子边缘检测​编辑 4、算子生成函数 5、Scharr、Sobel的使用 6、Laplacian算子边缘检测 7、Canny算子边缘检测 8、Laplacian、Canny的使用 1、边缘检测原理 2、Sobel算子边缘检测 3、Scharr算子边缘检测 4、算子生成函…

MySQL数据库 - 表的操作

目录 一、创建表 二、创建表案例 1、显示当前使用的数据库名 2、创建表 2.1 MyISAM存储引擎表 2.2 InnoDB存储引擎表 三、查看表结构 四、修改表 1、新增列 2、修改列类型 3、修改列名 4、修改表名 5、删除列 五、删除表 表的操作至少会涉及如下两类SQL语句&…

adb日常使用命令

重启电脑adb服务 adb start-server和adb kill-server mac中uiautoviewer的位置 android-sdk→tools→bin→uiautomatorviewer.bat adb查看本机abi类型 adb shell getprop ro.product.cpu.abi github 比较好的adb教程&#xff1a; https://github.com/mzlogin/awesome-adb a…

[VUE学习]权限管理系统前端vue实现9-动态路由,动态标签页,动态面包屑

1.动态路由 1.因为我们左侧权限菜单是根据不同用户显示不一样的 所以我们需要使用动态路由 来动态生成右侧路由信息 在总体布局页面添加router <router-view> 是 Vue Router 提供的组件&#xff0c;用于动态展示匹配到的路由组件内容。通过在合适的位置放置 <router-v…

将word中超链接的字体颜色更换成白色

文章目录 1、问题描述2、解决方法&#xff08;两种&#xff09;2.1 临时修改2.2 永久修改 1、问题描述 超链接是蓝色&#xff0c;需要将其换成正常颜色的字体 2、解决方法&#xff08;两种&#xff09; 2.1 临时修改 直接选中该字体&#xff0c;从字体的颜色那里选主题颜色…

zabbix安装监控客户端应用

添加 zabbix 客户端主机 服务端和客户端都配置时间同步 服务端和客户端都设置 hosts 解析 设置 zabbix 的下载源&#xff0c;安装 zabbix-agent2 在服务端验证 zabbix-agent2 的连通性 ​编辑 在 Web 页面中添加 agent 主机 自定义监控内容 在客户端创建自定义 key 1.明确…

XSS学习

目录 什么是XSS 概念 理解 XSS分类 存储型XSS 反射型XSS 原理 攻击过程 DOM型 攻击过程 DOM行XSS与反射型XSS区别 存储型XSS与反射型XSS区别 DVWA实验 反射型XSS low等级 JavaScript弹窗函数 攻击思路 攻击者web设计 medium等级 high等级 impissible等级 …

【ES6】中构造函数的语法糖 —— Class(类)

在现代前端开发中&#xff0c;JavaScript的面向对象编程成为了主流。ES6引入了class关键字&#xff0c;使得开发者可以更方便地使用面向对象的方式编写代码&#xff0c;更接近传统语言的写法。ES6的class可以看作是一个语法糖&#xff0c;它的绝大部分功能ES5都可以做到&#x…

Java基础---动态代理

目录 典型回答 静态代理和动态代理的区别 动态代理的用途 Spring AOP的实现方式 JDK 动态代理的代码段 Cglib动态代理的代码段 典型回答 动态代理就是&#xff0c;在程序运行期&#xff0c;创建目标对象的代理对象&#xff0c;并对目标对象中的方法进行功能性增强的一种技…

electron+vue3全家桶+vite项目搭建【22】vite定义编译时全局变量,用于渲染进程判断当前是否为打包环境

引入 demo项目地址 我们在本地运行时往往显示的是一些方便调试的页面&#xff0c;如下所示: 通过页面路由选择&#xff0c;快速打开不同的窗口 而当我们打包运行时&#xff0c;往往希望直接进入软件的主页&#xff0c;而不显示这些调试页面&#xff0c;也许你会觉得&#xf…

设计模式之三:装饰者模式

装饰者模式可以在不修改任何底层代码的情况下&#xff0c;给对象赋予新的职责&#xff08;使用对象组合的方式&#xff0c;在运行时装饰类&#xff09;。 假定星巴兹咖啡需要更新订单系统&#xff0c;而他们原先类的设计如图&#xff1a; 现在他们考虑客户可以选择添加调料&am…

day62_ssm事务

今日内容 零、 复习昨日 零、 复习昨日 excel导入导出,cv配置和方法 aop: 面向切面编程 抽取与业务无关的代码,比如日志记录,事务控制,权限校验等,形成一个切面 利用动态代理的技术将切面中的增强方法,作用到目标方法上 aop日志 日志注解切面类 切入注解获得时间,ip,session中的…