Vue3

目录结构#

image-20230907232522914

image-20230907233515228

模版语法#

使用 JavaScript 表达式#

在我们的模板中,我们一直都只绑定简单的 property 键值,Vue.js 都提供了完全的 JavaScript 表达式支持

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

这些表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

<!--  这是语句,不是表达式:-->
{{ var a = 1 }}

<!-- 流程控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

原始 HTML#

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html 指令

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
data(){
    return{
        rawHtml:"<a href='https://www.itbaizhan.com'>百战</a>"
    }
}

属性 Attribute#

Mustache 语法不能在 HTML 属性中使用,然而,可以使用 v-bind 指令

<div v-bind:id="dynamicId"></div>
data(){
    return{
        dynamicId:1001
    }
}

温馨提示

v-bind: 可以简写成 :

条件渲染#

v-if#

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染。

<p v-if="flag">我是孙猴子</p>
data() {
    return {
        flag: true
    }
}

v-else#

你可以使用 v-else 指令来表示 v-if 的“else 块”

<p v-if="flag">我是孙猴子</p>
<p v-else>你是傻猴子</p>
data() {
    return {
        flag: false
    }
}

v-show#

另一个用于条件性展示元素的选项是 v-show 指令

<h1 v-show="ok">Hello!</h1>

v-if vs v-show 的区别#

v-if 是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好

视频

Class 与 Style 绑定#

image-20230910111436079

绑定 HTML class#

普通对象使用#

image-20230910111601920

image-20230910111622219

image-20230910111709539

Style绑定#

image-20230910112005436

image-20230910112055334

计算属性的使用#

列表渲染#

v-for#

v-for 把一个数组映射为一组元素#

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名

<ul>
    <li v-for="item in items">{{ item.message }}</li>
</ul>
data() {
    return {
        items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
}

维护状态#

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的 key attribute:

<div v-for="(item,index) in items" :key="item.id|index">
  <!-- 内容 -->
</div>

数组变化侦测#

Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

image-20230826111438975

image-20230910110904245

v-for 与对象#

image-20230910104626751

事件处理#

监听事件#

我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler"@click="handler"

事件处理器 (handler) 的值可以是:

  1. 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与 onclick 类似)。

    <button @click="counter += 1">Add 1</button>
    
    data() {
        return {
            counter: 0
        }
    }
    
  2. 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

    然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

    <button @click="greet">Greet</button>
    
    methods: {
        greet(event) {
            // `event` 是原生 DOM event
            if (event) {
                alert(event.target.tagName)
            }
        }
    }
    

事件修饰符#

image-20230910110505261

image-20230910110631924

按键修饰符#

计算属性#

image-20230910111215820

侦听器#

image-20230910112311219

image-20230910112525456

表单事件#

image-20230910112722083

复选框#

image-20230827225150342

单选框#

image-20230827225822458

选项框#

image-20230827230258417

多选框#

image-20230827230437742

修饰符#

image-20230910112937069

image-20230827231050453

模版引用#

dom操作

image-20230910115946537

组件基础#

使用组件#

<script>
import ButtonCounter from './ButtonCounter.vue'

export default {
  components: {
    ButtonCounter
  }
}
</script>

<template>
  <h1>Here is a child component!</h1>
  <ButtonCounter />
</template>

组件注册#

image-20230910121737267

父传子组件传值prop#

组件与组件之间是需要存在交互的,否则完全没关系,组件的意义就很小了

Prop 是你可以在组件上注册的一些自定义 attribute

<my-componentVue :title="标题"/>
<template>
    <h3>单文件组件</h3>
    <p>{{ title }}</p>
</template>

<script>
export default {
    name:"MyComponent",
    props:{
        title:{
            type:String,
            default:""
        }
    }
}
</script>

动态数据(父传子 )#

image-20230910122432599

Prop 类型#

Prop传递参数其实是没有类型限制的 prop是只读的

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function
}

温馨提示

数据类型为数组或者对象的时候,默认值是需要返回工厂模式

image-20230911221953607

子组件向父组件传值#

组件事件#

image-20230911222848535

子组件:

image-20230911223822162

父组件:

image-20230911223750645

父子组件的访问方式#

image-20230829202103131

