Vue3 详细教程


提示:以下是本篇文章正文内容,前端系列学习将会持续更新

在这里插入图片描述
官网https://cn.vuejs.org

一、API 风格

1.1 选项式 API

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

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

<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        count: 0
      }
  	},
    methods: {
      
      
      increment() {
      
      
        this.count++
      }
    },
    mounted() {
      
      
      // 该函数就会在组件挂载完成后被调用
      // 一般进行网络请求,重新渲染数据
    }
  }
</script>

1.2 组合式 API

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

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

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

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

  // 响应式状态
  const count = ref(0)

  // 用来修改状态、触发更新的函数
  function increment() {
      
      
    count.value++
  }

  // 生命周期钩子
  onMounted(() => {
      
      
    console.log(`The initial count is ${ 
        count.value}.`)
  })
</script>

回到目录…

二、Vue 指令

在这里插入图片描述

2.1 { {}} 文本插值

  • { {}} 的作用是:文本替换,内部可以计算数值
<template>
  <h2>{
   
   { message }}世界</h2>      <!-- 你好!世界 -->
  <h2>{
   
   { age + 1 }}</h2>    <!-- 13 -->
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        message: "你好!",
        age: 12
      }
    }
  }
</script>

2.2 v-html 标签元素

  • v-html 指令的作用是:设置元素的 innerHTML
  • 内容中有 html 结构会被解析为标签。
  • 解析文本使用 { {}},需要解析 html 结构使用 v-html。
<template>
  <p v-html="message"></p>  <!-- <p><a href='https://www.baidu.com'>百度百科</a></p> -->
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        message: "<a href='https://www.baidu.com'>百度百科</a>"
      }
    }
  }
</script>

2.3 v-on 绑定事件

  • v-on 指令的作用是:为元素绑定事件,包括:blur, focus, focusin, focusout, load, resize, scroll, unload, click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave,change, select, submit, keydown, keypress, keyup, error。
  • v-on 指令可以简写为 @,如 v-on:click="func()" 等同于 @click="func()"
  • 绑定的方法定义在 methods 属性中,方法内部通过 this 关键字可以访问定义在 data 中的数据。
<template>
  <p>{
   
   {message}}</p>
  <input type="button" value="触发事件1" v-on:click="func">
  <input type="button" value="触发事件2" @click="func2('张三')">
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        message: "Hello,World!"
      }
    },
    methods: {
      
      
      func() {
      
      
        alert(this.message);
      },
      func2(name) {
      
      
        alert("你好," + name);
      }
    }
  }
</script>

2.4 v-show 隐藏元素

  • v-show 指令的作用是:根据真假切换元素的显示状态
  • v-show 原理是修改元素的 display 值,实现显示和隐藏。其值最终会解析为布尔值 (true元素显示,false元素隐藏)。
  • 数据改变之后,对应元素的显示状态会同步更新。
<template>
  <div v-show="isShow">模块一</div>
  <div v-show="num > 10">模块二</div>
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        isShow: false,
        num: 18
      }
    }
  }
</script>

2.5 v-if 消除元素

  • v-if 指令的作用是:根据表达式的真假切换元素的显示状态
  • 本质是通过操纵 dom 元素来切换显示状态。表达式值为true时,元素存在于dom树中;值为false时,从dom树中移除。
  • 频繁的切换使用 v-show,反之使用 v-if,前者的切换消耗小。
<template>
  <div v-if="isShow">模块一</div>
  <div v-if="num > 10">模块二</div>
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        isShow: false,
        num: 18
      }
    }
  }
</script>

2.6 v-bind 属性渲染

  • v-bind 指令的作用是:为元素绑定属性
  • v-bind 指令可简写为 : ,如 v-bind:src="imgSrc" 等同于 :src="imgSrc"
  • 需要动态的增删 class 时,建议使用对象的方式。
<template>
  <a v-bind:href="myHref">百度百科</a>
  <input type="button" :value="myVal">
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        myHref: "https://www.baidu.com",
        myVal: "我的按钮"
      }
    }
  }
