Vue2&Vue3学习日常

学习vue的日常啦~~

Vue2

首先引入

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>

1. 主要结构

<script>
let v = new Vue({
    el: '#root', // 第一种关联方法
    data: { // 第一种写法对象式
        message:'hello,world'
        }
    data(){// 第二种函数式子 this是实例对象,不可以用箭头函数
        console.log(this)
        return {
            message:''
        }
    }
    methods: { // 在methods里写方法
        click1(){
            alert(1)
        },
    },
    computed:{
        计算属性名(){
            基于现有数据编写求职逻辑
            return 结果
        }
    }
     created() { // 生命周期函数直接写进去
        console.log('created')
    },
    updated() {
        console.log('updated')

    },
});
v.$mount('#root') // 另一种方式绑定, 第二种关联方法(挂在上面)
</script>

补充:

  1. export default
// 导出
export const router = new VueRouter({ ... });
export default router; // 用default在一个模块中只能用一次,并且导入的时候不需要大括号
// 导入
import router from './router'; // 导入的时候不需要大括号
  1. export
// 导出
export const router = new VueRouter({ ... }) 
export { router }; // 两者都可以,export可以多次使用
// 导入
import { router } from './router'; //导入的时候需要大括号

2. 基本语法

2.1 插值语法

用于解析标签体内容,写法:{{}} 两个大括号内的东西是js表达式(可得到返回值)!不是js语句,可以直接读取到data中的所有属性

<div class="fr">
    {{message}} // 
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14">
</script>
<script>
    let v = new Vue({ //必须实例化,有配置对象
        el:'.fr', // 第一种关联方法
        data: { // 第一种写法对象式
            message:'hello,world'
        }
        data(){ // 第二种函数式子 this是实例对象,不可以用箭头函数
            return {
                message:'hello,world'
            }
        }
    	methods: {
            click1(){
                alert(1)
            },
        },
    });
    v.$mount('.fr') // 第二种关联方法(挂在上面)
</script> // 容器与实例一一对应

2.2 指令语法

用于解析标签,同样是需要些表达式

* v-html

动态的修改innerhtml

<div id="root" v-html="msg">
</div>
<script>
    let v = new Vue({ //必须实例化,有配置对象
        el:'#root', // 第一种关联方法
        data:{
            msg: 'dadas'
        }
    });
</script> 
* v-bind

将Vue实例中的数据和方法与HTML元素的属性进行绑定,

<div class="fr">
    <a v-bind:href="href.toUpperCase()">悬浮</a>
    <a :href="href">悬浮</a> //  v-bind可简写为:
</div>
<script>
   let v = new Vue({
        el:'.fr',
        data:{
            href:'https://www.baidu.com/',
        }
   })
</script>

v-bind对于样式控制增强

  1. :class=“{类名1:布尔值,类名2:布尔值}” 适用于一个类名来回切换
  2. :class=“[类名1,类名2]” 适用于批量添加或删除类
  3. :style=“{CSS属性名1:CSS属性值,CSS属性名2:CSS属性值}”

补:在js中background-color这种带横杠的属性名不支持,有两种写法,一个是加引号‘background-color’,另一个是驼峰写法backgroundColor

* v-model

将表单输入框的内容同步给 JavaScript 中相应的变量,双向绑定

<div id="fr">
输入:<input type="text"  v-model="message" > {{message}}
输入:<input type="text" v-model:value="message" > {{message}}   
</div> // 由于v-model:value本身就是为了获取值,可以缩写为v-model
<script>
   let v = new Vue({
        el: '#fr',
        data:{
            message:'1'
        }
   });
</script>
<div id="fr">
性别:
<input type="radio" name="sex" v-model="message" value="男">男
<input type="radio" name="sex" v-model="message" value="女">女
<br>
我选择的是:{{message}}
</div>
<script>
   let v = new Vue({
        el: '#fr',
        data:{
            message:''
        }
   });
</script>

不仅是输入框,复选框,文本域,下拉菜单都可以,但都要设定value值,本质是在获取value

* v-on

事件处理

<button @mouseover="count--" @mouseleave="count--">-</button>
       <span>{{count}}</span>
<button @mouseenter="count++" @mouseleave="count++">+</button> // 也可以直接写内联语句在里面
<div class="root">
    <button v-on:click="click1(66)">点击1</button> // 第一种写法 66 如果只写click1函数则默认有一个点击事件
    <button @click="click2($event,5)">点击2</button> // 第二种写法v-on->@  同时$event为点击事件的占位符
	<button @click="clk(flag)">点击</button>
    <h1 v-show="flag">lzd</h1>