组合事件和v-model一起使用#

image-20230912214452861

组件数据传递prop 子传父#

子组件#

image-20230912215236074

父组件#

image-20230912215254500

插槽#

双标签 ,之前的是单标签

image-20230912215922088

父组件:

image-20230912215951911

子组件:

image-20230912220014635

通过插槽来分配内容#

具名插槽#

子组件有多个插槽

image-20230829210131418

父组件:

image-20230912220633010

子组件:

image-20230912220704337

简写:

image-20230912220510816

渲染作用域#

image-20230912220048465

备用内容#

作用插槽域#

image-20230829211437990

插槽使用父子组件的数据:#

普通方式:#

父组件:

image-20230912221928515

子组件:

image-20230912221943553

具名方式#

父组件:

image-20230912222135204

子组件:

image-20230912222250407

组件之间的跨级通信(依赖注入)#

将数据传递得更深:

image-20230913193246892

privide 和 inject#

image-20230913193446119

祖先:

image-20230913193715173

孙子:

image-20230913193733850

生命周期钩子#

image-20230913192210200

image-20230830203452795

应用#

获取dom:

image-20230913192503423

异步组件:#

image-20230913193117343

vue应用#

image-20230913194237526

组合式API#

image-20230830204617352

Vue3新特性1#

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

ref或者reactive#

在2.x中通过组件data的方法来定义一些当前组件的数据

data() {
  return {
    name: 'iwen',
    list: [],
  }
}

在3.x中通过ref或者reactive创建响应式对象

import { ref,reactive } from "vue"
export default {
  name: 'HelloWorld',
  setup(){
      const name = ref("iwen")
      const state = reactive({
          list:[]
      })

    return{
        name,
        state
    }
  }
}

methods中定义的方法写在setup()#

在2.x中methods来定义一些当前组件内部方法

methods:{
    http(){}
}

在3.x中直接在setup方法中定义并return

setup() {
    const http = ()=>{
        // do something
    }
    return {
      http
    };
}

setup()中使用props和context#

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

export default {
  props: {
    name: String,
  },
  setup(props,ctx) {
    console.log(props.name)
    ctx.emit('event')
  },
}

响应式 API#

  • 响应式 API:例如 ref()reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。
  • ref() 基础类型
  • reactive() 引用类型

image-20230830210612888

torefs 响应式#

image-20230830210708821

在setup 中使用watch#

基础类型#

image-20230830222621754

引用类型 watchEffect#

image-20230830223525461

使用watch#

image-20230830224009707

生命周期函数#

Vue3新特性2#

在setup中使生命周期函#

你可以通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。

下表包含如何在 setup () 内部调用生命周期钩子

Options API Hook inside setup
beforeCreate Not needed*
created Not needed*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('Component is mounted!')
    })
  }
}

Provide / Inject#

  • provide() 和 inject() 可以实现嵌套组件之间的数据传递。
  • 这两个函数只能在 setup() 函数中使用。
  • 父级组件中使用 provide() 函数向下传递数据。
  • 子级组件中使用 inject() 获取上层传递过来的数据。
  • 不限层级
// 父组件
import { provide } from "vue"

setup() {
    provide("customVal", "我是父组件向子组件传递的值");
}
// 子组件
import { inject } from "vue"

setup() {
    const customVal = inject("customVal");
    return {
      customVal
    }
}

Fragment#

Fragment翻译为:“碎片”

  • 不再限于模板中的单个根节点
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App" />
</template>

setup函数中的参数prop#

父传子

image-20230830224714616

setup函数中的参数 context#

听懵逼了、

provide和inject#

< script setup>#

script setup 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。

script setup

image-20230830231520802

vue-router#

Vue引入路由配置(初级)#

在Vue中,我们可以通过vue-router路由管理页面之间的关系

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举

在Vue中引入路由#

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

第二步:配置独立的路由文件

// index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

第三步:引入路由到项目

// main.js
import router from './router'
app.use(router)

第四步:指定路由显示入口 <router-view/>

第五步:指定路由跳转

<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>

路由传递参数#

页面跳转过程中,是可以携带参数的,这也是很常见的业务

例如:在一个列表项,点击进入查看每个列表项的详情

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