</script>

2.7 v-for 列表渲染

  • v-for 指令的作用是:根据数据生成列表结构。经常和数组结合使用。
  • v-for 语法是 item in arr(item,index) in arr,item 和 index 可以结合其他指令一起使用。
  • 当数组长度发生改变时,更新会同步到页面上,是响应式的。但它默认使用 “就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序。为了让 Vue 能跟踪每个节点的身份,从而重新排序现有元素,我们需要为 v-for 渲染的元素添加一个唯一的 key(可以使用index,但建议使用动态数据的唯一ID)
<template>
  <ul>
    <li v-for="item in vegetables">菜品:{
   
   { item.name }}, 价格:{
   
   { item.price }}</li>
  </ul>
  <p v-for="(it,index) in arr" :key="it.id|index">{
   
   { index+1 }}.{
   
   { it }}</p>
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        arr:["北京","上海","广州","深圳"],
        vegetables:[
          {
      
      name:"西兰花炒蛋", price:20},
          {
      
      name:"蛋炒西蓝花", price:30}
        ]
      }
    }
  }
</script>

2.8 v-model 数据双向绑定

  • v-model 指令的作用是便捷的设置和获取表单元素的值
  • 绑定的数据会和表单元素值相关联,绑定的数据 <=> 表单元素的值。
<template>
  <input type="text" v-model="message">
  <p>动态修改massage值: {
   
   { message }}</p>
</template>
<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        message:"沙丁鱼"
      }
    }
  }
</script>

回到目录…

三、组件

在这里插入图片描述

扫描二维码关注公众号,回复: 15394403 查看本文章

3.1 组件组合

①单文件组件(又名 *.vue 文件,缩写为 SFC) 是一种特殊的文件格式,它允许将 Vue 组件的模板逻辑样式封装在单个文件中。

<template>
  <h2>我的单文件组件</h2>
</template>

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

<style scoped>  <!-- scoped表示只改变该文件的模板样式 -->
  h2 {
      
      
    color: red;
  }
</style>

②加载组件
第一步:引入组件 import MyComponent from './components/MyComponent.vue'
第二步:挂载组件 components: { MyComponent }
第三步:显示组件 <MyComponent/>

<template>
  <h2>APP.vue的二级标题</h2>
  <MyComponent/>
</template>

<script>
  import MyComponent from './components/MyComponent.vue';
  export default {
      
      
    name: "APP",
    components: {
      
      
      MyComponent
    }
  }
</script>

查看效果
在这里插入图片描述

③组件的组织:通常一个应用会以一棵嵌套的组件树的形式来组织。
在这里插入图片描述

回到目录…

3.2 Props 组件交互

  • 父组件向子组件传递数据,Props 可以在组件上注册的一些自定义属性将数据传给子组件。

①传递属性:父组件向子组件传递属性值。

父组件:App.vue

<template>
  <h2>APP.vue的二级标题</h2>
  <MyComponent :title="title" :arr="arr"/>
</template>

<script>
  import MyComponent from './components/MyComponent.vue';
  export default {
      
      
    name: "APP",
    data() {
      
      
      return {
      
      
        title: "传递字符串",
        arr: ["zhangsan","lisi","wangwu"]
      }
    },
    components: {
      
      
      MyComponent
    }
  }
</script>

子组件:MyComponent.vue

<template>
  <h2>Props 组件交互</h2>
  <p>{
   
   { title }}</p>
  <p v-for="(item,index) in arr" :key="index">{
   
   { item }}</p>
</template>

<script>
  export default {
      
      
    name: "MyComponent",
    props: {
      
      
      title: {
      
      
        type: String, //数据类型
        default: ""   //默认值
      },
      arr: {
      
      
        type: Array,
        default: function() {
      
      
          return [];
        }
      }
    }
  }
</script>

②Prop 类型:Prop传递参数其实是没有类型限制的。

