简略前端学习Vue3

Vue 学习

Vue 基础使用


Vue3 - 插值
举例:
js:
let fruits = {
	apple:"苹果";
	banana:"香蕉";
}

html:
{{fruits.apple}} 
{{fruits.banana}} 
Vue3 - 常用指令

官方网站:https://cn.vuejs.org/api/built-in-directives.html

指令:v-xxx
基础指令:v-html、v-text
事件指令:v-on  事件绑定 
判断指令:v-if 
循环指令:v-for ; 例如:<li v-for="(f,i) in fruits">{{f}}==>{{i}}</li>
Vue3 - 属性绑定
v-bind:  语法糖==> :    属性绑定

v-on: 语法糖 ==> @ 例如:@click     事件绑定

v-model:   表单的双向绑定
Vue3 - 响应式
ref()
1、把基本数据、对象类型使用 ref() 包装成响应式数据
2、修改值时 代理对象.value = ""
3、页面取值、属性绑定时 直接 {{变量名}}

const count = ref(0)

function increment(){
	count.value++;
}

-----------------------------------------
reactive()
1、把对象类型使用 reactive()包装成响应式数据
2、修改值时 代理对象 = ""
3、页面取值、属性绑定时  直接 {{变量名}}

const animals = reactive({count:0})
function increments() {
  animals.count++
}

最佳实践
1、我们该使用哪个函数?
答:可用 ref() 一把梭,也可以 ref() 包装基本数据,reactive()包装对象
2、使用 const 声明响应式常量
Vue3 - 插件安装

浏览器中的扩展程序中搜索 Vue.js devtools 并安装下载

Vue3 - 计算属性
js:
const count = ref(0)
const animals = reactive({count:0})

function increment() {
  count.value++
}

function increments() {
  animals.count++
}

const totalPrice = computed(()=>count.value * animals.count);

html:
    <h4>总计:{{totalPrice}}</h4>

Vue 进阶使用


Vue3 - 监听 - watch()
// watch 监听
const num = ref(1);

watch(num,(value,oldValue,onCleanup)=>{
  console.log("value",value);
  console.log("oldValue",oldValue);
  if(num.value >3){
    alert("超出限购数量")
    num.value = 3
  }
})

<button @click="num++">加量 {{num}}</button>
Vue3 - 监听 - watchEffect()
const num = ref(1);
const count = ref(0);

watchEffect(()=>{

  if(num>3){
    alert("超出限购数量")
    num.value = 3
  }

  if(count.value>3){
    alert("数量达到最大")
    count.value = 3
  }
  
})

Vue3 - 生命周期

onBeforeMount() 元素在挂载到页面之前

onMounted() 元素已经挂载到页面

onBeforeUpdate() 页面元素更新之前

onUpdated() 页面元素更新完成

Vue3 - 组件传值 - 父传子

父传子 - Props

defineProps()

父组件给子组件传递值;

单向数据流效果:

  • 父组件修改值,子组件发生变化
  • 子组件修改值,父组件不会感知
Father 组件
<template>
    <div style="background-color: #f9f9f9;">
        <h2>我是父组件</h2>
        <button @click="data.money+=10">充值</button>
        <!-- <Son :money="money" :books="books" /> -->
        <!-- <Son v-bind="data"></Son> -->
        <Son :money="data.money" :books="data.books"/>
    </div>
</template>


<script setup>
import { reactive, ref } from 'vue';
import Son from './Son.vue';

//1、父传子,单项数据流
// const money = ref(100);
// const books = ref(["西游记","水浒传"]);

const data  =reactive({
    money:100,
    books:["西游记","水浒传"],
})

Son 组件
<template>
    <div style="background-color: blue; color: white;">
        <h2>我是子组件</h2>
        <div>账户:{{props.money}}</div>
        <div>图书:
        <li v-for="b in props.books">
            {{b}}
        </li>
        </div>
    </div>
    
</template>

<script setup>

//1、定义属性,只读
// let props = defineProps(['money',"books"]);

let props = defineProps({
    money:{
        type:Number,
        required:true,
        default:200
    },
    books:{
        type:Array
    }
})
Vue3 - 组件传值 - 子传父

**子传父 - Emit **

defineEmits()

props 用来父传子,emit 用来子传父

Emit 是一种事件机制,子组件发生事件,通知父亲即可