{
    path:"/list/:name",
    name:"list",
    component:() => import("../views/ListView.vue")
}

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

<li><router-link to="/list/内蒙">内蒙旅游十大景区</router-link></li>
<li><router-link to="/list/北京">北京旅游十大景区</router-link></li>
<li><router-link to="/list/四川">四川旅游十大景区</router-link></li>

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

<p>{{ $route.params.name }}城市旅游景区详情</p>

基础使用#

路由表#

index.js

import {
    createRouter,
    createWebHashHistory,
    createWebHistory
} from 'vue-router'
// 1. 定义路由组件.
// 也可以从其他文件导入
// 静态导入
// import Home from '../views/Home.vue'
import About from '../views/About.vue'
import User from '../views/User.vue'
import NotFound from  '../views/NotFound.vue'
import News from '../views/News.vue'
import Parent from '../views/Parent.vue'
import StyleOne from '../views/StyleOne.vue'
import StyleTwo from '../views/StyleTwo.vue'
import Page from '../views/Page.vue'
import ShopTop from '../views/ShopTop.vue'
import ShopMain from '../views/ShopMain.vue'
import ShopFooter from '../views/ShopFooter.vue'
// 路由懒加载,用到时再加载
const Home=()=>import('../views/Home.vue')
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [{
        path: "/",
        // 重定向
        // redirect:'/home'
        // 命名路由
        // redirect:{name:"home"}
        // 方法
        redirect: (to) => {
            // console.log(to);
            return {
                path: "/home"
            }
        }
    },
    {
        path: '/home',
        name: "home",
        component: Home
    },
    {
        path: '/about',
        component: About,
        // 每路守卫(路由独享的守卫)
        beforeEnter:(to,from,next)=>{//token
            console.log(to);
            console.log(from);
            if(123 === 123453){
                next()
            }
        }
    },
    {
        // 动态路由
        path: '/user/:id',
        component: User,
        props: true
    },
    {
        // 动态路由的参数一定是数字
        // path: "/news/:id(\\d+)",
        // 有多个参数 +
        // path: "/news/:id+",
        // 参数可有可无 * ,参数可以重复叠加
        name: "news",
        path: "/news/:id*",
        // 参数可有可无 ? ,但是参数不可以重复叠加
        // path: "/news/:id?",
        component: News
    },
    {
        path: "/parent",
        alias: ['/father', '/fuqin'], //起别名
        component: Parent,
        children: [{
                path: "styleone",
                component: StyleOne
            },
            {
                path: "styletwo",
                component: StyleTwo,

            }
        ],
    },

    {
        path: "/page",
        component: Page
    },
    {
        path: "/shop/:id",
        components: {
            default: ShopMain,
            // 它们与 `<router-view>` 上的 `name` 属性匹配
            ShopTop: ShopTop,
            ShopFooter: ShopFooter
        },
        props: {
            default: true,
            ShopFooter: true,
            ShopTop: false
        }
    },

    {
        // 404页面
        //使用正则的方式,匹配任意的
        path: '/:path(.*)',
        component: NotFound
    },

]

// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = createRouter({
    // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
    // history: createWebHashHistory(),
    // history模式,二者区别,有无#
    history: createWebHistory(),
    routes, // `routes: routes` 的缩写
})
// 全局守卫
// router.beforeEach((to,from,next)=>{
//     console.log(to);
//     console.log(from);
//     next()//通行证
// })
export default router

使用#

<script>
export default{
  data(){
    return {
      list:[]
    }
  }
}
</script>

<template>
  <div>
    <!-- vue-router是基于路由和组件的,路由是用来设定访问路径,将路径和组件映射起来, -->
     <h1>Hello App!</h1>
    <router-view name="ShopTop"></router-view>
    <router-view></router-view>
    <p>
    <!--使用 router-link 组件进行导航 -->
    <!--通过传递 `to` 来指定链接 -->
    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <!-- 使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码 -->
    <!-- to="路径" -->
    <router-link to="/">Go to Home</router-link>
      <router-link to="/about">Go to About</router-link>
      <router-link to="/user/123">Go to User</router-link>
      <!-- <router-link to="/news/456">Go to News</router-link> -->
      <router-link :to="{name:'news',params:{id:456}}">Go to News</router-link>
      <router-link to="/parent">Go to Parent</router-link>
      <router-link to="/page">Go to Page</router-link>
    </p>
    <!-- 路由出口,占位符 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    
    <router-view name="ShopFooter"></router-view>
  </div>