注意:数据类型为数组或者对象的时候,默认值是需要返回工厂模式 (使用函数返回)。

props: {
    
    
  title: String, // 字符串类型
  likes: Number, // 数字类型
  isPublished: Boolean, // 布尔类型
  commentIds: Array, // 数组类型
  author: Object, // 对象类型
  callback: Function // 函数类型
}

回到目录…

3.3 自定义事件组件交互

  • 子组件向父组件传递数据,自定义事件可以在组件中反向传递数据。

子组件:MyComponent.vue

<template>
  <h2>自定义事件组件交互</h2>
  <input type="button" value="发送数据" @click="sendData">
</template>

<script>
  export default {
      
      
    name: "MyComponent",
    methods: {
      
      
      sendData() {
      
      
        // 参数一: 接收数据的事件名称,参数二: 发送的数据
        this.$emit("onEvent", {
      
      name:"zhangsan",age:18});
      }
    }
  }
</script>

父组件:App.vue

<template>
  <h2>APP.vue的二级标题</h2>
  <MyComponent @onEvent="getData"/>
</template>

<script>
  import MyComponent from './components/MyComponent.vue';
  export default {
      
      
    name: "APP",
    components: {
      
      
      MyComponent
    },
    methods: {
      
      
      getData(student) {
      
      
        alert("姓名:" + student.name + ",年龄:" + student.age);
      }
    }
  }
</script>

回到目录…

3.4 组件生命周期

每个组件在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

在这里插入图片描述

为了方便记忆,我们可以将他们分类:

  • 创建时: beforeCreatecreated
  • 渲染时: beforeMountmounted
  • 更新时: beforeUpdateupdated
  • 卸载时: beforeUnmountunmounted

回到目录…

四、Vue 引入第三方 Swiper

在这里插入图片描述

  • 官方文档https://swiperjs.com/vue
  • Swiper 开源、免费、强大的触摸滑动插件。
  • Swiper 是纯 JavaScript 打造的滑动特效插件,面向手机、平板电脑等移动终端。
  • Swiper 能实现触屏焦点图、触屏 Tab 切换、触屏轮播图切换等常用效果。

①安装指定版本: npm install --save [email protected]

②基础实现

<template>
    <div class="hello">
        <swiper class="mySwiper" :modules="modules" :pagination="{ clickable:true }">
            <swiper-slide><img src="../assets/1.png"></swiper-slide>
            <swiper-slide><img src="../assets/2.png"></swiper-slide>
            <swiper-slide><img src="../assets/3.png"></swiper-slide>
        </swiper>
    </div>
</template>

<script>
import {
      
       Swiper, SwiperSlide } from 'swiper/vue'; //核心组件
import {
      
       Pagination } from 'swiper'; //指示器组件
import 'swiper/css';
import 'swiper/css/pagination';

export default {
      
      
    name: "HelloWorld",
    data() {
      
      
        return {
      
      
            modules: [Pagination]
        }
    },
    components: {
      
      
        Swiper,
        SwiperSlide
    }
}
</script>

<style scoped>
    img {
      
      
        width: 100%;
    }
</style>

效果展示
在这里插入图片描述

回到目录…

五、Axios 网络请求库

官网https://github.com/axios/axios

在这里插入图片描述

5.1 基本使用

①安装依赖

# Axios 需要单独安装 
npm install --save axios
# post 请求参数是需要额外处理,安装依赖; 参数转换格式: qs.stringify({})
npm install --save querystring

②组件中引入import axios from "axios"

③GET请求

axios({
    
    
    method: "get",
    url: "http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php"
}).then(res => {
    
    
    console.log(res.data);
})
axios.get("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php")
.then(res => {
    
    
	console.log(res.data);
})

④POST请求