Father 组件
父组件感知事件和接受事件值 ==>  @buy="moneyDown" 
<template>
    <div style="background-color: #f9f9f9;">
        <h2>我是父组件</h2>
        <button @click="data.money+=10">充值</button>
        <!-- <Son v-bind="data"></Son> -->
        <Son :money="data.money" @buy="moneyDown"/>
    </div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import Son from './Son.vue';

const data  =reactive({
    money:100
})

function moneyDown(arg){
        // alert("感知购买事件" + arg);
        data.money +=arg;
}


</script>
Son 组件
子组件定义发生事件
<template>
    <div style="background-color: blue; color: white;">
        <h2>我是子组件</h2>
        <div>账户:{{props.money}}</div>
        <button @click="buy()">买东西</button>
    </div>
</template>

<script setup>
//2、使用 emit:定义事件
let emits = defineEmits(['buy']);

function buy(){
    emits('buy',-5);
}

</script>
Vue3 - 插槽

子组件可以使用插槽 接受页面模板内容

v-slot 的简写为 #

具名插槽  <slot name="xxx" />

Father 组件
<template>
    <div style="background-color: #f9f9f9;">
         <Son v-bind="data">
            <template v-slot:title>
                哈哈SonSon
            </template>

            <template #btn>
                买东西
            </template>
         </Son>
    </div>
</template>
Son 组件
<template>
    <div style="background-color: blue; color: white;">
        <h2>我是子组件</h2>
        <h3>
            <slot name="title">
            </slot>
        </h3>
        <button @click="buy()">
            <slot name="btn" />
        </button>


    </div>
    
</template>
Vue3 - 路由
使用 Vue-Router

第一步,下载

# vue3下载 vue-router@4
npm install vue-router@4

第二步

//配置vue-router 并在 main.js中导入使用 router
import router from './router'
app.use(router);

//创建专属router的index.js
//定义路由表,举例
const routes = [
    { path: '/', component: Home },
    { path: '/hello', component: Hello},
    { path: '/haha', component: ()=> import('../views/HAHA.vue') },
   
  ]

//创建路由器
import {createRouter,createMemoryHistory} from 'vue-router';
const router = createRouter({
    history: createMemoryHistory(),
    routes,
  })

//导出路由器
export default router;

第三步

Vue 实例使用router

第四步

配置 <router-link to="/">首页</router-link> 和 <router-view></router-view>
完成路由的跳转功能
路径参数

index.js

const routes = [
    { path: '/haha/:id', component: ()=> import('../views/HAHA.vue') },
  ]

HAHA.vue

<template>
    <div>
        <h1>我是哈哈 {{ $route.params.id }}</h1>
    </div>
</template>

App.vue

 <template>
  <router-link to="/haha/111">Haha111</router-link>
  <router-link to="/haha/abc">Hahaabc</router-link>

  <hr/>
  
  <router-view></router-view>
 </template>

在页面中点击 路由匹配的路径 /haha/:id ,页面会跳转并显示 $route.params.id 内容

嵌套路由
const routes = [
    { path: '/', component: Home },
    {
        path:"/user/:id",
        component:User,
        children:[
            {
                // /user/007/profile
                path:"profile",
                component:UserProfile
            },
            {
                // /user/007/posts
                path:"posts",
                component:UserPosts
            },
         
        ],
    }
   
  ]

App.vue

<template>

  <router-link to="/">首页</router-link>

  <router-link to="/user/007">用户中心</router-link>

  <hr/>

  <router-view></router-view>


</template>

User.vue 包含 两个导航 UserProfile:个人信息 和 UserPosts:我的邮件

<template>
    <div>
        <h1>欢迎您:{{ $route.params.id }}</h1>
        <router-link to="/user/007/profile">个人信息</router-link>
        <router-link to="/user/007/posts">我的邮件</router-link>
    </div>
    <div style="border:5px solid red">
        <router-view></router-view>
    </div>
</template>

UserPosts.vue

<template>
    <div>
        <h3>未读邮件42封</h3>
    </div>
</template>

UserProfile.vue

<template>
    <div>
        <h3>我是007,性别男</h3>
    </div>
</template>
编程式导航
useRoute: 路由数据

路由传参跳转到指定页面后,页面需要取到传递过来的值,可以使用 useRoute 方法;

拿到当前页路由数据;可以做

1.获取到当前路径

2.获取到组件名

3.获取到参数

4.获取到查询字符串

<template>
    <div>
        <h3>个人详情</h3>
        <button @click="getInfo">页面信息</button>
    </div>