</template>

<style>
</style>

挂载#

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app=createApp(App)
app.use(router)
app.mount('#app')

动态路由#

路由表

  {
        // 动态路由
        path: '/user/:id',
        component: User,
        props: true
    },

组件

<template>
    <div>用户</div>
</template>
 <!---<script>
export default {
    props:['id'],
    mounted(){
        // $route表示当前活跃的路由对象
        console.log(this.$route.params.id);
        console.log(this.id);
    }
}
</script>
--->
<script setup> 
    import {useRoute} from 'vue-router'
    console.log(useRoute().params.id);
   const props= defineProps({
        id:String
    })
    console.log(props.id);
</script>

404路由#

路由表

 {
        // 404页面
        //使用正则的方式,匹配任意的
        path: '/:path(.*)',
        component: NotFound
    },

路由正则和重复参数#

路由正则 重复参数

    {
        // 动态路由的参数一定是数字
        // path: "/news/:id(\\d+)",
        // 有多个参数 +
        // path: "/news/:id+",
        // 参数可有可无 * ,参数可以重复叠加
        name: "news",
        path: "/news/:id*",
        // 参数可有可无 ? ,但是参数不可以重复叠加
        // path: "/news/:id?",
        component: News
    },

嵌套路由配置#

image-20230913221604055

路由嵌套是非常常见的需求

第一步:创建子路由要加载显示的页面

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

{
    path:"/news",
    name:"news",
    redirect:"/news/baidu",
    component:() => import("../views/NewsView.vue"),
    children:[
       {
       		path:"baidu",
            component:() => import("../views/NewsList/BaiduNews.vue"),
       },
       {
            path:"wangyi",
            component:() => import("../views/NewsList/WangyiNews.vue"),
       }
    ]
}

第三步:指定子路由显示位置<router-view></router-view>

第四步:添加子路由跳转链接

<router-link to="/news/baidu">百度新闻</router-link> | 
<router-link to="/news/wangyi">网易新闻</router-link>

第五步:重定向配置 redirect:"/news/baidu"

{
        path: "/parent",
        alias: ['/father', '/fuqin'], //起别名
        component: Parent,
        children: [{
                path: "styleone",
                component: StyleOne
            },
            {
                path: "styletwo",
                component: StyleTwo,

            }
        ],
    },

js跳转#

<template>
  <div>
    <h2>Page页面</h2>
    <button @click="goPage">跳转页面</button>
  </div>
</template>
<script>
export default {
  methods: {
    goPage: function () {
      // console.log(this.$router);
      //    if(123==1221343){
      // this.$router.push('/')
      // 通过传入对象
      // this.$router.push({path:"/"})
      // 带参数
      // this.$router.push({path:"/user/123456"})
      // this.$router.push({name:"news",params:{id:123456}})
      // 带问号
      this.$router.push({path:"/about",query:{name:"zhangsan"}})
      //替换当前位置
    //   this.$router.push({ path: "/about", query: { name: "zhangsan" },replace:true });
    // this.$router.replace({path:"/about",query:{name:"zhangsan"}})
      //    }
    },
  },
};
</script>

接受

<template>
  <div>
    <div>about</div>
    <button @click="goBack">后退</button>
  </div>
</template>
<script>
export default {
    mounted(){
        console.log(this.$route.query.name);
    },
    methods:{
        goBack(){
            // 前进,传入为正值,后退,传入的值则为负值
            // this.$router.go(-1)
            this.$router.back()//后退,等于go(-1)
            this.$router.forword()//前进,等于go(1)
        }
    },

}
</script>

替换页面#

export default {
  methods: {
    goPage: function () {
      // console.log(this.$router);
      //    if(123==1221343){

    //   this.$router.push({ path: "/about", query: { name: "zhangsan" },replace:true });
    // this.$router.replace({path:"/about",query:{name:"zhangsan"}})
      //    }
    },
  },
};

历史页面#

<template>
  <div>
    <div>about</div>
    <button @click="goBack">后退</button>
  </div>