axios({
    
    
    method: "post",
	url: "http://iwenwiki.com/api/blueberrypai/login.php",
    data: qs.stringify({
    
    
        user_id: "[email protected]",
        password: "iwen123",
        verification_code: "crfvw"
    })
}).then(res =>{
    
    
    console.log(res.data);
})
axios.post("http://iwenwiki.com/api/blueberrypai/login.php", qs.stringify({
    
    
	user_id: "[email protected]",
	password: "iwen123",
    verification_code: "crfvw"
})).then(res => {
    
    
	console.log(res.data);
})

回到目录…

5.2 Axios 网络请求封装

在实际应用过程中,一个项目中的网络请求会很多,此时一般采取的方案是将网络请求封装起来。

①我们先创建 src/utils/request.js 文件,用来存储网络请求对象 axios。
参考文档Axios 看云

import axios from "axios"
import qs from "querystring"

const errorHandle = (status,info) => {
    
    
    switch(status) {
    
    
        case 400: console.log("语义有误"); break;
        case 401: console.log("服务器认证失败"); break;
        case 403: console.log("服务器拒绝访问"); break;
        case 404: console.log("地址错误"); break;
        case 500: console.log("服务器遇到意外"); break;
        case 502: console.log("服务器无响应"); break;
        default: console.log(info); break;
    }
}

const instance = axios.create({
    
    
    timeout:5000 // 网络请求的公共配置: 请求超时时间
})

// 发送数据前的拦截器
instance.interceptors.request.use(
    config => {
    
    
        if(config.method === "post") {
    
    
            config.data = qs.stringify(config.data);
        }
        return config; // 包括网络请求的所有信息
    },
    error => Promise.reject(error)
)
// 获取数据前的拦截器
instance.interceptors.response.use(
    response => response.status === 200 ? Promise.resolve(response) : Promise.reject(response),
    error => {
    
    
        const {
    
     response } = error;
        errorHandle(response.status, response.info);
    }
)

export default instance;

②创建 src/api/path.js 用来存放网络请求路径;创建 src/api/index.js 用来存放网络请求路径

// path.js
const base = {
    
    
    baseUrl:"http://iwenwiki.com",
    chengpin:"/api/blueberrypai/getChengpinDetails.php"
}
export default base
// index.js
import path from "./path"
import axios from "../utils/request"

export default {
    
    
    getChengpin() {
    
    
        return axios.get(path.baseUrl + path.chengpin);
    }
}

③在组件中直接调用网络请求

<script>
import api from './api/index';
export default {
      
      
    name: "APP",
    mounted() {
      
      
        api.getChengpin().then(res => {
      
      
            console.log(res.data);
        })
    }
}
</script>

回到目录…

5.3 网络请求跨域解决方案

JS 采取的是同源策略。同源策略是浏览器的一项安全策略,浏览器只允许 js 代码请求当前所在服务器的域名、端口、协议相同的数据接口上的数据,这就是同源策略
也就是说,当协议、域名、端口任意一个不相同时,都会产生跨域问题,所以又应该如何解决跨域问题呢?

跨域错误提示信息http://iwenwiki.com/api/FingerUnion/list.php
在这里插入图片描述
目前主流的跨域解决方案有两种:后台解决:cors;前台解决:proxy

①我们找到父工程下的 vue.config.js 或 vite.config.js 文件,向它的 defineConfig 项中添加以下配置:

// vue.config.js配置
devServer: {
    
    
  proxy: {
    
    
  '/api': {
    
    
    target: 'http://xxxxxxxxxxx.xxx/api',
    changOrigin: true,
    pathRewrite: {
    
    
      '^/api': ''
    }
  }
}
// vite.config.js配置
server: {
    
    
  proxy: {
    
    
    '/api': {
    
    
      target: 'http://iwenwiki.com/api', // 凡是遇到 /api 路径的请求,都映射到 target 属性
      changeOrigin: true,
      rewrite: path => path.replace(/^\/api/, '') // 重写 /api 为 空,就是去掉它
    }
  }
}

②如何访问跨域的地址?

mounted() {
    
    
  // 使用'/url'代替域名部分即可
  axios.get("/api/FingerUnion/list.php")
  .then(res => {
    
    
    console.log(res.data);
   })
}

注意:解决完跨域配置之后,要记得重启服务器才行哦!

回到目录…

六、Vue Router 路由

官方文档https://router.vuejs.org/zh

在这里插入图片描述

在 Vue 中,我们可以通过 vue-router 路由管理页面之间的关系,Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让 Vue.js 构建单页应用变得轻而易举。

6.1 引入路由

第一步:安装路由 npm install --save vue-router

第二步:配置独立的路由文件 src/router/index.js

import {
    
     createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
    {
    
    
        path: '/',
        name: 'home',
        component: Home
    },
    {
    
    
        path: '/about',
        name: 'about',
        component: () => import('../views/About.vue') //异步加载方式
    }
]

const router = createRouter({
    
    
    /**
     * createWebHashHistory
     *      home: http://localhost:5176/#/
     *      about: http://localhost:5176/#/about
     * 原理: a标签锚点链接
     * 
     * createWebHistory, 此种方式需要后台配合做重定向,否则会出现404问题
     *      home: http://localhost:5176/
     *      about: http://localhost:5176/about
     * 原理: HTML5的pushState()
     */
    history: createWebHashHistory(),
    routes
})

export default router

第三步:引入路由到项目,在 src/main.js 中添加 use(router)

import './assets/main.css'

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

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

第四步:指定路由显示入口、路由跳转

<template>
  <nav>
    <RouterLink to="/">主页</RouterLink> | 
    <router-link to="/about">关于</router-link>
  </nav>
  <RouterView></RouterView> <!-- 路由跳转入口 -->
</template>

在这里插入图片描述

回到目录…

6.2 根据URL参数路由

页面跳转过程中,是可以携带参数的,这也是很常见的业务,比如我们的分页功能、商品项详情。

第一步:在路由配置中指定参数的 key

{
    
    
    path: '/news/details/:name',
    name: 'newsdetails',
    component: () => import('../views/NewsDetails.vue') //对应新闻详情页
}

第二步:在跳转过程中携带参数

<template>
    <h1>News页面</h1>
    <ul>
        <li><RouterLink to="/news/details/百度">百度新闻</RouterLink></li>
        <li><RouterLink to="/news/details/新浪">新浪新闻</RouterLink></li>
        <li><RouterLink to="/news/details/网易">网易新闻</RouterLink></li>
    </ul>
</template>

第三步:在详情页面读取路由携带的参数

<template>
    <h1>新闻详情</h1>
    <p>{
   
   { $route.params.name }}</p>
</template>

在这里插入图片描述

回到目录…

6.3 多级路由嵌套

路由嵌套是十分常见的需求,比如二级导航。
在这里插入图片描述

第一步:创建子路由要加载显示的页面
关于我们的页面:src/views/aboutsub/AboutUS.vue
更多信息的页面:src/views/aboutsub/AboutInfo.vue

第二步:在路由配置文件中添加子路由配置

{
    
    
	path: '/about',
    name: 'about',
    redirect: '/about/us', //重定向到指定的子路由
    component: () => import('../views/About.vue'),
    children: [
        {
    
    
            path: 'us', //子路由不加'/'
            component: () => import('../views/aboutsub/AboutUS.vue')
        },
        {
    
    
            path: 'info',
            component: () => import('../views/aboutsub/AboutInfo.vue')
        }
    ]
}

第三步:指定子路由显示位置、跳转链接

<template>
    <h1>About页面</h1>
    <RouterLink to="/about/us">关于我们</RouterLink> | 
    <RouterLink to="/about/info">更多信息</RouterLink>
    <RouterView></RouterView>
</template>

在这里插入图片描述

回到目录…

七、Vuex 状态管理

官方文档https://vuex.vuejs.org/zh/index.html
在这里插入图片描述
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

简单来说,状态管理可以理解成为了更方便的管理组件之间的数据交互,提供了一个集中式的管理方案,任何组件都可以按照指定的方式进行读取和改变数据。

如何引入Vuex?

第一步:安装Vuex npm install --save vuex

第二步:配置Vuex文件,创建 src/store/index.js

import {
    
     createStore } from 'vuex'
// Vuex的作用就是帮助我们管理组件之间的状态的
export default createStore({
    
    
    state: {
    
    
        // 所有状态(数据)都存储在这里
    },
    getters: {
    
    
        // 可以通过这里的方法进行数据过滤
    },
    mutations: {
    
    
        // 数据更改
    },
    actions: {
    
    
        // 数据异步更改
    }
})

第三步:在 main.js 主文件中引入Vuex

import store from './store'
createApp(App).use(store).mount('#app')

回到目录…

7.1 State 数据存储

①所有数据存储在 state 中:

state: {
    
    
	num: 13
}

②在组件中读取数据:

<p>{
   
   { $store.state.num }}</p>

或者

<template>
  <p>{
   
   { num }}</p>
</template>

<script>
  import {
      
       mapState } from 'vuex';
  export default {
      
      
    name: "APP",
    computed: {
      
      
      ...mapState(['num'])
    }
  }
</script>

回到目录…

7.2 Getter 数据过滤

①对 Vuex 中的数据进行过滤:

getters: {
    
    
	getNum(state) {
    
    
		return state.num>18 ? state.num : '未成年'
    }
}

②在组件中获取过滤后的数据:

<p>{
   
   { $store.getters.getNum }}</p>

或者

<template>
  <p>{
   
   { num }}</p>
  <p>{
   
   { getNum }}</p>
</template>

<script>
  import {
      
       mapState, mapGetters } from 'vuex';
  export default {
      
      
    name: "APP",
    computed: {
      
      
      ...mapState(['num']),     // 直接获取数据
      ...mapGetters(['getNum']) // 获取过滤数据
    }
  }
</script>

回到目录…

7.3 Mutation 数据更改

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type) 和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