</template>
<script setup>
import { useRoute} from 'vue-router';
//路由
const route = useRoute();
    
function getInfo() {
    console.log(route.params);//参数
    console.log(route.name);//组价名
    console.log(route.path);//路径
    //....
    
}
</script>
useRouter: 路由器

拿到路由器;可以控制跳转、回退等。

<template>
    <div>
        <h3>个人详情</h3>
        <button @click="getHome">回到首页</button>
        <button @click="backFrontPage">返回上一页</button>
    </div>
</template>
<script setup>
import {useRouter } from 'vue-router';

//路由器
const router = useRouter();


//返回首页
function getHome(){
    router.push('/')
}

//返回上一页
function backFrontPage(){
    router.go(-1);
}
路由传参
1. params 参数

params 传参方式有 使用useRouter 的 router.push 编程式传参 和 使用 router-link 式的 传参,

其中,编程式传参 和 router-link 式,都有路径传参 和 对象传参 这两种方式。

App.vue

<script setup>
import { useRouter } from 'vue-router';

const router = useRouter();

// 编程式传参 使用 useRouter 的push
function paramsTest(){
  // params 在对象传参方式时 必须使用组件 name
  //路径传参
  // router.push('/haha/222/zhangsan/50')
  //对象传参
  router.push({
    name:'HAHA',
    params:{
      id:888,
      name: 'zhangsan',
      age: '333'
    }
  })
}

</script>

<template>

  <router-link to="/">首页</router-link>

  <!-- link式传参 使用路径传,或者动态绑定:to,用对象传-->
  <router-link to="/haha/111/zhangsan/18">路径传参</router-link>
  <router-link :to="{
    name:'HAHA',
    params:{
      id:777,
      name: 'zhangsan',
      age: '22'
    }
  }">对象传参</router-link>
  <button @click="paramsTest">params传参</button>
  <hr/>

  <router-view></router-view>

</template>

index.js

const routes = [
    { path: '/', component: Home },
    { path: '/hello', component: Hello},
    { path: '/haha/:id/:name/:age', // 路径 params 参数
      name: 'HAHA',
      component: ()=> import('../views/HAHA.vue') },
   
  ]

HAHA.vue

<template>
      <!--params 取值要么用内置对象$route 要么用 useRoute 的route-->
    <div>
        <h1>我是哈哈 {{$route.params.id}} ==> {{$route.params.name}} ==> {{$route.params.age}}</h1>
        <h5>对象传参 {{ route.params.id }} ==>{{ route.params.name}} ==> {{route.params.age}}</h5>
    </div>
</template>

<script setup>
import { useRoute } from 'vue-router';

let route = useRoute();


</script>
2. query 参数

地址栏上会显示拼接 如http://127.0.0.1:5173/hello?id=7788&name=haha

params 传参方式有 使用 useRouter 的 router.push 编程式传参 和 使用 router-link 式的传参,

其中,编程式传参 和 router-link 式传参,都有路径传参 和 对象传参 这两种方式。

Hello.vue

<template>
    <!-- query 取值要么用内置对象$route 要么用 useRoute 的route -->
    <div>
    <h1>
        我是Hello
        {{ $route.query.id }} ==> {{ $route.query.name }}
    </h1>
    <div>
        <h1>
        {{ route.query.id }} ==> {{ route.query.name }}
        </h1>
    </div>
    </div>
</template>

<script setup>
import { useRoute } from 'vue-router';

const route = useRoute();


</script>

App.vue

<template>
<!-- query传参,路径和对象方式 -->
  <router-link to="/hello?id=1&name=zhangsan">Hello query路径传参</router-link>
  <router-link :to="{
    path:'/hello',
    query:{
      id:7788,
      name:'haha'
    }
  }">Hello query对象传参</router-link>


<button @click="helloQuery">hello 传参,编程式传参 使用 useRouter 的push</button>
</template>

<script>
function helloQuery(){
  //路径传参
  // router.push('/hello?id=1&name=aaaaa')
  // query  在对象传参方式时 必须使用组件 name
  //对象传参
  router.push({
    path:'/hello',
    query:{
      id:1,
      name:'xxx'
    }
  })
}
</script>
路由(导航)守卫

是一种拦截器思想,在路由器跳转到指定页面之前进行拦截,后面加上自己的逻辑判断来实现需求。

全局前置守卫

router.beforeEach