</template>
<script>
export default {
    mounted(){
        console.log(this.$route.query.name);
    },
    methods:{
        goBack(){
            // 前进,传入为正值,后退,传入的值则为负值
            // this.$router.go(-1)
            this.$router.back()//后退,等于go(-1)
            this.$router.forword()//前进,等于go(1)
        }
    },

}
</script>

命名路由#

命名

    {
        // 动态路由的参数一定是数字
        // path: "/news/:id(\\d+)",
        // 有多个参数 +
        // path: "/news/:id+",
        // 参数可有可无 * ,参数可以重复叠加
        name: "news",
        path: "/news/:id*",
        // 参数可有可无 ? ,但是参数不可以重复叠加
        // path: "/news/:id?",
        component: News
    },

使用

 <router-link :to="{name:'news',params:{id:456}}">Go to News</router-link>

命名视图#

一个页面显示 多个组件

定义

    {
        path: "/shop/:id",
        components: {
            default: ShopMain,
            // 它们与 `<router-view>` 上的 `name` 属性匹配
            ShopTop: ShopTop,
            ShopFooter: ShopFooter
        },
        props: {
            default: true,
            ShopFooter: true,
            ShopTop: false
        }
    },

使用

<template>
  <div>
    <!-- vue-router是基于路由和组件的,路由是用来设定访问路径,将路径和组件映射起来, -->
     <h1>Hello App!</h1>
    <router-view name="ShopTop"></router-view>
    <router-view></router-view>
   
    <!-- 路由出口,占位符 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    
    <router-view name="ShopFooter"></router-view>
  </div>
</template>

重定向和别名#

重定向

     path: "/",
        // 重定向
        // redirect:'/home'
        // 命名路由
        // redirect:{name:"home"}
        // 方法
        redirect: (to) => {
            // console.log(to);
            return {
                path: "/home"
            }
        }

别名

   {
        path: "/parent",
        alias: ['/father', '/fuqin'], //起别名
        component: Parent,
        children: [{
                path: "styleone",
                component: StyleOne
            },
            {
                path: "styletwo",
                component: StyleTwo,

            }
        ],
    },

路由组件传参#

不同的历史模式#

Hash 模式

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

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    //...
  ],
})

html5

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

const router = createRouter({
  history: createWebHistory(),
  routes: [
    //...
  ],
})

路由守卫#

就是路由跳转 可以进行判断的地方

全局路由#

// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = createRouter({
    // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
    // history: createWebHashHistory(),
    // history模式,二者区别,有无#
    history: createWebHistory(),
    routes, // `routes: routes` 的缩写
})
// 全局守卫
// router.beforeEach((to,from,next)=>{
//     console.log(to);
//     console.log(from);
//     next()//通行证
// })

每路守卫#

    {
        path: '/about',
        component: About,
        // 每路守卫(路由独享的守卫)
        beforeEnter:(to,from,next)=>{//token
            console.log(to);
            console.log(from);
            if(123 === 123453){
                next()
            }
        }
    },

组件类的守卫#

<template>
  <div>新闻</div>
</template>
<script>
export default {
  data() {
    return {
      age: 18,
    };
  },
  beforeRouteEnter(to, from,next) {//拿不到实例对象,通过next的回调函数
    console.log(to);
    console.log(from);
    next((vm)=>{
      console.log(vm.age);
    })
    console.log("路由进入组件之前");
  },
  beforeRouteUpdate() {
    console.log("路由更新组件之前");
  },
  beforeRouteLeave() {
    console.log("路由离开组件之前");
  },
};
</script>

路由懒加载#

// 路由懒加载,用到时再加载
const Home=()=>import('../views/Home.vue')



状态管理#

状态 == 数据

在不使用 vuex的情况下 使用 // provide/inject 跨级通信 进行数据管理

导入数据#

<script>
import Home from "./views/Home.vue";
import store from "./store";
// vue3中如何设置状态管理
// provide/inject 跨级通信
export default { 
  provide: {
    store,
  },
  components: {
    Home,
  },
};
</script>

<template>
  <Home />
</template>
 
<style>
</style>