①更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

mutations: {
    
    
	incrNum(state) {
    
    
		state.num++
	},
    addNum(state,n) {
    
    
        state.num += n
    }
}

②在组件中通过触发事件来修改数据:

<template>
  <button @click="onEvent">修改数据</button>
  <button @click="onEvent2">修改数据2</button>
</template>

<script>
  export default {
      
      
    name: "APP",
    methods: {
      
      
      onEvent() {
      
      
        this.$store.commit('incrNum')
      },
      onEvent2() {
      
      
        this.$store.commit('incrNum', 10)
      }
    }
  }
</script>

或者

<template>
  <button @click="onEvent3">修改数据3</button>
</template>

<script>
  import {
      
       mapState, mapGetters, mapMutations } from 'vuex';
  export default {
      
      
    name: "APP",
    methods: {
      
      
      ...mapMutations(['addNum']),
      onEvent3() {
      
      
        this.addNum(20)
      }
    }
  }
</script>

回到目录…

7.4 Action 异步更改

Action 类似于 mutation,实际上 Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。

①对 Vuex 中的数据进行异步更改

mutations: {
    
    
    addNum(state,n) {
    
    
        state.num += n
    }
},
actions: {
    
    
	asyncAddNum({
     
      commit }) {
    
    
        axios.get('http://iwenwiki.com/api/generator/list.php')
        .then(res => {
    
    
            commit('addNum', res.data[0]) // 调用mutations中的方法
        })
    }
}

②在组件中通过触发事件来进行异步修改:

<template>
  <button @click="asyncEvent">异步修改</button>
</template>

<script>
  export default {
      
      
    name: "APP",
    methods: {
      
      
      asyncEvent() {
      
      
        this.$store.dispatch('asyncAddNum')
      }
    }
  }
</script>

或者

<template>
  <button @click="asyncEvent2">异步修改2</button>
</template>

<script>
  import {
      
       mapState, mapGetters, mapMutations, mapActions } from 'vuex';
  export default {
      
      
    name: "APP",
    methods: {
      
      
      ...mapActions(['asyncAddNum']),
      asyncEvent2() {
      
      
        this.asyncAddNum()
      }
    }
  }