每次跳转之前拦截

//2、创建路由器
const router = createRouter({
    history: createWebHistory(),
    routes,
  })


router.beforeEach((to, from) => {
    console.log("to",to);
    console.log("from",from);
    // ...
    // 返回 false 以取消导航

    //1、返回 false :取消导航
    //2、返回 true,不返回:继续导航
    //3、返回'路径':跳转到指定页面
    // return true
    if(to.path==='/hello'){
      console.log("禁止访问");
      return "/"
    }
  })

or

 router.beforeEach(async (to, from) => {
   if (
     // 检查用户是否已登录
     !isAuthenticated &&
     // ❗️ 避免无限重定向
     to.name !== 'Login'
   ) {
     // 将用户重定向到登录页面
     return { name: 'Login' }
   }
 })

可选的第三个参数 next

// GOOD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})
全局解析守卫

你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,因为它在每次导航时都会触发,不同的是,解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。这里有一个例子,确保用户可以访问自定义 meta 属性 requiresCamera 的路由:

router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 处理错误,然后取消导航
        return false
      } else {
        // 意料之外的错误,取消导航并把错误传给全局处理器
        throw error
      }
    }
  }
})

router.beforeResolve 是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置

全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  sendToAnalytics(to.fullPath)
})

Axios


简介

Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js

npm install axios
import axios from "axios"
axios.get('/user')
	 .then(res => console.log(resp.data))

API 测试服务器:http://43.139.239.29/

发送请求
<template>
  <div>
   <button @click="getInfo">GET 请求</button>
   <button @click="getInfoParam">GET 请求 携带参数</button>
   <button @click="postInfo">POST 请求</button>
   <button @click="postInfoParam">POST 请求 携带参数</button>
  </div>

</template>

<script setup>
import axios from 'axios'

// get 无参
function getInfo(){
  axios.get("http://43.139.239.29/get")
       .then(resp =>{
        console.log(resp.data);
       })
}

// get 携带参数
function getInfoParam(){
  axios.get("http://43.139.239.29/get",{
    params:{
      id:1,
      username:'zhangsan',
    }
  }).then(resp =>{
        console.log(resp.data);
       })
}

// post 无参
function postInfo(){
  axios.post("http://43.139.239.29/post").then(resp =>{
    console.log(resp);
  })
}

// post 携带参数
function postInfoParam(){
  //数据会自动转为json
  axios.post("http://43.139.239.29/post",{
    id:222,
    username:'lisi',
    age:18,
  }).then(resp =>{
    console.log(resp);
  })
}

</script>
实例配置

详细配置参考 https://www.axios-http.cn/docs/instance

创建文件夹 utils/http/index.js 进行配置

import axios from 'axios'

const http = axios.create({
    baseURL: 'http://43.139.239.29',
    timeout: 1000, // `timeout` 指定请求超时的毫秒数。
    // 如果请求时间超过 `timeout` 的值,则请求会被中断
    // 默认值是 `0` (永不超时)
    headers: {'X-Custom-Header': 'foobar'}
  });


export default http;

使用例子:

<script setup>
import http from './utils/http/index.js'


// get 无参
function getInfo(){
  http.get("/get")
       .then(resp =>{
        console.log(resp.data);
       })
}

// get 携带参数
function getInfoParam(){
  http.get("/get",{
    params:{
      id:1,
      username:'zhangsan',
    }
  }).then(resp =>{
        console.log(resp.data);
       })
}

// post 无参
function postInfo(){
  http.post("/post").then(resp =>{
    console.log(resp);
  })
}

// post 携带参数
function postInfoParam(){
  //数据会自动转为json
  http.post("/post",{
    id:222,
    username:'lisi',
    age:18,
  }).then(resp =>{
    console.log(resp);
  })
}

</script>
Axios 拦截器

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

axios封装的index.js中添加 请求拦截器 和 响应拦截器并配置

    import axios from 'axios'


    const http = axios.create({
        baseURL: 'http://43.139.239.29',
        timeout: 1000, // `timeout` 指定请求超时的毫秒数。
        // 如果请求时间超过 `timeout` 的值,则请求会被中断
        // 默认值是 `0` (永不超时)
        headers: {'X-Custom-Header': 'foobar'}
    });


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

    // 添加响应拦截器
    http.interceptors.response.use(function (response) {
        // 2xx 范围内的状态码都会触发该函数。
        // 对响应数据做点什么
        return response;
    }, function (error) {
        // 超出 2xx 范围的状态码都会触发该函数。
        // 对响应错误做点什么
        console.log("服务器错误"+error);
        return Promise.reject(error);
    });