fetch获取数据#

  // http://localhost:3001/banner
  // fetch//原生JS,是http数据请求的一种方式
  created() {
    // fetch返回promise对象
    // fetch('http://localhost:3001/banner').then((res)=>{//默认执行get请求
    //   //json()将响应的body,解析json的promise
    //   // console.log(res.json());
    //   return res.json()
    // }).then((res)=>{
    //   console.log(res);
    //   // this.bannersList=res.banners
    //   this.store.updateBannersList(res.banners)
    // })

    // axios:基于promise的http库
    // axios.get('http://localhost:3001/banner').then((res)=>{
    //   console.log(res);
    // })
    // https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4
    // 跨域请求数据,浏览器同源策略的保护机制,通过proxy实现跨域请求数据
    axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4").then((res) => {
      console.log(res);
    });
  },

Axios网络请求#

Axios 是一个基于 promise 的网络请求库

安装#

Axios的应用是需要单独安装的 npm install --save axios

引入#

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

全局引用:

import axios from "axios"

const app = createApp(App);
app.config.globalProperties.$axios = axios
app.mount('#app')

// 在组件中调用
this.$axios

网络请求基本示例#

get请求

axios({
    method: "get",
    url: "http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php"
}).then(res => {
    console.log(res.data);
})
    // axios:基于promise的http库
    // axios.get('http://localhost:3001/banner').then((res)=>{
    //   console.log(res);
    // })
    // https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4
    // 跨域请求数据,浏览器同源策略的保护机制,通过proxy实现跨域请求数据
    axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4").then((res) => {
      console.log(res);
    });
  },

post请求

温馨提示

post请求参数是需要额外处理的

  1. 安装依赖: npm install --save querystring
  2. 转换参数格式: qs.stringify({})
axios({
    method:"post",
    url:"http://iwenwiki.com/api/blueberrypai/login.php",
    data:qs.stringify({
        user_id:"iwen@qq.com",
        password:"iwen123",
        verification_code:"crfvw"
    })
}).then(res =>{
    console.log(res.data);
})

快捷方案#

get请求

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

post请求

axios.post("http://iwenwiki.com/api/blueberrypai/login.php", qs.stringify({
      user_id: "iwen@qq.com",
      password: "iwen123",
      verification_code: "crfvw"
    }))
      .then(res => {
        console.log(res.data);
      })

Axios网络请求封装#

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

src目录下创建文件夹utils,并创建文件request,用来存储网络请求对象 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,并创建文件indexpath分别用来存放网络请求方法和请求路径

// 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)
    }
}

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

import api from "../api/index"

api.getChengpin().then(res =>{
    console.log(res.data);
})

vite通过proxy代理解决跨域问题#

vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server:{//中转服务器
    proxy:{//通过代理实现跨域
      //https://i.maoyan.com
      '/path':{
        target:'https://i.maoyan.com',//替换的服务端地址
        changeOrigin:true,//开启代理,允许跨域
        rewrite:path=>path.replace(/^\/path/,'')//设置重写的路径
      }
    }
  }
})

通过vue_cli创建项目#

Vue状态管理(Vuex)#

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

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

引入Vuex的步骤#

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

第二步:配置Vuex文件

import { createStore } from 'vuex'

export default createStore({
  state: {
      counter:0
  }
})

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

import store from './store'
app.use(store)

第四步:在组件中读取状态

<p>counter:{{ $store.state.counter }}</p>
// 或者
import { mapState } from 'vuex';
computed:{
    ...mapState(["counter"])
}

Vue状态管理核心(Vuex)#

最常用的核心概念包含: StateGetterMutationAction

Getter#

对Vuex中的数据进行过滤

import { createStore } from 'vuex'

export default createStore({
  state: {
    counter: 0
  },
  getters: {
    getCount(state){
      return state.counter > 0 ? state.counter : "counter小于0,不符合要求"
    }
  }
})
import { mapState,mapGetters } from 'vuex';
computed:{
    ...mapGetters(["getCount"])
}

Mutation#

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

import { createStore } from 'vuex'

export default createStore({
  state: {
    counter: 0
  },
  getters: {
  },
  mutations: {
    setCounter(state, num) {
      state.counter += num
    }
  }
})
import { mapState,mapMutations } from 'vuex';