</script>

回到目录…

八、Vue3 组合式 API

  • Vue3 是目前 Vue 的最新版本,自然也是新增了很多新特性。
  • Performance:性能更比 Vue 2.0 强。
  • Tree shaking support:可以将无用模块“剪辑”,仅打包需要的。
  • Composition API:组合 API。
  • Fragment, Teleport, Suspense:“碎片”,Teleport 即 Protal 传送门,“悬念”。
  • Better TypeScript support:更优秀的Ts支持。
  • Custom Renderer API:暴露了自定义渲染 API。

8.1 ref 和 reactive

  • ref 通常用来定义基本类型数据;reactive 通常用来定义对象或数组类型数据。
  • ref 也可以用来定义对象或者数组类型的数据,内部会通过 reactive 转为代理对象。
<template>
  <div id="app">
    <p>{
   
   { message }}</p>
    <p v-for="(item,index) in citys.arr" :key="index">{
   
   { item }}</p>
    <p v-for="(item,index) in list" :key="index">{
   
   { item }}</p>
    <p>{
   
   { citys.age }}</p>
  </div>
</template>

<script>
import {
      
      ref, reactive} from 'vue'
export default {
      
      
  setup() {
      
      
    const message = ref('张三')
    const list = ref(['a','b','c'])
    const citys = reactive({
      
      
      arr: ['北京','上海','广州','深圳'],
      age: 18
    })
    return {
      
      
      message,
      citys,
      list
    }
  }
}
</script>

回到目录…

8.2 setup() 定义方法

  • 在 setup() 中不仅可以定义数据,还可以定义方法 (以lamada表达式的方式)。
  • 在 setup() 中定义的数据和方法,都需要使用 return 返回才能被调用。
<template>
  <div id="app">
    <button @click="onEvent">不带参数</button>
    <button @click="onEvent2('张三')">携带参数</button>
    <button @click="changeData">修改数据</button>
  </div>
</template>

<script>
import {
      
      ref, reactive} from 'vue'
export default {
      
      
  setup() {
      
      
    const onEvent = () => {
      
      
      alert('触发事件');
    }
    const onEvent2 = (arg) => {
      
      
      alert(arg);
    }
    const changeData = () => {
      
      
      message.value = '李四'
    }
    return {
      
      
      onEvent,
      onEvent2,
      changeData
    }
  }
}
</script>

回到目录…

8.3 props 和 context

在2.x中,组件的方法中可以通过this获取到当前组件的实例,并执行 data 变量的修改,方法的调用,组件的通信等等,但是在3.x中,setup() 在 beforeCreate 和 created 时机就已调用,无法使用和2.x一样的 this,但是可以通过接收 setup(props,ctx) 的方法,获取到当前组件的实例和 props。

父组件发送数据

<template>
  <div id="app">
    <Home title="哈哈"/>
  </div>
</template>

<script>
import Home from './views/Home.vue'
export default {
      
      
    components: {
      
       Home }
}
</script>

子组件接收数据:props 的新用法。

<template>
    <h1>Home页面</h1>
    <p>{
   
   { title }}</p>
</template>

<script>
export default {
      
      
    name: 'home',
    props: {
      
      
        title: String
    },
    setup(props) {
      
      
        const title = props.title
        return {
      
      
            title
        }
    }
}
</script>

子组件发送数据:setup() 中取消了 this 关键字,而是用 context 代替。

<template>
    <h1>Home页面</h1>
    <input type="button" value="发送数据" @click="sendData">
</template>

<script>
export default {
      
      
    name: 'home',
    setup(props,ctx) {
      
      
        const sendData = () => {
      
      
            ctx.emit("onEvent", {
      
      name:"zhangsan",age:18});
        }
        return {
      
      
            sendData
        }
    }
}
</script>

父组件接收数据