export default http;

Pinia


核心概念

Vue.js中做状态管理的库,保存状态数据,在组件间共享数据。(Pinia做状态管理)

为什么要使用 Pinia?

Pinia做状态管理

数据单元:defineStore()

State:核心数据

Getter:定义读取=>数据获取

Action:定义操作=>数据操作

案例实战

安装依赖

npm install pinia

在main.js中配置

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'

const pinia = createPinia()
const app = createApp(App)

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

创建 stores文件夹 编写相关 js配置

import { defineStore } from "pinia";
import { ref,computed } from 'vue'

// 1.Option 写法:定义一个money储存单元
export const useMoneyStore = defineStore('money', {
    state: () => ({ money: 100 }),
    getters: {
      rmb: (state) => state.money,
      usd: (state) => state.money * 0.14,
      eur: (state) => state.money * 0.13,
    },
    actions: {
      win(arg) {
        this.money+=arg;
      },
      pay(arg){
        this.money -= arg;
      }
    },
  })

// 2.Setup写法:
export const useMoneyStore = defineStore('money', ()=>{
    const money = ref(100)

    const rmb = computed(() => money.value)
    const usd = computed(() => money.value*0.14)
    const eur = computed(() => money.value*0.13)

    const win = (arg)=>{
        money.value += arg
    }

    const pay = (arg)=>{
        money.value -= arg
    }

    return {money,rmb,usd,eur,win,pay}

})
  

Setup Store 中:

  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions

注意,要让 pinia 正确识别 state,你必须在 setup store 中返回 state 的所有属性。这意味着,你不能在 store 中使用私有属性。不完整返回会影响 SSR ,开发工具和其他插件的正常运行。

Setup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。不过,请记住,使用组合式函数会让 SSR 变得更加复杂

Wallet.vue

<template>
    <div>
        工资:{{ moneyStore.money }}
        <h2>¥:{{ moneyStore.rmb }}</h2>
        <h2>$:{{ moneyStore.usd }}</h2>
        <h2>€:{{ moneyStore.eur }}</h2>
    </div>
</template>
  
<script setup>
import { useMoneyStore } from '../stores/money.js'
  
let moneyStore = useMoneyStore();

</script>

Game.vue

<template>
    <div>
         <button @click="guaguale">刮刮乐</button>
         <button @click="buybangbang">买棒棒糖</button>
    </div>
</template>
  
<script setup>
import { useMoneyStore } from '../stores/money.js'
  
let moneyStore = useMoneyStore();


function guaguale() {
    moneyStore.win(100)
}

function buybangbang() {
    moneyStore.pay(5)
}



</script>

脚手架


在我们创建项目时,需要自己手动下载很多组件包,如 npm create vitenpm install vue-router@4 、``npm install pinia` 等等,现在可以使用工具链 create-vue 一次性安装这些依赖

npm create vue@latest

npm create vue@latest 会自动安装 router,pinia。

/cookbook/composables.html) ,开发工具和其他插件的正常运行。

Setup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。不过,请记住,使用组合式函数会让 SSR 变得更加复杂

Wallet.vue

<template>
    <div>
        工资:{{ moneyStore.money }}
        <h2>¥:{{ moneyStore.rmb }}</h2>
        <h2>$:{{ moneyStore.usd }}</h2>
        <h2>€:{{ moneyStore.eur }}</h2>
    </div>
</template>
  
<script setup>
import { useMoneyStore } from '../stores/money.js'
  
let moneyStore = useMoneyStore();

</script>

Game.vue

<template>
    <div>
         <button @click="guaguale">刮刮乐</button>
         <button @click="buybangbang">买棒棒糖</button>
    </div>
</template>
  
<script setup>
import { useMoneyStore } from '../stores/money.js'
  
let moneyStore = useMoneyStore();


function guaguale() {
    moneyStore.win(100)
}

function buybangbang() {
    moneyStore.pay(5)
}



</script>

脚手架


在我们创建项目时,需要自己手动下载很多组件包,如 npm create vitenpm install vue-router@4 、``npm install pinia` 等等,现在可以使用工具链 create-vue 一次性安装这些依赖

npm create vue@latest

npm create vue@latest 会自动安装 router,pinia。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值