methods:{
    ...mapMutations(["setCounter"]),
    clickHandler(){
      // this.$store.commit("setCounter",20)
	  // 或者
      // this.setCounter(10)
    }
}

Action#

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态
  • Action 可以包含任意异步操作
import { createStore } from 'vuex'
import axios from "axios"

export default createStore({
  state: {
    counter: 0
  },
  getters: {
    getCount(state){
      return state.counter > 0 ? state.counter : "counter小于0,不符合要求"
    }
  },
  mutations: {
    setCounter(state, num) {
      state.counter += num
    }
  },
  actions: {
    asyncSetCount({ commit }){
      axios.get("http://iwenwiki.com/api/generator/list.php")
      .then(res =>{
        commit("setCounter",res.data[0])
      })
    }
  }
})

import { mapState,mapMutations,mapGetters,mapActions } from 'vuex';

methods:{
    ...mapActions(["asyncSetCount"]),
    clickAsyncHandler(){
        // this.$store.dispatch("asyncSetCount")
        // 或者
        // this.asyncSetCount()
    }
}

使用state#

image-20230903203306873

加载

访问

image-20230903203243817

mitaion#

改变存储data

定义:

image-20230903203456891

使用:

image-20230903203658206

getter#

image-20230903213954676

Action#

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

让我们来注册一个简单的 action:

const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

pinia#

Store 是什么?#

Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念stategetteraction,我们可以假设这些概念相当于组件中的 datacomputedmethods

定义 Store#

在深入研究核心概念之前,我们得知道 Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字:

import { defineStore } from 'pinia'

// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {
  // 其他配置...
})

这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use... 是一个符合组合式函数风格的约定。

defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。

Option Store#

与 Vue 的选项式 API 类似,我们也可以传入一个带有 stateactionsgetters 属性的 Option 对象

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。

为方便上手使用,Option Store 应尽可能直观简单。

Setup Store #

也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。

js

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

Setup Store 中:

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

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

使用 Store #

虽然我们前面定义了一个 store,但在我们使用 <script setup> 调用 useStore()(或者使用 setup() 函数,像所有的组件那样) 之前,store 实例是不会被创建的:

vue

<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>

你可以定义任意多的 store,但为了让使用 pinia 的益处最大化(比如允许构建工具自动进行代码分割以及 TypeScript 推断),你应该在不同的文件中去定义 store

如果你还不会使用 setup 组件,你也可以通过映射辅助函数来使用 Pinia

State#

在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。

js

import { defineStore } from 'pinia'

const useStore = defineStore('storeId', {
  // 为了完整类型推理,推荐使用箭头函数
  state: () => {
    return {
      // 所有这些属性都将自动推断出它们的类型
      count: 0,
      name: 'Eduardo',
      isAdmin: true,
      items: [],
      hasChanged: true,
    }
  },
})

image-20230903170316203

image-20230903170446611

Getter#

Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数:

js

export const useStore = defineStore('main', {
  state: () => ({
    count: 0,
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
})

大多数时候,getter 仅依赖 state,不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 this 访问到整个 store 实例但(在 TypeScript 中)必须定义返回类型。这是为了避免 TypeScript 的已知缺陷,不过这不影响用箭头函数定义的 getter,也不会影响不使用 this 的 getter

Vue3加载Element-plus#

Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库

Element Plus 基于 Vue 3,面向设计师和开发者的组件库

安装Element-Plus#

npm install element-plus --save

完整引用#

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

import { createApp } from 'vue'
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')

按需导入#

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

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

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

然后修改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()]
      })
    ]
  }
})

最后,可以直接在组件中使用

<template>
	<el-button>Default</el-button>
	<el-button type="primary">Primary</el-button>
</template>

Vue3加载Element-plus的字体图标#

Element-plus不仅仅是提供了各种组件,同时还提供了一整套的字体图标方便开发者使用

安装icons字体图标#

npm install @element-plus/icons-vue

全局注册#

在项目根目录下,创建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";
app.use(elementIcon)

使用方式#

接下来就可以直接在组件中引入使用了

<el-icon class="expand" color="#409EFC" :size="30">
    <expand />
</el-icon>

作者:Esofar

出处:https://www.cnblogs.com/firsthelloworld/p/17709871.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   我不想学编丿程  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示