<template>
  <div id="app">
    <Home @onEvent="getData"/>
  </div>
</template>

<script>
import Home from './views/Home.vue'
export default {
      
      
	components: {
      
       Home },
    setup() {
      
      
        const getData = (student) => {
      
      
          alert("姓名:" + student.name + ",年龄:" + student.age);
        }
        return {
      
      
            getData
        };
    }
}
</script>

回到目录…

8.4 Provide 和 Inject

  • provide() 和 inject() 可以实现嵌套组件之间的数据传递,这两个函数只能在 setup() 函数中使用。
  • 父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。
  • 不限层级。
// 父组件
import {
    
    provide} from 'vue'
import Home from './views/Home.vue'
export default {
    
    
    components: {
    
     Home },
    setup() {
    
    
        provide('num', 18);
    }
}
// 子组件
import {
    
     inject } from 'vue';
export default {
    
    
    setup() {
    
    
        const num = inject('num')
        return {
    
    
            num
        }
    }
}

回到目录…

8.5 setup中生命周期钩子

Options API Hook inside setup
beforeCreate Not needed*
created Not needed*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted

在 setup() 中可以定义多个相同的生命周期函数,并且在周期内都会执行。

export default {
    
    
  setup() {
    
    
    onMounted(() => {
    
    
      console.log('业务一')
    })
    onMounted(() => {
    
    
      console.log('业务二')
    })
  }
}

回到目录…

九、Element-plus

官网Element Plus, 面向设计师和开发者的组件库
在这里插入图片描述

安装 Element-Plusnpm install element-plus --save

9.1 加载组件

方式一:完整引用,如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便。

// main.js
import './assets/main.css'

import {
    
     createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

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

方式二:按需导入才是我们的最爱,毕竟在真实的应用场景中并不是每个组件都会用到,这会造成不小的浪费。

①首先你需要安装 unplugin-vue-componentsunplugin-auto-import 这两款插件。

npm install -D unplugin-vue-components unplugin-auto-import

②然后修改 vue.config.js 或 vite.config.js 配置文件

// vue.config.js
const {
    
     defineConfig } = require('@vue/cli-service')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const {
    
     ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = defineConfig({
    
    
  transpileDependencies: true,
  configureWebpack: {
    
    
    plugins: [
      AutoImport({
    
    
        resolvers: [ElementPlusResolver()]
      }),
      Components({
    
    
        resolvers: [ElementPlusResolver()]
      })
    ]
  }
})
// vite.config.js
import {
    
     fileURLToPath, URL } from 'node:url'
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'

// https://vitejs.dev/config/
export default defineConfig({
    
    
  plugins: [
    vue(),
    AutoImport({
    
    
      resolvers: [ElementPlusResolver()]
    }),
    Components({
    
    
      resolvers: [ElementPlusResolver()]
    })
  ],
  resolve: {
    
    
    alias: {
    
    
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

回到目录…

9.2 加载字体图标

在这里插入图片描述

第一步:安装 icons 字体图标

npm install @element-plus/icons-vue

第二步:全局注册,创建 src/plugins/icons.js 文件,添加以下内容

import * as components from "@element-plus/icons-vue";
export default {
    
    
    install: (app) => {
    
    
        for (const key in components) {
    
    
            const componentConfig = components[key];
            app.component(componentConfig.name, componentConfig);
        }
    }
};

第三步:引入文件,在 main.js 中引入 icons.js 文件

import elementIcon from "./plugins/icons"
createApp(App).use(elementIcon).mount('#app')

第四步:直接复制代码即可

<el-icon><CirclePlus /></el-icon>

回到目录…


总结:
提示:这里对文章进行总结:
本文是对Vue的学习,学习了Vue的选项式API和组合式API,组件之间的交互,引入swiper第三方插件,网络请求封装,还有Router路由,Vuex状态管理,加载Element-plus组件。之后的学习内容将持续更新!!!

猜你喜欢

转载自blog.csdn.net/qq15035899256/article/details/130785462