</div>
<script>
   let v = new Vue({ 
        el:'#root', 
        data:{
            flag:true
        },
        methods: {
            click1(num){
                alert(num)
            },
            click2(e,num){
                console.log(e.target.innerHTML)
                console.log(num)
            },
            clk:function(flag){
                this.flag = !this.flag
            }
        },
    });
</script>
* v-if

条件渲染,本质上是在创建和销毁

<div class="fr">
    <p v-if="ok">yes</p>
    <p v-else>no</p>
</div>
<script>
   let v = new Vue({
        el:'.fr',
        data:{
            ok:false
        }
   })
</script>

else if

<div class="fr">
    <p v-if="type==='a'">a</p>
    <p v-else-if="type==='b'">b</p>
    <p v-else-if="type==='c'">c</p>
    <p v-else>other</p>
</div>
<script>
   let v = new Vue({
        el:'.fr',
        data:{
            type:'d'
        }
   })
</script>
* v-show

与v-if基本一直但是本质上修改display: none;

* v-for

一定要绑定上key,并且要用唯一id,不能用index下标值,否则会出现奇怪bug

<div class="fr">
    <ul>
        <li v-for="ele in fruits" :key="ele.id">
            <span>{{ele.name}}</span>
            <span>{{ele.author}}</span>
            <button @click="clk(ele.id)">删除</button>
        </li>
    </ul>
</div>
<script>
   let v = new Vue({
        el:'.fr',
        data:{
            flag:true,
            fruits:[
                {id:1,name:'《红楼梦》',author:'曹雪芹'},
                {id:2,name:'《西游记》',author:'吴承恩'},
                {id:3,name:'《水浒传》',author:'施耐庵'},
                {id:4,name:'《三国演义》',author:'罗贯中'}
            ]
        },
        methods: {
            clk:function(id){
                this.fruits = this.fruits.filter(ele=>ele.id!==id)
            }
        },
   })
</script>

2.3 指令的修饰符

  1. 按键修饰符:@keyup.enter指键盘回车监听
  2. v-model修饰符:v-model.trim 指去除首尾空格 v-model.number 转化为数字
  3. 事件修饰符:@事件名.stop 阻止冒泡 @事件名.prevent 阻止默认行为

3. computed计算属性

性能更佳,有缓存机制,除非现有数据改变会存下数据,在多次使用时比methods性能更优,不需要声明在data对象中

普通写法

computed:{
    计算属性名(){
        基于现有数据编写求职逻辑
        return 结果
    }
}
这是一个属性,在调用时只需要{{计算属性名}}即可

完整写法,当直接修改绑定的值时才这样写,一般用不到

<div class="root">
    姓:<input type="text" name="" v-model="fr">
    名:<input type="text" name="" v-model="se">
    ={{res}}<br>
    <button @click="change">change</button>
</div>
<script>
    let vm = new Vue({
        el:'.root',
        data:{
            fr:'',
            se:'',
        },
        methods: {
            change:function(){
                this.res = '常雨轩'
            }
        },
        computed:{
            res:{ 
                get(){  // 可以接收值
                    return this.fr+this.se
                },
                set(value){ // 直接修改时则需要set
                    this.fr = value.slice(0,1)
                    this.se = value.slice(1)
                }
            }
        }
    })
</script>

4. watch监听

如果原值发生变化则会触发,

 <div class="root">
    <input type="text" v-model="obj.fr">
    ={{obj.fr}}<br>
</div>
<script>
    let btn = document.querySelector('button')
    let vm = new Vue({
        el:'.root',
        data:{
            obj:{
                fr:''
            }
        },
        watch:{ // 
            'obj.fr'(newv,oldv){ // 或者写成objFr
                console.log(newv,oldv)
            }
        }
    })
</script>

当在data中定义简单的数据属性不需要引号,Vue 默认将其作为响应式数据处理;定义对象时访问嵌套属性要使用字符串形式来表示属性路径,Vue 2.x 默认使用的是 Object.defineProperty 来实现响应式系统,它要求属性名必须是一个字符串。

完整的写法:

<div class="root">
    <select v-model="obj.lang">
        <option value="意大利">意大利</option>
        <option value="英语">英语</option>
        <option value="汉语">汉语</option>
    </select>
    <input type="text" v-model="obj.fr">
    ={{ans}}<br>
</div>
<script>
    let vm = new Vue({
        el:'.root',
        data:{
            obj:{
                fr:'adw',
                lang:'意大利'
            },
            ans:''
        },
        watch:{ 
            obj:{ // 以对象作为监视对象
                deep:true, // 表示是否深度监视,当出现多个对象的时候则需要深度监视
                immediate:true, // 是否在页面刷新时立即执行一次
                handler(newv){
                    clearTimeout(this.time)
                    this.time = setTimeout(()=>{
                        axios({
                            url:'https://applet-base-api-t.itheima.net/api/translate',
                            params:newv
                        }).then(result=>{
                            let res = result.data.data
                            this.ans = res
                        })
                    },300)
                }
            }
        }
    })
</script>

5. 生命周期函数

生命周期函数

生命周期函数直接写入vue对象,首先是created,mounted,是固定的,结束后就是update在频繁更新,最后是destroy

<div class="root">
    <button @click="change">{{res}}</button>
</div>
<script>
    let vm = new Vue({
        el:'.root',
        data:{
            res:''
        },
        methods: {
            change:function(){
                this.res = '常雨轩'
            }
        },
        created() {
            console.log('created')
        },
        updated() {
            console.log('updated')

        },
    })
</script>

实际应用:

let vm = new Vue({
    el:'.root',
    data:{
        res:'',
        list:[]
    },
    created() { // 在网页初始时请求数据
      let xhr = new XMLHttpRequest()
      xhr.open('get','http://hmajax.itheima.net/api/news')
      xhr.addEventListener('loadend',()=>{
        console.log(JSON.parse(xhr.response));
      })
      xhr.send()
     },
    mounted() { // 在dom元素加载好后便在输入框自动获取焦点
        document.querySelector('[name=text]').focus()
    }
})

6 工程化开发&脚手架

首先配置环境变量

npm install -g @vue/cli

查看版本号

vue --version

创建项目

vue create project-name

启动

npm run serve

主要包含

项目运行流程

* 组件(自定义元素)

局部注册:需要在component文件下写组件,并且在App.vue导入

<template>
    <div class="root">
        <PageHeader></PageHeader> // 以标签的形式写入
        <PageBody></PageBody>
    </div>
</template>
<script>
import PageHeader from './components/PageHeader.vue' // 导入组件
import PageBody from './components/PageBody.vue'
export default{
    components:{ // 加入组件
        PageHeader,
        PageBody
    }
}
</script>

全局注册:需要在component文件下写组件,并且在main.js导入

import PageButton from './components/PageButton.vue'
Vue.component('PageButton',PageButton)

注意:两者分别是components和component,区分开

7. 样式冲突

scoped:本质上是给不用的组件内的元素加了一个哈希值,data-v-hash,并且自动加了div[data-v-hash]属性选择器,以防止不同组件之间的互相影响:

8. data函数

在脚手架下写data需要将其作为一个函数,每个实例可以维护一份被返回对象的独立的拷贝,如果 data 是一个对象,则它的引用会在所有组件实例之间共享,这会导致如果一个组件实例更改了这个对象,所有其他组件实例的状态也会受到影响,从而导致数据污染。

data(){
	return{
	 count:999
	}
}

9. 组件通信

9.1 父子关系

  • 传入data用props,

父传子,在组件上加入对象

<MyButton :title="mytitle"></MyButton> // 传对象

子接收

export default {
    props:['title'],// 获取对象
}
  • 传出修改父亲data,用$emit( event(自定义事件), arg ),允许多参数传入

子:

<button @click="change('cyx')">改变</button>
export default {
methods: {
    change(name){
        this.$emit('changetitle',name)
    }
} }

父:

<MyButton :title="mytitle" @changetitle="changet"></MyButton>
export default {
    methods: {
    changet(newt){
      this.mytitle = newt
    }
}
* props

props是组件上的自定义属性,用于向子组件传递信息,可以传递任意数量任意类型的属性。子不能直接修改爹的数据。

简朴写法:

props:{
    title:String, // 校验的属性名:类型,如Number String Boolean
}

复杂写法(写成对象来校验):

props:{
    title:{
        type: String, // 类型
        required: true, // 是否必填
        default: 'lzd', // 默认值
        default(){
            return {}
        } // 如果是对象就return回去
        validator(value){ // 自定义校验,返回true或false
            return ...
        }    
    },
},

9.2 兄弟关系

简易消息传递Event bus总线(复杂场景–>Vuex)

  • 首先创建一个vue空实例(在util功能目录下)
import Vue from 'vue' // 导入包
const Bus = new Vue() // 创建空实例
export default Bus // 导出包
  • 给要修改的组件内添加监听这个空实例(订阅消息)
import Bus from '../utils/EventBus.js'
export default {
    created(){
        Bus.$on('sendMsg',(msg)=>{
            this.msg = msg
        })
    },
	...
}
  • 添加发送触发事件,传参
import Bus from '../utils/EventBus.js'
export default {
   methods: {
    changeMsg(){
        Bus.$emit('sendMsg','快来找我玩ssssssss')
    }
   },
}

9.3 provide-inject方法

实现跨层级共享数据(当我们想让一个父级下的所有子孙都使用这些属性)

  • 在父级填写
export default {
provide(){
    return{
      color:this.color, // 普通类型非响应式
      object:this.object // 复杂类型响应式(推荐)
    }
}	}
  • 在子孙直接使用
export default {
    ...,
    inject:['color','object'],
}

9.4 v-model(固定传值value)

原理:v-model本质就是v-bind绑定后,通过事件监听封装后的双向绑定

我们可以借此来进行子组件和父组件的双向绑定

  • 父级
<MyB :msg="msg" @click="msg=$event"></MyB> 
<MyB :msg="msg" @cg="msg=$event"></MyB> // 两个含义相同
  • 子级
<button @click="changeMsg">改变{{msg}}</button>
export default {
    props:['msg'],
    methods: {
        changeMsg(){
            this.$emit('cg','lzd')
        },
    },
    ...
}

(目前形参只能传一个适用)

简单写法有限制需要监听事件是input,因为v-model默认会监听组件触发的input事件,并将传递给该事件的数据作为新值赋给绑定的属性。这是Vue.js的约定。

  • 父级
<MyB v-model="msg"></MyB>
  • 子级
export default {
    props:['msg'],
    methods: {
        changeMsg(){
            this.$emit('input', 'lzd'); // 使用input事件
        },
    },
}

9.5 sync修饰符(非固定value)

  • 父级
<MyB :msg="msg" @update:msg="msg=$event"></MyB>
<MyB :msg="msg" @update:msg="newm=>msg=newm"></MyB>
<MyB :msg.sync="msg"></MyB> // 三个含义相同
  • 子级
export default {
    props:['msg'],
    methods: {
        changeMsg(){
            this.$emit('update:msg', 'lzd'); // 使用input事件
        },
    },
}

10. ref&&refs

可以用于获取dom元素或者组件实例

  1. 目标组件添加ref属性
<MyB ref="My"></MyB>
<button @click="clk">输出</button>
  1. 恰当时机,通过调用this.$refs.ref属性名,获取目标组件,可以调用组件对象里面的方法
export default {
  ...,
  methods: {
    clk(){
      this.$refs.My.changeMsg()
    }
  },
}

同样可以取代**document.querySelector()**直接用this.$refs.ref属性名得到dom元素

11. $nextTick函数

当我们要让输入框显示的时候实际上它在队列里等待更新,不会立即渲染出来,所以常规下接this. r e f s . i n p . f o c u s ( ) ∗ ∗ 并不会获取焦点,这是因为异步更新。但是我们可以让他等一下异步更新则用 ∗ ∗ refs.inp.focus()**并不会获取焦点,这是因为异步更新。但是我们可以让他等一下异步更新则用** refs.inp.focus()并不会获取焦点,这是因为异步更新。但是我们可以让他等一下异步更新则用nextTick

<button v-show="isshow" ref="ref" @click="clk"> 输出 </button>
<input type="text" v-show="!isshow" ref="inp"> 
export default {
  data(){
    return{
      isshow:true,
    }
  },
  methods: {
    clk(){
      this.isshow = !this.isshow
      this.$nextTick(()=>{ // $nextTick
        console.log(this)
        this.$refs.inp.focus()
      })
    }
  },
}

12. 自定义指令

12.1 全局注册

main.js中全局注册,

Vue.directive('focus',{
  inserted(el){
    el.focus()
  }
})

12.2 局部注册

在组件中注册

export default {
  ...,
  directives:{
    focus:{
      inserted(el){
        el.focus()
      }
    }
  }
}
  • 当值修改的时候,仅仅是inserted无法更新视图,需要update
export default {
  data() {
    return {
      color1:'red',
      color2:'blue'
    }
  },
  directives:{
    color:{
      // 仅仅是提供元素被添加到页面中时的逻辑
      inserted(el,binding){
        el.style.color = binding.value
      },
      // update 提供当值变化后,dom更新的逻辑
      update (el,binding) {
        console.log('修改!')
        el.style.color = binding.value
      },
    }
  }
}

13. 插槽

  • 子组件内用slot占位,有时候并不需要在插槽中填值,我们可以在slot中填入内容将作为默认值
<template>
    <div class="Son1">
        我是头
    <slot name="head" num="10" msg="测试文本">我是头和中间</slot> 传值时在slot里添加,像是在添加属性名一样
        我是中间
        <slot name="foot">我是中间和尾巴</slot> // 当有多个slot时用name属性区分
        我是尾巴
    </div>
</template>
  • 父组件就可以直接使用
<template>
  <div id="app">
    <MySon1>
         <template #head="content"> // 对应占位名字,直接可以获得内容,content是形参,随便定义,一个slot时为default
          <div class="head" >{{content.num}}</div> 
         </template>
         <template #foot> 
          <div class="foot">foot</div>
         </template>
    </MySon1>
  </div>
</template>

组件之间需要传值

14. Router路由(Vue2)

14.1 路由基础

路径和组件的映射关系

* 基础步骤

首先在项目里配置,vue3将用router4版本

npm add vue-router@3.6.5

然后在main.js里配置插件

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router' // 引入
Vue.config.productionTip = false
Vue.use(VueRouter) // 安装注册
const router = new VueRouter() // 创建路由对象
new Vue({
  render: h => h(App),
  router // 注入建立关联
}).$mount('#app')

url出现/#/则表示成功

* 核心步骤

创建views目录在里面添加路由组件,然后再main.js中配规则

import VueRouter from 'vue-router'
import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'
Vue.config.productionTip = false
Vue.use(VueRouter)
const router = new VueRouter({
  routes:[ //路由规则们
    {path:'/find',component: Find}, // route一条规则有路径path和组件component
    {path:'/My',component: My},
    {path:'/Friend',component: Friend},
  ]
})

准备导航链接,配置路由出口(匹配的组件展示的位置)

在需要切换的组件页面类似于:

<template>
  <div>
    <div class="footer_wrap">
      <a href="#/find">发现音乐</a> <!-- 配置路径 -->
      <a href="#/my">我的音乐</a>  <!-- 配置路径 -->
      <a href="#/friend">朋友</a>  <!-- 配置路径 -->
    </div>
    <div class="top">
       <!-- 路由出口,想在哪里显示就放在哪里 -->
        <router-view></router-view>
    </div>
  </div>
</template>

组件分类问题

  • src/views:页面组件 – 页面展示 – 配合路由用
  • src/components:复用组件 – 展示数据 – 常用于复用

14.2 路由进阶

为了便于维护,新建一个router文件夹里存index.js,同时可以使main.js变得更加干净整洁,只需要导入就行

  • router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
import Find from '../views/Find.vue'
import My from '../views/My.vue'
import Friend from '../views/Friend.vue'
Vue.config.productionTip = false
Vue.use(VueRouter)
export const router = new VueRouter({
  routes:[
    {path:'/find',component: Find},
    {path:'/My',component: My},
    {path:'/Friend',component: Friend},
  ]
})
// export { router }
// export default router
  • main.js
import Vue from 'vue'
import App from './App.vue'
import {router} from './router/index.js'
new Vue({
  render: h => h(App),
  router
}).$mount('#app')
* router-link声明式导航

<router-link to=“”>来代替a标签,用to来接收路径并且可以省去#,最大优点是自动添加一个激活类名,可以帮助高亮

<template>
  <div>
    <div class="footer_wrap">
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/friend">朋友</router-link>
    </div>
  </div>
</template>
<style>
.footer_wrap a.router-link-exact-active{
  background-color: #654ba4;
} // router-link-exact-active 自动给一个类
</style>

有两个类名:

  1. router-link-exact-active:精确匹配,/find会高亮,在二级嵌套时/find/one则不会高亮

  2. router-link-active:模糊匹配,/find会高亮,在二级嵌套时/find/one也会高亮

  3. 当然我们可以自定义

const router = new VueRouter({
    routes:[...],
    linkActiveClass:'active', // 自定义模糊
    linkExactActiveClass:'exactactive' // 自定义精确
})
* 嵌套路由
  1. 编写News的子路由:Detail.vue

  2. 配置路由规则,使用children配置项:

    const router = createRouter({
      history:createWebHistory(),
    	routes:[
    		{
    			name:'xinwen',
    			path:'/news',
    			component:News,
    			children:[
    				{
    					name:'xiang',
    					path:'detail',
    					component:Detail
    				}
    			]
    		},
            ....,
    	]
    })
    export default router
    
* 声明式导航,传参
  1. 查询参数传参(适合多个参数传参):在链接后面加查询参数,
  • 语法格式
<router-link to="/friend?key=我的朋友">我的朋友</router-link>
  • 查询:$route.query.参数名
<span>{{$route.query.key}}</span> // 直接用
export default {
    name:'FindMusic',
    mounted() {
        console.log(this.$route.query.key) // js中需要用this
    },
}
  1. 动态路由传参(适合单个参数传参):是必选的,如果不传就不会匹配到,可以选择在跳转路由后面加空格/ ,也可以在**/:参数名?**后面加?
  • 配置路由:设置为**/:words**
new VueRouter({
    routes:[
        {path:'/find/:key',component: Find},
    ],
    ...
})
export { router }
  • 跳转语法格式,直接写/
<router-link to="/find/发现音乐">发现音乐</router-link>
  • 查询:$route.params.参数名
<span>{{$route.params.key}}</span>
export default {
    name:'FindMusic',
    mounted() {
        console.log(this.$route.params.key)
    },
}
* 重定向

当一开始进入页面时也有可能是空的,需要自动跳转到某一页,可以使用重定向redirect,配置路由

{path:'/',redirect:'/find/我的发现'},
* 404

在所有的路径后配这个,在搜索不到时会有这个页面

{path:'*',component: NotFind},
* 模式设置

hash路由(默认):http://localhost:8080/#/find

history路由(常用):http://localhost:8080/find

在配置中:

new VueRouter({
    routes:[...],
    mode:'history'
})
* 路由跳转方式(router!!!)
  1. 路径跳转
export default {
  methods: {
    goother(){ // 添加点击事件
      this.$router.push('/my/我的音乐') //跳向对应的路由,在此页面不可重复跳转
      this.$router.push({ // 查询参数传参
          url:'/my/我的音乐' 
          query:{
          参数1:value1,
          参数2:value2,
     	 }
      }) 
    	this.$router.push('/my/${value1}/${value2}')  // 动态传参
    }  
  },
};
  1. 名字跳转(适合路径名长的场景):
  • 添加名字
{name:'hahah',path:'/find/:key?',component: Find},
  • 跳转
export default {
  methods: {
    goother(){ // 添加点击事件
      this.$router.push({ // 查询参数传参
          name:'hahah' 
          query:{
          参数1:value1,
          参数2:value2,
     	 }
      })  
      this.$router.push({ // 查询参数传参
          name:'hahah' 
          params:{
          参数1:value1,
          参数2:value2,
     	 }
      }) 
    }  
  },
};
* 路由守卫
  • 全局
  1. beforeEach(全局前置):每一个路由跳转前都需要经过这个,当我们需要满足某些情况时才访问这个路由,比如对应的学校才能访问这个路由时需要。
router.beforeEach((to, from, next) => { // to是到哪个路由,to是来自哪个路由,next()函数表示通行,满足条件则通行
    console.log(to,from)
    if(from.path === '/'){
        next()
    }
})

补充:我们也可以给每个路由meta属性里添加对象,用来判断这个路由是不是需要beforeEach

const router = new Router({
    routes: [{..., meta:{istrue:true}  }]   
})
  1. afterEach没有next,直接跳转走了,用处不多,例如只有在跳转后才改变document.title时适用这个
  • 独享

前置独享守卫:beforeEnter;没有后置独享守卫

  • 组件

前置组件守卫:beforeRouteEnter,后置组件守卫beforeRouteLeave

* 路由缓存(vue3)

基本使用,会保留原来的数据:

<KeepAlive>
  <component :is="activeComponent" />
</KeepAlive>

如果想要条件性地被 KeepAlive`缓存,就必须显式声明一个 name 选项。

<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b">
  <component :is="view" />
</KeepAlive>
<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
  <component :is="view" />
</KeepAlive>

15. Vuex状态管理

受不了了真勾吧多,建议直接去看最下面的pinia



Vue3(组合式api)

更容易维护,更快速度,更小体积,更优数据响应

1. 创建项目

npm init vue@latest

pnmp create vue

2. template小操作

比如这种操作,为了防止ulli布局打乱,可以使用template,在循环之后template并不会显示会消失

<ul>
    <template v-for="item in items">
        <li>{{item.msg}}</li>
        <li class="box">{{item.msg}}</li>
    </template>
</ul>

3. Set up

也是生命周期函数,在beforeCreate钩子之前自动执行,也要定义函数setup(),然后以对象方式return数据从而使其能被调用,但是太麻烦了这样

所以我们语法糖封装更好使用,之后则不能使用this因为时间线太早还未创建Vue对象

<script setup> </script>

4. ref&reactive

4.1 生成响应式数据

在vue中本身都不是响应式的,需要生成响应式数据

  1. ref函数可以返回简单或复杂类型的数据
<script setup>
import {ref} from 'vue'
let obj = ref({
    count:100,
    age:10
}) // 返回的是一个响应式对象,会在外面“包一层”,所以脚本里要用count.value获取值
let getc = () => {  obj.value.count++  }
</script>
<template>
<div class="root">
  {{count}} // 注意在模板中自动脱一层,可以直接写count
  <button @click="getc">佳佳</button>
</div>
</template>
  1. reactive函数只可以用于返回复杂类型
let obj = reactive({
    count:100,
    age:10
})
let getc = () => { obj.count++ }// 不需要写成obj.value.count

推荐使用ref,既可以用于简单也可以用于复杂类型

4.2 defineExpose借用ref

子属性暴露给父的元素

  • 子,要调用defineExpose来让自己的属性暴露,才能让父访问到这个数据
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
  a,
  b
})
</script>
  • 父,因为setup时间非常早,所以要在mounted后使用
<script setup>
import Son from '@/components/Son.vue'
let son = ref(null) // 要先声明
onMounted(() => { console.log(son.value.a) })  // 用子元素的暴露的属性
</script>
<template>
<div class="root">
  <Son ref="son"></Son>{{cnt}} // 要给传进来的组件一个ref变量值,才能用子元素的暴露的属性
</div>
</template>

5. computed计算属性

导入后改写法

import {computed} from 'vue'
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

6. watch监听

简朴写法,直接传不需要.value,就是传ref对象的

watch([num,name],([newnum,newname],[oldnum,oldname])=>{ // 也可以写成 (newn,oldn)  [1, 'lzd'] ,[0, 'lzd']
  console.log(newnum,oldnum) // 新
  console.log(newname,oldname) // 旧  分开输出
})

复杂写法

let obj = ref({ num:0, name:'lzd'})
watch(()=>obj.value.name,(newn,oldn)=>{ // 如果只想监视对象里的某个值需要这样写return那个值
  console.log(newn,oldn)
},{ // 再加个括号里写属性
  deep:true,
  immediate:true
})

7. 生命周期函数

组合式API的生命周期函数:setuponBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted(销毁Unmounted)

8. 组件通信

组合式API下,不需要写components就可以直接调用

import Son from '@/components/Son.vue'

8.1 父子关系

  1. 父传子
  • 父亲不变
  • 子需要用defineProps(不需要导入)编译器宏,
<script setup>
const props = defineProps({
    name:String
})
console.log(props.name) // 在脚本中应该这样写
</script>
<template>
    <div>
        儿子:{{name}} // 模板中不需要写props
    </div>
</template>
  1. 子传父
  • 父不变
const change1 = (newn) => { obj.value.name = newn }
const change2 = (newn) => { obj.value.name = newn }
<template>
<div class="root">
  父亲:{{obj.name}}
  <Son :name="obj.name" @change1="change1" 
  @change2="change2"></Son>
</div>
</template>
  • 子,需要用到:defineEmits(不需要导入)声明
<script setup>
const emit = defineEmits(['change1','change2'])
const change1 = () => { emit('change1','lzd') }
const change2 = () => { emit('change2','hhh') }
</script>
<template>
<div> 儿子:{{name}}
    <button @click="change1">change1</button>
    <button @click="change2">change2</button>
</div> </template>

8.2 provide-inject方法

  1. 爷传孙
  • 爷用provide(‘key‘,顶层数据)一次只能提供一个数据
let cnt = ref(10)
provide(
  'cnt',cnt
)
  • 子,key值必须相同
let num = inject('cnt')
console.log(num.value)
  1. 孙传爷
  • 爷用provide(‘key‘,顶层数据)传递一个函数
provide('changecnt',(newcnt)=>{ cnt.value = newcnt })
  • 子用爷的函数改爷的数据,实际控制权还是爷
let changenum = inject('changecnt')
const change2 = () => {
    console.log(num.value)
    changenum(1000) 
    console.log(num.value) }

9. defineOptions

用了

defineOptions({ name:'registerindex' })

10. v-model

仅使用一次在一个组件内

<Son v-model="cnt" ></Son>{{cnt}}
  • 子,引入defineModel,补上代码后,则可以任意修改
    ”这个元素“
let model = defineModel()

11. Pinia状态管理

手动添加时: npm install pinia,然后再main.js文件中加

import {createPinia} from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app') // 将其挂载上去

都需要先新建一个store文件夹管理,导入import {defineStore} from ‘pinia’,并且暴露所有的内容(return)

11.1 同步管理

import {ref,computed} from 'vue'
import {defineStore} from 'pinia'
export const useCounterStore = defineStore('counter',()=>{ //(仓库的唯一表示不可乱写,()=>{})
    const cnt = ref(10)
    // 声明操作数据的方法action函数
    const addcnt = () => cnt.value++
    const subcnt = () => cnt.value--
    // 声明基于数据派生的计算属性 getters(computed)
    const double = computed(() => cnt.value * 2)
    return{
        cnt,
        subcnt,
        addcnt,
        double
    }
})

在App.vue中需要引入

<script setup>
import Son1 from '@/components/Son1.vue'
import {useCounterStore} from '@/store/counter.js'
const counterStore = useCounterStore()
</script>
<template>
  <div>
    我是父亲:{{counterStore.cnt}}
    <Son1></Son1>
  </div>
</template>

组件Son1.vue中需要引入

<script setup>
import {useCounterStore} from '@/store/counter.js'
const counterStore = useCounterStore()
</script>
<template>
  <div>
    我是Son1:{{counterStore.cnt}} - {{counterStore.double}}
    <button @click="counterStore.addcnt">+</button>
    <button @click="counterStore.subcnt">-</button>
</div>
</template>

11.2 异步操作(action也接受异步)

  • pinia中
import {defineStore} from 'pinia'
import axios from 'axios'
import {ref} from 'vue'
export const useChannelStore = defineStore('channel',()=>{
    let list = ref([])
    let getList = async () => {
        const {data:{data}} = await axios.get('http://geek.itheima.net/v1_0/channels')
        list.value = data.channels
        console.log(list.value)
    }
    return{
        list,
        getList
    }
})
  • App.vue中
<script setup>
import Son1 from '@/components/Son1.vue'
import {useChannelStore} from '@/store/channel.js'
const channelStore = useChannelStore()
</script>
<template>
  <div>
    我是父亲:{{counterStore.cnt}}
    <Son1></Son1>
     <button @click="channelStore.getList">得到属性</button>
     <ul>
      <li v-for="(item,index) in channelStore.list" :key="item.id">{{item.name}}-{{index}}</li>
     </ul>
  </div>
</template>

注意:虽然useChannelStore这些都是对象,但是不能直接解构,会丢失响应性,这就好像是对props解构,代表仅仅是声明了值。并且store是用reactive包装的对象,不需要在getters后面写value

所以我们需要使用storeToRefs借助解构来添加响应式的引用来包装,而函数不需要storeToRefs

import {storeToRefs} from 'pinia'
import {useCounterStore} from '@/store/counter.js'
const counterStore = useCounterStore()
let {cnt} = storeToRefs(counterStore)

(两种写法取决于实际场景)

12. pinia-plugin-persistedstate持久化插件!!!

先安装到本地

npm i pinia-plugin-persistedstate

再配置到main.js里配置全局

import { createApp } from 'vue'
import {createPinia} from 'pinia'
import App from './App.vue'
import persist from 'pinia-plugin-persistedstate' // 插件
const pinia = createPinia()
createApp(App).use(pinia.use(persist)).mount('#app')

基础在需要store文件夹里需要持久化的数据里,在下一项里写

defineStore('counter',() => { ..., }, { persist:true // 开启持久化 })

可以补充

defineStore('counter',() => { ..., }, { persist:{
    key:'cyx', // 改变存储名字key
    paths:['cnt'] // 表示需要存储的值
	}
}) // 默认存储到localstorage

还有很多可以到官网看https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/

13. unplugin-auto-import自动导入插件

安装

pnpm install unplugin-auto-import

然后在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'; // 自动导入插件

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({ // 自动导入插件
      imports:[
        'vue', //导入vue
      ]
    })  
  ],
})
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值